Pop Instructions Should be Illegal
swrop
FLAG{REDACTED}
Simple W(??) rop. For this one all we need is to redirect code execution, using a simple buffer overflow. Only difference is that there is no win
function.
- The
system
call is already in thenot_call
function /bin/sh
is in the data region for some reason.
With that in mind, we call system
, putting the address to /bin/sh
at the top of the stack.
1 | #!/usr/bin/python3 |
static
FLAG{REDACTED}
Graduating from a single rop gadget, we move to larger rop chains. The goal in this one was to pop a shell.
Since it is a statically compiled binary, we have a lot of rop gadgets to play with. While there is no /bin/sh
, the pointer to the buffer we overflow is put loaded into eax
after the call.
- As the buffer is read in using fgets, we can simulate the string by putting
/bin/sh\x00
into the buffer. This way, the “string” in the buffer is only 8 bytes long. - Set the registers appropriately as:
eax = 0xb
—> execveebx = <address of the buffer>
—>/bin/sh\x00
ecx = 0
edx = 0
- Run a syscall using
int 0x80
cat flag
1 | #!/usr/bin/python3 |
roproprop
FLAG{REDACTED}
This one was very similar to the previous one, except it has PIE enabled, and is dynamically linked.
- Dynamically linked, means that there aren’t enough rop gadgets to easily make a chain.
- PIE means that we need to calculate the actual addresses of the rop gadgets by offsetting them with the address of libc-base
Fortunately, we have a pointer into a libc leaked to us. Turns out, it points to the setbuf
function. In that case, all we need is to calculate the start of libc by subtracting the offset to setbuf
from it. Since the exact version of libc is provided, we can use that to get the offset of setbuf
and calculate the value of libc-base. After that we can simply offset each of the rop gadgets found in libc with the libc base calculated at runtime and pop a shell using the method from static.
Did I mention pop
instructions are too amazing?
1 | #!/usr/bin/python3 |
ropme
FLAG{REDACTED}
The final rop challenge.
I’m sure there’s a way to run puts(<puts@plt>)
, leaking libc, but unlike the previous challenges, I simply used the read and write syscalls to dump the flag instead of trying to pop a shell.
Interestingly, because of how the program works, the flag file is bound to be opened in the next available file descriptor: 3
(after stdin
, stdout
and stderr
). Exploiting this fact, we can dump the flag
file without a shell.
Notes:
- What you want to be careful is not to put too many bytes into the stream, so you don’t end up going beyond the possible number of bytes the program can take in before you crash it. The payload is fairly large, and the “buffer” rather small at only 8 bytes.
- On the flip-side, you also need to remember that you’re overwriting the buffer you’re putting the rop chain in, so you need to make sure that the next rop instruction is not accidently overwritten. This probably won’t happen because all the rop gadgets are 4 bytes long, whereas the instruction inside:
add edx, 1; ret;
only adds one byte the total number of characters to write.
1 | #!/usr/bin/python3 |
re.c
re.c
Can’t believe I was having difficulty introducing a bug
This was an interesting reversing challenge. A first with IDA, hopefully the last. Why does it not have dark mode lol.
It was fun trying to recognize and understand the struct linked list pattern. The program loops until the counter is greater than 9, creating one node in the list on every iteration. There is a check to see if the malloc
worked properly. What was particularly amusing is that the list is created backwards. So the linked list would look like ...E -> D -> C -> B -> A -> NULL
instead of the opposite. Arguably, you don’t need the if statement in the loop, but I guess it adds to the challenge.
If I was confused about something it would be as to why the struct in memory looks like
<char: 1 byte> + <padding: 3 bytes> + <next pointer: 4 bytes>
instead of
<next pointer: 4 bytes> + <char: 1 byte>
but perhaps this gets removed when (and if) the binary is optimized.
1 | struct linkedList { |
Conclusion
Ropped