磁碟底層與資料救援(二):不用 strings | grep,用鑑識流程解 picoCTF Disk, disk, sleuth!
聲明:本文是 picoCTF「Disk, disk, sleuth!」的解題記錄,所有操作都在下載的 image 檔上進行,不會碰到真實硬碟。
為什麼第二課是實戰
第一課的碟是自己造的:檔案自己放、inode 自己查、block 自己算,這一課換一顆「別人的碟」:picoCTF 2021 的 Disk, disk, sleuth!,一個小型 Linux 系統的硬碟 image,題目只說裡面有一個 flag,
先講清楚:這題用 strings image | grep picoCTF 三秒就出來,但這篇不走那條,走的是鑑識的流程——看結構、列名單、縮範圍、最後才碰內容,flag 是順便的,流程才是要帶走的。
環境
工具還是 Sleuth Kit,題目檔下載後,如果是在 WSL,Windows 的下載資料夾要透過 /mnt/ 拿:
cp /mnt/d/Download/dds1-alpine.flag.img.gz ~/lab/ && cd ~/lab
gunzip dds1-alpine.flag.img.gz
Sleuth Kit 的工具名有規則,看懂就不用背:第一個字母是「看哪一層」,後面是「做什麼」。
- 前綴:
mm= 分割表,f= 檔名,i= inode,blk= block,j= journal - 後綴:
ls= 列出來,stat= 印詳細資料,cat= 把內容倒出來
所以 mmls = 列分割表,fls = 列檔名,istat = 看一張 inode,icat = 倒一個檔案的內容。

Step 1:看結構
mmls dds1-alpine.flag.img
DOS Partition Table
Offset Sector: 0
Units are in 512-byte sectors
Slot Start End Length Description
000: Meta 0000000000 0000000000 0000000001 Primary Table (#0)
001: ------- 0000000000 0000002047 0000002048 Unallocated
002: 000:000 0000002048 0000262143 0000260096 Linux (0x83)
跟第一課自己切的那顆長得一模一樣:第 0 格是分割表、1 到 2047 是 1 MiB 對齊留白、分割區從 2048 開始,260096 個 sector 約 127 MiB,類型 0x83 Linux。
重點:記住 2048,後面每個指令都要用 -o 2048 告訴工具「分割區從這裡開始」,跟第一課 losetup 的 -o 是同一件事,只是單位是 sector 不是 byte。
Step 2:列根目錄
fls -o 2048 dds1-alpine.flag.img
d/d 10161: home
d/d 11: lost+found
r/r 12: .dockerenv
d/d 2033: bin
d/d 8129: boot
d/d 6097: dev
d/d 16257: etc
d/d 28449: lib
...
d/d 26418: root
...
d/d 20324: var
V/V 32513: $OrphanFiles
d/d 是資料夾、r/r 是檔案,冒號前是 inode 編號,兩個線索:
.dockerenv這不是裝在實體機上的 Linux,是一個 Docker container 被倒成硬碟 image,container 通常很乾淨,多出來的東西會很顯眼。$OrphanFilesSleuth Kit 自己加的虛擬資料夾,放「inode 還在、但名單上已經沒名字」的檔案,刪一半的、被藏的東西會出現在這,真實案子裡也是第一批要看的地方。
以「有人故意藏東西」來想,優先順序:root(登入者的家)、home(有沒有其他使用者)、$OrphanFiles。
Step 3:一格一格翻,還是整顆倒出來
fls 後面接 inode 編號,就是「列這個資料夾裡面的東西」:
fls -o 2048 dds1-alpine.flag.img 26418 # root
fls -o 2048 dds1-alpine.flag.img 10161 # home
兩個都是空的,空的也是資訊:root 沒被用過、沒有其他使用者。
一格一格翻適合摸清結構,實務上第一步是整顆倒出來:-r 遞迴到底、-p 印完整路徑,存成檔案,之後所有判斷都在這個清單上做。
fls -o 2048 -r -p dds1-alpine.flag.img > listing.txt
wc -l listing.txt
grep -c '^\*' listing.txt
1973 listing.txt
0
1973 個項目,0 個開頭有 *(被刪掉但名字還在名單上的),所以 flag 是以正常檔案的身分放著的,不用走復原那條路,問題只是「在哪」,
Step 4:排除「有人新增的目錄」
container 預設幾個目錄應該是空的,從清單裡看它們底下有沒有東西:
grep -E ': (home|root|tmp|opt|srv|mnt|media)(/|$)' listing.txt
只印出目錄本身那幾行,沒有多出來的檔案,所以放 flag 的人沒走「新增檔案」那條懶路,東西藏在現有檔案裡,或塞在不顯眼的路徑。
Step 5:時間線
換一個真正鑑識會用的角度:時間,image 是一次建好的,兩千個檔案的修改時間會擠在同一段;之後被人動過的,時間會脫隊。
fls -l 每行多印四個時間和大小,再用 sort 照修改時間排:
fls -o 2048 -r -p -l dds1-alpine.flag.img | sort -t$'\t' -k3 | tail -15
新手坑:-l 的欄位順序是「類型 編號」、「路徑」、然後才是修改時間,所以是第 3 欄不是第 2 欄,我第一次用 -k2 排,結果整排都是 var/ 開頭——那是照路徑排的。
r/r 30729: lib/modules/.../tcp_vegas.ko 2021-02-17 04:55:28 ...
r/r 28871: lib/modules/.../scsi_transport_fc.ko 2021-02-17 04:55:28 ...
r/r 28598: lib/modules/.../poly1305_generic.ko 2021-02-17 04:55:28 ...
d/d 8129: boot 2021-02-17 04:55:29 ...
r/r 8136: boot/ldlinux.sys 2021-02-17 04:55:29 ...
r/r 8137: boot/ldlinux.c32 2021-02-17 04:55:29 ...
r/r 8138: boot/syslinux.cfg 2021-02-17 04:55:29 ... 219
全部 04:55:28,只有 boot 底下晚一秒,時間線在這題幫不上太多——整顆碟是腳本一秒內產出的,不是人用了很久的機器,container image 都這樣。
但這個「晚一秒」還是能讀:最後被動的是開機設定 boot/syslinux.cfg,219 bytes 的純文字,想藏在「看起來是系統檔、但其實是人手寫的」地方,這種小設定檔剛好合適,反正它很小,直接看。
Step 6:讀內容
icat -o 2048 dds1-alpine.flag.img 8138
DEFAULT linux
SAY Now booting the kernel from SYSLINUX...
SAY picoCTF{Redacted}
LABEL linux
KERNEL /boot/vmlinuz-virt
APPEND ro root=/dev/sda1 rootfstype=ext3 initrd=/boot/initramfs-virt
SAY 是開機時印一行字給使用者看的指令,第一行是正常的,第二行不是開機設定該有的東西。
順便看 APPEND 那行的 rootfstype=ext3:這顆碟是 ext3,跟 ext4 同一家,一樣有 journal,第三課會用到。
那暴力搜可不可以?
可以,而且有三種,各有涵蓋不到的地方:
strings image | grep picoCTF直接掃整顆碟,連被刪的、journal 裡的舊版、沒被任何檔案佔用的空格子都掃到,範圍最大,缺點:檔案碎掉時字串會斷;flag 被 base64、壓縮、拆成兩半就看不到。tsk_recover照名單把每個檔案 icat 出來、用原本路徑存到資料夾,等於「把每張 inode 都倒一遍」的迴圈,之後用一般 grep、find 去搜,「照地圖走」,每個檔案的邊界都對,碎掉的也會拼好,但只涵蓋名單上有的東西。
tsk_recover -o 2048 -a dds1-alpine.flag.img out/
grep -r picoCTF out/
-a 只倒正常檔案,-e 連 orphan 和被刪的一起倒,這題倒出 1565 個檔案,比 listing 的 1973 少,差的是資料夾和 symlink,那些沒有內容可倒,
3. 縮範圍 就是這篇走的路,慢,但不依賴「知道 flag 長什麼樣」。

關鍵句:真實案子沒有 picoCTF{,你要找的可能是一封信、一個帳號、某天晚上發生了什麼,不知道長什麼樣的東西 grep 不到,專業做法是兩條並行:靠假設縮範圍,同時拿已知關鍵字掃整顆碟,互相印證,這篇練的是前者。
結語

這一課帶走的四件事:
- 拿到一顆碟的順序:mmls 看結構 → fls 倒名單 → 排除 → 縮範圍 → 最後才 icat 看內容
- 空的目錄、0 個刪除檔,都是資訊,每一步都在縮小範圍
- 時間線是鑑識的基本功,這題剛好沒用,但排序前先確認欄位在第幾欄
- 暴力搜是正規手法,不是作弊,但要知道它漏哪裡
Happy Hunting!
Member discussion