「關機前套用更新」這支程式,
跑了 17,613 分鐘

速讀:5 分鐘掌握重點
一句話結論
業務早已失效的語音 AI 排程機持續單核滿載,最耗 CPU 的行程名稱是假的。
他要的是什麼
一台對外開著免驗證 RCE、又沒人登入的機器,是最理想的礦工宿主:穩定、安靜、電費別人付。
你今天做一件事
「沒事做卻很忙」是最早也最便宜的訊號。閒置主機的 CPU 告警加上出向流量管控,第一天就能抓到。 直接去做 →
哪些能信
5 可證明2 推論1 缺口 每個主張都標了等級,缺口是誠實寫出來的。
破口速查
受害端一律去識別化;寫「未公開」代表公開資料查不到。
伺服器服務商
AWS EC2 t2.small(ap-northeast-1,業務早已失效、沒人登入的排程機)
網路服務商
AWS VPC/Security Group:開發用 API 經 nginx 反向代理整條對公網開放,egress 未管制,可直連境外礦池;攻擊端聚合代理在 OVH 機房
漏洞出現的地方
應用/主機層:開發用 API 綁在 0.0.0.0 又免驗證,執行它的應用帳號有 NOPASSWD:ALL,從公網到 root 只要兩步
馬上自我檢測
uptime ps aux --sort=-%cpu | head -5
漏洞修補方式
開發/內部 API 一律綁 127.0.0.1 或內網;移除應用帳號的 NOPASSWD:ALL;Security Group 只開必要的埠並管制 egress;閒置主機做 CPU 高載與礦池埠告警
有 15 分鐘再看:其餘 2 點
  1. ss 抓到連往 OVH 機房 3333 埠的 ESTABLISHED 連線,發起程序叫 log,執行檔在 /var/log/log。
  2. 讀取可疑檔案就 segfault,證實 LD_PRELOAD rootkit;再找到三重自啟與免密碼 sudo 的後門帳號。

一支只該在關機前跑幾秒的程式,累積了超過十二天的 CPU 時間。順著這個矛盾,五條線索逼出 rootkit、礦工與後門帳號,全程唯讀不動任何檔案。

挖礦劫持 · 偵測篇·2025-09-24 → 2026-09-15·全文 12 分鐘
本篇目錄
:3333load 1.7 · cpu 100% · 業務已失效

從一個矛盾開始:沒事做,卻很忙

2026-09-15 那天,有人終於登入了一台沒有人在看的機器。uptime 回來的 load average 是 1.7,一顆核心整顆滿載;而這台機器該做的工作,其實在數個月前就停止產出了。最耗 CPU 的那支行程更奇怪:名字寫著「關機前套用更新」,累積 CPU 時間卻是 17,613 分鐘,換算超過十二天。一支只該在關機前跑幾秒的程式,不可能累積出這個數字。

順著這個矛盾追下去,一天之內挖出來的東西是:一支 XMRig 礦工、一支把檢視工具全部接管的 LD_PRELOAD rootkit、三個互為備援的開機自啟點,以及一個偽裝成系統帳號、掛著免密碼 sudo 的後門。入侵起點釘在 2025-09-24,也就是說它在這裡待了大約 356 天,沒有一天被打擾。整個過程全程唯讀:沒有殺行程、沒有掃毒、沒有刪檔,主機最後保持 stopped,等待砍掉重建。

這一篇只講偵測:那個矛盾怎麼一步步變成五條互相印證的線索,以及為什麼在 rootkit 面前,ps 的輸出不能當證據。錢包怎麼在礦工還活著的時候撈出來,見取證篇;離線掛載與四封檢舉信,見舉發篇。

它原本該做什麼:一支每 15 分鐘失敗一次的排程

要判斷「異常」,得先知道正常長什麼樣。這台 t2.small 唯一的業務是一支處理員工錄音的流程:每 15 分鐘由 crontab 觸發,讀試算表拿到錄音清單、下載原始檔、轉逐字稿、改寫、合成語音,再回寫試算表。

問題是這條流程早就死了。它依賴的服務帳戶在數月前被刪除,每一次執行都在 log 尾端固定拋出 Invalid JWT Signature,業務面沒有任何產出。沒有產出,卻持續滿載——這正是開頭那個矛盾的由來。

技術面的形狀很單純:nginx 反向代理擺在最前面,後面是一支 Flask 開發用 API 綁在 5000 埠,整條路徑對公網開放;執行 Flask 的是應用帳號 ubuntu,而 ubuntu 在 sudoers 裡有 NOPASSWD:ALL;呼叫 AI 服務的兩把 API key 以明文常數寫死在排程腳本裡。至於原本就有的防護,講白了只有一項幫上忙:這台沒有掛 IAM Role,也沒有存放雲端憑證,所以橫向擴散到雲端帳號的路徑被擋住。那是設定的結果,不是設計的結果——egress 沒有管制、沒有檔案完整性監控、log 沒有集中外送、閒置高載沒有告警,一樣都沒有。

把這台主機上「等於憑證」的東西全部攤開,會發現真正外洩的不只是 API key,還包括攻擊者自己新增的登入能力;而最後一列是他用來收錢的識別,擁有者是他,不是我們。

leaked credentials這次外洩了什麼:服務、用途、存在形式
服務憑證類型用途存在形式放在哪權限範圍狀態
受害主機(Linux 本機帳號)攻擊者自建的可登入帳號與密碼攻擊者的長期登入管道,偽裝成系統帳號/etc/passwd 條目(有 home 與 shell)+ /etc/shadow 密碼雜湊受害主機本機搭配 sudoers 規則等同 root2026-09-15 起主機隔離、保持 stopped,待砍掉重建
受害主機(sudo 授權)免密碼 sudo 規則(等於免憑證提權)讓應用程式帳號與後門帳號直接取得 root明文 sudoers 檔案中的一行 NOPASSWD:ALL 規則受害主機 /etc/sudoers以 root 執行任意指令重建時一併移除
對外開放的開發用 API沒有憑證:端點本身就是入口原為內部測試用途,卻經反向代理對公網開放HTTP 請求參數被拼進 shell 指令字串受害主機的 Flask 應用以應用程式帳號執行指令,再經 sudo 到 root入侵前 log 已被清除,無法直證是由此進入
AI 服務供應商 API(語音轉文字與文字改寫)長期有效的 API key × 2排程流程呼叫轉逐字稿與改寫原始碼常數:明文字串寫死在排程腳本裡受害主機的腳本目錄(root 可讀)該供應商帳號的 API 用量與帳單必須視為已外洩並撤銷;是否被盜用需查供應商用量
同叢集主機的 SSH 金鑰/共用密碼可能被沿用的登入憑證維運人員登入其他主機格式符合 PEM 私鑰或共用密碼,但無法確認這台上是否留有可用的一份未取得完整盤點若共用即等於橫向擴散路徑gap:僅能假設有,處置時一併輪替
攻擊端:礦池收款識別(SupportXMR/C3Pool)門羅幣錢包位址+worker 代號把偷來的算力換成錢礦工設定 JSON 的 user/pass 欄,只常駐在行程記憶體攻擊者的 XMRig 設定與 Stratum 登入封包攻擊端 IOC,不是我方憑證已提交礦池;SupportXMR 回覆已將該位址 denylist
憑證本身已遮蔽或失效;這裡只說明它是哪個服務、拿來做什麼、以什麼形式存放,讓你能對照自己的環境。

這張表裡沒有一把是被暴力破解的。攻擊者拿到的是「執行權限」,然後自己替自己發了一組憑證。

一個密碼設定日,釘住了 356 天的起點

時間軸的起點不是靠存取紀錄推出來的——入侵前的網站 log 被清掉了。真正釘住日期的是兩個互相獨立的東西:後門帳號的密碼設定日,以及離線磁碟上惡意檔案的時間戳。它們指向同一天。

  1. 2025-09-23
    後門帳號 systemd 建立
    密碼設定日與惡意檔案時間戳一致。
  2. 2025-09-24 05:30
    植入 XMRig、rootkit sshdd.so、第二礦工
    離線磁碟分析取得檔案時間戳。
  3. 2025-09-24 00:00 之前
    入侵前的網站存取 log 全數消失
    access.log 最早一筆停在 9/24 00:00;攻擊者清過 log。
  4. 2025-09-24 → 2026-09-15
    潛伏約 356 天,單核持續替攻擊者挖礦
    業務失效後無人登入,所以無人發現。
  5. 2026-09-15
    偵測 → 活體取證 → 錢包 → 離線鑑識 → 關機封存
    全程唯讀。

中間那一格橫跨將近一年,卻只有一行字可寫,因為那一年什麼事都沒發生——對我們而言。最後一格相反,一行字裡塞了一整天:分鐘級的取證順序在取證篇,離線掛載與舉發在舉發篇。

從公網到 root 只有兩步,剩下的時間都在收租

七個階段依 ATT&CK 順序排列,不是依時間:提權其實發生在持久化之前,而且前四格全部擠在 2025-09-24 那一天完成。之後將近一年的時間,全部落在最後一格。另外有兩個技術橫跨整條鏈——rootkit(T1014)與偽裝成系統檔名的行程(T1036.005)——它們不屬於任何單一階段,卻是他能撐 356 天的真正原因。

發生了什麼 · MITRE ATT&CK T1190
一支開發用 Flask API 經 nginx 反向代理整條對公網開放,沒有任何驗證,端點把參數拼進 shell 指令字串。
當時留下的訊號
ss -tlnp 可見 5000 埠監聽;nginx access log 有陌生來源打 /api/ 路徑(本案已被清除)。
能擋下它的控制
開發/內部 API 一律綁 127.0.0.1 或內網 SG;對外只留反向代理後有驗證的路徑。

把這七格的 signal 欄連起來讀,會得到一份很便宜的偵測清單:監聽埠、preload 檔、新 systemd unit、對外連線、閒置高載。沒有一項需要買工具,而我們一項都沒在看。

五條線索:每一條都在說「ps 不可信」

鑑識不是等掃毒軟體給答案,而是看到一個不合理就追問為什麼。「沒事做卻很忙」是第一個該追的矛盾——它不一定是挖礦,合法報表也會把 CPU 吃滿,所以它不能定案,只能當起點。真正把它推到定案的,是後面五條線索彼此咬合的方式。

第一條:最耗 CPU 的行程在說謊

ps aux --sort=-%cpu | head -3READ-ONLY
USER   PID  %CPU %MEM     TIME  COMMAND
root   404  25.9  8.3 17613:25  /usr/bin/python3 .../unattended-upgrade-shutdown --wait-for-signal
mysql  562   0.3 18.2   218:45  /usr/sbin/mysqld

# 一支「關機前套用更新」的程式燒了 17,613 分鐘 CPU?不合理。

第二條:換個角度,不看行程看連線

sudo ss -tnp | grep -v :22READ-ONLY
State  Local Address:Port   Peer Address:Port      Process
ESTAB  10.x.x.x:36708       141.94.96.71:3333      users:(("log",pid=479))

# 141.94.96.71 → OVH SAS / 法國。port 3333 = 礦池 Stratum 常用埠。
# 發起程序名叫 "log",執行檔卻是 /var/log/log。

第三條:檔案一被讀取就崩潰

想算 /var/log/log 的雜湊去比對公開情資,結果一碰就 core dump;連 /etc/ld.so.preload 都讀不了。

rootkit 讀取即崩潰READ-ONLY
$ sudo sha256sum /var/log/log
Segmentation fault      /var/log/log (core dumped)

$ sudo cat /etc/ld.so.preload
Segmentation fault      /etc/ld.so.preload (core dumped)

# 一般檔案讀取不會 segfault。這是 LD_PRELOAD rootkit 的防偵察反應,
# 也解釋了為何 ps 抓不到礦工:它被藏起來了。

第四條:不只一個開機自啟點

/proc/479/exe 是 rootkit 攔不到的路徑,順著它確認了礦工本體就是 9.4 MB 的 /var/log/log。接著往持久化找,一找就是三個。

systemd + crontabREAD-ONLY
$ cat /etc/systemd/system/log.service
[Unit]
Description=log Plugin Service
[Service]
ExecStart=/usr/bin/log -sshd
Restart=always
User=root

$ sudo crontab -l
@reboot /var/log/log > /dev/null 2>&1 & disown

# 偽裝成「log 外掛服務」+ root 的 @reboot,加上 ld.so.preload,三重保險。

第五條:一個「像系統」的帳號,還免密碼 sudo

/etc/passwd + sudoersREAD-ONLY
$ grep -vE 'nologin|false' /etc/passwd
root:x:0:0:root:/root:/bin/bash
ubuntu:x:1000:1000:Ubuntu:/home/ubuntu:/bin/bash
systemd:x:1001:1001::/home/systemd:/bin/bash      # ← 假的系統帳號

$ sudo grep -r '' /etc/sudoers*
systemd ALL=(ALL) NOPASSWD:ALL

$ sudo chage -l systemd | head -1
Last password change : Sep 23, 2025               # ← 入侵時間錨點

五條之外:兩件只能推論、一件根本查不到

五條線索彼此咬合,把「疑似」推到了「確診」。但同一份材料也劃出了三條界線,它們比前面五條更值得寫清楚。

破口在應用層,原因在沒有人認領這台機器

表面上的結論是一支叫 log 的程序長期佔滿一顆核心,替攻擊者跑 RandomX 運算並向境外礦池提交 share。這是五條線索可以證明的部分。

再往下一層是技術原因,而它短得令人不安。那支開發用的 Flask 對外開了一個端點,把使用者傳入的參數直接拼進 shell 執行,沒有驗證也沒有白名單:

# 使用者可控的 param_n、param_data 直接進了指令字串
cmd = "python3 /var/www/app/" + param_n + ".py " + param_data
subprocess.run(cmd, shell=True)

整條路徑是公網 → nginx :80 → Flask :5000 → 指令注入 → 以 ubuntu 執行 → ubuntu 有 NOPASSWD:ALL → root。兩步,沒有第三步。

但技術原因只解釋了「怎麼進來」,解釋不了「為什麼可以待 356 天」。往上追到管理層,答案就不再是技術問題:業務在數月前就失敗,每 15 分鐘失敗一次,卻沒有人關掉這台、沒有人認領它,排程失敗也沒有人收信。這一層只能寫成推論,因為「沒有人負責」本來就不會留下紀錄。再往上一層的制度原因同樣是推論:缺少資產盤點與閒置資源回收機制,也缺少「這台主機該有多忙」的基線、egress 管制與 log 集中外送這三條共通防線。第二層決定了他怎麼進來,第三、第四層決定了他可以待多久。

訊號一直都在,只是每一條都沒有人負責

這 356 天裡,沒有一天是「沒有跡象」的。

最明顯的是閒置主機單核滿載將近一年,但沒有 CPU 高載告警,也沒有「這台該有多忙」的基線——沒有基線,滿載就只是一個沒人解讀的數字。第二條是排程每 15 分鐘失敗一次:業務死了,通知沒有寄給任何人,也就沒有人想到要把這台關掉。第三條是整年對境外 3333 埠的主動連線,出向流量既沒有人看,也沒有 VPC Flow Logs 可以事後回溯。

另外三條則是被攻擊者主動蓋掉的。ps、ls、top 的輸出被 rootkit 接管,而我們沒有任何執行期監控,等於只用原機工具看原機——拿嫌犯的證詞當證據。ld.so.preload、systemd unit、/etc/passwd 三個最該盯的位置都被改過,而沒有檔案完整性監控可以比對基準。最後,入侵前的存取紀錄被清得乾乾淨淨,因為 log 就留在被入侵的那台上:攻擊者拿到 root,也就等於拿到歷史的編輯權。

六條裡有五條,補起來只需要一條告警規則或一份基線,不需要買新工具。

那一天:全程唯讀,沒有動過任何一個檔案

處置的第一個決定不是「清掉它」,是「先不要碰」。

偵測本身花掉的是判讀時間而不是工具時間:從那個矛盾出發,依序走完負載與行程、對外連線、讀檔 segfault、三重自啟、可登入帳號五條線索,確認礦工、rootkit 與後門帳號同時存在。確認之後的第一個動作是隔離並保持 stopped,而不是殺行程或掃毒——在 rootkit 底下,殺行程只是讓它從你看不見的地方重來,掃毒則會把時間戳全部破壞掉。過程中曾經有一次重開機,@reboot 把礦工拉了回來;這既是失誤,也意外讓活體取證有了機會(這一段的判讀見取證篇)。

清除這一格,我們刻意留空。後門帳號與 sudoers 規則都沒有在原機刪除,整台以 stopped 狀態封存為證物,等待砍掉重建;腳本裡兩把 API key 則視為已外洩,必須撤銷並回供應商後台查同期用量。至於復原,這台的業務早已失效,沒有任何服務需要恢復,所以也沒有復原壓力——這是全案唯一一件輕鬆的事。

這件事被發現,靠的不是監控

先講難看的部分:沒有任何一套系統告訴我們這台出事了,是有人終於去看了一台沒人看的機器。業務數月前就失效,主機卻一直開著,沒有人回收也沒有人認領;單核滿載將近一年完全沒有告警,最便宜的那條防線根本沒設;兩把 API key 明文寫死在腳本裡,也是出事才盤點到。加上處置過程中那次重開機——揮發證據等於重來一次,運氣好才沒有全丟。

做得好的部分幾乎都發生在追查的那幾個小時裡:從矛盾一路追到底,沒有停在「重開機看看」;不信任 ps 與 top,改用 ss 與 /proc/PID/exe 交叉驗證,才認出礦工本體;找到一個自啟點之後繼續翻,把三處一次盤完;全程唯讀,時間戳與證據完整保留。把兩邊排在時間軸上看,結論很清楚:判讀能力救回了這件事,但判讀能力本來不該被用在第 356 天。

之後我們改了什麼

排序原則只有一條:先做不必改架構就能做完的,再做需要改設計的。

當天就能做完的有四件:主機維持隔離與 stopped,不重開機、不掃毒、不刪檔;撤銷腳本裡兩把 AI 服務 API key,並到供應商後台查同期用量;把開發用 API 從公網下架,Security Group 收斂,5000 埠不對外;拿本案 IOC(錢包、礦池 IP、/var/log/log、ld.so.preload、後門帳號、@reboot 排程)掃過同叢集其餘主機。

接下來一個月做的是把臨場判斷變成預設值:開發/內部 API 一律綁 127.0.0.1 或內網,對外只留有驗證的路徑;移除應用程式帳號的 NOPASSWD:ALL,必要時限定到單一指令;閒置主機加 CPU 高載告警(規則名我們取 idle-but-busy-e5t,翻告警一眼就對得上這篇),並對 3333、5555、9443、14444 等礦池埠告警;金鑰移出原始碼改用 Secrets Manager,並在 CI 跑 gitleaks;log 集中外送,讓攻擊者清不掉時間軸。

這一季以後要處理的是制度:建立資產盤點與閒置資源回收,沒在用的主機就關掉,每一台都要有負責人;egress 白名單管制,未知境外目的地預設拒絕;部署檔案完整性監控,盯 ld.so.preload、/etc/systemd/system、crontab、/etc/passwd;把「先採活體、再隔離、不重開機」寫進事件處置 SOP 並定期演練。

這些事要花多少錢,可以和這台機器替別人賺的錢放在一起看。下面這張表把雙方的帳並排:我們付的是雲端機時,他賺的是門羅幣。

cost這台機器替別人挖礦的帳:用量、單價、期間成本
項目用量單價期間US$NT$舉證
EC2 t2.small(排程機)
估:業務早已失效,這 356 天的機時等於全部白付
8,544 小時(356 天 × 24)US$0.0304/小時2025-09-24 → 2026-09-15259.748,312期間可證明(後門帳號 chage -l 密碼設定日+離線磁碟檔案時間戳);金額為牌價 × 機時推算,未匯出 Cost Explorer 明細
EBS 根磁碟 gp2
估:文中未載明容量,以常見的 30 GB 根磁碟計
30 GB × 11.7 個月US$0.12/GB·月同上42.121,348容量為假設值,無帳單佐證;要證明需 Cost Explorer 的 EBS:VolumeUsage 明細
公網 IPv4 位址
估:2024-02 起 AWS 對所有公網 IPv4 位址計費
8,544 小時US$0.005/小時同上42.721,367主機有對外服務(nginx 對公網開放)可證明位址存在;金額為牌價推算
對外流量(提交 share)
估:挖礦幾乎不吃頻寬,所以帳單上看不出任何異常——這點比金額重要
約 2 GB/年每月首 100 GB 免費,超出 US$0.114/GB同上0.000無 VPC Flow Logs,流量僅由 Stratum 協定特性推估
我方偵測與判讀人力
估:偵測這件事本身,比這台機器一年的機時還貴
約 1 人日:五條線索判讀、活體取證、叢集盤點顧問日費 NT$20,000~35,000(估),取中間值 NT$27,5002026-09-15859.3827,500當日鑑識步驟推估,無正式工時單;日費為假設值
攻擊者的植入成本
估:他真正的成本只有掃描時間
1 次入侵,工具全是現成品公開的 XMRig 與 rootkit,取得成本 02025-09-240.000離線鑑識取得的 binary 為公開版本 XMRig 6.22.2,非自行開發
攻擊者的聚合 proxy 主機
估:以入門級 VPS 月租推估;同一台同時吃上百台肉雞的算力
1 台歐洲機房 VPS × 12 個月約 US$7/月356 天84.002,688proxy 位置可證明(ESTABLISHED 連線至該 IP,OVH SAS);租金為公開牌價推估
攻擊者從本機賺到的
估/推論:22.19 XMR 是整個 C4 活動的總額,不是本機損失
1~2 KH/s ÷ 148 KH/s × 22.19 XMR ≈ 0.15 XMR(取下限)≈ US$509/XMR(依下一篇 2026-09-15 匯率換算)356 天76.422,445礦池 API 的活動總額可證明,但單機算力佔比無法從礦池端取得,只能用機型算力推估
寫死在腳本裡的兩把 API key 被盜用的金額
必須視為已外洩並撤銷
—————缺口:未取得該供應商同期用量帳單,無法確認有沒有被消費
這台是否還被拿去做別的事(跳板、掃描、寄信)
只能假設有
—————缺口:入侵前的 nginx access.log 已被清除(最早一筆停在 2025-09-24 00:00),且無 VPC Flow Logs
合計(僅可證明列)0.000
匯率假設 1 USD ≈ NT$32。單價為公開牌價或帳單實付,來源:AWS 公開牌價 ap-northeast-1(2026-09 查詢);攻擊者那幾列是推估,列出只為對照。「舉證」欄標明每筆損失的證據來源與等級( 可證明 推論 缺口)。

這張表沒有一列是 proven,因為我們手上沒有這段期間的帳單明細;要把它升級成可證明,只要去 Cost Explorer 匯出這台在該期間的機時就行——這正是下一篇要講的,損害量化用機時,不是用礦池上的數字。而推估的比例已經夠清楚:他從這台賺到約 US$76,我們為此付出的雲端費用約 US$344,還不含工時。挖礦劫持的經濟學就長這樣:他的收益只是你成本的零頭,因為機時、磁碟、位址、電力、人力全部由你支付。

帶得走的五件事

站在他的角度,這是一筆極划算的生意。免驗證的 RCE 加上免密碼 sudo,從公網到 root 只要兩步,成本趨近於零;而一台業務失效、沒人登入的機器,是最穩定的礦工宿主——沒人會看 top,也沒人會重開機。行程改名、rootkit 藏檔、三重自啟、假系統帳號,每一層都只為了拉長「被發現」的時間,因為潛伏的每一天都是收益。worker 代號還暗示這只是他上百台肉雞的其中一台:你不是被鎖定的目標,你是被掃到的。

lessons本案的 5 個教訓
  1. 01
    「沒事做卻很忙」是最早、最便宜、也是唯一真正有效的訊號;追一個矛盾,比買一套工具有用。
  2. 02
    rootkit 在場時,ps、ls、top 全部是攻擊者寫的劇本;用 ss 與 /proc/PID/exe 這類較難攔截的介面交叉驗證。
  3. 03
    惡意程式很少只有一個啟動點。本案有三個,只清掉你先找到的那一個,隔天它就回來了。
  4. 04
    應用程式執行帳號的一行 NOPASSWD:ALL,把公網上的一個請求直接變成 root。
  5. 05
    log 留在被入侵的那台機器上,等於把歷史的編輯權交給攻擊者;入侵前的紀錄一旦被清,進入點就永遠停在推論。

如果只想記十個詞,就記這十個。順序是照「讀完最該帶走」排的,不是照它們在文章裡出現的先後。

keywords重點學習:最需要學習的 10 個關鍵字10 個
  1. 01
    load average(閒置卻高載)
    「沒事做卻很忙」是本案最早、最便宜、也是唯一真正有效的訊號,整條調查從這個數字開始。
  2. 02
    ss -tnp
    ps 會被 rootkit 騙,連線表不會;rootkit 在場時這是少數還能相信的介面。
  3. 03
    ld.so.preload / LD_PRELOAD
    檔案一讀就 segfault 的原因,也是「你看到的系統狀態本身不可信」的根源。
  4. 04
    /proc/PID/exe
    行程名稱可以造假,這條符號連結指向真正的執行檔,礦工本體就是靠它認出來的。
  5. 05
    Stratum 礦池埠(3333/5555/9443)
    主機主動外連這些埠,幾乎等同確診;也是最容易設告警的一條防線。
  6. 06
    持久化(systemd unit/@reboot/preload)
    惡意程式很少只有一個啟動點,只清掉找到的那一個,隔天它就回來了。
  7. 07
    NOPASSWD:ALL
    應用程式執行帳號有這一行,公網上的一個請求就能直接變成 root。
  8. 08
    shell=True 指令注入
    本案破口的形狀:使用者參數被拼進指令字串,「資料」就變成了「命令」。
  9. 09
    chage -l(密碼變更日)
    後門帳號的密碼設定日是最可靠的入侵時間錨點,尤其在 log 被清掉之後。
  10. 10
    egress 管控
    你未必擋得住他進來,但可以讓算力送不出去;礦工連不上礦池就沒有收益。

如果重來一次,前 60 分鐘該這樣排

遇到 root 級 rootkit,不要嘗試就地清毒:你無法確認清乾淨,因為你看到的系統狀態本身就不可信。真正該爭取的是前 60 分鐘的順序。

前十五分鐘只做一件事——確認主機還是不是 running,因為揮發證據存不存在完全取決於這一格。這段時間裡不重開機、不掃毒、不砍檔(@reboot 會讓礦工復活,掃毒會破壞時間戳),只用 ss -tnp 記下礦池連線與 PID,把 uptime、ps、ss 的輸出原樣存下來。

第二個十五分鐘可以開始止血,但止血不等於關機:改 Security Group 切斷對外連線(尤其 egress),先斷掉他的收益,主機仍然留在 running,趁礦工還活著採集行程記憶體與 Stratum 登入封包(做法見取證篇)。接下來的半小時才輪到磁碟:對 EBS 建立 snapshot,再離線唯讀掛到乾淨主機算雜湊(見舉發篇),同時把三個自啟點與 /etc/passwd、sudoers 一次盤完。

一小時之後才是慢工:輪替這台碰過的所有憑證並到各後台檢查異常用量;盤點同一把 pem、同一組密碼有沒有被其他主機共用,一併輪替並移除 DNS 記錄與健康檢查;terminate 時確認磁碟一併刪除、Elastic IP 釋放,從乾淨映像重建,切勿沿用舊 volume;最後補上閒置高載告警、礦池埠告警與 log 集中外送,讓下一次不必靠人眼發現。

就算移除了已知的礦工與後門,也無法證明沒有殘留的第二層植入。唯一能回到已知乾淨狀態的方法,就是砍掉重建。

上面那一小時需要的所有判斷,都可以先在自己手上的主機練一次。下面六步全部唯讀,十五分鐘內做得完。

try today今天就可以來試試約 15 分鐘

全部唯讀,不改任何設定;做完你會知道自己有沒有同樣的破口。

  1. 挑一台你說不清楚「現在在忙什麼」的主機,看負載與最耗 CPU 的行程。
    uptime
    ps aux --sort=-%cpu | head -5
    看到什麼代表什麼:load 接近核心數、但你講不出這個負載對應哪一項業務,就進入下一步;能對上合法工作就先放過。
  2. 看它主動連去哪裡,排除 SSH 與一般 HTTPS。
    sudo ss -tnp | grep ESTAB | grep -vE ':22|:443'
    看到什麼代表什麼:出現 3333、5555、7777、9443、13333、14444 這類埠,或連往你不認識的境外機房,直接當已感染處理,先別關機。
  3. 檢查有沒有 preload 型 rootkit 的痕跡。
    ls -l /etc/ld.so.preload 2>&1
    cat /etc/ld.so.preload 2>&1
    看到什麼代表什麼:檔案存在而你從沒裝過任何 preload 函式庫,或讀取時直接 segfault,代表 ps/ls/top 的輸出全部不可信。
  4. 盤點可以登入的帳號,以及誰有免密碼 sudo。
    grep -vE 'nologin|false' /etc/passwd
    sudo grep -rh NOPASSWD /etc/sudoers /etc/sudoers.d 2>/dev/null
    看到什麼代表什麼:有 home 目錄與 shell 的「系統風格」帳號幾乎必定是後門;應用程式帳號有 NOPASSWD:ALL,等於把提權送給對方。
  5. 盤點開機自啟點。
    ls -la /etc/systemd/system/*.service | tail -20
    sudo crontab -l
    看到什麼代表什麼:服務描述含糊(像是某某 Plugin Service)、或 root 的 @reboot 直接執行 /var、/tmp 下的檔案,都要追到底。
  6. 看這台實際對外開了哪些埠。
    sudo ss -tlnp
    看到什麼代表什麼:5000、8888、9200 這類開發/除錯埠綁在 0.0.0.0,就是對公網開著的門,與 Security Group 對照後排入本週處理。
想看全部細節?

環境背景、時間線、攻擊鏈對照 ATT&CK、關鍵證據、根因、處置與檢討都在完整版,約 12 分鐘。

本文由 AI 團隊協助研究與撰寫,經隱私與事實檢查後發布;證據等級依來源可信度標註。

看完的心情
ai prompt把這篇的切入點帶回你的環境

複製下面這段,貼給你在用的 AI 工具(Claude Code、Cursor、Copilot、ChatGPT 都可以)。它會用本篇的破口位置、自我檢測與學習重點,對你的專案或環境做一次資安掃描,列出潛在問題與建議的調整或加強方式。

預設是唯讀:只發現問題、提出建議,不會改你任何檔案或設定。

§
對應課程模組

M4 主機偵測(12 條獵捕指令)· M5 活體取證與離線鑑識 · 想在自己的環境做一次同樣的盤點,從 自我盤點 開始。

quiz · 資安意識積分
讀完了?1 分鐘重點回饋,累積資安意識積分

4 題單選,答對每題 +10 分、全對再 +10 分。加入 LINE 官方帳號、在 LINE 裡作答,成績與積分直接存入;在網頁作答會拿到一組序號,傳到 LINE 就能累積。

1 分鐘重點回饋 →
newsletter
下一篇拆解直接寄到信箱

每篇新文章一封:三點摘要、關鍵字定義、今天能做的一件事。或先 加入會員 記錄你完成的任務。