網站被植入惡意程式怎麼辦?從最近三起實際案例看修復流程

最近一個多月,我們陸續處理了幾起客戶網站「被植入」的事件:有的是訪客瀏覽器被偷偷載入外部腳本,有的是網站底下多出幾百個賭博、詐騙垃圾頁被搜尋引擎收錄,也有網站突然整站顯示 500 錯誤,追查後才發現是攻擊者裝進來的後門外掛自己寫錯了。

這篇文章把這幾起案例整理出來,不公開任何網站名稱、網址或客戶資料,只說明它們是怎麼被植入、我們怎麼找出來,以及實際的修復步驟。如果你的網站也遇到類似狀況,可以照著這個流程檢查。

三起案例的入侵入口、植入內容與被發現的症狀對照
圖:三起案例的入口、植入內容與症狀

一、案例一:設定檔被插入外部腳本,攻擊者潛伏好幾年

症狀

一個多年沒有更新內容的 WordPress 網站,網站主準備重建前請我們先檢查。直接連到主機查看原始回應時發現,每一個頁面(連 REST API 的 JSON 回應也是)最前面都多了一行 <script src="外部網域">,訪客一打開網站,瀏覽器就會去載入攻擊者的 JavaScript。

植入位置

  • wp-config.php 第一行:在 <?php 之前被加了一行 HTML。因為每個 PHP 請求都會載入這個設定檔,所以全站都中招。從網站流量統計回推,這段腳本最晚已經存在半年以上。
  • 資料庫裡有 6 個不明的管理員帳號,建立時間橫跨好幾年,分成幾批出現,其中幾個直到幾個月前都還有登入紀錄。有一個帳號的權限是直接寫入資料庫的,不是透過 WordPress 正常流程建立。
  • 一個「後台 PHP 主控台」外掛:任何管理員都能在後台直接執行 PHP 程式。安裝時間就在其中一批不明管理員出現前後,判斷是攻擊者拿到管理權限後裝來方便操作的工具。
  • 另有一個沒有任何檔案的空外掛目錄,疑似是假外掛清理後的殘留。

入侵路徑判斷

網站核心與外掛長年沒有更新,其中一批不明管理員的建立時間,剛好落在某個熱門快取外掛的「未驗證權限提升」漏洞公開後不久,該漏洞可以讓攻擊者直接建立管理員。由於當時的存取日誌已經不在了,只能列為「最可能的入口」,無法百分之百證實。這也說明了日誌保留天數太短,出事後就很難追查。

值得注意的地方

我們用主機上常見的惡意程式掃描工具掃了十幾萬個檔案,三個主要的植入跡象一個都沒有抓到:wp-config.php 前面的一行 script 不像典型的 PHP 木馬,不明管理員只存在資料庫裡,主控台外掛本身也是「正常」軟體。真正發現問題的方法,是直接連到主機看原始回應、核對核心檔案的官方校驗值,以及逐一列出所有管理員帳號。事後我們也把這幾種特徵加進自家的弱點掃描系統。

修復方式

這個網站本來就只有一篇文章、一個頁面,網站主決定不救了,直接重建。我們的處理順序是:

  1. 先把整站檔案打包,計算 SHA256 雜湊,主機與本地各保存一份,作為證據。
  2. 確認同一台主機上其他網站的 wp-config.php 都沒有同樣的植入(只有這一站受影響)。
  3. 刪除整個主機帳號,重新開立新帳號,不沿用任何舊檔案、舊資料庫與舊密碼。
  4. 安裝最新版 WordPress,並以 wp core verify-checksums 核對所有核心檔案與官方一致;wp-config.php 權限設為 600。
  5. 新站建置期間先設定不讓搜尋引擎收錄。

如果網站內容很多、必須保留,就不能直接砍掉重練,要改用本文第四節的完整清除流程。內容可以匯出搬過去,但外掛、佈景請從官方重新下載,不要從被入侵的網站複製程式碼。

二、案例二:舊版網頁編輯器被拿來上傳數百個 SEO 垃圾頁

症狀

主機的防毒系統跳出一則告警,指出某個網站的編輯器上傳資料夾裡有一個可疑 HTML 檔。實際進去看,同一個資料夾裡總共有 269 個 HTML 檔,全部都是中文賭博、詐騙類的 SEO 垃圾連結頁,寫入時間橫跨半年多。防毒軟體只抓到最新的那一個。

更麻煩的是,這些頁面已經被各大搜尋引擎與 AI 爬蟲收錄,短短三週就有三千多次存取。對網站主來說,最直接的傷害是網站名聲與搜尋排名被拿去替別人導流。

上傳途徑

這個網站是自行開發的程式,後台使用一套 2012 年的舊版網頁編輯器(KindEditor 4.0.6)。編輯器附帶的上傳程式其實是官方範例程式,完全沒有登入檢查,而且允許上傳 html 檔,這是早已公開的任意檔案上傳漏洞(CVE-2017-1002024)。我們比對了垃圾頁的檔名(由上傳程式依時間自動命名)與檔案修改時間,兩者幾乎完全吻合;FTP 與主機檔案管理員都查無上傳紀錄,可以確認就是透過這支上傳程式寫進來的。

修復方式

  1. 備份與隔離:先備份原本的編輯器程式,再把 269 個垃圾頁打包(附 SHA256)後移到網站目錄以外的隔離區,先不刪除。
  2. 加上登入檢查:在上傳程式與檔案瀏覽程式開頭,加上和後台相同的登入判斷,沒有登入一律回 403。
  3. 縮小允許的副檔名:從上傳允許清單移除 htm、html。
  4. 封鎖範例程式:用 .htaccess 拒絕存取編輯器附帶的 demo 程式。
  5. 上傳目錄禁止執行:在上傳資料夾放一份 .htaccess,關閉目錄列表、PHP 與 CGI 執行,並拒絕 php、phtml、phar、html、svg、js 等可執行或可被瀏覽器解析的類型。
  6. 重新掃描編輯器目錄,確認沒有其他惡意檔案。

修復後實測:未登入上傳回 403、範例程式回 403、原本的垃圾頁網址回 404,在上傳目錄放測試 HTML 會被擋,圖片與一般文字檔仍可正常讀取,網站首頁與編輯器腳本不受影響。

後續還要做兩件事:到 Google Search Console 申請移除垃圾頁網址,並長期考慮把編輯器升級到有維護的版本。同一台主機上的其他網站如果也用了同一套舊編輯器,也要一起檢查。

三、案例三:上次入侵沒換掉的遠端金鑰,讓攻擊者又回來了

症狀

一個 WooCommerce 購物網站的首頁突然變成 HTTP 500。查看錯誤日誌,發現 500 是一個當天才出現、偽裝成「導覽列工具」的外掛造成的。

這個外掛其實是後門:只要網址帶上特定參數與通行碼,就會自動建立一個具有安裝外掛權限的帳號,並直接替攻擊者登入後台。好在攻擊者把程式寫錯了,PHP 每個請求都直接發生致命錯誤,後門帳號反而沒有建立成功。如果程式沒寫錯,網站看起來一切正常,網站主恐怕完全不會發現。

植入途徑

比對存取日誌後發現,攻擊者是透過備份外掛附帶的遠端管理功能(UpdraftCentral),送出「上傳並啟用外掛」的指令。前一個多月就有好幾個不同來源反覆嘗試,都因為解密失敗而沒有成功;直到當天,某個來源使用的金鑰是有效的,一分鐘內就裝好並啟用了後門外掛。

這把遠端管理金鑰是好幾年前建立的,連同私鑰存在資料庫裡。這個網站幾個月前曾被入侵過,攻擊者當時取得了完整資料庫,但那次修復只清掉了木馬,沒有撤銷這把金鑰。換句話說,攻擊者是拿著上次偷走的鑰匙,從正門走進來的。

修復方式

  1. 保存修復前完整資料庫、後門外掛原檔、相關日誌與設定值,放在只有 root 可讀的目錄並附雜湊值。
  2. 把後門外掛移出外掛目錄,並從資料庫的啟用外掛清單中移除。
  3. 刪除資料庫中的遠端管理金鑰,以及後門留下的通行碼與暫存資料。
  4. 確認後門帳號不存在、管理員只剩原本的帳號、必載外掛(mu-plugins)目錄是空的,並列出近期被修改過的所有 PHP 檔逐一確認。
  5. 順手修掉一個長期存在的金流外掛錯誤,它讓 REST API 一直回 500。
  6. 測試首頁、商品頁、購物車、結帳、登入頁與 REST API 都恢復正常,帶後門通行碼的網址也只會看到一般首頁。

接下來最重要的一步是全面更換憑證:WordPress 管理員密碼、wp-config.php 的 salts、資料庫密碼、主機與 FTP 密碼,以及金流 API 金鑰。只要上次被偷走的資料庫裡有什麼金鑰,就要當作已經外洩。

四、從三起案例整理出的修復流程

保全證據、隔離、清除、封住入口、輪替憑證、驗證監控六個修復步驟
圖:網站被植入後的修復流程

三個案例的入口完全不同,但修復時要做的事情其實是同一套流程,而且順序很重要:

1. 保全證據

動手清理之前,先把網站檔案、資料庫與存取日誌完整備份,並計算雜湊值。這份備份不是拿來還原的(裡面就有木馬),而是用來追查入口、比對時間軸。很多主機的存取日誌只保留幾週,先存下來,之後才有東西可以查。

2. 隔離,不要急著刪

可疑檔案先移到網站目錄以外、權限受限的隔離區。直接刪除的話,萬一判斷錯誤就無法復原,也失去了分析惡意程式行為的機會。

3. 清除:檔案、資料庫、排程都要看

設定檔開頭、資料庫、外掛目錄、上傳目錄、必載外掛與排程、後台工具外掛六個常見藏匿位置
圖:惡意程式最常藏匿的位置

只刪掉掃毒軟體報出來的檔案通常不夠。以 WordPress 為例,至少要檢查:

  • 核心檔案:用 wp core verify-checksums 與官方校驗值比對,有差異就從官方重新覆蓋;外掛可用 wp plugin verify-checksums --all。
  • 設定檔與入口檔:wp-config.php、index.php、.htaccess 的開頭與結尾,特別是 <?php 之前有沒有多出內容。
  • 使用者:wp user list --role=administrator 列出所有管理員,逐一確認建立時間與是否為本人;編輯者帳號也要看有沒有被額外加了權限。
  • 外掛與佈景:不認識的名稱、沒有版本資訊、官方目錄找不到的外掛,以及可以在後台執行 PHP 或管理檔案的工具型外掛。
  • 必載外掛(mu-plugins)與排程(wp-cron、主機 crontab):這是最常見的「刪了又長回來」的原因。
  • 上傳目錄:圖片資料夾裡不應該有 .php,也不應該有成批的 .html。
  • 資料庫選項:網站網址、遠端管理金鑰、不明的自動載入選項。

4. 封住入口

找不出入口就清除,等於把門擦乾淨但沒鎖上。依照案例的不同,入口可能是過期的外掛、沒有驗證的上傳程式,或是沒撤銷的金鑰。更新到最新版、移除用不到的程式、補上登入檢查、在上傳目錄禁止執行程式,都是這一步要做的事。

5. 輪替所有憑證

案例三就是最好的教訓:只要資料庫或檔案曾經被攻擊者讀過,裡面所有的密碼、金鑰都要當作已經外洩。管理員密碼、salts、資料庫密碼、主機/FTP 密碼、第三方 API 金鑰(金流、物流、郵件)、遠端管理與備份服務的連線金鑰,全部重新產生。

6. 驗證與持續監控

修完之後要實際測試:前台各主要頁面、後台登入、購物流程、REST API 是否正常;原本的惡意網址是否已經 404;攻擊手法重來一次是否會被擋下。之後幾週持續觀察錯誤日誌與存取日誌,並到 Search Console 檢查有沒有殘留的垃圾索引。

五、告警不一定等於被入侵

同一段期間,我們也收到不少「可疑程序」告警,例如某個網站的後台設定頁 PHP 程序執行太久。追查後發現,那是外部掃描器在幾分鐘內對網站探測幾百個常見的後門路徑,大部分被防火牆擋下,回 200 的只有登入頁等正常頁面;核對該 PHP 檔與官方校驗值一致,上傳目錄也沒有 PHP 檔,最後判定只是掃描,沒有入侵。

這種判斷同樣要靠證據:告警時間對得上哪一筆請求、那筆請求的回應是什麼、相關檔案是否與官方一致。不要一看到告警就慌著刪檔或重灌,也不要因為「大部分都是誤報」就略過不查。

六、修好之後:防止再被植入

移除不用的程式、定期更新、盤點帳號金鑰、上傳目錄禁止執行、兩步驟驗證、異地備份與日誌
圖:防止再被植入的六個習慣
  • 移除不用的東西:停用的外掛、舊佈景、編輯器範例程式、測試檔,沒在用的程式就是沒人顧的入口。
  • 定期更新:核心、外掛、佈景都要更新。漏洞一公開,自動化的批次掃描通常比網站主更新得還快。
  • 定期盤點帳號與金鑰:每個管理員、每把遠端管理金鑰、每個應用程式密碼,都要知道是誰的、還需不需要。
  • 上傳目錄禁止執行:用 .htaccess 或主機設定,讓上傳資料夾無論放進什麼都不能被執行。
  • 兩步驟驗證與登入限制:搭配強密碼,降低暴力登入與撞庫的風險。
  • 異地備份與足夠的日誌:備份讓你能還原到乾淨狀態,日誌讓你查得出入口。案例一就是因為日誌太短,最後只能推測入口。

結語

這三起案例有一個共同點:網站主都不知道自己被入侵了。案例一潛伏了好幾年,案例二的垃圾頁被收錄了半年多,案例三如果不是攻擊者自己寫錯程式,也不會被發現。網站被植入往往不會有明顯的症狀,所以「定期檢查」比「出事再處理」重要得多。

如果你懷疑自己的網站有異狀,或是想請我們協助檢查,歡迎透過客服系統與我們聯繫。