Lazy binding - ianchen0119/About-Security GitHub Wiki

當程式是利用 Dynamic linking 的方式做連結時,其函式位址會在執行週期才確定。這樣做的好處顯而易見: 程式引入的 library 的函式有千百個,但在執行周其中並不會都使用到,當函式被呼叫時再去載入它,就可以大幅提升執行效率。

判別是否為 Lazy-binding 的方法: 當我們利用逆向工具查看組合語言時,如果有發現 call function 的形式如 call puts@plt ,就代表該函式會在執行期間才做載入。

  • GOT: Global Offset Table

    GOT 其實就是一個存放函式指標的陣列。

    用來記錄在 ELF file 中用到的 Shared library 中符號的絕對地址, GOT 主要涵蓋以下內容:

    • .dynamic

    • .got 儲存全域變數的位址。

    • .got.plt

      Name Description
      address of .dynamic 指向 GOT 的 .dynamic
      link_map 一個鍊結串列,用來紀錄用到的 Library
      dl_runtime_resolve 找出函式的位址
    • .data

  • PLT: Procedure Linkage Table

流程

  • 程式碼中呼叫了 func
  • 跑去 PLT 執行 func@plt
  • PLT 中的 func@plt 會跳到 GOT 的 .got.plt 尋找 func 的位置
  • func 的 id 推入 Stack 中。
  • 由於 func 是第一次呼叫,所以沒辦法順利在 .got.plt 找到函式位址,這時系統就會把 func 的位址寫進 .got.plt 當中。
  • 如此一來,等到 func 第二次被呼叫時,就可以直接找到其位址。

Lazy-loading 造成的安全性問題

Lazy-binding 雖能夠大幅度的提升程式的執行效率,但也因為該機制需要 GOT 能夠被寫入,所以如果有有心人士將 PLT 的對應位置改成 system call 的位置,那呼叫 plt function 時便會變呼叫 system call ,這點需要特別注意。

如何找到 libc_base

  • 方法一
ldd ./yourelf
readelf -a ./libc.so.6 | grep puts
  • 取得有用到的 libc 函式,如: puts_got
  • puts_got 的 value 就會等於 libc_base 加上 puts_lic (有點像是相對位置)

取得 libc_base 後,我們就可以取得指定的 libc 函式,像是: system_got 的 value = libc_base + system_libc

  • 方法二 使用 one_gadget :
  • 可以一次取得 libc 中 get shell 的位址
  • 必須符合條件 (constraints) 且知道 libc_base
one_gadget /lib/x86_64-linux-gnu/libc.so.6