所谓代码审计是一种以发现程序错误,安全漏洞和违反程序规范为目标的源代码分析。在安全领域,为了发现安全问题,常通过黑盒测试、白盒测试方法来尽可能的发现业务程序中的安全问题,代码审计就是白盒测试的常用方法,相较于黑盒测试,由于白盒测试能接触到源代码,可以更加详细的理解业务程序逻辑,也能更全面的发现安全风险。接下来本系列文章将以php代码审计为切入点,过程中结合常见源代码扫描工具和动态调试方法,来讲解php代码审计的常见漏洞点和分析方法。本章节以安洵杯Web题目easy_serialize_php为审计对象,相关源代码网上也都有公开。
可以看到seay发现了任意文件读取和变量覆盖问题,rips发现了变量覆盖问题,而fortify基本上是一些信息泄漏、配置问题及变量覆盖问题,并把变量覆盖问题划分为中危等级。
接下来看下源代码
大致的逻辑是根据请求的function值,如果function是show_image,则会读取该文件内容,并打印出内容。CTF的题目最终是要获取flag的内容,所以猜测这里的问题应该是出在读取的问题上。
现在来看一下靶机的网页
点进去就能看到源码
默认f的参数就是highlight_file,对应源码里的代码逻辑就是
if(!$function){
echo '<a href="index.php?f=highlight_file">source_code</a>';
}
这里输入phpinfo查看是否有敏感信息
一般主要查看fopen、disable_、root类似字符,找到有文件名为d0g3_f1ag.php
到这里2个判断逻辑都走过了,初步判断这里需要通过show_image逻辑,读取d0g3_f1ag.php文件。根据file_get_contents()函数的逻辑,从后往前看,主要逻辑如下。
$serialize_info = filter(serialize($_SESSION));
$userinfo = unserialize($serialize_info);
echo file_get_contents(base64_decode($userinfo['img']));
如果想要读取d0g3_f1ag.php文件,就需要保证base64_decode($userinfo['img'])= d0g3_f1ag.php。
然后继续跟踪,需要
$userinfo['img'] = ZDBnM19mMWFnLnBocA==,即d0g3_f1ag.php的64位编码。
而$userinfo是反序列化得到,因此这里必定要存在
所以就要重点关注$serialize_info的生成过程。
filter(serialize($_SESSION));
filter是一个过滤函数,用于将php/flag/php5/php4/fl1g这几个字符,替换成空。而serialize($_SESSION)是将该变量进行序列化成字符串。然后再看$_SESSION的生成过程,我们发现存在extract()函数,这个函数可以将数组转化成变量,因此理论上来讲我们可以控制$_SESSION变量。
如果没有经过filter函数过滤,那么payload就需要是
{s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==" }
因此这里我们可以构建恶意的输入
$_SESSION[aflagflagc]= "";s:1:"2";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}"
该数据通过序列化后得到
s:10:"aflagflagc";s:49:"";s:1:"2";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}"
而在经过filter函数处理后,flagflag都被被置空,得到序列化后的数据为
s:10:"ac";s:49:"";s:1:"2";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}"
这里我们来看一下反序列的过程
首先是s:10,表示这个变量有10位。
s:10:"ac";s:49:"";s:1:"2";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}"
如上标红的地方就成为新的key,标红地方的最后一个双引号是我们输入的,其后跟的分号表示变量的分隔符,如果将这些反序列后,其结果就是
$_SESSION[ac";s:49:"]=2
$_SESSION= ZDBnM19mMWFnLnBocA==
由于最右边的花括号已经把序列化数据都分隔了,后面的数据就不会再引入。
我们请求看一下,成功读取d0g3_f1ag.ph文件
如果想要读取/d0g3_fllllllag文件,只需要编码后替换并修改s:20的长度为替换后长度即可。这里因为编码后为L2QwZzNfZmxsbGxsbGFn,长度仍为20,无须修改
最终获取flag{9194ad31-3fba-4d13-b93d-6788c3ba8c0e}
网上还有一种payload是
_SESSION[phpflag]=;s:1:"1";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}
原理是一样的,phpflag去空后,多了7位,因此它这里通过;使前面强制闭合,而用flagflag就有8位,通过多一个引号,形成偶数对双引号。
这道题的关键函数有extract;file_get_contents;serialize三个,利用到变量覆盖、反序列化漏洞、文件读取三个点,从代码审计工具的效果来看,seay发现了2个点,这跟它是利用函数名为关键词进行正则匹配有关,在做CTF题可以快速挖掘关键点,但要是审计含有大量代码的系统,最终呈现的就是一堆没有关联的可能存在恶意攻击点的函数。而Rips发现了1个点和fortify相同,但fortify提供了其他一堆漏洞,存在有误报率问题。
原文始发于微信公众号(第59号):php代码审计案例之easy_serialize_php
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论