VDSO hijacking

tags: malware

mitre T1055.014

這是 mitre 上面提到的 linux 獨有的 process injection 方式,不過從 reference 來看好像沒有已知的攻擊是用這種方式(我覺得是太麻煩的緣故 XD),但他的確是一個可行的攻擊技巧

作者原文

本質上是透過 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 技巧慢慢找應該還是找的到
  2. 利用地址資訊和 parsing elf 的格式,找到 got 地址
  3. ptrace attach program
  4. 利用 PTRACE_SYSCALL 在進入離開 syscall 會被暫停的功能找到位於 vdso 的 sysenter
    • x86 模式下暫停的時候 pc 暫存器位在 vdso sysenter 指令附近
    • x64 應該就直接停在 libc 的 syscall 附近
      • 因為指令不是固定的,定位起來比較麻煩,但用 ptrace 的讀取前面的記憶體加上反組譯應該還是找的到
  5. 保存本來要送去 syscall 的暫存器資料
  6. 將暫存器改成 open shared library 並且執行 sysenter
  7. 將暫存器改成 mmap shared library 並且執行 sysenter ,執行後拿返回的 mmap 地址,這個是 shared library 地址
    • 這步驟要執行兩次,一次是 mmap text segment ,另一次是 data segment ,因為權限不同所以要分開執行
  8. 回復之前保存的暫存器並執行 sysenter
  9. 在 tracee process 裡面搜尋想要執行的 evil function 地址
    • 感覺可以記偏移直接拿到地址不用再爬一遍
  10. 保存 tracee process 中想要攔截的 GOT 地址
  11. 將 GOT 地址改成 evil function
    • Full relro 的話會失敗,不過 process 內應該很多 function pointer 可以 hijack
  12. 將 evil function 結尾修改成步驟 10 保存的 GOT 地址
  13. 結束

對 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 ,同樣會因為編譯器關係導致偏移不一樣