前言:
转载的 P神 的文章,以写来深入理解思路。 原文章地址:无字母数字webshell之提高篇 | 离别歌 (leavesongs.com)
代码:
<?php
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__);
}
CTF 题 一般都会有 类似这种的题型 简单一点的题型 都没有长度限制,而这个代码 有长度限制,那么该如何 RCE呢?
一般的思路 都是只有
位运算
自增运算
异或
除了长度限制 和不能有字母数字外,还不能包含 $ 和 _
而前面的所有方法都可能会用到 php中的变量进行操作,最后调用函数。 因为$不能使用 所以不能构造变量了。
那么如何解决呢?
PHP7
看到p神的测试知道, PHP7前是不允许使用 ($a)(); 这样的方法来执行动态函数的。但是PHP7以上可以 如('phpinfo')(); 是可以来执行函数的。
那么构造一个可以生成phpinfo 这个字符串的php表达式即可:
(~%8F%97%8F%96%91%99%90)();
PHP5
但是换在 php5的环境中是不允许使用这种表达式的
解决问题:
因为反引号不属于 字母 、 数字 ,所以我们可以执行系统命令,但是如何利用无字母、数字、$的系统命令来rce 呢?
借用p神的思路:
shell 下 可以利用 . 来执行任意脚本
Linux 文件名支持用glob 通配符进行匹配代替
对于 。 的解释,他的作用和source 一样,就是用当前的shell 来执行 文件中的命令。
比如,当前运行的shell 是bash 则 . file 的意思就是用bash 来执行file 中的命令!
用 . file 执行文件,是不需要file 有 x (执行)权限的,那么如果目标有一个可控的文件,那不就能够可以利用 . 来执行他了吗
这个文件其实也好得到,我们可以发送上传文件的post 包,此时php 会将我们上传的文件保存在临时文件夹下 默认文件名是 /tmp/phpXXXXXX , 文件名最后6个字符是随机的大小写。
第二个难题来了 , 用 . /tmp/phpXXXXXX 执行文件,也是有字母的 此时就要用到linux 的glob通配符了:
* 和正则匹配一样 可以代替0个及以上任意字符
? 可以代表 1 个任意字符
那么 , /tmp/phpXXXXXX 就可以表示为 /*/?????????或者 /???/?????????
但我们尝试执行 . /???/?????????,却得到以下错误
这是因为 能匹配到这个通配符的文件有很多。
有非常多的文件,而我们要匹配的文件在最后 为/tmp/phpscdsnZ
在执行第一个匹配上的文件就出现了错误,导致停止了这个流程,根本不会执行到我们上传的文件。
深入理解glob 通配符:
对于通配符,可能知道的只有 * 和 ? 其实,在LINUx 有很多的用法。
其中,glob 支持 [^x] 的方法 来构造 “这个位置的字符 不是 x” 。 那么我们用这个方法试下干掉以bin目录的文件
可见我们的文件还是在最后一位。
排除了特殊字符外,仍然排在最后一位。 都不包含特殊字符,这个 方法似乎不能用了
根据p神的思路 得到了一个特殊的用法:
就跟正则表达式 类似,glob 支持利用 [0-9 a-z]来表示一个范围。
我们再回头看看之前列出的那些干扰我们的文件。
所有文件都是小写,只有PHP生成的临时文件包含大写字母,那么答案呼之欲出,只要找到一个可以表示 “大写字母”的glob 通配符 去匹配我们的文件,就能精准找到我们要执行的文件出来
打开ascii 码,可见大写字母位于 @ 与 【 之间:
那么我们可以利用 [@-[] 来表示 大写字母:
显然是可以做到的
利用:
php 生成的临时文件名是随机的,最后一个字符 不一定是大写字母。不过还是要多尝试多几次就知道了。
用一下p神的图:
传入的code为?><?=`. /???/????????[@-[]`;?>
,发送数据包如下:
成功执行任意命令。