2
votes

So I have this function:

void print_usage(char* arg) 
{
    char buffer[640];
sprintf(buffer, "Usage: %s [options]\n"
        "Randomly generates a password, optionally writes it to /etc/shadow\n"
        "\n"
        "Options:\n"
        "-s, --salt <salt>  Specify custom salt, default is random\n"
        "-e, --seed [file]  Specify custom seed from file, default is from stdin\n"
        "-t, --type <type>  Specify different encryption method\n"
        "-v, --version      Show version\n"
        "-h, --help     Show this usage message\n"
        "\n"
        "Encryption types:\n"
        "   0 - DES (default)\n"
        "   1 - MD5\n"
        "   2 - Blowfish\n"
        "   3 - SHA-256\n"
        "   4 - SHA-512\n", arg);
    printf(buffer);
}

I wish to utilize a format string vulnerability attack (my assignment). Here is my attempt:

I have an exploit program which fills a buffer with noops and shell code (I have used this program to buffer overflow the same function, so I know its good). Now, I did an object dump of the file to find the .dtors_list address and I got 0x0804a20c, adding 4 bytes to get the end I get 0x804a210.

Next I used gdb to find at what address my noops begin while running my program. Using this I got 0xffbfdbb8.

So up to this point I feel like I'm correct, now I know I want to use format string to copy the noop address into my .dtors_end address. Here is the string I came up with (this is the string I'm providing as user input to the function):

"\x10\xa2\x04\x08\x11\xa2\x04\x08\x12\xa2\x04\x08\x13\xa2\x04\x08%%.168u%%1$n%%.51u%%2$n%%.228u%%3$n%%.64u%%4$n"

This doesn't work for me. The program runs normally and the %s is replaced with the string I input (minus the little endian memory address at the front, and the two percent signs are now one percent sign for some reason).

Anyways, I'm kind of stumped here, any help would be appreciated.

1
printf() formats %% as % so that your output can still contain % signs.FatalError
Oh so I have to use one percent sign then?user1415374
Rather, if you wanted the output to have %% the format string needs to have %%%%.FatalError
Well I don't really care about my output, I just want those numbers (representing my noop address) to be copied to the specified .dotrs_end. I'm kind of a C noob so I'm pretty sure I'm messing something up in the string I'm providing.user1415374

1 Answers

0
votes

Disclaimer: I'm no expert.

You're passing "\x10\xa2\x04\x08\x11\xa2\x04\x08\x12\xa2\x04\x08\x13\xa2\x04\x08%%.168u%%1$n%%.51u%%2$n%%.228u%%3$n%%.64u%%4$n" as the value of arg? That means that buffer will contain

"Usage:\x20\x10\xa2\x04\x08\x11\xa2\x04\x08\x12\xa2\x04\x08\x13\xa2\x04\x08%.168u%1$n%.51u%2$n%.228u%3$n%.64u%4$n [options]\x0aRandomly..."

Now let's further assume that you're on an x86-32 target (if you're on x86-64, this won't work), and that you're compiling with an optimization level that doesn't put anything in print_usage's stack frame except for the 640-byte buffer array.

Then printf(buffer) will do the following things, in order:

  • Push the 4-byte address &buffer.
  • Push a 4-byte return address.
  • Invoke printf...
  • Print out "Usage:\x20\x10\xa2\x04\x08\x11\xa2\x04\x08\x12\xa2\x04\x08\x13\xa2\x04\x08" (a sequence of 23 bytes).
  • %.168u: Interpret the next argument to printf as an unsigned int and print it in a field of width 168. Since printf has no next argument, this is actually going to print the next thing on the stack; that is, the first four bytes of buffer; that is, "Usag" (0x67617355).
  • %1$n: Interpret the second argument to printf as a pointer to int and store 23+168 at that location. This stores 0x000000bf in location 0x67617355. So this is your main problem: You should have used %2$n instead of %1$n and added one junk byte to the front of your arg. (Incidentally, notice that GNU says "If any of the formats has a specification for the parameter position all of them in the format string shall have one. Otherwise the behavior is undefined." So you should go through and add 1$s to all your %us just to be on the safe side.)
  • %.51u: Print another 51 bytes of garbage.
  • %2$n: Interpret the third argument to printf as a pointer to int and store 0x000000f2 in that garbage location. As above, this should have been %3$n.
  • ... etc. etc. ...

So, your major bug here is that you forgot to account for the "Usage: " prefix.

I assume you were trying to store the four bytes 0xffbfdbb8 into address 0x804a210. Let's say you'd gotten that to work. But then what would your next step be? How do you get the program to treat the four-byte quantity at 0x804a210 as a function pointer and jump through it?

The traditional way to exploit this code would be to exploit the buffer overflow in sprintf, rather than the more complicated "%n" vulnerability in printf. You just need to make your arg roughly 640 characters long and make sure that the 4 bytes of it that correspond to print_usage's return address contain the address of your NOP sled.

Even that part is tricky, though. You might conceivably be running into something related to ASLR: just because your sled exists at address 0xffbfdbb8 in one run doesn't mean it'll exist at that same address in the next run.

Does this help?