PHP反序列化漏洞-字符逃逸
原理
一般由preg_replace
/str_replace
导致,序列化后的字符串发生了字符增多或减少的情况:
1 |
|
PHP的反序列化操作会在匹配到;}
后停止反序列化,舍弃后续的字符
此时就可以通过构造值、构造键等方式,反序列化出一个我们想要的对象,从而绕过某些限制,进行后续的操作
字符增多
1 |
|
首先在$name
末端添加需要构造的内容:";s:3:"age";s:2:"18";}
新增的长度为22,而preg_replace
会将h
变为hhh
,即新增两个字符,因此需要11个h
来实现提前闭合
输出一下会发现age变成了我们想要的18
1 | O:1:"A":2:{s:4:"name";s:39:"izayoihhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh";s:3:"age";s:2:"18";}";s:3:"age";s:5:"10086";} |
O:1:"A":2:{s:4:"name";s:39:"izayoihhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh";s:3:"age";s:2:"18";}"
刚好满足反序列化要求,后续的;s:3:"age";s:5:"10086";}
直接被无视
字符减少
1 |
|
这回把要构造的内容加到age上,把原先的10086吃了作为name的一部分,先输出一下看看
O:1:"A":2:{s:4:"name";s:7:"izayoi";s:3:"age";s:26:"10086";s:3:"age";s:2:"18";}";}
可以看到为了让age变18就得吃了izayoi";s:3:"age";s:26:"10086
这部分,让这部分变为name的值,而这部分长度为29,原先的长度为7,所以name要加上22个h
1 |
|
1 | object(A)#2 (2) { |
WriteUp [安洵杯 2019]easy_serialize_php (字符减少)
题目提示了要看phpinfo
,那就看一眼,找到一个奇怪的php,结合题目应该是要用file_get_contents
获取内容,即$userinfo['img']
base64解码后需要得到字符串d0g3_f1ag.php
$userinfo
是$serialize_info
反序列化后产物,$serialize_info
是$_SESSION
序列化后,通过filter
函数处理后获得,再结合这一段
1 | if(!$_GET['img_path']){ |
告诉我们要传个img_path
的参数,但是这段除了给img_path
做了base64加密处理以外还进行了sha1,所以传了也没啥用,因此再往上看
1 | if($_SESSION){ |
这段重置了$_SESSION
,又提取了$_POST
,不知道要干嘛,再往上看
1 | function filter($img){ |
先前提到的filter
函数,把array中提到的关键字直接替换为空了,结合之前的序列化反序列化操作,判断出要利用反序列化的字符逃逸(字符减少类型),构造出一个$_SESSION
对象,使得$_SESSION['img']
的值为base64后的d0g3_f1ag.php
extract($_POST)
提示用变量覆盖的方式传入$_SESSION
,但是如果直接传_SESSION[img]
会被后续代码直接覆盖掉,所以要把值写进其他变量中进行绕过
先构造;s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}
,先本地输出看看效果:
可以看到s:3:"img";s:20:"ZDBnM19mMWFnLnBocA=="
是个完整的键值对,前面这一段格式不太正确,所以再加一段构成键值对:
看上去比较乱,其实就是这么一个数组array('aaa";s:48:' => 'a', 'img' => 'ZDBnM19mMWFnLnBocA==')
,就是说第一个元素的key变成了aaa";s:48:
但此时反序列化是会报错的,因为key的长度明显不是3,所以需要利用filter
,构造一个合适的key,使得这一串字符串符合反序列化的规则,这题filter
函数又有三字节又有四字节,挺好凑的,简单凑一个,顺便试一下反序列化:
反序列化成功,利用这个payload查看一下d0g3_f1ag.php
内容
1 | GET /index.php?f=show_image |
好嘛还有一层,再重新构造一下,把base64那串替换一下就行(注意长度变化),得到flag,over