I ran into an integer overflow problem, which I managed to solve, but only by trial and error.
Since it's an integer overflow problem, I've wrote some code to print out the buffer. The beginning of the buffer is the address where array[0] is stored. Then, I started to pass MAX_INT and MIN_INT values to the program. I've noticed that when I passed MIN_INT value to argv[1], it overwrote the begining of the buffer. so I passed MIN_INT+1 value, and noticed that it overwrote the second address of the buffer. from there it was easy to solve.
The thing is, I dont understand why this works. In place_int_array function, when i pass, let's say, MIN_INT+6 value to argv[1], the if statement "if (slot > 3)" returns false, so it goes to the "else" statement, since MIN_INT+6 is a negative value, so it's less than or equal to 3, so slot's value got interpreted as "MIN_INT+6" value. But at the "else" statement, array[slot] went up in the buffer to the address of "array[0]+6", which means, slot's value as an index of "array" was interpreted as "6".
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int secretCode = 10;
void dump_stack(void **stack, size_t n, void **arg0) {
printf("Stack dump (stack at %p, len %d): \n", stack, n);
//if the buffer if not aligned on a dword boundary - force alignment of print
void** alignedStack = (void**)(((unsigned int)stack >> 2) << 2);
while (n-- > 0) {
printf("0x%08x: 0x%08x", (unsigned int)&alignedStack[n], (unsigned int)alignedStack[n]);
if (n == 0) {
printf(" (beginning of buffer)");
}
if (&alignedStack[n] == arg0 + 6+1) {
printf(" (main first argument (argc))");
}
if (&alignedStack[n] == arg0 + 6) {
printf(" (main return address (saved eip))");
}
if (&alignedStack[n] == arg0) {
printf(" (second argument)");
}
if (&alignedStack[n] == arg0 - 1) {
printf(" (first argument)");
}
if (&alignedStack[n] == arg0 - 2) {
printf(" (place_int_array return address (saved eip))");
}
if (&alignedStack[n] == arg0 - 3) {
printf(" (saved ebp)");
}
printf("\n");
}
}
void print_secret_code() {
//TODO - call this from somewhere...
printf("You get better at this stuff, ah?! Go get your treasure, the code is %d\n", secretCode);
}
void fill_array(int array[], size_t len) {
unsigned int i;
for (i = 0; i < len; i++) {
array[i] = i * 10;
}
}
void place_int_array(int slot, int value) {
int array[3];
printf("place_int_array ret address: %p\n", __builtin_return_address(0));
printf("&array: %p\n", &array);
printf("slot: %d\n", slot);
printf("&array[slot]: %p\n", &array[slot]);
fill_array(array, sizeof(array) / sizeof(array[0]));
printf("slot: %d\n", slot);
if (slot > 3) //we stop bad guys here
printf("safe number is greater than 3, out of bounds.\n");
else {
array[slot] = value;
dump_stack((void **) array, 30, (void **) &value);
printf("filled safe %d with %d.\n", slot, value);
}
return;
}
int main(int argc, char **argv) {
printf("print_secret_code function = %p\n", print_secret_code);
if (argc != 3) {
printf("Welcome to Alladin's magic cave!\n");
printf("Enter the secret number into the right safe and get the treasure cave entrance code!\n");
printf("syntax: %s [SAFE NUMBER] [SECRET NUMBER]\n", argv[0]);
//TEMP TEMP - for debugging only
printf("print_secret_code function = %p\n", print_secret_code);
}
else
{
place_int_array(atoi(argv[1]), atoi(argv[2]));
}
exit(0);
}
Here is the output:
[lab8_IntegerOverflow]$ ./aladdinSafe -2147483648 1
print_secret_code function = 0x804864b
place_int_array ret address: 0x804880d
&array: 0xffbd8464
slot: -2147483648
&array[slot]: 0xffbd8464
slot: -2147483648
Stack dump (stack at 0xffbd8464, len 30):
0xffbd84d8: 0xbca9b1bd
0xffbd84d4: 0x72f595ac
0xffbd84d0: 0x00000000
0xffbd84cc: 0x00000000
0xffbd84c8: 0x00000000
0xffbd84c4: 0xf773c000
0xffbd84c0: 0x0804825c
0xffbd84bc: 0x0804a01c
0xffbd84b8: 0xffbd84d4
0xffbd84b4: 0xffbd8534
0xffbd84b0: 0x00000003
0xffbd84ac: 0xf7765cca
0xffbd84a8: 0xffbd8544
0xffbd84a4: 0xffbd8534
0xffbd84a0: 0x00000003 (main first argument (argc))
0xffbd849c: 0xf75aaaf3 (main return address (saved eip))
0xffbd8498: 0x00000000
0xffbd8494: 0xf773c000
0xffbd8490: 0x08048820
0xffbd848c: 0xf773c000
0xffbd8488: 0x0804882b
0xffbd8484: 0x00000001 (second argument)
0xffbd8480: 0x80000000 (first argument)
0xffbd847c: 0x0804880d (place_int_array return address (saved eip))
0xffbd8478: 0xffbd8498 (saved ebp)
0xffbd8474: 0xf7778938
0xffbd8470: 0xffbda5cb
0xffbd846c: 0x00000014 //address of array[2], filled at "fill_array" function with 2*10 = 20 = 0x14
0xffbd8468: 0x0000000a //address of array[1], filled at "fill_array" function with 1*10 = 10 = 0xa
0xffbd8464: 0x00000001 (beginning of buffer) //address of array[0], overwritten with argv[2] = 1.
filled safe -2147483648 with 1.
I expect that if "slot > 3"'s slot was interpreted as MIN_INT+6, then "array[slot]"'s slot would be interpreted the same.
Why is slot's value changed according to its use?
@IanAbbott got it. The integer "slot" underflows at "array[slot]" because the compiler multiply "slot" by 4. so when INT_MIN is multiplied by 4 - it under flows to 0. and when slot = INT_MIN + 6, when trying to calculate the address of array[slot], it first multilies slot by 4, which equals to (INT_MIN + 6)*4, which underflows to 0 + 6*4, which is exactly 6 addresses above the address of "array[0]". but at this statement "if (slot > 3)" - slot is simply a negative number, which is INT_MIN + 6, so "slot > 3" returns false.
User contributions licensed under CC BY-SA 3.0