在很多的漏洞復現流程中,對於定位到攻擊位置和盤點攻擊面的說明都相當概括,但現實案例中卻是相反的情況,往往會花許多時間盤點攻擊面和溯源程式內容。在廠商修復完成後對比新舊版差異的 patch diffing 有時候也會發現一些沒有修好的實作,因此今天想來介紹一下 Patch Diffing 的方法!
舉個例子,假設舊版存在漏洞:
void copy_name(char *input) {
char name[64];
strcpy(name, input);
}
修補後則變成:
void copy_name(char *input) {
char name[64];
[+] if (strlen(input) >= sizeof(name))
[+] return;
strcpy(name, input);
}
兩個版本放在一起比較,很容易就能發現新增了一段邊界檢查。但是在 diff 模式下可以輕易從修補內容反向推導原始 vulnerability。也因此在研究 1-day 相關漏洞內容或是製造商沒有明確說明修補的漏洞時,可以透過 patch diffing的方式快速定位。
在 Firmware 中的 Patch Diff 也不只是直接丟 BinDiff,可以進行分層收斂成:Firmware Diff → Filesystem Diff → ELF Diff → Function Diff → Instruction Diff
來看個 CVE:
於是我們選一個相鄰的版本: QTS 5.1.7.2770,再來觀察 2823版本的更新紀錄[1],發現同時修補了多項CVE
拿到韌體、解壓縮之後我們會有兩個很大包的 rootfs ,包含了很多檔案,直接開 IDA 也不知道要先看哪邊,因此會先建立清單:
cd old-rootfs && find . -type f -print0 | sort -z | xargs -0 sha256sum > ../old.sha256
cd new-rootfs && find . -type f -print0 | sort -z | xargs -0 sha256sum > ../new.sha256
diff -qr old-rootfs new-rootfs | grep '^Files .* differ'
用hash是因為直接 diff 不好讀,hash再diff後的結果會分類成 Added / Removed / Modified / Unchanged,我們只關心 Modified。再更進階一點是說,只關注 binary 類型 (IoT 通常在 binary 都會是已經封裝的程式,其他零散的可能是變動比較大的前端頁面內容,不太需要去看。)
while read -r f; do file "$f"; done < changed-files.txt | grep ELF
拿到差異較大的 ELF 檔案之後,通常會使用 strings 或者是其他的 IDA BinDiff 工具,可以一次去查看兩個版本中的差別,但這邊我想介紹一個新的工具來使用看看。
https://github.com/pwnfuzz/diffrays
DiffRays 把 old / new binary 轉成 matched / modified / added / deleted functions,再做 function-level 比較。
diffrays diff <bin file> -o work/diffrays/foo.sqlite
diffrays server --db-path work/diffrays/foo.sqlite
精神有點恍惚了,希望明天爬得起來
之後會來看看硬體 debugging 相關的介紹(?)
[1] https://www.qnap.com/en-us/security-advisory/qsa-24-33