今日披露的 NGINX Rift 漏洞,属于 ngx_http_rewrite_module 中的一個堆緩衝區溢出,自 2008 年起存在於程式碼庫中。攻擊者無需任何認證,只需發送一個構造好的 HTTP 請求,即可令 NGINX worker 程序崩潰(單一請求即可穩定觸發 DoS);若目標系統關閉了 ASLR,則可進一步實現遠端程式碼執行。
觸發條件須同時滿足以下三點:
rewrite替換字串中使用了無名捕獲組($1、$2等)- 替換字串中包含
?(即構造了查詢字串) rewrite指令後方緊跟另一條rewrite、if或set指令
rewrite 規則在國內運用極為普遍,而 Discuz! X 官方推薦的偽靜態規則全部命中上述三個條件,大量 LNMP 面板、虛擬主機和 VPS 使用者沿用至今:
Note: AMH 面板已經發佈 NGINX 1.30.1。(https://amh.sh/bbs/post-11462-1-1.htm)
rewrite ^([^\.]*)/topic-(.+)\.html$ $1/portal.php?mod=topic&topic=$2 last;
rewrite ^([^\.]*)/article-([0-9]+)-([0-9]+)\.html$ $1/portal.php?mod=view&aid=$2&page=$3 last;
rewrite ^([^\.]*)/forum-(\w+)-([0-9]+)\.html$ $1/forum.php?mod=forumdisplay&fid=$2&page=$3 last;
rewrite ^([^\.]*)/thread-([0-9]+)-([0-9]+)-([0-9]+)\.html$ $1/forum.php?mod=viewthread&tid=$2&extra=page%3D$4&page=$3 last;
rewrite ^([^\.]*)/group-([0-9]+)-([0-9]+)\.html$ $1/forum.php?mod=group&fid=$2&page=$3 last;
rewrite ^([^\.]*)/space-(username|uid)-(.+)\.html$ $1/home.php?mod=space&$2=$3 last;
rewrite ^([^\.]*)/blog-([0-9]+)-([0-9]+)\.html$ $1/home.php?mod=space&uid=$2&do=blog&id=$3 last;
rewrite ^([^\.]*)/archiver/(fid|tid)-([0-9]+)\.html$ $1/archiver/index.php?action=$2&value=$3 last;
rewrite ^([^\.]*)/([a-z]+[a-z0-9_]*)-([a-z0-9_\-]+)\.html$ $1/plugin.php?id=$2:$3 last;
9 條規則全部滿足觸發條件:
無名捕獲組($1、$2 等) | 替換字串含 ? | 後跟 rewrite/if/set |
|---|---|---|
| ✅ | ✅ | ✅ |
溢出量的大小取決於捕獲變數的內容,漏洞路徑是 ngx_http_script_copy_capture_code 透過 ngx_escape_uri(NGX_ESCAPE_ARGS 模式)對捕獲變數重新跳脫,+、%、& 各從 1 byte 膨脹為 3 bytes。捕獲組的正則越寬鬆,攻擊者能製造的溢出量就越大:
- 最危險:
topic-(.+)與space-…-(.+)的捕獲組是.+,攻擊者可任意填入+、%、&,溢出量完全可控 - 相對難利用:
thread-([0-9]+)的捕獲組限定為純數字,無法觸發字元膨脹
臨時修復:將無名捕獲組改為具名捕獲組
無需修改任何業務邏輯,只需在每個捕獲組的括號內加上 ?<名稱>,並在替換字串中改用 $名稱 引用,即可繞過存在漏洞的程式碼路徑。以第一條規則為例:
修改前
rewrite ^([^.]*)/topic-(.+).html$ $1/portal.php?mod=topic&topic=$2 last;
修改後
rewrite ^(?<base>[^.]*)/topic-(?<slug>.+).html$ $base/portal.php?mod=topic&topic=$slug last;
Discuz! X 完整修復版本:
rewrite ^(?<base>[^\.]*)/topic-(?<slug>.+)\.html$ $base/portal.php?mod=topic&topic=$slug last;
rewrite ^(?<base>[^\.]*)/article-(?<aid>[0-9]+)-(?<page>[0-9]+)\.html$ $base/portal.php?mod=view&aid=$aid&page=$page last;
rewrite ^(?<base>[^\.]*)/forum-(?<fid>\w+)-(?<page>[0-9]+)\.html$ $base/forum.php?mod=forumdisplay&fid=$fid&page=$page last;
rewrite ^(?<base>[^\.]*)/thread-(?<tid>[0-9]+)-(?<page>[0-9]+)-(?<extra>[0-9]+)\.html$ $base/forum.php?mod=viewthread&tid=$tid&extra=page%3D$extra&page=$page last;
rewrite ^(?<base>[^\.]*)/group-(?<fid>[0-9]+)-(?<page>[0-9]+)\.html$ $base/forum.php?mod=group&fid=$fid&page=$page last;
rewrite ^(?<base>[^\.]*)/space-(?<type>username|uid)-(?<val>.+)\.html$ $base/home.php?mod=space&$type=$val last;
rewrite ^(?<base>[^\.]*)/blog-(?<uid>[0-9]+)-(?<id>[0-9]+)\.html$ $base/home.php?mod=space&uid=$uid&do=blog&id=$id last;
rewrite ^(?<base>[^\.]*)/archiver/(?<action>fid|tid)-(?<value>[0-9]+)\.html$ $base/archiver/index.php?action=$action&value=$value last;
rewrite ^(?<base>[^\.]*)/(?<plug>[a-z]+[a-z0-9_]*)-(?<parg>[a-z0-9_\-]+)\.html$ $base/plugin.php?id=$plug:$parg last;修改後驗證並重載:
nginx -t && nginx -s reload受影響版本
NGINX Open Source 0.6.27 – 1.30.0(詳見 F5 官方公告)
上述設定改法僅作為無法立即升級時的臨時緩解措施。PoC 已公開發布於 DepthFirstDisclosures/Nginx-Rift。

