什么是RCE?
RCE漏洞,可以让攻击者直接向后台服务器远程注入操作系统命令或者代码,从而控制后台系统。也就是远程命令执行。命令执行是在目标服务器上任意执行系统命令。它属于高危漏洞之一,也属于代码执行的范畴。命令执行漏洞与文件上传漏洞类似,攻击原理易于理解,但是攻击的点非常多。
限制输入长度,如何突破?
前期理解
这里我们先来看看一段源码:
我们可以通过查找PHP的官方文档可以知道,shell_exec()这个函数,通过 shell 执行命令并将完整的输出以字符串的方式返回。也就是$param里面是可以直接写入命令的。下面我们也可以试一下,输入id。
这也是一种典型的限制输入长度的一个函数,我们现在需要考虑的就是如何在限制长度的情况下,突破这个8位的限制,来进行远程命令执行。因为在Linux下的命令长度一般都比较长,命令长度较短的无非也就那几个(w,id,whoami等等)。而我们需要用到的命令长度却是远远大于8位的。
到这,就需要了解到Linux系统的重定向命令 > 和 >>。这两个区别在于:
- >是重定向到一个文件,
- >>是追加内容到一个文件。
如果文件不存在,那么这两个命令都会首先创建这个文件。而我们就可以通过 >m 这一条极短的命令,来创建一个文件。但是要清楚的是,我们写入的是文件名,不是文件内容。
而Linux中还有一个按照时间的排序,打印出当前目录的文件。这个命令就是 ls -t 。该命令可以按照时间,让最近创建的文件排在最前面。为此就可以引发一个思考,我们是否可以通过排序和拼接的方式来进行命令执行呢?
实战操作
接下来,我们就继续操作。
首先,先对需要执行的PHP代码进行base64编码,这里我写的是 phpinfo(); (测试后发现,那两个 ' = ' 可以不要)
然后,在网页上,对param一个一个的输入(也可以在自己电脑的Linux下进行,这样更直观一点)。输入的时候注意长度不要超过8位。
输入>ls -t>0,将这些文件按照创建时间排序,并将文件名追加到0这个文件中。查看一下 0 这个文件里面的内容。
直接sh 0 执行,这里可能会报错,但是文件还是会生成的。
在Linux下,我们查看一下文件是否创建完成。都完成之后,就可以发现我们的目录下出现了从。c.php这个文件,
直接访问。
限制字母和数字如何进行命令执行?
我们可以再看一个例子
<?php if(isset($_GET['code'])){ var_dump($_GET['code']); $code = $_GET['code']; if(strlen($code)>35){ die("Long."); } if(preg_match("/[A-Za-z0-9_$]+/",$code)){ die("NO."); } eval($code); }else{ highlight_file(__FILE__); }
解读代码
- 限制了输入长度,但是不想上一个例子限制的那么严
- "/[A-Za-z0-9_$]+/"这段正则,限制了输入的内容,不能使用大小写字母、数字、_、$
- 最后用eval执行,highlight_file()高亮显示
这里的PHP版本我使用的是5.6.9版本。
流程思路
那么,该如何绕过这个限制呢?
这里我们得重点关注这个正则。它虽然过滤了大小写字母、数字、_、$,在Linux上确实很多的命令都不能使用,但真的是这样吗?
这里要补充一个知识点:在Linux下,我们可以使用 ? 来进行文件的匹配。也就是说,如果我们要匹配到 /tmp 目录下的文件的话,就可以使用/???/?????????类似这种格式,来进行一个匹配。但是如果只是这样的话,匹配到的文件就太多了,还是不能精准的对这个临时文件进行匹配。
可以知道的一点是,在我们进行文件上传的时候,Linux中的 /tmp 目录下会自动生成一个文件,它的文件名是随机的,但是它的文件名的最后几位通常存在一个或多个大写字母,这和我们Linux的其他文件是很不相同的。此时,就需要想一个办法,既然我们已经知道这个文件名的特征了,就可以从这个方面入手。
根据ascii码表,我们可以知道大写字母的范围是96~123这个范围内,但是因为不能使用数字,所以说就可以用 @-[ 来对其进行替代。也就是说现在,我们的匹配命令就可以用ls -al /???/????????[@-[] 来进行一个精准匹配。
知道原理之后,就可以进行实战的操作了
实战操作
先简单写一个上传文件的网页,上传成功之后,跳转到web2.php页面。
因为把之前的临时文件删了,我这里就自己在/tmp目录下创建了一个phaaaaaaA文件。
然后,对刚才的文件上传页面,我们用BP来抓包,分析一下POST上传文件的结构。
在回到web2.php,对这个网页抓包。
对比一下这两个包的结构,很明显可以看出一个是POST传参,一个是GET传参 。我们将这两个数据包都放到Rpeater模块中去,进行重放。
首先,我们要对之前web2.php这个页面的参数进行修改。 将GET改成POST,然后将Content-Type修改成和文件上传的那个包里的格式相同。然后,就可以将重点放到 ? 后面的内容。
第一个问题:这里为什么要用 ?><? 呢?
这里通过查找PHP的官方文档可以知道,我们需要在 = 前面加上 ?> 来进行闭合,这样才能重新进入到PHP模式。
第二个问题:为什么要使用 ` ` (反引号)?
因为eval() 可以使用反引号来直接执行系统命令。也就是eval()可以利用反引号来执行命令,而反引号中的命令,可以在Linux下直接执行。所以说此时,代码执行变成了命令执行。
第三个问题:为什么要对其进行编码?
因为是在地址栏中,所以要遵守URLcode编码。而且需要注意的是,里面不能出现空格,需要用+来进行连接。
第四个问题:这里为什么既有POST传参又有GET传参呢?
这里是PHP的一个流程。POST传参和GET传参是可以并行处理的,相互不会影响。
最后,直接send
成功执行了。