安洵杯2019
easy_serialize_php
考点
变量覆盖
预包含
反序列化中的对象逃逸
题解
首先打开网页界面,看到source_code,点击就可以直接看到源码。
从上往下阅读代码,很明显的可以发现一个变量覆盖。至于这个覆盖怎么用,暂时还不知道,这是第一个考点。
往下看,可以看到我们可以令function为phpinfo来查看phpinfo,此时就可以看到我的第二个考点:
我在php.ini中设置了auto_prepend_file隐式包含了d0g3_f1ag.php,直接访问可以发现没有任何内容,说明我们需要读取这个文件的内容。
接着往下看代码,可以看到最终执行了一个file_get_contents,从这个函数逆推回去$userinfo[“img”]的值,可以发现这个值虽然是我们可控的,但是会经过sha1加密,而我没有解密,导致无法读取任何文件。
此时需要把注意力转移到另外一个函数serialize上,这里有一个很明显的漏洞点,数据经过序列化了之后又经过了一层过滤函数,而这层过滤函数会干扰序列化后的数据。
PS:任何具有一定结构的数据,如果经过了某些处理而把结构体本身的结构给打乱了,则有可能会产生漏洞。
- payload1(值逃逸):
1 | _SESSION[user]= flagflagflagflagflagflag |
这里我令_SESSION[user]为flagflagflagflagflagflag,正常情况下序列化后的数据是这样的:
1 | a:3:{ |
而经过了过滤函数之后,序列化的数据就会变成这样:
1 | a:3:{ |
可以看到,user的内容长度依旧为24,但是已经没有内容了,所以反序列化时会自动往后读取24位:
1 | a:3:{ |
会读取到上图的位置,然后结束,由于user
的序列化内容读取数据时需要往后填充24位,把function
这个键当成了user
的值覆盖了,导致后面function
的内容也发生了改变,吞掉了其双引号,导致我们可以控制后面的序列化内容
而php反序列化时,当一整段内容反序列化结束后,后面的非法字符将会被忽略,而如何判断是否结束呢,可以看到,前面有一个a:3
,表示序列化的内容是一个数组,有三个键,而以{
作为序列化内容的起点,}
作为序列化内容的终点。
所以此时后面的";s:3:"img";s:28:"L3VwbG9hZC9ndWVzdF9pbWcuanBn";}
在反序列化时就会被当作非法字符忽略掉,导致我们可以控制$userinfo["img"]
的值,达到任意文件读取的效果。
- payload2(键逃逸):
这儿只需要一个键值对就行了,我们直接构造会被过滤的键,这样值得一部分充当键,剩下得一部分作为单独得键值对
var_dump的结果为:
1 | _SESSION[flagphp]=;s:1:"1";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";} |
var_dump的结果为:
1 | "a:2:{s:7:"";s:48:";s:1:"1";s:3:"img";s:20:"ZDBnM19mbGxsbGxsYWc=";}";s:3:"img";s:20:"Z3Vlc3RfaW1nLnBuZw==";}" |
这儿得s:7:""之所以为空,是因为我们构造得键flagphp都是会被过滤得,所以显示为空,这样就能吃掉一部分值了,然后将剩下得值充当另一个对象逃逸出去~~
在读取完d0g3_f1ag.php后,得到下一个hint,获取到flag文件名,此时修改payload读根目录下的flag即可。