Path: blob/master/web-gui/buildyourownbotnet/core/dao.py
1292 views
#!/usr/bin/python1# -*- coding: utf-8 -*-2'DAO (Build Your Own Botnet)'34# standard library5import os6import json7import math8import hashlib9import collections10from datetime import datetime1112from flask import current_app13from flask_login import login_user, logout_user, current_user, login_required14from buildyourownbotnet.models import db, User, Session, Task, Payload, ExfiltratedFile15from buildyourownbotnet.modules import util161718class UserDAO:19def __init__(self, model):20self.model = model2122def get_user(self, user_id=None, username=None):23"""24Get user data from database.2526`Required`27:param int user_id: User ID28OR29:param str username: Username30"""31user = None32if user_id:33user = db.session.query(self.model).get(user_id)34elif username:35user = db.session.query(self.model).filter_by(username=username).first()36return user3738def add_user(self, username, hashed_password):39"""40Add user to database.4142`Required`43:param str username: username44:param str hashed_password: bcrypt hashed password45"""46user = User(username=username, password=hashed_password)47db.session.add(user)48db.session.commit()49return user505152class SessionDAO:53def __init__(self, model, user_dao):54self.model = model55self.user_dao = user_dao5657def get_session(self, session_uid):58"""Get session metadata from database."""59return db.session.query(self.model).filter_by(uid=session_uid).first()6061def get_user_sessions(self, user_id, verbose=False):62"""63Fetch sessions from database6465`Required`66:param int user_id: User ID6768`Optional`69:param bool verbose: include full session information7071Returns list of sessions for the specified user.72"""73user = self.user_dao.get_user(user_id=user_id)74if user:75return user.sessions76return []7778def get_user_sessions_new(self, user_id):79"""80Get new sessions and update 'new' to False.8182`Required`83:param int user_id: User ID84"""85user = self.user_dao.get_user(user_id=user_id)86new_sessions = []87if user:88sessions = user.sessions89for s in sessions:90if s.new:91s.new = False92new_sessions.append(s)93db.session.commit()94return new_sessions9596def handle_session(self, session_dict):97"""98Handle a new/current client by adding/updating database99100`Required`101:param dict session_dict: session host machine session_dictrmation102103Returns the session information as a dictionary.104"""105# assign new session UID106if not session_dict.get('uid'):107# use unique hash of session characteristics to identify machine if possible108identity = str(session_dict['public_ip'] + session_dict['mac_address'] + session_dict['owner']).encode()109session_dict['uid'] = hashlib.md5(identity).hexdigest()110session_dict['joined'] = datetime.utcnow()111112# upddate session status to online113session_dict['online'] = True114session_dict['last_online'] = datetime.utcnow()115116# check if session metadata already exists in database117session = self.get_session(session_dict['uid'])118119# if session for this machine not found, assign this machine to the listed owner120if not session:121user = self.user_dao.get_user(username=session_dict['owner'])122if user:123sessions = user.sessions124125# increment session id if necessary (TODO: db autoincrement?)126if sessions:127session_dict['id'] = 1 + max([s.id for s in sessions])128else:129session_dict['id'] = 1130131# convert str dates to datetime objects if necessary (should never happen but just in case)132if not isinstance(session_dict['joined'], datetime):133session_dict['joined'] = datetime.utcnow()134if not isinstance(session_dict['last_online'], datetime):135session_dict['last_online'] = datetime.utcnow()136137session = Session(**session_dict)138db.session.add(session)139user.bots += 1140db.session.commit()141142else:143# if user doesn't exist don't add anything144util.log("User not found: " + session_dict['owner'])145else:146# if session metadata found, set session status to online147session.online = True148session.last_online = datetime.utcnow()149db.session.commit()150151if session:152session.new = True153session_dict['id'] = session.id154db.session.commit()155156return session_dict157158def update_session_status(self, session_uid, status):159"""160Update online/offline status of the specified session.161162`Required`163:param int session_id: Session UID164:param bool status: True (online), False (offline)165"""166session = db.session.query(self.model).filter_by(uid=session_uid).first()167if session:168session.online = bool(status)169db.session.commit()170171def delete_session(self, session_uid):172"""173Delete a session from the database.174175`Required`176:param int session_id: Session UID177"""178session = db.session.query(self.model).filter_by(uid=session_uid)179if session:180session.delete()181db.session.commit()182183184class TaskDAO:185def __init__(self, model, session_dao):186self.model = model187self.session_dao = session_dao188189def get_task(self, task_uid):190"""Get task metadata from database."""191return db.session.query(self.model).filter_by(uid=task_uid).first()192193def get_session_tasks(self, session_uid):194"""195Fetch tasks from databse for specified session.196197`Optional`198:param int session_id: Session ID199"""200session = session_dao.get_session(session_uid)201if session:202return session.tasks203return []204205def get_session_tasks_paginated(self, session_id, page=1):206"""207Fetch tasks from database for specified session (paginated).208209`Optional`210:param int session_id: Session ID211212Returns list of tasks for the specified session, and total pages of tasks.213"""214session = db.session.query(self.model).filter_by(id=session_id).first()215if session:216tasks = session.tasks217# janky manual pagination218pages = int(math.ceil(float(len(tasks))/20.0))219blocks = [i for i in range(0, len(tasks), 20)]220if (page - 1 >= 0) and (page + 1 <= len(blocks)):221start, end = blocks[page - 1:page + 1]222if (start >= 0) and (end <= len(tasks)):223return tasks[start:end], pages224return [], 0225226def handle_task(self, task_dict):227"""228Adds issued tasks to the database and updates completed tasks with results229230`Task`231:attr str session: associated session UID232:attr str task: task assigned by server233:attr str uid: task ID assigned by server234:attr str result: task result completed by client235:attr datetime issued: time task was issued by server236:attr datetime completed: time task was completed by client237238Returns task information as a dictionary.239240"""241if not isinstance(task_dict, dict):242task_dict = {'result': 'Error: client returned invalid response: "{}"'.format(str(task_dict))}243return task_dict244if not task_dict.get('uid'):245identity = str(str(task_dict.get('session')) + str(task_dict.get('task')) + datetime.utcnow().__str__()).encode()246task_dict['uid'] = hashlib.md5(identity).hexdigest()247task_dict['issued'] = datetime.utcnow()248task = Task(**task_dict)249db.session.add(task)250# encode datetime object as string so it will be JSON serializable251task_dict['issued'] = task_dict.get('issued').__str__()252else:253task = self.get_task(task_dict.get('uid'))254if task:255task.result = task_dict.get('result')256task.completed = datetime.utcnow()257db.session.commit()258return task_dict259260261class FileDAO:262def __init__(self, model, user_dao):263self.model = model264self.user_dao = user_dao265266def add_user_file(self, owner, filename, session, module):267"""268Add newly exfiltrated file to database.269270`Required`271:param int user_id: user ID272:param str filename: filename273:param str session: public IP of session274:param str module: module name (keylogger, screenshot, upload, etc.)275"""276user = self.user_dao.get_user(username=owner)277if user:278exfiltrated_file = ExfiltratedFile(filename=filename,279session=session,280module=module,281owner=user.username)282db.session.add(exfiltrated_file)283db.session.commit()284return exfiltrated_file285286def get_user_files(self, user_id):287"""288Get a list of files exfiltrated by the user.289290`Required`291:param int user_id: user ID292"""293user = self.user_dao.get_user(user_id=user_id)294if user:295return user.files296return []297298299class PayloadDAO:300def __init__(self, model, user_dao):301self.model = model302self.user_dao = user_dao303304def get_user_payloads(self, user_id):305"""306Get a list of the user's payloads.307308`Required`309:param int user_id: user ID310"""311user = self.user_dao.get_user(user_id=user_id)312if user:313return user.payloads314return []315316def add_user_payload(self, user_id, filename, operating_system, architecture):317"""318Add newly generated user payload to database.319320`Required`321:param int user_id: user ID322:param str filename: filename323:param str operating_system: nix, win, mac324:param str architecture: x32, x64, arm64v8/debian, arm32v7/debian, i386/debian325"""326user = self.user_dao.get_user(user_id=user_id)327if user:328payload = Payload(filename=filename,329operating_system=operating_system,330architecture=architecture,331owner=user.username)332db.session.add(payload)333db.session.commit()334return payload335336user_dao = UserDAO(User)337session_dao = SessionDAO(Session, user_dao)338task_dao = TaskDAO(Task, session_dao)339payload_dao = PayloadDAO(Payload, user_dao)340file_dao = FileDAO(ExfiltratedFile, user_dao)341342343