跳轉到

Cookbook:場景式審計食譜

針對本 repo helper 與 TV channel 的實戰、場景優先導覽。每篇食譜回答一個 具體問題(「有人從新 IP 登入嗎?」),給你完整命令順序,並標出輸出中 要看什麼。

概念分層見 section README。Helper 密集對照表見 helpers.md

食譜索引


1. 現在誰登入這台 server?

工具audit-sessions,不需 sudo、不需 auditd。

audit-sessions

看第三段(== who -a ==)。每列都是即時 session:

alice    pts/0   2024-05-08 09:21 (10.0.0.42)
bob      pts/1   2024-05-08 09:45 (10.0.0.99)

警訊: - 你不認識的 user。 - 從 public IP 連來的 pts/N(你的 sshd 通常該只看到內網 / VPN 段)。 - 同一 user 多個 pts/N(可能合法 tmux 用法 — 如果是你自己的帳號交叉 比對 tmux ls)。

可疑就跳食譜 #7。


2. 本週有人從新 IP 登入嗎?

工具:人眼掃用 audit-sessions;互動瀏覽用 tv sessions

audit-sessions | head -100 | awk '{print $1, $3}' | sort -u

dump user IP 對。對照你的「已知 IP」清單(VPN 段、家裡 IP、CI runner IP)。

或互動式

tv sessions
# 打 username,看他近期所有登入,捲動檢查 IP

看到非預期 IP 就跳食譜 #7。


3. 暴力破解 / password-spray 檢查

工具audit-failed-logins (Linux only,sudo)。透過 lastb/var/log/btmp

audit-failed-logins | head -100

按 IP 群組看模式:

sudo lastb -F -i | awk 'NR>1 && $3 ~ /[0-9]/ {print $3}' | sort | uniq -c | sort -rn | head -20

輸出是 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。

audit-sudo alice

輸出列像:

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 解。

只看失敗:

audit-sudo alice | grep -E 'FAILED|NOT in sudoers|incorrect password'

5. 「我懷疑有人從這台跑 nmap」 — Level 2 / Level 3

Level 2 (process accounting,需事先啟用)

sudo lastcomm nmap | head -20

顯示 command flags user tty seconds 開始時間。沒有 argv(看不到他掃了 什麼),但能證明跑過。

Level 3 (auditd,本 repo 的 audit-execve)

audit-execve nmap

每個事件含: - pid=NNNNppid=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-baseline05-privileged10-execve99-finalize) 內,所以 chezmoi apply 不會覆寫。


6. /etc/sudoers 被改過嗎?什麼時候?誰改的?

工具audit-file(需要 baseline sudoers watch 規則 — 本 repo auditd role 預設就放)。

audit-file /etc/sudoers
audit-file /etc/sudoers.d/

每事件顯示: - type=PATH name=/etc/sudoers — 被監看物件。 - type=SYSCALL ... auid=1000 uid=0auid 是原始登入 UID(人類), uid 是執行身份(sudo 後是 root)。 - comm=exe= — 做修改的程式 (vimvisudosedcp ...)。

對照 sudo log 找包住的 sudo 呼叫:

audit-sudo | grep -i 'visudo\|sudoers'

合起來告訴你: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. 框出登入時段

audit-sessions <user> | head -50

找出可疑 session:奇怪 IP、非工作時間、長時間閒置的「不是你」session。

7c. 他們 sudo 了什麼?

audit-sudo <user> | head -100

注意 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. 他們動了什麼?

audit-file /home/<user>/.ssh/
audit-file /etc/passwd
audit-file /etc/shadow
audit-file /etc/sudoers

EXECVE 事件的 useraddusermodpasswdchsh — 這些建立 持久化。

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 才有)

sudo sudoreplay -l user=<user>
sudo sudoreplay <session-id>

回放整個 TTY:每個 keystroke、看到的每個畫面。注意 — 包含 TTY 打的 密碼(不該打但常常打)。

選項 C — process accounting

sudo lastcomm | grep -B2 -A2 ' bash '

粗但 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:

# 在 ~/.config/dotfiles/ansible.local.yml 或用 -e:
auditd_log_all_execve: false
chezmoi apply --force

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。鎖到下次重開機。不可變切換要算進這點。


另見


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

user-info alice

一頁含 id、groups、passwd entry、last 5 logins、last 5 sudo 事件、 authorized_keys 數量。覺得不對勁時的第一站。

11c. 用 tv users 互動瀏覽

tv users

Ctrl+S 切 5 個來源:passwd → 可登入 → groups → sudoers → SSH key。 預覽顯示 user 細節。Enterlnav 開完整身份報告。Alt+G 只顯示 user 的 group 清單。Alt+Evisudo

11d. 近期身份變更(只 auditd)

user-recent-changes --days 30

顯示過去 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 秒)

fw-listening | grep -vE '127\.0\.0\.1|::1'

每一列都是世界能連的東西。每筆:

  • 認得 process 嗎?(sshd、nginx — 是;bash listen 在 port 4444 — persistence implant)
  • Firewall 對外有擋嗎?(fw-rules)

12b. 目前 active 的連線

fw-conn | grep -vE '127\.0\.0\.1|::1'

active outbound + inbound。連到不認得的 public IP(過濾掉 DNS、套件 mirror、你的監控等合法流量後)就值得看。

# 鑽入特定連線
fw-port <可疑 port>

12c. 排了什麼會自動跑?

cron-list --timers   # systemd timer — 多數現代主機
cron-list --system   # /etc/crontab + cron.d/ (套件安裝的 job)
cron-list            # 全部,含 user crontab

對照 user crontab 與 user-list --loginnobodydaemon 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 也可以:

audit-execve <可疑 binary>

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=trueProgramArguments 不認識 cron-list (macOS) 開機時 persistence