Search code examples
cformat-string

Overwriting of Arbitrary memory using format string


I was reading an old article about format string exploit back in the 2000's, link can be found here: Article

At page 15, the author describes the mean to overwrite a variable's content by increasing the printf internal stack pointer as so: Stack pushed

    unsigned char   canary[5];
    unsigned char   foo[4];
    memset (foo, ’\x00’, sizeof (foo));

    /* 0 * before */ strcpy (canary, "AAAA");

    /* 1 */  printf ("%16u%n", 7350, (int *) &foo[0]);
    /* 2 */  printf ("%32u%n", 7350, (int *) &foo[1]);
    /* 3 */  printf ("%64u%n", 7350, (int *) &foo[2]);
    /* 4 */  printf ("%128u%n", 7350, (int *) &foo[3]);

    /* 5 * after */ printf ("%02x%02x%02x%02x\n", foo[0], foo[1],
            foo[2], foo[3]);

    printf ("canary: %02x%02x%02x%02x\n", canary[0],
            canary[1], canary[2], canary[3]);

Returns the output “10204080” and “canary: 00000041”

Unfortunately the author doesn't explain the reason why the stack gets pushed like this, in other terms what part of the printf procedure is provoking the override in memory?

Edit: I do understand that the instruction in /1/ will create a right padded field of width 16 then write the number of written bytes (16) to the address of foo[0]. The question is why does it overwrite to the adjacent memory? You would normally think that it would only write on the address of foo[0] which is one byte not 4.


Solution

  • The code has undefined behavior and is invalid.

    Anyway, the statement:

    printf ("%16u%n", 7350, (int *) &foo[0]);
    

    is basically doing:

    *(int *)&foo[0] = 16;
    

    The biggest issue is with the last one:

    printf("%128u%n", 7350, (int *) &foo[3]);
    

    it's doing:

    *(int *)&foo[3] = 128;
    

    but foo[3] is an unsigned char. Assuming sizeof(int) = 4, ie. int has 4 bytes, then this writes 3 bytes out of bounds to foo + 3. x86 stores stack in reverse order - the memory reserved for canary is put after the memory for foo. The stack memory looks like this:

       <-- foo ---><--- canary ----->
       [0][1][2][3][0][1][2][3][4][5]
                ^^^^^^^^^^^^ 
                     storing (int)128 here in **little endian**
    

    Because x86 is little endian, foo[3] is assigned the value of 128, and canary[0..2] are zeroed (because 128 = 0x00000080).

    You can do:

    // I want it to print 0xDEAD
    // I swap bytes for endianess I get 0xADDE
    // I then shift it left by 8 bytes and get 0xADDE00
    // 0xADDE00 = 11394560
    // The following printf will do foo[3] = 0x00
    // but also: canary[0] = 0xDE, canary[1] = 0xAD and canary[2] = 0x00
    fprintf("%11394560u%n", 7350, (int *) &foo[3]);
    printf("0x%02x%02x\n", canary[0], canary[1]);
    // will output 0xDEAD