# VDSO hijacking ###### tags: `malware` [mitre T1055.014](https://attack.mitre.org/techniques/T1055/014/) 這是 mitre 上面提到的 linux 獨有的 process injection 方式,不過從 reference 來看好像沒有已知的攻擊是用這種方式(我覺得是太麻煩的緣故 XD),但他的確是一個可行的攻擊技巧 [作者原文](https://web.archive.org/web/20150711051625/http://vxer.org/lib/vrn00.html#c7) 本質上是透過 `ptrace` 達到加載 shared library 和修改 GOT table 達到劫持的目的 ## kernel pwn 的 vdso hijacking 如果直接 google vdso hijacking ,可能會找到某個 kernel pwn 的題目,雖然都叫 vdso hijacking ,但兩者可以說是天差地遠,一個是在 kernel 層去修改 vdso 的執行流程,另一個則是在 user space 透過 ptrace 去攔截 syscall 並加以利用 其實對 vdso hijacking 這個技術的講解看上面的作者原文就夠了,畢竟根本沒什麼人在討論這個手法,大概也找不到其他比較有價值的文章 ## 攻擊流程 這個攻擊很大部分是在繞過一個叫 GRSEC 的 kernel patch ,他的功用影響比較大的有: * NX protection * maps file blank * 不確定 blank 是 blank 哪些地址還是全部都 blank * 從文中來看 process 載入地址應該是固定的,可能是 2009 年 PIE 技術還不流行 NX 是現在 linux 發行版應該都有的保護, maps file blank 就比較麻煩,不過我沒花時間去弄這個 patch ... 1. 透過讀取 `/proc/<pid>/maps` 拿到 process 地址資訊 * 如果拿不到地址,我覺得 x86 環境下用 ptrace + egg hunter 技巧慢慢找應該還是找的到 3. 利用地址資訊和 parsing elf 的格式,找到 got 地址 4. 用 `ptrace` attach program 5. 利用 `PTRACE_SYSCALL` 在進入離開 syscall 會被暫停的功能找到位於 vdso 的 sysenter * x86 模式下暫停的時候 pc 暫存器位在 vdso sysenter 指令附近 * x64 應該就直接停在 libc 的 syscall 附近 * 因為指令不是固定的,定位起來比較麻煩,但用 ptrace 的讀取前面的記憶體加上反組譯應該還是找的到 6. 保存本來要送去 syscall 的暫存器資料 7. 將暫存器改成 open shared library 並且執行 sysenter 8. 將暫存器改成 mmap shared library 並且執行 sysenter ,執行後拿返回的 mmap 地址,這個是 shared library 地址 * 這步驟要執行兩次,一次是 mmap text segment ,另一次是 data segment ,因為權限不同所以要分開執行 9. 回復之前保存的暫存器並執行 sysenter 10. 在 tracee process 裡面搜尋想要執行的 evil function 地址 * 感覺可以記偏移直接拿到地址不用再爬一遍 12. 保存 tracee process 中想要攔截的 GOT 地址 14. 將 GOT 地址改成 evil function * Full relro 的話會失敗,不過 process 內應該很多 function pointer 可以 hijack 16. 將 evil function 結尾修改成步驟 10 保存的 GOT 地址 17. 結束 ## 對 code 的修正 作者給的 code 不知道是在哪個環境下實驗的,我用 ubuntu 20.04 或是 9.04 都執行失敗,推測應該是編譯器編譯出來的 machine code 長的不一樣導致,建議自己根據自己的環境編譯出來的 code 做修改,以下提到幾個要改的部分: * tracee text segment 在 ubuntu 20.04 下 offset 不再是 0x0 * 我直接修改成 0x1000 * mmap 第一個地址盡量自己指定 * 我遇到不指定地址,結果第一個 text segment 被 map 到 libc 和 ld 中間,導致後續 data segment 和 text segment 不連續,這樣會在之後定位 evil function 的步驟出了點問題(不過看起來可以來硬的寫死在裡面) * evilsig * 其實就是 evil function 前幾個 bytes ,因為編譯器關係長的會跟文中的 code 不一樣 * tc * 這個 array 是放 evil function 要跳轉回正常 function 的 machine code ,同樣會因為編譯器關係導致偏移不一樣 ##