一道Bypass正则过滤的反序列化漏洞题目
/0x01 题目分析
一道PHP代码审计的题目,直接看源代码:
1 |
|
__construct():构造函数,当一个对象创建时被调用。具有构造函数的类会在每次创建新对象时先调用此方法,所以非常适合在使用对象之前做一些初始化工作。
__destruct():析构函数,当一个对象销毁时被调用。会在到某个对象的所有引用都被删除或者当对象被显式销毁时执行。
反序列化漏洞调用链分析
考察点是原生的PHP unserialize反序列化漏洞。Template类有两个成员变量cacheFile和template,构造方法__construct()中调用了loadData()和render(),而析构方法__destruct()则调用了createCache();其中,loadData()是通过if判断对参数进行过滤、再调用unserialize()方法反序列化参数内容,render()是在界面输出参数的name对应的值,createCache()中以Template类的两个成员变量为参数调用call_user_func();很明显,漏洞点在于call_user_func()函数的任意函数调用,其代码逻辑在createCache()中,而createCache()函数是在析构函数__destruct()中调用,而在代码的最后是通过POST请求传入data参数来构造新的Template类实例,当所有代码执行完之后析构函数__destruct()必然会被调用;也就是说,data参数可控,可以通过构造方法调用到unserialize()方法中实现对data内容的反序列化操作,最终在代码执行完成时通过析构函数实现任意函数调用,那么反序列化利用的逻辑就理清了。
Bypass
我们看到在loadData()中是通过if判断对参数进行过滤、再调用unserialize()方法反序列化参数内容。要想进入反序列化的逻辑,必须通过下面的if判断:
1 | if (substr($data, 0, 2) !== 'O:' && !preg_match('/O:\d:\/', $data)) |
第一个判断条件是开头前两个字符不能为’O:’开始,第二个判断条件是正则表达式不能匹配到’O:’后面加数字这样的情况。
我们知道,正常序列化的内容为:
1 | O:4:"Test":2:{s:4:"name";s:5:"SKI12";s:4:"blog";s:28:"https://blog.csdn.net/ski_12";} |
PHP中可反序列化类型有String、Integer、Boolean、Null、Array、Object等,这里看来Object是行不通了。再结合代码看下:
1 | public function render($data) { |
在render()函数中调用了传入参数data的name对应的值,调用方式为$data[‘name’],表明data是个数组,间接提示我们可以采用数组中存储对象进行绕过。
至于第二个判断条件的Bypass,这里直接借鉴参考的文章。
第二个if判断,匹配 字符串为 \’O:任意十进制:’,将对象放入数组进行反序列化后,仍然能够匹配到,返回为空,考虑一下如何绕过正则匹配,PHP反序列化处理部分源码如下:
在PHP源码var_unserializer.c,对反序列化字符串进行处理,在代码568行对字符进行判断,并调用相应的函数进行处理,当字符为’O’时,调用 yy13 函数,在 yy13 函数中,对‘O‘字符的下一个字符进行判断,如果是’:’,则调用 yy17 函数,如果不是则调用 yy3 函数,直接return 0,结束反序列化。接着看 yy17 函数。通过观察yybm[]数组可知,第一个if判断是否为数字,如果为数字则跳转到 yy20 函数,第二个判断如果是’+’号则跳转到 yy19 ,在 yy19 中,继续对 +号 后面的字符进行判断,如果为数字则跳转到 yy20 ,如果不是则跳转到 yy18 , y18 最终跳转到 yy3 ,退出反序列化流程。由此,在’O:’,后面可以增加’+’,用来绕过正则判断。
利用思路
- 构造序列化内容,将两个成员变量分别初始化为恶意函数和参数,这里设置为assert和system(‘ls’);
- 在恶意构造的序列化内容中的’O:’后面加上+号;
- 通过POST将序列化内容传递给data参数来触发反序列化漏洞;
Exp
1 |
|
输出为:
1 | a:1:{i:0;O:8:"Template":2:{s:9:"cacheFile";s:6:"assert";s:8:"template";s:17:"system('whoami');";}} |
在’O:’后面加上+号:
1 | a:1:{i:0;O:+8:"Template":2:{s:9:"cacheFile";s:6:"assert";s:8:"template";s:17:"system('whoami');";}} |
触发利用: