php反序列化字符逃逸的
前言
最近做了道关于php反序列化字符逃逸的题目,感觉很有意思,这里记一下。
背景知识
PHP 在反序列化时,底层代码是以 ; 作为字段的分隔,以 } 作为结尾(字符串除外),并且是根据长度判断内容的。
例如:
1 |
|
结果:
1 | a:2:{i:0;s:4:"hihi";i:1;s:3:"hi1";} |
很显然,如果我们能通过某种手段控制前一个字段的值,就可以通过加入反序列化字符串的方式,修改对象反序列化之后的结果。例如这里将第二个数组的值从hi1改为wqer。
在题目中这种情况通常是由某种不合适的字符串替换造成的,通常会将需要过滤的字符替换为更长的字符串。如果我们能控制输入,便可以通过这种方式控制反序列化后的结果。
题目
- babyphp 来源i春秋战疫
这是一道代码审计题,通过反序列化逃逸和构造pop达到登录绕过的效果。核心的代码在lib.php中
1 |
|
很明显,通过getNewInfo方法传入age和nickname的值,这里会进行一次过滤,将非法的关键词的值替换。这里显然有我们刚才说的问题。
1 | $array = array('union', 'regexp', 'load', 'into', 'flag', 'file', 'insert', "'", '\\', "*", "alter"); |
很显然hacker和*等字符长度不一致,如果能巧妙的构造输入,利用safe方法将*的替换为hacker,将我们构造的payload挤到后面去,从而构造反序列化漏洞。
这里有个点需要注意,构成payload的长度有限制,需要是是5的倍数(如果我们使用*来构造的话,*和hacker的长度差为5),*的个数和5的积就是payload允许的长度。
pop链的构造,(Info)nickname 拼接sql(update方法中,可以触发toString方法):(User)__toString => (Info) nickname->update(age):(Info)__call=>(dbCtrl) login 写sql出结果
dbCtrl的login方法中,如果session中的token是admin,那么直接返回id值。而在User的login方法中,只要存在id值,那么就算登录成功我们,只需要将session中的token值修改为admin,之后就可以使用任意密码登录admin,从而获取flag。
1 | //update.php |
最后payload为
1 |
|
1 | //payload.php |
- 0ctf piapiapia
这道题就简单地说下主要也是更新profile时做了过滤导致反序列化逃逸。
1 |
|
这题在绕过nickname的长度限制时有个技巧,preg_match和strlen函数如果传入的值不是字符串就会报错返回false,可以利用数组来绕过长度限制。
1 | payload |
最后读取config.php的值,可以获得flag。
总结
算是搞明白一个点了,很开心。