php代码
<?phpif(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__);}
题目的限制:
-
webshell长度不超过35位
-
除了不包含字母数字,还不能包含
$
和_
PHP7.0版本巧妙解法
在PHP7.0版本之后进行了改动如下
7版本相对于5版本的更新内容.png
PHP7前是不允许用($a)();
这样的方法来执行动态函数的,但PHP7中增加了对此的支持。所以,我们可以通过('phpinfo')();
来执行函数,第一个括号中可以是任意PHP表达式。但是这样依然有字符,所以需要构造一个可以生成phpinfo
这个字符串的PHP表达式即可。payload如下(不可见字符用url编码表示):
(~%8F%97%8F%96%91%99%90)();取反 --- 反码 --- ~
PHP5版本的绕过
此时,我们尝试用PHP7的payload,将会得到一个错误。因为PHP5版本不支持这种方法。所以我们无法通过这种方法来继续下去。
此时我换种方法,我们是不是可以通过文件来执行。然后通过执行该文件的方式来绕过字母数字的限制?
在PHP代码中,如果存在文件上传,无论PHP代码是否接收该文件,都会生成临时文件。然后等待PHP代码执行完成之后立马删除文件。我们是不是可以通过上传文件,然后再通过代码中的GET传参执行该文件呢?
所以接下来问题很明显了
-
代码中最后是通过eval函数执行的,但是他不能执行Linux命令
-
Linux下如何执行没有执行权限的文件?
-
如何抢在删除文件前执行该文件?
-
如何精准找到临时文件的位置?
解决问题
第一个问题:代码中最后是通过eval函数执行的,但是他不能执行Linux命令。
在Linux系统中可以通过反引号执行命令,因为反引号不属于“字母”、“数字”,所以我们可以执行系统命令。
第二个问题:Linux下如何执行没有执行权限的文件?
shell下可以利用.
来执行任意脚本,.
或者叫period,它的作用和source一样,就是用当前的shell执行一个文件中的命令。比如,当前运行的shell是bash,则. file
的意思就是用bash执行file文件中的命令。用. file
执行文件,是不需要file有x权限的。那么,如果目标服务器上有一个我们可控的文件,那不就可以利用.
来执行它了。
第三个问题:如何抢在删除文件前执行该文件?
在PHP中如果在代码运行过程中接收到的临时文件,是不会立刻删除的。等到整个PHP代码全部执行完毕之后才会立刻删除临时文件。所以我们先加上sleep,增加代码执行时间。
并且也不用担心POST的提交是提交到这个php代码中的,此时的get和pos请求是同步触发的。也就意味着PHP的代码不执行结束,临时文件是不会被删除的。
第四个问题:如何精准找到临时文件的位置?
就算能执行文件了,但是如果想精确匹配到文件。我们依然需要文件目录和文件名称。但是PHP生成的临时文件名称是随机的,不固定。并且在写匹配路径的时候也需要写字母或者数字,仍然过不去正则。
在PHP的配置文件中,可以找到临时文件生成目录。
vim /etc/php/7.3/fpm/php.ini.....sys_temp_dir="/tmp" # 临时文件目录.....update_tmp_dir = "/tmp" # 上传文件临时目录.....
现在就剩下文件名称了。此时就可能想到学的Linux下模糊匹配。对于通配符,可能大部分知道的都只有*
和?
-
*
可以代替0个及以上任意字符 -
?
可以代表1个任意字
所以我们可以试一下使用/*/????????? 或者/???/?????????来匹配文件。此时会发现有大量的文件,根本找不到想要的文件。
所以只使用以上两个通配符就解决不了问题了。所以需要去官方文档查看更多的glob通配符。阅读Linux的文档( glob(7) - Linux manual page ),可以学到更多有趣的知识点。
其中我们发现可以发现,glob支持用[^x]
的方法来构造“这个位置不是字符x”。那么,我们用这个姿势干掉一部分不是连续字母的。因为PHP生成的临时文件名称全是随机的大小写字母加数字。所以临时文件名称不可能存在特殊字符。通过[^.]
、[^-]
、[^_]
可以排除一些内容,但是这样发现依然很多内容。
所以我们需要找到临时文件的通性,我们通过不断的生成文件,我们发现PHP生成的临时文件是有规律的,文件名字中大小写各有百分之五十的概率。并且Linux的文件名称基本上没有大写字母。所以我们可以匹配文件名称的最后一个字母。
但是要如何实现匹配大写字母呢?
glob的文档,我们可以发现一个和正则表达式类似,glob支持利用[0-9]
来表示一个范围。
并且通过查看ascii编码发现大写字母都是字@ 和 [ 之间。那么,我们可以利用[@-[]
来表示大写字母。
经过查看资料修改,此时的匹配语句如下。此时可以匹配到文件了。
/???/????????[@-[]
构造语句,执行命令
通过BurpSuite软件进行抓包、改包。来上传文件。由于文件上传的时候需要一个特殊的格式。我们可以编写一个HTML的网页用来上传文件。
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title></head><body><form action="web.php" method="post" enctype="multipart/form-data"><input type="file" name="file" id=""><input type="submit" value="提交"></form></body></html>
通过GET传递的参数,需要到eval里写代码匹配到文件然后执行。在PHP的官方文档中,明确说明传递的参数如果写代码是需要先加上?>
,之后在写PHP代码。
所以构建的code参数如下
?code=?><?=`. /???/????????[@-[]`;?>
修改POST的包来进行文件上传。并且同时通过GET传递参数访问临时文件。
注意如果是在URL之中填写的话注意空格会被编码。所以需要使用加号代替。并且对符合要进行编码,避免错误。加号不需要再次编码,否则无法显示内容。