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.
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