Shellcode Runner 3 + Revenge (pwn)
Your favorite series of Shellcode runners is back! This time, we're not limiting you with any sandboxes. This should be an easy challenge...right?
Last updated
Your favorite series of Shellcode runners is back! This time, we're not limiting you with any sandboxes. This should be an easy challenge...right?
Last updated
As the name suggests, the binary prompts us for shellcode and proceeds to run it. Let's inspect the pseudocode using Ghidra:
Note that mprotect(__s, 100, 4)
means that the shellcode is not writable and readable but only executable. So any form of self-modifying shellcode would not work here.
Let's look at the blacklist function first:
It essentially stops our shellcode from being run if the 0f
byte is detected. This byte is present in the syscall
instruction, which means that our shellcode can no longer use syscalls. One way to go around this is by using int 0x80
which is a legacy method of invoking syscalls, but I did not use that approach. Let's further reverse the binary.
There is even more going on under the hood when we look at the assembly. Just before the jmp rdi
instruction which jumps to the shellcode, the program zeroes out all the registers to minimise our chances of getting any useful addresses:
After some researching into this challenge, I found this: https://github.com/NUSGreyhats/greyctf24-challs-public/tree/main/finals/pwn/super_secure_blob_runner
It is a pwn challenge made by NUS Greyhats for the 2024 greyctf finals, and has the same ideas: Blacklisting syscall
bytes and zeroing registers. The idea behind the solution was to read the fs
register, which points to thread local storage (TLS), a region of memory adjacent to libc.
In pwndbg, I use vmmap
to view the process' memory mappings, then info reg fs_base
to see where the fs
register is pointing to.
As we can see, it is pointing to 0x7ffff7d7d740
, which is within the anon_7ffff7d7d
region which is between 0x7ffff7d7d000
and 0x7ffff7d80000
. This region comes right before libc. Hence, the fs
register essentially acts as a libc leak for us.
Below is my shellcode:
I first transfer the value of fs
into rbx
, then let rbx
store the address of execve
in libc. Then, I make rdi
point to the /bin//sh
string, and call rbx
.
And below is my full solve script: