-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsuwudo.py
executable file
·375 lines (292 loc) · 12.2 KB
/
suwudo.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
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
#!/usr/bin/env python3
# These may be edited to your liking
import os
import platform
import random
import shutil
import subprocess
import sys
import time
from datetime import datetime
from multiprocessing import Pool
from statistics import variance
from typing import Dict, List, Set, Tuple
# This is the list of custom insults. You may replace any one of them.
# Unicode etc. should work if your terminal supports it. Mine does.
_custom_insults: List[str] = [
# Default insults
"Take one of your stress pills and think things over :D ",
"If only you used more than just two fingers... :D",
# Gir-ify-ed default insults
"Just what do you think you're doing ✨Emily✨? ( ◕‿◕✿)",
"I'm sorry, but that's something I cannot allow to happen ^-^",
"Are you on drugs? No. Seriously. Are you?",
"🦦 My pet ferret can type better than you! <3",
"You silly twisted girl you 💞",
# Custom insults
"Love you <3",
"Not today girl!",
"uwu :3",
"ʘwʘ",
"Ouch ÕwÕ",
"Hey Emily~",
"uwu ^-^",
"Please don't hit the keyboard so hard 🥺",
"🥺 P- Please? 🥺 👉👈 🥺",
# Big emojis
"""With love 💗
/) /)
( ᵔ ᵕ ᵔ )
/ づ づ ~ 💖\
""",
"""\
/) /)
( •-• ) <(i want a hug pwease 💖)
/づづ\
""",
"""\
/\\___/\\
꒰ ˶• ༝ - ˶꒱
./づ~ ♥
you deserve this ! ♥""",
]
static_mapping = {
b"""We trust you have received the usual lecture from the local System
Administrator. It usually boils down to these three things:
#1) Respect the privacy of others.
#2) Think before you type.
#3) With great power comes great responsibility.
""":
b"""We twust that u weceived the uwusuwal wecture fwom the local System
Adwinistwator. It uwusualy boils down to thees thwee things:
#1) wespect the pwivacy of others.
#2) Think before u type.
#3) With gweat power comes gweat wesponsibiwity.
""",
}
### DON'T EDIT ###
all_insults = [
b"Just what do you think you're doing Dave?",
b"It can only be attributed to human error.",
b"That's something I cannot allow to happen.",
b"My mind is going. I can feel it.",
b"Take a stress pill and think things over.",
b"This mission is too important for me to allow you to jeopardize it.",
b"I feel much better now.",
b"And you call yourself a Rocket Scientist!",
b"Where did you learn to type?",
b"Are you on drugs?",
b"My pet ferret can type better than you!",
b"Do you think like you type?",
b"Your mind just hasn't been the same since the electro-shock, has it?",
b"Maybe if you used more than just two fingers...",
b"BOB says: You seem to have forgotten your passwd, enter another!",
b"stty: unknown mode: doofus",
b"Listen, burrito brains, I don't have time to listen to this trash.",
b"I've seen penguins that can type better than that.",
b"Have you considered trying to match wits with a rutabaga?",
b"You speak an infinite deal of nothing",
b"He has fallen in the water!",
b"We'll all be murdered in our beds!",
b"You can't come in. Our tiger has got flu",
b"I don't wish to know that.",
b"You'll starve!",
b"... and it used to be so popular...",
b"Have a gorilla...",
b"There must be cure for it!",
b"You do that again and see what happens...",
b"Ying Tong Iddle I Po",
b"Harm can come to a young lad like that!",
b"You gotta go owwwww!",
b"I have been called worse.",
b"It's only your word against mine.",
b"I think ... err ... I think ... I think I'll go home",
b"You silly, twisted boy you.",
b"Wrong! You cheating scum!",
b"I wish to make a complaint.",
b"You type like i drive.",
b"Sorry about this, I know it's a bit silly.",
b"What, what, what, what, what, what, what, what, what, what?",
b"You can't get the wood, you know.",
b"Pauses for audience applause, not a sausage",
b"Hold it up to the light --- not a brain in sight!",
b"There's a lot of it about, you know.",
b"And with that remarks folks, the case of the Crown vs yourself was proven",
b"Speak English you fool --- there are no subtitles in this scene.",
b"I can't hear you -- I'm using the scrambler.",
b"The more you drive -- the dumber you get.",
b"That is no basis for supreme executive power!",
b"You empty-headed animal food trough wiper!",
b"I fart in your general direction!",
b"Your mother was a hamster and your father smelt of elderberries!",
b"You must cut down the mightiest tree in the forest... with... a herring!",
b"I wave my private parts at your aunties!",
b"He's not the Messiah, he's a very naughty boy!",
b"When you're walking home tonight, and some homicidal maniac comes after you with a bunch of loganberries, don't come crying to me!",
b"This man, he doesn't know when he's beaten! He doesn't know when he's winning, either. He has no... sort of... sensory apparatus...",
b"There's nothing wrong with you that an expensive operation can't prolong.",
b"I'm very sorry, but I'm not allowed to argue unless you've paid.",
]
# Sort the existing lists, so we can abuse that for the algorithm
custom_insults = sorted([item.encode() for item in _custom_insults], key=len)
all_insults = sorted([item for item in all_insults], key=len)
# Will stop the algorithm after ↓ unsuccessful attempts
stop_after_iterations = 45
# Set the seed to something preselected. This will guarantee a good mapping.
random.seed(8557)
def find_mapping_random_improvement() -> Dict[bytes, bytes]:
"""
This algorithm is kinda shitty. I'll leave it here to compare against.
"""
insult_nums = [0 for _ in range(len(custom_insults))]
solution: Dict[int, Set[int]] = {i: set() for i in range(len(custom_insults))}
back_mapping = {i: 0 for i in range(len(all_insults))}
insult_nums[0] = len(all_insults)
solution[0] = set(range(len(all_insults)))
i = 0
while i < stop_after_iterations:
to_improve = random.randint(0, len(all_insults) - 1)
new_index = random.randint(0, len(custom_insults) - 1)
if len(all_insults[to_improve]) < len(custom_insults[new_index]):
continue
prev_var = variance(insult_nums)
insult_nums[back_mapping[to_improve]] -= 1
insult_nums[new_index] += 1
cur_var = variance(insult_nums)
if cur_var > prev_var:
# Undo the mistake
insult_nums[back_mapping[to_improve]] += 1
insult_nums[new_index] -= 1
i += 1
continue
i = 0
solution[back_mapping[to_improve]].remove(to_improve)
solution[new_index].add(to_improve)
back_mapping[to_improve] = new_index
mapping = {}
for k, row in solution.items():
for index in row:
mapping[all_insults[index]] = custom_insults[k]
return mapping
def find_mapping_random_guesses() -> Dict[bytes, bytes]:
assert len(custom_insults) < len(all_insults)
mapping: Dict[bytes, bytes] = {}
possible: List[int] = [i for i in range(len(custom_insults))]
cutoff = 0
weights = [1 for _ in range(len(custom_insults))]
for old in all_insults:
while cutoff < len(custom_insults):
if len(custom_insults[cutoff]) > len(old):
break
cutoff += 1
chosen_index = random.choices(possible[:cutoff], weights[:cutoff], k=1)[0]
weights[chosen_index] /= 5
mapping[old] = custom_insults[chosen_index]
return mapping
mapping_algorithm = find_mapping_random_guesses
def _wasted_space(seed: int) -> Tuple[int, int, int]:
random.seed(seed)
mapping = mapping_algorithm()
lst = list(mapping.values())
nums = [lst.count(item) for item in custom_insults]
return seed, sum(len(item) for item in mapping.keys()) - sum(len(item) for item in mapping.values()), variance(nums)
def bruteforce_random_seed() -> None:
s = time.perf_counter()
with Pool(16) as ex:
nums = ex.map(_wasted_space, range(30000))
min_var = min(nums, key=lambda x: x[2])[2]
best = sorted(nums, key=lambda x: x[1] if x[2] == min_var else 999999)[0]
print(f"Done in {time.perf_counter() - s:.3f}s")
print(f"Seed is {best[0]}")
random.seed(best[0])
mapping = mapping_algorithm()
wasted_space = sum(len(item) for item in mapping.keys()) - sum(len(item) for item in mapping.values())
nums = [list(mapping.values()).count(item) for item in custom_insults]
print(f"Seed: {best[0]}, variance: {variance(nums):.3f}, wasted space: {wasted_space}")
print(nums)
exit(0)
def show_hist() -> None:
import matplotlib.pyplot as plt
plt.hist([len(item.decode()) for item in all_insults])
plt.hist([len(item) for item in custom_insults], color="r")
plt.show()
def install_distro_package() -> None:
try:
import distro
except ImportError:
print("""You linux distribution may have a deviating path for the `sudoers.so` file.
If your distribution is in the following you *will* have to install it for the executable to be installed.
{"Fedora"}
If you do not install the package I will go with the default path.
Is that okay? [y/n]""")
while True:
choice = input()
if choice in {"y", "n"}:
break
print('You choice is not in the expected: {"y", "n"}.')
if choice == "n":
print("Alright. I will stick with `/usr/lib/sudo/sudoers.so` for the path.")
return
exit_code = subprocess.check_call([sys.executable, "-m", "pip", "install", "distro"])
if exit_code:
print("\033[1;91mError!\033[0m Something went wrong while installing the package. Aborting!")
exit(exit_code)
def main() -> None:
if platform.system() == "Windows":
print("Your operating system is not supported.")
exit(1)
install_distro_package()
import distro
# Distro agnostic sudoers path
sudoers_path = "/usr/lib/sudo/sudoers.so"
lectured_file = f"/var/db/sudo/lectured/{os.environ['USER']}"
dist = distro.like() or distro.id()
if dist == "fedora":
sudoers_path = "/usr/libexec/sudo/sudoers.so"
elif dist == "debian":
lectured_file = f"/var/lib/sudo/lectured/{os.environ['USER']}"
# Back up the file
sudo_backup_path = os.path.join(os.path.expanduser("~"), ".cache", "sudo")
sudo_backup_file_time = os.path.join(sudo_backup_path, "sudoers.so.bak." + str(int(datetime.now().timestamp())))
sudo_backup_file = os.path.join(sudo_backup_path, "sudoers.so.bak")
os.makedirs(sudo_backup_path, exist_ok=True)
shutil.copyfile(sudoers_path, sudo_backup_file_time)
if os.path.exists(sudo_backup_file):
os.unlink(sudo_backup_file)
os.symlink(sudo_backup_file_time, os.path.join(sudo_backup_path, "sudoers.so.bak"))
# Now the real fun begins
with open(sudoers_path, "rb") as f:
content = f.read()
# show_hist()
# bruteforce_random_seed()
mapping = mapping_algorithm()
mapping |= static_mapping
mapping = {k: v.ljust(len(k)) for k, v in mapping.items()}
# Make sure that sudo won't crash afterwards
for k, v in mapping.items():
assert len(v) == len(k)
# Replace all old strings with new opes in *only 1 pass*
# Copied from https://stackoverflow.com/a/15175239
# Unfortunately this produced wrong code. I don't know why.
# regex = re.compile(b"(%s)" % b"|".join(map(re.escape, mapping.keys())))
# content = regex.sub(lambda mo: mapping[mo.string[mo.start():mo.end()]], content)
for old, new in mapping.items():
content = content.replace(old, new)
new_nums = {custom_insult.decode(): content.count(custom_insult) for custom_insult in custom_insults}
print("\nI've achieved the following distribution:\n")
print("{")
for st, count in new_nums.items():
print(f" {st}: {count},")
print("}")
with open("./sudoers.so", "wb") as f:
f.write(content)
# Install the file and remove lecture prompt
os.system(f"sudo cp ./sudoers.so {sudoers_path}")
os.system(f"sudo rm -f {lectured_file} &>/dev/null | cat")
print("Installed successfully")
# Future ideas
# - sudo terminates after 3 wrong attempts
# - Bring this to the arch user repository
# - ansible config
if __name__ == '__main__':
main()