PLT(Procedure Linkage Table)とGOT(Global Offset Table)

GOT Overwriteの問題をやる前に、共有ライブラリの関数呼び出しはどのようなものなのか?PLTとGOTについて勉強する。

PLTとGOTを具体的にみる

以下のプログラムを使う:

// gcc test.c -fcf-protection=none -z norelro -no-pie -g
#include <stdio.h>

int main(void) {
   printf("Hello, ");
   puts("World!");
   printf("printf-2");
}

PLTセクションは以下のようになっている:

┌──(shoebill㉿shoebill)-[~/test_GOT-PLT]
└─$ objdump --no-show-raw-insn -M intel -j .plt -d ./a.out

セクション .plt の逆アセンブル:

0000000000401020 <puts@plt-0x10>:
  401020:   push   QWORD PTR [rip+0x22c2]        # 4032e8 <_GLOBAL_OFFSET_TABLE_+0x8>
  401026:   jmp    QWORD PTR [rip+0x22c4]        # 4032f0 <_GLOBAL_OFFSET_TABLE_+0x10>
  40102c:   nop    DWORD PTR [rax+0x0]

0000000000401030 <puts@plt>:
  401030:   jmp    QWORD PTR [rip+0x22c2]        # 4032f8 <puts@GLIBC_2.2.5>
  401036:   push   0x0
  40103b:   jmp    401020 <_init+0x20>

0000000000401040 <printf@plt>:
  401040:   jmp    QWORD PTR [rip+0x22ba]        # 403300 <printf@GLIBC_2.2.5>
  401046:   push   0x1
  40104b:   jmp    401020 <_init+0x20>

gdbで解析

一番最初のprintfが呼ばれる様子を、siコマンドで一つ一つの命令を追いながらみていく。

最初のprintfにブレイクポイントを打って走らせると

一命令実行すると(上で示したPLTセクションも参考に)

このタイミングでGOTエントリをみると、printfのアドレスではなくprintf@plt+6になっている:

telescope 0x403300 3
gdb-peda$ telescope 0x403300 3
0000| 0x403300 --> 0x401046 (<printf@plt+6>:   push   0x1)
0008| 0x403308 --> 0x0 
0016| 0x403310 --> 0x0 

二命令進めると

さらに二命令進めると

_dl_runtime_resolve_xsaveという関数は、libc内にあるprintfのアドレスを解決し、最終的にそのアドレスに遷移する。

ここで二番目のputsのにブレイクポイントを打つ。その後continueコマンドでcall 0x401030 <puts@plt>の前までいく。

このタイミングで再度GOTエントリをみてみると

gdb-peda$ telescope 0x403300 3
0000| 0x403300 --> 0x7ffff7c58380 (<__printf>: sub    rsp,0xd8)
0008| 0x403308 --> 0x0 
0016| 0x403310 --> 0x0 

先ほどprintf@plt+6と書いてあった部分が、今はprintfの実体のアドレスになっている。

上記でみてきたjmp命令たちをまとめると以下のように動いてる:

call 0x401040 <printf@plt> [PLT] → 0x403300 <printf@GLIBC_2.2.5> [GOT] → 0x401046 <printf@plot+6> → 0x401020 <.plt> → 0x4032f0 <_dl_runtime_resolve_xsave>

共有ライブラリの関数が呼ばれる時は、初回にアドレス解決を行い、次回以降はその解決結果を利用して関数に遷移する。

そこで、GOTエントリを編集してジャンプする先を制御する。これがGOT Overwrite攻撃。