前言
现在就把之前参加的某公司月赛的web题做一个总结并深究反序列化逃逸的原理。
源码
直接给出源码;如下:
简单的分析代码,有三个类;通常ctf比赛里也就是三个类然后构造pop链进行攻击;但是这里出了两个方法。write和read方法;
这里分析两个方法,write方法是将chr(0) .'*'. chr(0) 转为\0\0\0 这里也可以理解为将三个字符转化为了六个字符;(在下面将通过实验验证)反观read方法,是将六个字符转化为了三个字符;这里基本上就是考反序列化逃逸了,接着向下看最后有个反序列化;那就实锤了,考点就是反序列化逃逸;下面就来深究到底是如何进行逃逸的;服务器到底怎么解析我们的代码的;
源码
我在本地搭建了一个环境用于实践;为了更方便的看到序列化和反序列化的过程,我在代码下面加入如下的代码;用来观察其序列化过程;
我们本地实践一下,传入数据 a=1 b=2的到如下的效果;
发现一切正常;然后我们传入敏感的字符 a=\0\0\0 b=2;
这里我们就发现了蹊跷;发现经过read方法和write方法的处理之后,我们得到的效果就是第三行结果所示;这里我们可以分析一下,很容易就看了这里发生了序列化的问题,然后再经过反序列化之后,因为username的值为6个字符,但是引号里面就只有三个字符,所以就会发生吞噬的问题,也就是会导致username的值非正常化;并且向后吞噬了后面的内容也就是username的值会变成;*";s(在星号的两边有空字符)显然我们可以通过控制\0\0\0的个数来进行控制序列化的内容。
攻击题目
这里我们看到显然是构造pop链了;看到三个类,无疑是通过file_get_contents()方法来进行读取了。我们看到C是由a和b组成的;显然就是要让flag.php逃逸出来;这里看到如果想要调用危险函数就要调用_toString()方法,这里我们需要将一个类当作字符串才会调用。看到刚好有个echo;就利用这个输出来做了,将B类中的b赋值为一个实例化c的对象;这样就可以直接调用到危险函数;现在的问题就是如何让flag.php逃逸出来;这样才可以被file_get_contents();并echo;
为了更加的看清楚我们传入的字符到底经历了怎样的历程,我们这里在加上几行代码。将unserialize(read(write(serialize($a))))给var_dump一下;然后再细致的观察它的username和password的值是怎么变化的;
我们传入值进行观察;传入a=1 b=2先来观察一下;得到一下的结果;
可以看得清楚倒数后两行是我们输出的username和password的值;然后我们输入敏感的字符比如\0\0\0看看效果;
发现这里有三行报错,我来解释一下,第一行报错是针对于unserialize(read(write(serialize($a))))的报错,因为我们只是传入了一个\0\0\0所以导致反序列化出了问题而爆的bool型错误,第二行报错是我们输出username的报错,因为没有准确的识别,第三行是对password的报错,也是因为没有准确识别;
因为我们这个题中username和password参数我们是可控的;所以我们就需要借助这个漏洞吞噬掉中间的字符,让其都作为username的值,然后我们控制password让其为一个序列化内容flag.php就可以顺利解决这道题;简单测验一下我们构造如下的字符 \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0&b=2";s:8:"password";s:6:"s1mple";} 放进去实验一下;发现如下的结果;这里要注意分号(;)和引号的闭合情况;发现后面的成功逃逸,那问题就简单了,控制a属性为固定的\0从而吞噬掉中间的字符串,然后password进行pop链构造利用pop即可(这么做的原因主要还是因为要利用file_get_contents()函数所以必须走pop链)
我们构造exp设置类A的password属性为类B的实例化对象。并且设置类B的b属性为类C的实例化对象。并且设置类c的c属性为flag.php。得到flag
得到如下的序列化字符;
O:1:"A":2:{s:8:"username";N;s:8:"password";O:1:"B":1:{s:1:"b";O:1:"C":1:{s:1:"c";s:8:"flag.php";}}},
照着之前我们的方法,打入固定的\0然后因为password被吞噬了,所以我们还需要在b中输入完整的password的序列化字符;目的是在进行反序列化的时候可以将我们的恶意序列化当作password进行输出然后file_get_contents();
最后我们传入
a=\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0&b=2";s:8:"password";O:1:"B":1:{s:1:"b";O:1:"C":1:{s:1:"c";s:8:"flag.php";}}}
得到flag;因为我环境没部署flag,所以读不出来,但是看到已经利用成功了;
PHP序列化漏洞实践 (过本次实验,大家将会明白什么是反序列化漏洞,反序列化漏洞的成因以及如何挖掘和预防此类漏洞。)
http://hetianlab.com/expc.do?ec=ECID9d6c0ca797abec2017042716211200001