事件經過
2021 年 3 月 28 日,php-src 在 git.php.net 上的 master 分支出現了兩筆提交,推送者署名為 PHP 創作者 Rasmus Lerdorf 與核心開發者 Nikita Popov。這兩筆提交的標題為「[skip-ci] Fix typo」——這個主旨小到足以略過審查,而加上 skip-ci 前綴,更是刻意要繞過持續整合。這項變更位於一個被廣泛引入的標頭檔中,它會檢查伺服器變數中的 HTTP User-Agent,若該值以字串「zerodium」開頭,便將標頭其餘部分當作 PHP 程式碼執行:也就是在任何執行遭汙染直譯器的伺服器上達成遠端程式碼執行。
它並沒有得逞,而原因頗具啟發性:社群成員在閱讀公開動態時,於數小時內就發現了這些提交——一名留言者在提交本身標記了這項變更,PHP 開發者 Jake Birchall 也確認它會執行來自 user-agent 標頭的程式碼。Popov 當晚宣布,種種跡象都指向 git.php.net 伺服器遭入侵,而非任何個人帳號。提交文字本身帶有「REMOVETHIS: sold to zerodium, mid 2017」這一行,暗示這個後門早在數年前就已仲介賣給某家漏洞利用供應商。
該專案的回應是移除這項資產,而非修補它。git.php.net 當場停用;原本居於次要地位的 GitHub 鏡像成為正式儲存庫;寫入權限轉移至 php GitHub 組織,且成員資格要求強制啟用雙因素驗證;所有 PHP 發行版則暫停兩週,同時稽核各儲存庫是否還有其他竄改。這兩筆提交從未送達任何一位終端使用者。
代價為何
對終端使用者沒有影響——兩筆提交在任何發行前都已被還原。真正的代價是該專案自家的 git 基礎設施,以及所有 PHP 發行版凍結兩週。
為何發生
該專案自行運行 git 伺服器,並以自製的 karma 系統管理寫入權限——這是一套量身打造的驗證介面,規模不大的核心團隊必須完全靠自己防禦。
該伺服器上的提交身分依賴推送時的簽核,而非密碼學簽章,因此任何控制該伺服器的人,都能以專案中最受信任的名義提交。
這層偽裝是針對審查流程量身調整的:一個字的「typo」修正,位於幾乎所有地方都會引入的標頭檔中,並加上前綴以完全略過 CI。
拯救了該專案的 GitHub 鏡像,多年來一直只是次等副本——之所以能派上用場,僅因團隊選擇在緊急情況下將它們提升為正式來源。
教訓
自行託管版本控制,對專案毫無好處,只換來一個必須獨自防禦的攻擊面。鏡像唯有在你需要它們的那一刻就能成為真相來源,才值得存在。
後續發展
php.net 在調查進行期間正式將發行版暫停兩週,並表示這些惡意提交「立即被發現並還原,因而從未送達終端使用者」。各儲存庫經過審查,確認除這兩筆提交外並無其他竄改;GitHub 成為 php-src 永久的正式家園,組織成員資格如今要求 2FA。後門的程式碼註解——「sold to zerodium, mid 2017」——仍公開留存於提交歷史中;該供應商從未證實買下它。
追溯依據
參考來源
- Changes to Git commit workflow (php.internals #113838) news-web.php.net · news-web.php.net ·
- PHP releases on hold php.net · php.net ·
- [skip-ci] Fix typo (commit c730aa26) GitHub php/php-src · github.com ·
- PHP's Git server hacked to add backdoors to PHP source code BleepingComputer · bleepingcomputer.com ·