格式化字符串基础

%p、%x 泄露数据

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
32
33
34
35
36
printf("a1 = %d, a2 = %d, a3 = %d", a1, a2, a3);

32
a3
a2
a1
addr -> "a1 = %d, a2 = %d, a3 = %d"
retaddr
ebp
(printf stack)

---------------------------------------

printf("a1 = %d, a2 = %d, a3 = %d", a1, a2);

32
(unknow data) 不应该被printf访问,不应该被printf读取
a2
a1
addr -> "a1 = %d, a2 = %d, a3 = %d"
retaddr
ebp
(printf stack)

printf("a1 = %p, a2 = %p, a3 = %p");

data3
data2
data1
addr -> "a1 = %p, a2 = %p, a3 = %p"
retaddr
ebp
(printf stack)

一个问题?
如何泄露很远的数据?如何泄露指定位置的数据?

%x$ 指定位置参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
printf("%5$p");

32
data6
data5 <- leak
data4
data3
data2
data1
addr -> "%5$p"
retaddr
ebp
(printf stack)

64位
传参方式不同
先用寄存器,再用栈
有6个寄存器
rdi, rsi, rdx, rcx, r8, r9, 栈
"%7$p"

%n 更改指定位置存储的值所指向的值

  • %n
    • 将指定位置指向的值修改为前面所输出的字符个数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

stackaddr3 -> addr1 -> addr2 -> addr3 <- %9$
stackaddr2 -> addr2 -> addr3 <- %8$
stackaddr1 -> data <- %7$

gdb调试

如果要修改addr3,我们就需要%8$n;

printf("a%8$n");
addr3 = ? --> addr3 = 1;

printf("aaa%8$n");
addr3 = 3;

%n -> 4个字节
64
stackaddr data(8个字节) 0x12345678 -> 0x00000000

%hn
0x12345678 -> 0x12340000

%hhn
0x12345678 -> 0x12345600

指针1 —> 值
指针2 -> 指针1

%c 输出特定数量字符

1
2
3
4
5
6
printf("%8c);
输出8个字符

printf("%4660c%8$hn");

0x12345678 -> 0x12341234

format

泄露libc
泄露pie
泄露canary
泄露堆

*更改任意数据

栈上格式化字符串
更改数据方便,1次printf调用就能更改数据

非栈上格式化字符串
更改一个任意数据很可能需要调用两次printf或者%n
如果format长度很长,就可以用两次%n来更改数据,如果很短,就需要两次printf

本作品由 automata 于 2026-03-21 18:36:28 发布
作品地址:格式化字符串基础
除特别声明外,本站作品均采用 CC BY-NC-SA 4.0 许可协议,转载请注明来自 凹凸麦塔
Logo
上一篇伪造IO_FILE进行任意地址读写下一篇0x1.GWDB-2022-0012-施耐德电气IGSS堆越界写漏洞