Cookbook:場景式審計食譜¶
針對本 repo helper 與 TV channel 的實戰、場景優先導覽。每篇食譜回答一個 具體問題(「有人從新 IP 登入嗎?」),給你完整命令順序,並標出輸出中 要看什麼。
概念分層見 section README。Helper 密集對照表見 helpers.md。
食譜索引¶
- 1. 現在誰登入這台 server?
- 2. 本週有人從新 IP 登入嗎?
- 3. 暴力破解 / password-spray 檢查
- 4. 某使用者今天用了 sudo 嗎?做了什麼?
- 5. 「我懷疑有人從這台跑 nmap」 — Level 2 / Level 3
- 6.
/etc/sudoers被改過嗎?什麼時候?誰改的? - 7. 事後處理:使用者回報帳號被入侵
- 8. 每日 5 分鐘健康檢查(cron 友善)
- 9. 「root shell 做了什麼?」 — sudo log 之外
- 10. 快速關掉吵雜的 audit 規則但保留其他
- 11. 盤點本機 user inventory
- 12. 網路曝險 + persistence:每週掃一次
1. 現在誰登入這台 server?¶
工具:audit-sessions,不需 sudo、不需 auditd。
看第三段(== who -a ==)。每列都是即時 session:
警訊:
- 你不認識的 user。
- 從 public IP 連來的 pts/N(你的 sshd 通常該只看到內網 / VPN 段)。
- 同一 user 多個 pts/N(可能合法 tmux 用法 — 如果是你自己的帳號交叉
比對 tmux ls)。
可疑就跳食譜 #7。
2. 本週有人從新 IP 登入嗎?¶
工具:人眼掃用 audit-sessions;互動瀏覽用 tv sessions。
dump user IP 對。對照你的「已知 IP」清單(VPN 段、家裡 IP、CI runner
IP)。
或互動式:
看到非預期 IP 就跳食譜 #7。
3. 暴力破解 / password-spray 檢查¶
工具:audit-failed-logins (Linux only,sudo)。透過 lastb 讀
/var/log/btmp。
按 IP 群組看模式:
輸出是 count IP。單一 IP 一天超過 ~50 就值得防火牆封。對照你的 sshd
config — 已停密碼驗證 (PasswordAuthentication no) 的話這裡的失敗多半
是噪音;真實故事在 journalctl _COMM=sshd | grep -i 'invalid\|preauth'。
4. 某使用者今天用了 sudo 嗎?做了什麼?¶
工具:audit-sudo。systemd 主機透過 journalctl;其他用
auth.log/secure grep。
輸出列像:
May 08 10:14:22 host sudo[12345]: alice : TTY=pts/0 ; PWD=/home/alice ; USER=root ; COMMAND=/usr/bin/apt update
May 08 10:14:55 host sudo[12399]: alice : TTY=pts/0 ; PWD=/home/alice ; USER=root ; COMMAND=/bin/bash
讀法:<時間> <主機> sudo[PID]: <user> : TTY=<tty> ; PWD=<cwd> ;
USER=<目標> ; COMMAND=<argv0+args>。
/bin/bash 那行就是警訊 — alice 一拿到互動 root shell,sudo log
就停止記她做了什麼。跳食譜 #9 看 auditd 解。
只看失敗:
5. 「我懷疑有人從這台跑 nmap」 — Level 2 / Level 3¶
Level 2 (process accounting,需事先啟用):
顯示 command flags user tty seconds 開始時間。沒有 argv(看不到他掃了
什麼),但能證明跑過。
Level 3 (auditd,本 repo 的 audit-execve):
每個事件含:
- pid=NNNN 與 ppid=MMMM — 用 audit-execve 對 parent 反查上游。
- auid=N — 原始登入 UID(過 sudo / su 也保留)。用 id <auid>
翻成名字找出真正起頭的人。
- 啟用 10-execve.rules 後(role variable auditd_log_all_execve)有
完整 argv;沒啟用就只有 binary 路徑(且預設 nmap 不在 privileged
rule 內 — 要的話擴 05-privileged.rules)。
單台主機加 nmap 到 privileged rule set:
echo '-a always,exit -F path=/usr/bin/nmap -F perm=x -F auid>=1000 -F auid!=unset -k privileged' \
| sudo tee /etc/audit/rules.d/50-local.rules
sudo augenrules --load
audit-rules-show | grep nmap
50-local.rules 檔名不在 auditd role 管理集
(00-baseline、05-privileged、10-execve、99-finalize) 內,所以
chezmoi apply 不會覆寫。
6. /etc/sudoers 被改過嗎?什麼時候?誰改的?¶
工具:audit-file(需要 baseline sudoers watch 規則 — 本 repo
auditd role 預設就放)。
每事件顯示:
- type=PATH name=/etc/sudoers — 被監看物件。
- type=SYSCALL ... auid=1000 uid=0 — auid 是原始登入 UID(人類),
uid 是執行身份(sudo 後是 root)。
- comm= 與 exe= — 做修改的程式 (vim、visudo、sed、cp ...)。
對照 sudo log 找包住的 sudo 呼叫:
合起來告訴你:Alice 10:14 跑 sudo visudo,10:15 寫到 /etc/sudoers。
7. 事後處理:使用者回報帳號被入侵¶
你在 host 上,user 剛跟你說「我覺得有人進了我的帳號」。做任何破壞性
動作之前 按順序跑下面這些(先別 kill session 或重設密碼 —
證據會掉)。
7a. 即時狀態快照(5 秒,零上下文流失)¶
who -a > /tmp/forensics-who.txt
ps -eo user,pid,ppid,tty,etime,cmd | head -200 > /tmp/forensics-ps.txt
ss -tnp 2>/dev/null > /tmp/forensics-ss.txt # 活躍 TCP,含 PID
sudo iptables -S > /tmp/forensics-iptables.txt 2>/dev/null
date -u > /tmp/forensics-time.txt
7b. 框出登入時段¶
找出可疑 session:奇怪 IP、非工作時間、長時間閒置的「不是你」session。
7c. 他們 sudo 了什麼?¶
注意 sudo bash / sudo -i / sudo su - — 這些是提權點;那行之後
所有東西對 sudo log 都不可見。記下時間。
7d. 他們 exec 了什麼?(只 auditd)¶
對可疑 sudo 提權後的時段:
sudo ausearch --start '<YYYY-MM-DD HH:MM:SS>' --end '<YYYY-MM-DD HH:MM:SS>' \
-sc execve -i | head -200
沒開 auditd_log_all_execve 時只看得到 05-privileged.rules 裡的
binary exec。開了就全看,慢但完整。
7e. 他們動了什麼?¶
找 EXECVE 事件的 useradd、usermod、passwd、chsh — 這些建立
持久化。
7f. SSH key 篡改(典型入侵後手法)¶
sudo ls -la /home/<user>/.ssh/
sudo cat /home/<user>/.ssh/authorized_keys
# 不認得的 = 攻擊者的持久化 key。
# 找全機所有 authorized_keys(含 root 管的 user):
sudo find / -name 'authorized_keys' 2>/dev/null -exec ls -la {} \;
7g. 現在可以動手了¶
- 鎖帳號:
sudo passwd -l <user> - Kill 全部 session:
sudo pkill -KILL -u <user> - 換 SSH key(合法
authorized_keys移開,寫新的)。 - 重開機前把
/tmp/forensics-*.txt存到 host 外。
如果這是合規環境,到此打住、叫 IR 團隊 — 任何後續動作可能破壞 chain of custody。
8. 每日 5 分鐘健康檢查(cron 友善)¶
早上快速檢查:昨晚有什麼怪事嗎?
#!/bin/sh
# /usr/local/bin/audit-morning-check.sh — cron @ 08:00
set -eu
echo "== 過去 24h session =="
last -F -i --since '24 hours ago' 2>/dev/null | head -20
echo
echo "== 過去 24h 失敗登入 =="
sudo lastb -F -i 2>/dev/null | awk '$0 !~ /^$/' | head -20
echo
echo "== 過去 24h sudo 事件 =="
sudo journalctl _COMM=sudo --since '24 hours ago' --no-pager | tail -20
echo
echo "== Audit 摘要 =="
sudo aureport --start '24 hours ago' --summary -i 2>/dev/null
加到 crontab,pipe 到 email / Slack:
0 8 * * * /usr/local/bin/audit-morning-check.sh 2>&1 | mail -s "Audit @ $(hostname)" you@example.com
audit-* shell helper 本身需互動 TTY 才能 sudo prompt,所以 cron 用就
直接 sudo journalctl / sudo aureport(或對特定命令配 passwordless
sudo via sudoers.d/ 片段)。
9. 「root shell 做了什麼?」 — sudo log 之外¶
audit-sudo 看到 sudo bash。Sudo log 從這裡變黑。怎麼辦?
選項 A — auditd execve(auditd_log_all_execve: true 時的預設):
# 找該 bash session
sudo ausearch -k execve-all -p <bash-pid> -i
# 或按 parent PID — 找 root bash 的子程序
sudo ausearch -k execve-all --start '<時間>' -i | grep -A2 "ppid=<bash-pid>"
沒開 execve-all 就只看 05-privileged.rules 內的 exec
(su、passwd、mount 等)。
選項 B — sudoreplay(事件前已在 sudoers 設 Defaults
log_input,log_output 才有):
回放整個 TTY:每個 keystroke、看到的每個畫面。注意 — 包含 TTY 打的 密碼(不該打但常常打)。
選項 C — process accounting:
粗但 acct/psacct 裝了就能用無需事先設定。看不到 argv。
選項 D — 太遲了:
A/B/C 事件前都沒設好的話,從這 host 答案不可知。這就是為什麼
atuin-vs-audit.md 叫你事件前就先設好 audit。
10. 快速關掉吵雜的 audit 規則但保留其他¶
場景:auditd_log_all_execve: true 一天產 5 GB;要保留基準規則,但
現在殺掉 execve 洪水,不走 chezmoi apply 流程。
# 1. 列載入規則找出兇手
sudo auditctl -l | grep execve
# 2. 從 running kernel 刪掉 execve 規則
sudo auditctl -d always,exit -F arch=b64 -S execve,execveat -k execve-all
sudo auditctl -d always,exit -F arch=b32 -S execve,execveat -k execve-all
# 3. 確認
audit-rules-show | grep execve # 應為空
這是 runtime-only 變更 — 下次重開機(或下次 augenrules --load)規則
還會回來,如果檔還在。要永久解:翻 role 變數重 apply:
Role 的「Remove full execve rules when not opted in」task 會刪
/etc/audit/rules.d/10-execve.rules,handler 會 reload。
auditd_immutable: true (即 -e 2 已載入) 時,步驟 2 會 fail
Operation not permitted。鎖到下次重開機。不可變切換要算進這點。
另見¶
- 本 repo 提供的 helper — 上述工具的密集對照表
- auditd 框架 — 概念 + FAQ(含「為什麼
/etc/audit/rules.d/不用 chezmoi 管」) - sudo 審計 — Level 1 深入、
sudoreplay設定 - Atuin vs audit — 為什麼個人 shell history 替代 不了上述任何一個
11. 盤點本機 user inventory¶
你接手一台 server。要知道:誰存在、誰能登入、誰能 sudo、誰有 SSH key,再相信其他事。
11a. CLI 快掃 (90 秒)¶
user-list --login # 誰有真 shell?
user-sudoers # 誰能 sudo?(group + sudoers.d/)
user-ssh-keys # 誰有 authorized_keys,含 fingerprint?
按順序讀:--login 是能力面、sudoers 是特權面、ssh-keys 是
遠端存取面。三個都出現的 user 是滿權限主體 — 確保每個你都認得。
11b. 鑽進單一 user¶
一頁含 id、groups、passwd entry、last 5 logins、last 5 sudo 事件、 authorized_keys 數量。覺得不對勁時的第一站。
11c. 用 tv users 互動瀏覽¶
Ctrl+S 切 5 個來源:passwd → 可登入 → groups → sudoers → SSH key。
預覽顯示 user 細節。Enter 在 lnav 開完整身份報告。Alt+G 只顯示 user
的 group 清單。Alt+E 開 visudo。
11d. 近期身份變更(只 auditd)¶
顯示過去 30 天的 ausearch -k identity 和 -k sudoers 事件。需要
本 repo auditd role 安裝的基準規則集。能抓:新帳號 (useradd)、
shell 變更 (chsh)、sudoers 授權、密碼變更。
11e. 要找的警訊¶
| Pattern | 可能是什麼 |
|---|---|
除 root 外有 uid 0 的 user |
後門帳號 (uid=0 不論名字都是 root) |
| 不認得的可登入 user | 忘記 / 未授權帳號 |
authorized_keys comment 是 <unknown>@<unknown> 或沒 comment |
有人放進來但懶得標註 |
非系統 user 有 NOPASSWD: ALL in /etc/sudoers.d/ |
永久免密碼 root — 通常是踩雷或後門 |
User home 不在 /home/ (e.g. /tmp/x) |
攻擊者建立以避 quota / 監控 |
nologin shell 但有 SSH key |
常見攻擊招 — user 不能互動 ssh 但仍可 ssh user@host '<cmd>' 執行命令(如果沒設 ForceCommand) |
12. 網路曝險 + persistence:每週掃一次¶
把 firewall、listening socket、scheduled job 合成一次每週掃描。能抓: 忘了關的曝險服務、攻擊者裝的 call-home cron、藏在 launchd / systemd timer 裡的 persistence。
12a. 網路攻擊面 (60 秒)¶
每一列都是世界能連的東西。每筆:
- 認得 process 嗎?(sshd、nginx — 是;
bashlisten 在 port 4444 — persistence implant) - Firewall 對外有擋嗎?(
fw-rules)
12b. 目前 active 的連線¶
active outbound + inbound。連到不認得的 public IP(過濾掉 DNS、套件 mirror、你的監控等合法流量後)就值得看。
12c. 排了什麼會自動跑?¶
cron-list --timers # systemd timer — 多數現代主機
cron-list --system # /etc/crontab + cron.d/ (套件安裝的 job)
cron-list # 全部,含 user crontab
對照 user crontab 與 user-list --login。nobody 或 daemon user 有
crontab 就可疑。
12d. 合用:「凌晨 3 點誰 call home?」¶
# 1. 找接近可疑時間觸發的 timer
cron-list --timers | sort
# 2. 看它啟動什麼 unit
systemctl cat <unit>.timer
systemctl cat <unit>.service # 檢查 ExecStart
# 3. 跟下一次執行確認
sudo journalctl -fu <unit>.service
# (另一個 terminal)
fw-conn --all | grep -v 127
Service 做了你沒預期的 outbound 連線就是答案。開了 installAuditd 也可以:
12e. 警訊清單¶
| Pattern | 在哪看到 | 為什麼擔心 |
|---|---|---|
0.0.0.0:<奇怪 port> 上 listener,owner 是 bash / python / nc |
fw-listening |
Reverse / bind shell |
| 該本機的 process(如 local postgres)有對 public IP 的 ESTAB 連線 | fw-conn |
資料外流 |
crontab -u nobody 存在 |
cron-list |
Persistence (nobody 不該有 crontab) |
/etc/systemd/system/ 內非套件來的 unit |
cron-list --timers;對照 dpkg -S / rpm -qf |
手動裝的 unit(可能合法、可能 implant) |
launchd plist RunAtLoad=true 且 ProgramArguments 不認識 |
cron-list (macOS) |
開機時 persistence |