First commit
This commit is contained in:
		
							
								
								
									
										15
									
								
								server/frontend/app/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								server/frontend/app/__init__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,15 @@
 | 
			
		||||
#!/usr/bin/env python3
 | 
			
		||||
# -*- coding: utf-8 -*-
 | 
			
		||||
 | 
			
		||||
from sqlalchemy import create_engine, MetaData, Table
 | 
			
		||||
from sqlalchemy.orm import scoped_session, mapper
 | 
			
		||||
from sqlalchemy.orm.session import sessionmaker
 | 
			
		||||
import sys
 | 
			
		||||
 | 
			
		||||
parent = "/".join(sys.path[0].split("/")[:-2])
 | 
			
		||||
engine = create_engine('sqlite:////{}/tinycheck.sqlite3'.format(parent), convert_unicode=True)
 | 
			
		||||
metadata = MetaData(bind=engine)
 | 
			
		||||
session = scoped_session(sessionmaker(autocommit=False, autoflush=False, bind=engine))
 | 
			
		||||
 | 
			
		||||
class Model(object):
 | 
			
		||||
    query = session.query_property()
 | 
			
		||||
							
								
								
									
										12
									
								
								server/frontend/app/assets/hostapd.conf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								server/frontend/app/assets/hostapd.conf
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
			
		||||
country_code=GB
 | 
			
		||||
interface={IFACE}
 | 
			
		||||
ssid={SSID}
 | 
			
		||||
hw_mode=g
 | 
			
		||||
channel=7
 | 
			
		||||
auth_algs=1
 | 
			
		||||
wpa=2
 | 
			
		||||
wpa_passphrase={PASS}
 | 
			
		||||
wpa_key_mgmt=WPA-PSK
 | 
			
		||||
wpa_pairwise=TKIP
 | 
			
		||||
rsn_pairwise=CCMP
 | 
			
		||||
disassoc_low_ack=0
 | 
			
		||||
							
								
								
									
										29
									
								
								server/frontend/app/blueprints/analysis.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								server/frontend/app/blueprints/analysis.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,29 @@
 | 
			
		||||
#!/usr/bin/env python3
 | 
			
		||||
# -*- coding: utf-8 -*-
 | 
			
		||||
 | 
			
		||||
import re
 | 
			
		||||
import os
 | 
			
		||||
import json
 | 
			
		||||
import sys
 | 
			
		||||
from flask import Blueprint, jsonify
 | 
			
		||||
from app.classes.analysis import Analysis
 | 
			
		||||
import subprocess as sp
 | 
			
		||||
import json
 | 
			
		||||
 | 
			
		||||
analysis_bp = Blueprint("analysis", __name__)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@analysis_bp.route("/start/<token>", methods=["GET"])
 | 
			
		||||
def api_start_analysis(token):
 | 
			
		||||
    """ 
 | 
			
		||||
        Start an analysis
 | 
			
		||||
    """
 | 
			
		||||
    return jsonify(Analysis(token).start())
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@analysis_bp.route("/report/<token>", methods=["GET"])
 | 
			
		||||
def api_report_analysis(token):
 | 
			
		||||
    """ 
 | 
			
		||||
        Get the report of an analysis
 | 
			
		||||
    """
 | 
			
		||||
    return jsonify(Analysis(token).get_report())
 | 
			
		||||
							
								
								
									
										26
									
								
								server/frontend/app/blueprints/capture.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								server/frontend/app/blueprints/capture.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,26 @@
 | 
			
		||||
#!/usr/bin/env python3
 | 
			
		||||
# -*- coding: utf-8 -*-
 | 
			
		||||
 | 
			
		||||
from flask import jsonify, Blueprint
 | 
			
		||||
from app.classes.capture import Capture
 | 
			
		||||
 | 
			
		||||
capture = Capture()
 | 
			
		||||
capture_bp = Blueprint("capture", __name__)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@capture_bp.route("/start", methods=["GET"])
 | 
			
		||||
def api_capture_start():
 | 
			
		||||
    """ Start the capture """
 | 
			
		||||
    return jsonify(capture.start_capture())
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@capture_bp.route("/stop", methods=["GET"])
 | 
			
		||||
def api_capture_stop():
 | 
			
		||||
    """ Stop the capture """
 | 
			
		||||
    return jsonify(capture.stop_capture())
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@capture_bp.route("/stats", methods=["GET"])
 | 
			
		||||
def api_capture_stats():
 | 
			
		||||
    """ Stop the capture """
 | 
			
		||||
    return jsonify(capture.get_capture_stats())
 | 
			
		||||
							
								
								
									
										13
									
								
								server/frontend/app/blueprints/device.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								server/frontend/app/blueprints/device.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,13 @@
 | 
			
		||||
#!/usr/bin/env python3
 | 
			
		||||
# -*- coding: utf-8 -*-
 | 
			
		||||
 | 
			
		||||
from flask import jsonify, Blueprint
 | 
			
		||||
from app.classes.device import Device
 | 
			
		||||
 | 
			
		||||
device_bp = Blueprint("device", __name__)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@device_bp.route("/get/<token>", methods=["GET"])
 | 
			
		||||
def api_device_get(token):
 | 
			
		||||
    """ Get device assets """
 | 
			
		||||
    return jsonify(Device(token).get())
 | 
			
		||||
							
								
								
									
										30
									
								
								server/frontend/app/blueprints/misc.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								server/frontend/app/blueprints/misc.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,30 @@
 | 
			
		||||
#!/usr/bin/env python3
 | 
			
		||||
# -*- coding: utf-8 -*-
 | 
			
		||||
 | 
			
		||||
import subprocess as sp
 | 
			
		||||
from flask import Blueprint, jsonify
 | 
			
		||||
from app.utils import read_config
 | 
			
		||||
 | 
			
		||||
misc_bp = Blueprint("misc", __name__)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@misc_bp.route("/reboot", methods=["GET"])
 | 
			
		||||
def api_reboot():
 | 
			
		||||
    """ 
 | 
			
		||||
        Reboot the device 
 | 
			
		||||
    """
 | 
			
		||||
    sp.Popen("reboot", shell=True)
 | 
			
		||||
    return jsonify({"mesage": "Let's reboot."})
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@misc_bp.route("/config", methods=["GET"])
 | 
			
		||||
def get_config():
 | 
			
		||||
    """ 
 | 
			
		||||
        Get configuration keys relative to the GUI 
 | 
			
		||||
    """
 | 
			
		||||
    return jsonify({
 | 
			
		||||
        "virtual_keyboard": read_config(("frontend", "virtual_keyboard")),
 | 
			
		||||
        "hide_mouse": read_config(("frontend", "hide_mouse")),
 | 
			
		||||
        "download_links": read_config(("frontend", "download_links")),
 | 
			
		||||
        "sparklines": read_config(("frontend", "sparklines")),
 | 
			
		||||
    })
 | 
			
		||||
							
								
								
									
										50
									
								
								server/frontend/app/blueprints/network.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								server/frontend/app/blueprints/network.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,50 @@
 | 
			
		||||
#!/usr/bin/env python3
 | 
			
		||||
# -*- coding: utf-8 -*-
 | 
			
		||||
 | 
			
		||||
from flask import Blueprint, jsonify, request
 | 
			
		||||
from app.classes.network import Network
 | 
			
		||||
 | 
			
		||||
network = Network()
 | 
			
		||||
network_bp = Blueprint("network", __name__)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@network_bp.route("/status", methods=["GET"])
 | 
			
		||||
def api_network_status():
 | 
			
		||||
    """ Get the network status of eth0, wlan0 """
 | 
			
		||||
    return jsonify(network.check_status())
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@network_bp.route("/wifi/list", methods=["GET"])
 | 
			
		||||
def api_get_wifi_list():
 | 
			
		||||
    """ List available WIFI networks """
 | 
			
		||||
    return jsonify(network.wifi_list_networks())
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@network_bp.route("/wifi/setup", methods=["POST", "OPTIONS"])
 | 
			
		||||
def api_set_wifi():
 | 
			
		||||
    """ Set an access point and a password """
 | 
			
		||||
    if request.method == "POST":
 | 
			
		||||
        data = request.get_json()
 | 
			
		||||
        res = network.wifi_setup(data["ssid"], data["password"])
 | 
			
		||||
        return jsonify(res)
 | 
			
		||||
    else:
 | 
			
		||||
        return ""
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@network_bp.route("/wifi/connect", methods=["GET"])
 | 
			
		||||
def api_connect_wifi():
 | 
			
		||||
    """ Connect to the specified wifi network """
 | 
			
		||||
    res = network.wifi_connect()
 | 
			
		||||
    return jsonify(res)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@network_bp.route("/ap/start", methods=["GET"])
 | 
			
		||||
def api_start_ap():
 | 
			
		||||
    """ Start an access point """
 | 
			
		||||
    return jsonify(network.start_ap())
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@network_bp.route("/ap/stop", methods=["GET"])
 | 
			
		||||
def api_stop_ap():
 | 
			
		||||
    """ Generate an access point """
 | 
			
		||||
    return jsonify(network.stop_hostapd())
 | 
			
		||||
							
								
								
									
										21
									
								
								server/frontend/app/blueprints/save.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								server/frontend/app/blueprints/save.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,21 @@
 | 
			
		||||
#!/usr/bin/env python3
 | 
			
		||||
# -*- coding: utf-8 -*-
 | 
			
		||||
 | 
			
		||||
from flask import Blueprint, jsonify, request
 | 
			
		||||
from app.classes.save import Save
 | 
			
		||||
from app.classes.device import Device
 | 
			
		||||
 | 
			
		||||
save = Save()
 | 
			
		||||
save_bp = Blueprint("save", __name__)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@save_bp.route("/usb-check", methods=["GET"])
 | 
			
		||||
def api_usb_list():
 | 
			
		||||
    """ List connected usb devices """
 | 
			
		||||
    return save.usb_check()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@save_bp.route("/save-capture/<token>/<method>", methods=["GET"])
 | 
			
		||||
def api_save_capture(token, method):
 | 
			
		||||
    """ Save the capture on the USB or for download """
 | 
			
		||||
    return save.save_capture(token, method)
 | 
			
		||||
							
								
								
									
										60
									
								
								server/frontend/app/classes/analysis.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								server/frontend/app/classes/analysis.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,60 @@
 | 
			
		||||
#!/usr/bin/env python3
 | 
			
		||||
# -*- coding: utf-8 -*-
 | 
			
		||||
 | 
			
		||||
import subprocess as sp
 | 
			
		||||
import json
 | 
			
		||||
import sys
 | 
			
		||||
import re
 | 
			
		||||
import os
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Analysis(object):
 | 
			
		||||
 | 
			
		||||
    def __init__(self, token):
 | 
			
		||||
        self.token = token if re.match(r"[A-F0-9]{8}", token) else None
 | 
			
		||||
 | 
			
		||||
    def start(self):
 | 
			
		||||
        """
 | 
			
		||||
            Start an analysis of the captured communication by lauching
 | 
			
		||||
            analysis.py with the capture token as a paramater.
 | 
			
		||||
 | 
			
		||||
            :return: dict containing the analysis status
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        if self.token is not None:
 | 
			
		||||
            parent = "/".join(sys.path[0].split("/")[:-2])
 | 
			
		||||
            sp.Popen("{} {}/analysis/analysis.py /tmp/{}".format(sys.executable,
 | 
			
		||||
                                                                 parent, self.token), shell=True)
 | 
			
		||||
            return {"status": True,
 | 
			
		||||
                    "message": "Analysis started",
 | 
			
		||||
                    "token": self.token}
 | 
			
		||||
        else:
 | 
			
		||||
            return {"status": False,
 | 
			
		||||
                    "message": "Bad token provided",
 | 
			
		||||
                    "token": "null"}
 | 
			
		||||
 | 
			
		||||
    def get_report(self):
 | 
			
		||||
        """
 | 
			
		||||
            Generate a small json report of the analysis
 | 
			
		||||
            containing the alerts and the device properties.
 | 
			
		||||
 | 
			
		||||
            :return: dict containing the report or error message.
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        device, alerts = {}, {}
 | 
			
		||||
 | 
			
		||||
        # Getting device configuration.
 | 
			
		||||
        if os.path.isfile("/tmp/{}/device.json".format(self.token)):
 | 
			
		||||
            with open("/tmp/{}/device.json".format(self.token), "r") as f:
 | 
			
		||||
                device = json.load(f)
 | 
			
		||||
 | 
			
		||||
        # Getting alerts configuration.
 | 
			
		||||
        if os.path.isfile("/tmp/{}/alerts.json".format(self.token)):
 | 
			
		||||
            with open("/tmp/{}/alerts.json".format(self.token), "r") as f:
 | 
			
		||||
                alerts = json.load(f)
 | 
			
		||||
 | 
			
		||||
        if device != {} and alerts != {}:
 | 
			
		||||
            return {"alerts": alerts,
 | 
			
		||||
                    "device": device}
 | 
			
		||||
        else:
 | 
			
		||||
            return {"message": "No report yet"}
 | 
			
		||||
							
								
								
									
										108
									
								
								server/frontend/app/classes/capture.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										108
									
								
								server/frontend/app/classes/capture.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,108 @@
 | 
			
		||||
#!/usr/bin/env python3
 | 
			
		||||
# -*- coding: utf-8 -*-
 | 
			
		||||
 | 
			
		||||
import subprocess as sp
 | 
			
		||||
from app.utils import terminate_process, read_config
 | 
			
		||||
from os import mkdir, path
 | 
			
		||||
from flask import send_file, jsonify
 | 
			
		||||
import datetime
 | 
			
		||||
import shutil
 | 
			
		||||
import random
 | 
			
		||||
import sys
 | 
			
		||||
import re
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Capture(object):
 | 
			
		||||
 | 
			
		||||
    def __init__(self):
 | 
			
		||||
        self.working_dir = False
 | 
			
		||||
        self.capture_token = False
 | 
			
		||||
        self.random_choice_alphabet = "ABCDEF1234567890"
 | 
			
		||||
 | 
			
		||||
    def start_capture(self):
 | 
			
		||||
        """
 | 
			
		||||
        Start a tshark capture on the created AP interface and save
 | 
			
		||||
        it in a temporary directory under /tmp/.
 | 
			
		||||
 | 
			
		||||
        :return: dict containing capture token and status. 
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        # Kill potential tshark zombies instances, if any.
 | 
			
		||||
        terminate_process("tshark")
 | 
			
		||||
 | 
			
		||||
        # Few context variable assignment
 | 
			
		||||
        self.capture_token = "".join(
 | 
			
		||||
            [random.choice(self.random_choice_alphabet) for i in range(8)])
 | 
			
		||||
        self.working_dir = "/tmp/{}/".format(self.capture_token)
 | 
			
		||||
        self.pcap = self.working_dir + "capture.pcap"
 | 
			
		||||
        self.iface = read_config(("network", "in"))
 | 
			
		||||
 | 
			
		||||
        # For packets monitoring
 | 
			
		||||
        self.list_pkts = []
 | 
			
		||||
        self.last_pkts = 0
 | 
			
		||||
 | 
			
		||||
        # Make the capture directory
 | 
			
		||||
        mkdir(self.working_dir)
 | 
			
		||||
 | 
			
		||||
        try:
 | 
			
		||||
            sp.Popen(
 | 
			
		||||
                "tshark -i {} -w {} -f \"tcp or udp\" ".format(self.iface, self.pcap), shell=True)
 | 
			
		||||
            return {"status": True,
 | 
			
		||||
                    "message": "Capture started",
 | 
			
		||||
                    "capture_token": self.capture_token}
 | 
			
		||||
        except:
 | 
			
		||||
            return {"status": False,
 | 
			
		||||
                    "message": "Unexpected error: %s" % sys.exc_info()[0]}
 | 
			
		||||
 | 
			
		||||
    def get_capture_stats(self):
 | 
			
		||||
        """
 | 
			
		||||
            Get some dirty capture statistics in order to have a sparkline 
 | 
			
		||||
            in the background of capture view.
 | 
			
		||||
 | 
			
		||||
            :return: dict containing stats associated to the capture
 | 
			
		||||
        """
 | 
			
		||||
        with open("/sys/class/net/{}/statistics/tx_packets".format(self.iface)) as f:
 | 
			
		||||
            tx_pkts = int(f.read())
 | 
			
		||||
        with open("/sys/class/net/{}/statistics/rx_packets".format(self.iface)) as f:
 | 
			
		||||
            rx_pkts = int(f.read())
 | 
			
		||||
 | 
			
		||||
        if self.last_pkts == 0:
 | 
			
		||||
            self.last_pkts = tx_pkts + rx_pkts
 | 
			
		||||
            return {"status": True,
 | 
			
		||||
                    "packets": [0*400]}
 | 
			
		||||
        else:
 | 
			
		||||
            curr_pkts = (tx_pkts + rx_pkts) - self.last_pkts
 | 
			
		||||
            self.last_pkts = tx_pkts + rx_pkts
 | 
			
		||||
            self.list_pkts.append(curr_pkts)
 | 
			
		||||
            return {"status": True,
 | 
			
		||||
                    "packets": self.beautify_stats(self.list_pkts)}
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def beautify_stats(data):
 | 
			
		||||
        """
 | 
			
		||||
            Add 0 at the end of the array if the len of the array is less 
 | 
			
		||||
            than max_len. Else, get the last 100 stats. This allows to 
 | 
			
		||||
            show a kind of "progressive chart" in the background for 
 | 
			
		||||
            the first packets.
 | 
			
		||||
 | 
			
		||||
            :return: a list of integers.
 | 
			
		||||
        """
 | 
			
		||||
        max_len = 400
 | 
			
		||||
        if len(data) >= max_len:
 | 
			
		||||
            return data[-max_len:]
 | 
			
		||||
        else:
 | 
			
		||||
            return data + [1] * (max_len - len(data))
 | 
			
		||||
 | 
			
		||||
    def stop_capture(self):
 | 
			
		||||
        """
 | 
			
		||||
            Stoping tshark if any instance present.
 | 
			
		||||
            :return: dict as a small confirmation.
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        # Kill instance of tshark if any.
 | 
			
		||||
        if terminate_process("tshark"):
 | 
			
		||||
            return {"status": True,
 | 
			
		||||
                    "message": "Capture stopped"}
 | 
			
		||||
        else:
 | 
			
		||||
            return {"status": False,
 | 
			
		||||
                    "message": "No active capture"}
 | 
			
		||||
							
								
								
									
										52
									
								
								server/frontend/app/classes/device.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								server/frontend/app/classes/device.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,52 @@
 | 
			
		||||
#!/usr/bin/env python3
 | 
			
		||||
# -*- coding: utf-8 -*-
 | 
			
		||||
 | 
			
		||||
import json
 | 
			
		||||
import os
 | 
			
		||||
import re
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Device(object):
 | 
			
		||||
 | 
			
		||||
    def __init__(self, token):
 | 
			
		||||
        self.token = token if re.match(r"[A-F0-9]{8}", token) else None
 | 
			
		||||
        return None
 | 
			
		||||
 | 
			
		||||
    def get(self):
 | 
			
		||||
        """
 | 
			
		||||
            Get the device properties (such as Mac address, name, IP etc.)
 | 
			
		||||
            By reading the device.json file if exists. Or reading
 | 
			
		||||
            the leases files and writing the result into device.json. 
 | 
			
		||||
 | 
			
		||||
            :return: dict containing device properties.
 | 
			
		||||
        """
 | 
			
		||||
        if not os.path.isfile("/tmp/{}/device.json".format(self.token)):
 | 
			
		||||
            device = self.read_leases()
 | 
			
		||||
            if device["status"] != False:
 | 
			
		||||
                with open("/tmp/{}/device.json".format(self.token), "w") as f:
 | 
			
		||||
                    f.write(json.dumps(device))
 | 
			
		||||
        else:
 | 
			
		||||
            with open("/tmp/{}/device.json".format(self.token)) as f:
 | 
			
		||||
                device = json.load(f)
 | 
			
		||||
        return device
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def read_leases():
 | 
			
		||||
        """
 | 
			
		||||
            Read the DNSMasq leases files to retrieve
 | 
			
		||||
            the connected device properties. 
 | 
			
		||||
 | 
			
		||||
            :return: dict containing device properties.
 | 
			
		||||
        """
 | 
			
		||||
        with open("/var/lib/misc/dnsmasq.leases") as f:
 | 
			
		||||
            for line in f.readlines():
 | 
			
		||||
                return {
 | 
			
		||||
                    "status": True,
 | 
			
		||||
                    "name": line.split(" ")[3],
 | 
			
		||||
                    "ip_address": line.split(" ")[2],
 | 
			
		||||
                    "mac_address": line.split(" ")[1],
 | 
			
		||||
                    "timestamp": int(line.split(" ")[0])
 | 
			
		||||
                }
 | 
			
		||||
            else:
 | 
			
		||||
                return {"status": False,
 | 
			
		||||
                        "message": "Device not connected"}
 | 
			
		||||
							
								
								
									
										331
									
								
								server/frontend/app/classes/network.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										331
									
								
								server/frontend/app/classes/network.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,331 @@
 | 
			
		||||
#!/usr/bin/env python3
 | 
			
		||||
# -*- coding: utf-8 -*-
 | 
			
		||||
 | 
			
		||||
import subprocess as sp
 | 
			
		||||
import netifaces as ni
 | 
			
		||||
import requests as rq
 | 
			
		||||
import sys
 | 
			
		||||
import time
 | 
			
		||||
import qrcode
 | 
			
		||||
import base64
 | 
			
		||||
import random
 | 
			
		||||
import requests
 | 
			
		||||
 | 
			
		||||
from wifi import Cell
 | 
			
		||||
from os import path, remove
 | 
			
		||||
from io import BytesIO
 | 
			
		||||
from app.utils import terminate_process, read_config
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Network(object):
 | 
			
		||||
 | 
			
		||||
    def __init__(self):
 | 
			
		||||
        self.AP_SSID = False
 | 
			
		||||
        self.AP_PASS = False
 | 
			
		||||
        self.iface_in = read_config(("network", "in"))
 | 
			
		||||
        self.iface_out = read_config(("network", "out"))
 | 
			
		||||
        self.enable_interface(self.iface_in)
 | 
			
		||||
        self.enable_interface(self.iface_out)
 | 
			
		||||
        self.enable_forwarding()
 | 
			
		||||
        self.reset_dnsmasq_leases()
 | 
			
		||||
        self.random_choice_alphabet = "abcdef1234567890"
 | 
			
		||||
 | 
			
		||||
    def check_status(self):
 | 
			
		||||
        """
 | 
			
		||||
            The method check_status check the IP addressing of each interface 
 | 
			
		||||
            and return their associated IP.
 | 
			
		||||
 | 
			
		||||
            :return: dict containing each interface status.
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        ctx = {"interfaces": {
 | 
			
		||||
            self.iface_in: False,
 | 
			
		||||
            self.iface_out: False,
 | 
			
		||||
            "eth0": False},
 | 
			
		||||
            "internet": self.check_internet()}
 | 
			
		||||
 | 
			
		||||
        for iface in ctx["interfaces"].keys():
 | 
			
		||||
            try:
 | 
			
		||||
                ip = ni.ifaddresses(iface)[ni.AF_INET][0]["addr"]
 | 
			
		||||
                if not ip.startswith("127") or not ip.startswith("169.254"):
 | 
			
		||||
                    ctx["interfaces"][iface] = ip
 | 
			
		||||
            except:
 | 
			
		||||
                ctx["interfaces"][iface] = "Interface not connected or present."
 | 
			
		||||
        return ctx
 | 
			
		||||
 | 
			
		||||
    def wifi_list_networks(self):
 | 
			
		||||
        """
 | 
			
		||||
            The method wifi_list_networks list the available WiFi networks
 | 
			
		||||
            by using wifi python package.
 | 
			
		||||
            :return: dict - containing the list of Wi-Fi networks.
 | 
			
		||||
        """
 | 
			
		||||
        networks = []
 | 
			
		||||
        try:
 | 
			
		||||
            for n in Cell.all(self.iface_out):
 | 
			
		||||
                if n.ssid not in [n["ssid"] for n in networks] and n.ssid and n.encrypted:
 | 
			
		||||
                    networks.append(
 | 
			
		||||
                        {"ssid": n.ssid, "type": n.encryption_type})
 | 
			
		||||
            return {"networks": networks}
 | 
			
		||||
        except:
 | 
			
		||||
            return {"networks": []}
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def wifi_setup(ssid, password):
 | 
			
		||||
        """
 | 
			
		||||
            Edit the wpa_supplicant file with provided credentials.
 | 
			
		||||
            If the ssid already exists, just update the password. Otherwise
 | 
			
		||||
            create a new entry in the file.
 | 
			
		||||
 | 
			
		||||
            :return: dict containing the status of the operation
 | 
			
		||||
        """
 | 
			
		||||
        if len(password) >= 8 and len(ssid):
 | 
			
		||||
            found = False
 | 
			
		||||
            networks = []
 | 
			
		||||
            header, content = "", ""
 | 
			
		||||
 | 
			
		||||
            with open("/etc/wpa_supplicant/wpa_supplicant.conf") as f:
 | 
			
		||||
                content = f.read()
 | 
			
		||||
                blocks = content.split("network={")
 | 
			
		||||
                header = blocks[0]
 | 
			
		||||
 | 
			
		||||
                for block in blocks[1:]:
 | 
			
		||||
                    net = {}
 | 
			
		||||
                    for line in block.splitlines():
 | 
			
		||||
                        if line and line != "}":
 | 
			
		||||
                            key, val = line.strip().split("=")
 | 
			
		||||
                            if key != "disabled":
 | 
			
		||||
                                net[key] = val.replace("\"", "")
 | 
			
		||||
                    networks.append(net)
 | 
			
		||||
 | 
			
		||||
                for net in networks:
 | 
			
		||||
                    if net["ssid"] == ssid:
 | 
			
		||||
                        net["psk"] = password.replace('"', '\\"')
 | 
			
		||||
                        found = True
 | 
			
		||||
 | 
			
		||||
                if not found:
 | 
			
		||||
                    networks.append({
 | 
			
		||||
                        "ssid": ssid,
 | 
			
		||||
                        "psk": password.replace('"', '\\"'),
 | 
			
		||||
                        "key_mgmt": "WPA-PSK"
 | 
			
		||||
                    })
 | 
			
		||||
 | 
			
		||||
            with open("/etc/wpa_supplicant/wpa_supplicant.conf", "r+") as f:
 | 
			
		||||
                content = header
 | 
			
		||||
                for network in networks:
 | 
			
		||||
                    net = "network={\n"
 | 
			
		||||
                    for k, v in network.items():
 | 
			
		||||
                        if k in ["ssid", "psk"]:
 | 
			
		||||
                            net += "    {}=\"{}\"\n".format(k, v)
 | 
			
		||||
                        else:
 | 
			
		||||
                            net += "    {}={}\n".format(k, v)
 | 
			
		||||
                    net += "}\n\n"
 | 
			
		||||
                    content += net
 | 
			
		||||
                if f.write(content):
 | 
			
		||||
                    return {"status": True,
 | 
			
		||||
                            "message": "Configuration saved"}
 | 
			
		||||
                else:
 | 
			
		||||
                    return {"status": False,
 | 
			
		||||
                            "message": "Error while writing wpa_supplicant configuration file."}
 | 
			
		||||
        else:
 | 
			
		||||
            return {"status": False,
 | 
			
		||||
                    "message": "Empty SSID or/and password length less than 8 chars."}
 | 
			
		||||
 | 
			
		||||
    def wifi_connect(self):
 | 
			
		||||
        """
 | 
			
		||||
            Connect to one of the WiFi networks present in the 
 | 
			
		||||
            WPA_CONF_PERSIT_FILE.
 | 
			
		||||
 | 
			
		||||
            :return: dict containing the TinyCheck <-> AP status.
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        # Kill wpa_supplicant instances, if any.
 | 
			
		||||
        terminate_process("wpa_supplicant")
 | 
			
		||||
        # Launch a new instance of wpa_supplicant.
 | 
			
		||||
        sp.Popen("wpa_supplicant -B -i {} -c {}".format(self.iface_out,
 | 
			
		||||
                                                        "/etc/wpa_supplicant/wpa_supplicant.conf"), shell=True).wait()
 | 
			
		||||
        # Check internet status
 | 
			
		||||
        for _ in range(1, 40):
 | 
			
		||||
            if self.check_internet():
 | 
			
		||||
                return {"status": True,
 | 
			
		||||
                        "message": "Wifi connected"}
 | 
			
		||||
            time.sleep(1)
 | 
			
		||||
 | 
			
		||||
        return {"status": False,
 | 
			
		||||
                "message": "Wifi not connected"}
 | 
			
		||||
 | 
			
		||||
    def start_ap(self):
 | 
			
		||||
        """
 | 
			
		||||
            The start_ap method generates an Access Point by using HostApd
 | 
			
		||||
            and provide to the GUI the associated ssid, password and qrcode.
 | 
			
		||||
 | 
			
		||||
            :return: dict containing the status of the AP
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        # Re-ask to enable interface, sometimes it just go away.
 | 
			
		||||
        if not self.enable_interface(self.iface_out):
 | 
			
		||||
            return {"status": False,
 | 
			
		||||
                    "message": "Interface not present."}
 | 
			
		||||
 | 
			
		||||
        # Generate the hostapd configuration
 | 
			
		||||
        if read_config(("network", "tokenized_ssids")):
 | 
			
		||||
            token = "".join([random.choice(self.random_choice_alphabet)
 | 
			
		||||
                             for i in range(4)])
 | 
			
		||||
            self.AP_SSID = random.choice(read_config(
 | 
			
		||||
                ("network", "ssids"))) + "-" + token
 | 
			
		||||
        else:
 | 
			
		||||
            self.AP_SSID = random.choice(read_config(("network", "ssids")))
 | 
			
		||||
        self.AP_PASS = "".join(
 | 
			
		||||
            [random.choice(self.random_choice_alphabet) for i in range(8)])
 | 
			
		||||
 | 
			
		||||
        # Launch hostapd
 | 
			
		||||
        if self.write_hostapd_config():
 | 
			
		||||
            if self.lauch_hostapd() and self.reset_dnsmasq_leases():
 | 
			
		||||
                return {"status": True,
 | 
			
		||||
                        "message": "AP started",
 | 
			
		||||
                        "ssid": self.AP_SSID,
 | 
			
		||||
                        "password": self.AP_PASS,
 | 
			
		||||
                        "qrcode": self.generate_qr_code()}
 | 
			
		||||
            else:
 | 
			
		||||
                return {"status": False,
 | 
			
		||||
                        "message": "Error while creating AP."}
 | 
			
		||||
        else:
 | 
			
		||||
            return {"status": False,
 | 
			
		||||
                    "message": "Error while writing hostapd configuration file."}
 | 
			
		||||
 | 
			
		||||
    def generate_qr_code(self):
 | 
			
		||||
        """
 | 
			
		||||
            The method generate_qr_code returns a QRCode based on 
 | 
			
		||||
            the SSID and the password.
 | 
			
		||||
 | 
			
		||||
            :return: - string containing the PNG of the QRCode.
 | 
			
		||||
        """
 | 
			
		||||
        qrc = qrcode.make("WIFI:S:{};T:WPA;P:{};;".format(
 | 
			
		||||
            self.AP_SSID, self.AP_PASS))
 | 
			
		||||
        buffered = BytesIO()
 | 
			
		||||
        qrc.save(buffered, format="PNG")
 | 
			
		||||
        return "data:image/png;base64,{}".format(base64.b64encode(buffered.getvalue()).decode("utf8"))
 | 
			
		||||
 | 
			
		||||
    def write_hostapd_config(self):
 | 
			
		||||
        """
 | 
			
		||||
            The method write_hostapd_config write the hostapd configuration 
 | 
			
		||||
            under a temporary location defined in the config file.
 | 
			
		||||
 | 
			
		||||
            :return: bool - if hostapd configuration file created
 | 
			
		||||
        """
 | 
			
		||||
        try:
 | 
			
		||||
            with open("{}/app/assets/hostapd.conf".format(sys.path[0]), "r") as f:
 | 
			
		||||
                conf = f.read()
 | 
			
		||||
                conf = conf.replace("{IFACE}", self.iface_in)
 | 
			
		||||
                conf = conf.replace("{SSID}", self.AP_SSID)
 | 
			
		||||
                conf = conf.replace("{PASS}", self.AP_PASS)
 | 
			
		||||
                with open("/tmp/hostapd.conf", "w") as c:
 | 
			
		||||
                    c.write(conf)
 | 
			
		||||
            return True
 | 
			
		||||
        except:
 | 
			
		||||
            return False
 | 
			
		||||
 | 
			
		||||
    def lauch_hostapd(self):
 | 
			
		||||
        """
 | 
			
		||||
            The method lauch_hostapd kill old instance of hostapd and launch a
 | 
			
		||||
            new one as a background process.
 | 
			
		||||
 | 
			
		||||
            :return: bool - if hostapd sucessfully launched.
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        # Kill potential zombies of hostapd
 | 
			
		||||
        terminate_process("hostapd")
 | 
			
		||||
 | 
			
		||||
        sp.Popen("ifconfig {} up".format(self.iface_in), shell=True).wait()
 | 
			
		||||
        sp.Popen(
 | 
			
		||||
            "/usr/sbin/hostapd {} > /tmp/hostapd.log".format("/tmp/hostapd.conf"), shell=True)
 | 
			
		||||
 | 
			
		||||
        while True:
 | 
			
		||||
            if path.isfile("/tmp/hostapd.log"):
 | 
			
		||||
                with open("/tmp/hostapd.log", "r") as f:
 | 
			
		||||
                    log = f.read()
 | 
			
		||||
                    err = ["Could not configure driver mode",
 | 
			
		||||
                           "Could not connect to kernel driver",
 | 
			
		||||
                           "driver initialization failed"]
 | 
			
		||||
                    if not any(e in log for e in err):
 | 
			
		||||
                        if "AP-ENABLED" in log:
 | 
			
		||||
                            return True
 | 
			
		||||
                    else:
 | 
			
		||||
                        return False
 | 
			
		||||
            time.sleep(1)
 | 
			
		||||
 | 
			
		||||
    def stop_hostapd(self):
 | 
			
		||||
        """
 | 
			
		||||
            Stop hostapd instance.
 | 
			
		||||
 | 
			
		||||
            :return: dict - a little message for debug.
 | 
			
		||||
        """
 | 
			
		||||
        if terminate_process("hostapd"):
 | 
			
		||||
            return {"status": True,
 | 
			
		||||
                    "message": "AP stopped"}
 | 
			
		||||
        else:
 | 
			
		||||
            return {"status": False,
 | 
			
		||||
                    "message": "No AP running"}
 | 
			
		||||
 | 
			
		||||
    def reset_dnsmasq_leases(self):
 | 
			
		||||
        """
 | 
			
		||||
            This method reset the DNSMasq leases and logs to get the new
 | 
			
		||||
            connected device name & new DNS entries.
 | 
			
		||||
 | 
			
		||||
            :return: bool if everything goes well
 | 
			
		||||
        """
 | 
			
		||||
        try:
 | 
			
		||||
            sp.Popen("service dnsmasq stop", shell=True).wait()
 | 
			
		||||
            sp.Popen("cp /dev/null /var/lib/misc/dnsmasq.leases",
 | 
			
		||||
                     shell=True).wait()
 | 
			
		||||
            sp.Popen("cp /dev/null /var/log/messages.log", shell=True).wait()
 | 
			
		||||
            sp.Popen("service dnsmasq start", shell=True).wait()
 | 
			
		||||
            return True
 | 
			
		||||
        except:
 | 
			
		||||
            return False
 | 
			
		||||
 | 
			
		||||
    def enable_forwarding(self):
 | 
			
		||||
        """
 | 
			
		||||
            This enable forwarding to get internet working on the connected device. 
 | 
			
		||||
            Method tiggered during the Network class intialization.
 | 
			
		||||
 | 
			
		||||
            :return: bool if everything goes well
 | 
			
		||||
        """
 | 
			
		||||
        try:
 | 
			
		||||
            sp.Popen("echo 1 > /proc/sys/net/ipv4/ip_forward",
 | 
			
		||||
                     shell=True).wait()
 | 
			
		||||
            sp.Popen("iptables -A POSTROUTING -t nat -o {} -j MASQUERADE".format(
 | 
			
		||||
                self.iface_out), shell=True).wait()
 | 
			
		||||
            return True
 | 
			
		||||
        except:
 | 
			
		||||
            return False
 | 
			
		||||
 | 
			
		||||
    def enable_interface(self, iface):
 | 
			
		||||
        """
 | 
			
		||||
            This enable interfaces, with a simple check. 
 | 
			
		||||
            :return: bool if everything goes well 
 | 
			
		||||
        """
 | 
			
		||||
        sh = sp.Popen("ifconfig {} ".format(iface),
 | 
			
		||||
                      stdout=sp.PIPE, stderr=sp.PIPE, shell=True)
 | 
			
		||||
        sh = sh.communicate()
 | 
			
		||||
 | 
			
		||||
        if b"<UP," in sh[0]:
 | 
			
		||||
            return True  # The interface is up.
 | 
			
		||||
        elif sh[1]:
 | 
			
		||||
            return False  # The interface doesn't exists (most of the cases).
 | 
			
		||||
        else:
 | 
			
		||||
            sp.Popen("ifconfig {} up".format(iface), shell=True).wait()
 | 
			
		||||
            return True
 | 
			
		||||
 | 
			
		||||
    def check_internet(self):
 | 
			
		||||
        """
 | 
			
		||||
            Check the internet link just with a small http request
 | 
			
		||||
            to an URL present in the configuration
 | 
			
		||||
 | 
			
		||||
            :return: bool - if the request succeed or not.
 | 
			
		||||
        """
 | 
			
		||||
        try:
 | 
			
		||||
            url = read_config(("network", "internet_check"))
 | 
			
		||||
            requests.get(url, timeout=10)
 | 
			
		||||
            return True
 | 
			
		||||
        except:
 | 
			
		||||
            return False
 | 
			
		||||
							
								
								
									
										69
									
								
								server/frontend/app/classes/save.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								server/frontend/app/classes/save.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,69 @@
 | 
			
		||||
#!/usr/bin/env python3
 | 
			
		||||
# -*- coding: utf-8 -*-
 | 
			
		||||
 | 
			
		||||
import pyudev
 | 
			
		||||
import psutil
 | 
			
		||||
import shutil
 | 
			
		||||
import re
 | 
			
		||||
import io
 | 
			
		||||
from os import mkdir
 | 
			
		||||
from datetime import datetime
 | 
			
		||||
from flask import jsonify, send_file
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Save():
 | 
			
		||||
 | 
			
		||||
    def __init__(self):
 | 
			
		||||
        self.mount_point = ""
 | 
			
		||||
        return None
 | 
			
		||||
 | 
			
		||||
    def usb_check(self):
 | 
			
		||||
        """
 | 
			
		||||
            Check if an USB storage is connected or not.
 | 
			
		||||
            :return: a json containing the connection status.
 | 
			
		||||
        """
 | 
			
		||||
        self.usb_devices = []
 | 
			
		||||
        context = pyudev.Context()
 | 
			
		||||
        removable = [device for device in context.list_devices(
 | 
			
		||||
            subsystem='block', DEVTYPE='disk')]
 | 
			
		||||
        for device in removable:
 | 
			
		||||
            if "usb" in device.sys_path:
 | 
			
		||||
                partitions = [device.device_node for device in context.list_devices(
 | 
			
		||||
                    subsystem='block', DEVTYPE='partition', parent=device)]
 | 
			
		||||
                for p in psutil.disk_partitions():
 | 
			
		||||
                    if p.device in partitions:
 | 
			
		||||
                        self.mount_point = p.mountpoint
 | 
			
		||||
                        return jsonify({"status": True,
 | 
			
		||||
                                        "message": "USB storage connected"})
 | 
			
		||||
        self.mount_point = ""
 | 
			
		||||
        return jsonify({"status": False,
 | 
			
		||||
                        "message": "USB storage not connected"})
 | 
			
		||||
 | 
			
		||||
    def save_capture(self, token, method):
 | 
			
		||||
        """
 | 
			
		||||
            Save the capture to the USB device or push a ZIP
 | 
			
		||||
            file to download.
 | 
			
		||||
            :return: binary or json.
 | 
			
		||||
        """
 | 
			
		||||
        if re.match(r"[A-F0-9]{8}", token):
 | 
			
		||||
            try:
 | 
			
		||||
                if method == "usb":
 | 
			
		||||
                    cd = datetime.now().strftime("%d%m%Y-%H%M")
 | 
			
		||||
                    if shutil.make_archive("{}/TinyCheck_{}".format(self.mount_point, cd), "zip", "/tmp/{}/".format(token)):
 | 
			
		||||
                        return jsonify({"status": True,
 | 
			
		||||
                                        "message": "Capture saved on the USB key"})
 | 
			
		||||
                elif method == "url":
 | 
			
		||||
                    cd = datetime.now().strftime("%d%m%Y-%H%M")
 | 
			
		||||
                    if shutil.make_archive("/tmp/TinyCheck_{}".format(cd), "zip", "/tmp/{}/".format(token)):
 | 
			
		||||
                        with open("/tmp/TinyCheck_{}.zip".format(cd), "rb") as f:
 | 
			
		||||
                            return send_file(
 | 
			
		||||
                                io.BytesIO(f.read()),
 | 
			
		||||
                                mimetype="application/octet-stream",
 | 
			
		||||
                                as_attachment=True,
 | 
			
		||||
                                attachment_filename="TinyCheck_{}.zip".format(cd))
 | 
			
		||||
            except:
 | 
			
		||||
                return jsonify({"status": False,
 | 
			
		||||
                                "message": "Error while saving capture"})
 | 
			
		||||
        else:
 | 
			
		||||
            return jsonify({"status": False,
 | 
			
		||||
                            "message": "Bad token value"})
 | 
			
		||||
							
								
								
									
										35
									
								
								server/frontend/app/utils.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								server/frontend/app/utils.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,35 @@
 | 
			
		||||
#!/usr/bin/env python3
 | 
			
		||||
# -*- coding: utf-8 -*-
 | 
			
		||||
 | 
			
		||||
import psutil
 | 
			
		||||
import time
 | 
			
		||||
import yaml
 | 
			
		||||
import sys
 | 
			
		||||
import os
 | 
			
		||||
from functools import reduce
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def terminate_process(process):
 | 
			
		||||
    """
 | 
			
		||||
        Terminale all instances of a process defined by its name.
 | 
			
		||||
        :return: bool - status of the operation
 | 
			
		||||
    """
 | 
			
		||||
    terminated = False
 | 
			
		||||
    for proc in psutil.process_iter():
 | 
			
		||||
        if proc.name() == process:
 | 
			
		||||
            proc.terminate()
 | 
			
		||||
            if process == "hostapd":
 | 
			
		||||
                time.sleep(2)
 | 
			
		||||
            terminated = True
 | 
			
		||||
    return terminated
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def read_config(path):
 | 
			
		||||
    """
 | 
			
		||||
        Read a value from the configuration
 | 
			
		||||
        :return: value (it can be any type)
 | 
			
		||||
    """
 | 
			
		||||
    dir = "/".join(sys.path[0].split("/")[:-2])
 | 
			
		||||
    config = yaml.load(open(os.path.join(dir, "config.yaml"), "r"),
 | 
			
		||||
                       Loader=yaml.SafeLoader)
 | 
			
		||||
    return reduce(dict.get, path, config)
 | 
			
		||||
							
								
								
									
										50
									
								
								server/frontend/main.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								server/frontend/main.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,50 @@
 | 
			
		||||
#!/usr/bin/env python3
 | 
			
		||||
# -*- coding: utf-8 -*-
 | 
			
		||||
 | 
			
		||||
from flask import Flask, render_template, send_from_directory, jsonify, redirect
 | 
			
		||||
from app.blueprints.network import network_bp
 | 
			
		||||
from app.blueprints.capture import capture_bp
 | 
			
		||||
from app.blueprints.device import device_bp
 | 
			
		||||
from app.blueprints.analysis import analysis_bp
 | 
			
		||||
from app.blueprints.save import save_bp
 | 
			
		||||
from app.blueprints.misc import misc_bp
 | 
			
		||||
from app.utils import read_config
 | 
			
		||||
 | 
			
		||||
app = Flask(__name__, template_folder="../../app/frontend/dist")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@app.route("/", methods=["GET"])
 | 
			
		||||
def main():
 | 
			
		||||
    """
 | 
			
		||||
        Return the index.html generated by Vue
 | 
			
		||||
    """
 | 
			
		||||
    return render_template("index.html")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@app.route("/<p>/<path:path>", methods=["GET"])
 | 
			
		||||
def get_file(p, path):
 | 
			
		||||
    """
 | 
			
		||||
        Return the frontend assets (css, js files, fonts etc.)
 | 
			
		||||
    """
 | 
			
		||||
    rp = "../../app/frontend/dist/{}".format(p)
 | 
			
		||||
    return send_from_directory(rp, path) if p in ["css", "fonts", "js", "img"] else redirect("/")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@app.errorhandler(404)
 | 
			
		||||
def page_not_found(e):
 | 
			
		||||
    return redirect("/")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# API Blueprints.
 | 
			
		||||
app.register_blueprint(network_bp, url_prefix='/api/network')
 | 
			
		||||
app.register_blueprint(capture_bp, url_prefix='/api/capture')
 | 
			
		||||
app.register_blueprint(device_bp, url_prefix='/api/device')
 | 
			
		||||
app.register_blueprint(analysis_bp, url_prefix='/api/analysis')
 | 
			
		||||
app.register_blueprint(save_bp, url_prefix='/api/save')
 | 
			
		||||
app.register_blueprint(misc_bp, url_prefix='/api/misc')
 | 
			
		||||
 | 
			
		||||
if __name__ == '__main__':
 | 
			
		||||
    if read_config(("frontend", "remote_access")):
 | 
			
		||||
        app.run(host="0.0.0.0", port=80)
 | 
			
		||||
    else:
 | 
			
		||||
        app.run(port=80)
 | 
			
		||||
		Reference in New Issue
	
	Block a user