BSides Canberra 2018 CTF Write-Up: Fat Morphine

This is a write-up of the Fat Morphine challenge from the BSides Canberra 2018 CTF. This challenge was worth 400 points.

The format of this is easy enough, but my name will be least significant when you go for the flag.

Solution

The first thing we did was to run the binary and see what happens:

Output with no arguments

Providing a “name” resulted in that name being printed out, along with some other messages:

Output with the name “George”

Trying names with long lengths did not seem to affect the binary at all. We then re-read the problem description, and noticed a key word: “format”. We believed this could refer to format strings. We tried with a name of “%x”:

Interestingly, the output didn’t contain “%x”, rather some hex value.

Output with the name “%x”

Format Strings

Before we continue, we will briefly introduce format strings. Format strings assist in printing out all sorts of variables with a variety of types. Format strings are used in printf calls, for example:

char* str = "World!";
printf("Hello, %s", str); // Prints "Hello, World!"

There are a variety of “specifiers” telling printf how to print out a variable. Some of these specifiers are:

  • %x — prints out the value in hex
  • %p — prints out the value as a pointer
  • %s — prints out the value as a string (until a NULL byte is reached)
  • %n — stores the number of characters that have been printed into the value (which is an int*)

A width flag can be specified for padding purposes. Using a format string of %8x will print the hex value, padded to 8 characters with spaces. A selection flag can be used to specify which variable the specifier is referring to. Using a format string of %3$x refers to the third variable.

Exploiting Format Strings

A format string vulnerability looks like the following:

char* input = "%x"; /* This is actually user input */
printf(input);

Trying to compile this will result in a warning stating it is unsafe to directly put a variable as the first argument of a printf statement.

If this were to run, what would happen? There is no arguments, so printf will use variables on the stack. We can use the %x specifier to first find a place to write to, or to find where our inputted string is on the the stack, then use that specifier with the padding flag to increase the character count to a certain value, then use %n to write that value to a specific location.

Let’s do this for this challenge.

First, we have to find at what offset is out inputted string. We use multiple %xspecifiers to print out the hex values on the stack:

Output with multiple %x, using “..” to separate the values

We immediately see a range of values printed out. We see the fourth value representing the length of the input string, then some random values, then a recurring pattern (from the 7th value). These hex values look like our string!. We now know where the string starts from.

We now need to figure out what we want to write. From the previous challenges, there has been a read_flag() function. If we list the functions in the binary through gdb, we see that the function is still there:

Snippet of the function list in the fat_morphine binary

Running disas read_flag in gdb tells us the starting address is 0x400726. We do not need to account for ASLR since it is not enabled:

Now we need to figure out where we should be writing to. We can use similar techniques as in the previous challenges by overwriting an entry in the GOT. We have to find an appropriate entry to overwrite.

Disassembly of the main function

The code block above is the code block executed if there is a name argument provided. We notice that there are no printf calls, as we would expect there are 3, but there are 3 calls to a function at 0x4007ab (which is stored in 0x600d98). Disassembling the function at this address shows us the printfs:

Disassembly of 0x4007ab

We decided to overwrite the putchar entry since it occurred directly after the printf call. The GOT entry address for putchar is at 0x600d30.

Now we have what we want to write (0x400726), and we know where we want to write it (0x600d30). Now let’s structure our payload.

First, we want to use padding to get the character output count to what we want to write. We can do this by converting the hex value to decimal, then use that as the padding.

payload = ""
payload += "%4196134x"

Using this will print 0x400726 spaces (minus the amount of characters actually printed). The value printed out after all these spaces is 0x40095c.

We then want to write that value to a specific location. For the time being, we won’t worry about where. We also have to use a slight variation of the classic %n%lln. This writes the value as 64 bit number, so that it zero’s the upper 4 bytes of the GOT entry (otherwise the upper 4 bytes from the libc library will remain).

payload += "%{}$lln" # to fill

Using gdb, we will not find anywhere on the stack reference to 0x600d30, the place where we want to write to. However, we can put that value on the stack.

Recall that C strings are terminated by a NULL character, and the 4-byte address of where we want to write to is 0x00600d30. So as long as we put the address of where we want to write to at the end of our payload, it will automatically have the NULL character at the end of it.

payload += "\x30\x0d\x60"

Note we put the address in reverse due to how the string is stored in memory.

Now we have all our components, we can fill in the position where we want to write to. To find what position our string starts, we can use a string of the same length as above, with different characters every 4 characters, and use %<>$x to find the position, which turned out to be 7. Every increase of the position by 1 moves 4 bytes into the payload.

owever, we forgot that this was a 64 bit binary, so in fact an increase of 1 in the position would move forward by 8 characters!

f we assume the position to fill will be 2 digits long (>= 10), then each position would map to the following characters:

So using the position 10 would make our payload work.

payload = payload.format(10)

Running this print a lot of empty characters, followed by a value from the stack and the flag.

Final solution:

A big thank you to Elttam for organising the CTF challenges!