Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,5 @@ nest_asyncio==1.5.8
pyshark==0.6
Requests==2.32.3
pytest==8.2.2
Flask==3.0.3
pandas==1.5.3
80 changes: 80 additions & 0 deletions templates/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
<title>Wifi_db Viewer</title>
<style>
.tab-pane {
overflow: scroll;
}

.table tbody tr.highlight td {
background-color: lightblue;
}
</style>
</head>
<body>
<div class="container-fluid">
<h1 class="mt-4 mb-4">Wifi_db Viewer</h1>
<ul class="nav nav-tabs" id="tab" role="tablist">
{% for table in tables %}
<li class="nav-item" role="presentation">
<button class="nav-link {% if loop.first %}active{% endif %}" id="{{ table }}-tab" data-bs-toggle="tab" data-bs-target="#{{ table }}" type="button" role="tab" aria-controls="{{ table }}" aria-selected="true">{{ table }}</button>
</li>
{% endfor %}
</ul>
<div class="tab-content mt-3" id="tableContent">
{% for table, data in table_data.items() %}
<div class="tab-pane fade {% if loop.first %}show active{% endif %}" id="{{ table }}" role="tabpanel" aria-labelledby="{{ table }}-tab">
<input class="form-control mb-3" id="searchInput{{ loop.index }}" type="text" placeholder="Search in {{ table }}">
<table class="table table-bordered table-hover">
<thead class="table-light">
<tr>
{% for column in data.columns %}
<th>{{ column }}</th>
{% endfor %}
</tr>
</thead>
<tbody id="tableBody{{ loop.index }}">
{% for row in data.itertuples(index=False) %}
<tr>
{% for value in row %}
<td>{{ value }}</td>
{% endfor %}
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endfor %}
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>

<script>
document.addEventListener("DOMContentLoaded", function() {
{% for table in tables %}
document.getElementById('searchInput{{ loop.index }}').addEventListener('keyup', function() {
var value = this.value.toLowerCase();
var rows = document.querySelectorAll("#tableBody{{ loop.index }} tr");
rows.forEach(function(row) {
row.style.display = row.innerText.toLowerCase().includes(value) ? "" : "none";
});
});
{% endfor %}
const rows = document.querySelectorAll('tbody tr');

rows.forEach(function(row) {
row.addEventListener('click', function() {
rows.forEach(function(r) {
r.classList.remove('highlight');
});
this.classList.add('highlight');
});
});
});
</script>
</body>
</html>
19 changes: 19 additions & 0 deletions utils/database_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,25 @@ def insertMFP(cursor, verbose, bssid, mfpc, mfpr, file):

return int(1)

def insertCertificate(cursor, verbose, cert_info, file):
try:
error = 0
# Insert file
error += insertFile(cursor, verbose, file)

cursor.execute(
'''INSERT INTO certificate VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)''',
(cert_info['source'], cert_info['destination'], file, cert_info['issuer']['commonName'], cert_info['issuer']['countryName'], cert_info['issuer']['email'], cert_info['issuer']['localityName'], cert_info['issuer']['organizationName'], cert_info['issuer']['stateOrProvinceName'], cert_info['subject']['commonName'], cert_info['subject']['countryName'], cert_info['subject']['email'], cert_info['subject']['localityName'], cert_info['subject']['organizationName'], cert_info['subject']['stateOrProvinceName']))
return int(error)
except sqlite3.IntegrityError as error:
# errors += 1
if verbose:
print("insertCertificate" + str(error))
return int(0)
except sqlite3.Error as error:
if verbose:
print("insertCertificate Error " + str(error))
return int(1)

def insertHandshake(cursor, verbose, bssid, mac, file):
''''''
Expand Down
10,604 changes: 9,475 additions & 1,129 deletions utils/mac-vendors-export.csv

Large diffs are not rendered by default.

32 changes: 32 additions & 0 deletions utils/web_server.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#!/bin/python3
''' Utils to Web Viewer '''
# -*- coding: utf-8 -*-
import sqlite3
from flask import Flask, render_template
import pandas as pd
from os import path

def start(db_path, host, port):
template_dir = path.join(path.dirname(path.abspath(__file__)), '..', 'templates')
app = Flask(__name__, template_folder=template_dir)

@app.route('/')
def index():
conn = sqlite3.connect(db_path)
cursor = conn.cursor()

# Get table names
cursor.execute("SELECT name FROM sqlite_master WHERE type='table';")
tables = [table[0] for table in cursor.fetchall()]

# Retrieve table data
table_data = {}
for table in tables:
if table.isidentifier():
df = pd.read_sql_query(f"SELECT * FROM {table}", conn)
table_data[table] = df

conn.close()
return render_template('index.html', tables=tables, table_data=table_data)

app.run(host=host, port=port)
85 changes: 85 additions & 0 deletions utils/wifi_db_aircrack.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
# import platform
import binascii
import datetime
from cryptography import x509
from cryptography.hazmat.backends import default_backend


def parse_netxml(ouiMap, name, database, verbose):
Expand Down Expand Up @@ -373,9 +375,92 @@ def parse_cap(name, database, verbose, hcxpcapngtool, tshark):
parse_WPS(name, database, verbose)
parse_identities(name, database, verbose)
parse_MFP(name, database, verbose)
parse_certificates(name, database, verbose)
if hcxpcapngtool:
exec_hcxpcapngtool(name, database, verbose)

# Get certificates information from .cap
def extract_certificates(pkt):
certs = []
try:
cert_list = pkt.eap.tls_handshake_certificate.split(',')
for cert_hex in cert_list:
cert_data = bytes.fromhex(cert_hex.replace(':', ''))
cert = x509.load_der_x509_certificate(cert_data, default_backend())
certs.append(cert)
except Exception as e:
print(f"Error extracting certificates: {e}")
return certs

def extract_emails_from_cert(cert_obj):
return {
'subject': [email.value for email in cert_obj.subject.get_attributes_for_oid(x509.NameOID.EMAIL_ADDRESS)],
'issuer': [email.value for email in cert_obj.issuer.get_attributes_for_oid(x509.NameOID.EMAIL_ADDRESS)]
}

def parse_certificates(name, database, verbose):
try:
cursor = database.cursor()
certs_info = []
errors = 0
file = name
cap = pyshark.FileCapture(file, display_filter="tls.handshake.type == 11")

for pkt in cap:
#bssid = pkt.wlan.bssid if hasattr(pkt, 'wlan') else 'Unknown'
src = pkt.wlan.ta
dst = pkt.wlan.da
cert_objs = extract_certificates(pkt)
for cert_obj in cert_objs:
emails = extract_emails_from_cert(cert_obj)
cert_info = {
'source': src,
'destination': dst,
'issuer': {
'commonName': cert_obj.issuer.get_attributes_for_oid(x509.NameOID.COMMON_NAME)[0].value
if cert_obj.issuer.get_attributes_for_oid(x509.NameOID.COMMON_NAME) else '',
'countryName': cert_obj.issuer.get_attributes_for_oid(x509.NameOID.COUNTRY_NAME)[0].value
if cert_obj.issuer.get_attributes_for_oid(x509.NameOID.COUNTRY_NAME) else '',
'email': emails['issuer'][0] if emails['issuer'] else '',
'localityName': cert_obj.issuer.get_attributes_for_oid(x509.NameOID.LOCALITY_NAME)[0].value
if cert_obj.issuer.get_attributes_for_oid(x509.NameOID.LOCALITY_NAME) else '',
'organizationName': cert_obj.issuer.get_attributes_for_oid(x509.NameOID.ORGANIZATION_NAME)[0].value
if cert_obj.issuer.get_attributes_for_oid(x509.NameOID.ORGANIZATION_NAME) else '',
'stateOrProvinceName': cert_obj.issuer.get_attributes_for_oid(x509.NameOID.STATE_OR_PROVINCE_NAME)[0].value
if cert_obj.issuer.get_attributes_for_oid(x509.NameOID.STATE_OR_PROVINCE_NAME) else '',
},
'subject': {
'commonName': cert_obj.subject.get_attributes_for_oid(x509.NameOID.COMMON_NAME)[0].value
if cert_obj.subject.get_attributes_for_oid(x509.NameOID.COMMON_NAME) else '',
'countryName': cert_obj.subject.get_attributes_for_oid(x509.NameOID.COUNTRY_NAME)[0].value
if cert_obj.subject.get_attributes_for_oid(x509.NameOID.COUNTRY_NAME) else '',
'email': emails['subject'][0] if emails['subject'] else '',
'localityName': cert_obj.subject.get_attributes_for_oid(x509.NameOID.LOCALITY_NAME)[0].value
if cert_obj.subject.get_attributes_for_oid(x509.NameOID.LOCALITY_NAME) else '',
'organizationName': cert_obj.subject.get_attributes_for_oid(x509.NameOID.ORGANIZATION_NAME)[0].value
if cert_obj.subject.get_attributes_for_oid(x509.NameOID.ORGANIZATION_NAME) else '',
'stateOrProvinceName': cert_obj.subject.get_attributes_for_oid(x509.NameOID.STATE_OR_PROVINCE_NAME)[0].value
if cert_obj.subject.get_attributes_for_oid(x509.NameOID.STATE_OR_PROVINCE_NAME) else '',
}
}
if cert_info not in certs_info:
certs_info.append(cert_info)
if verbose:
print("Certificate information:", cert_info)

Check failure

Code scanning / CodeQL

Clear-text logging of sensitive information

This expression logs [sensitive data (certificate)](1) as clear text. This expression logs [sensitive data (certificate)](2) as clear text.
errors += database_utils.insertCertificate(cursor,
verbose,
cert_info,
file)
database.commit()
except pyshark.capture.capture.TSharkCrashException as error:
errors += 1
print("Error in parse_certificates (CAP), probably PCAP cut in the "
"middle of a packet: ", error)
print(".cap Certificates done, errors", errors)

Check failure

Code scanning / CodeQL

Clear-text logging of sensitive information

This expression logs [sensitive data (certificate)](1) as clear text.
except Exception as error:
errors += 1
print("Error in parse_certificates (CAP): ", error)
print(".cap Certificates done, errors", errors)

Check failure

Code scanning / CodeQL

Clear-text logging of sensitive information

This expression logs [sensitive data (certificate)](1) as clear text.

# Get handshakes from .cap
def parse_handshakes(name, database, verbose):
Expand Down
20 changes: 20 additions & 0 deletions utils/wifi_db_database.sql
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,26 @@ CREATE TABLE IF NOT EXISTS Probe
CONSTRAINT ProbesSent FOREIGN KEY (mac) REFERENCES Client (mac) ON UPDATE CASCADE ON DELETE CASCADE
);

CREATE TABLE IF NOT EXISTS Certificate
(
source TEXT NOT NULL,
destination TEXT NOT NULL,
file TEXT NOT NULL,
issuer_commonName TEXT NOT NULL,
issuer_countryName TEXT NOT NULL,
issuer_email TEXT NOT NULL,
issuer_localityName TEXT NOT NULL,
issuer_organizationName TEXT NOT NULL,
issuer_stateOrProvinceName TEXT NOT NULL,
subject_commonName TEXT NOT NULL,
subject_countryName TEXT NOT NULL,
subject_email TEXT NOT NULL,
subject_localityName TEXT NOT NULL,
subject_organizationName TEXT NOT NULL,
subject_stateOrProvinceName TEXT NOT NULL,
CONSTRAINT Key5 PRIMARY KEY (source,destination,file,issuer_commonName,issuer_countryName,issuer_email,issuer_localityName,issuer_organizationName,issuer_stateOrProvinceName,subject_commonName,subject_countryName,subject_email,subject_localityName,subject_organizationName,subject_stateOrProvinceName)
);


CREATE TABLE IF NOT EXISTS Handshake
(
Expand Down
16 changes: 12 additions & 4 deletions wifi_db.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@
from utils import update
from utils import database_utils
from utils import oui
from utils import web_server
import os
from os import path
import platform
import subprocess
import nest_asyncio
import re


# import nest_asyncio ; nest_asyncio.apply() ->
# Fix RuntimeError: This event loop is already running”

Expand Down Expand Up @@ -87,6 +87,14 @@ def main():
"extension is provided, all types will be added. "
"This option supports the use of "
"wildcards (*) to select multiple files or folders.")

parser.add_argument("-i", "--ip", type=str, default='127.0.0.1',
help="IP address to serve the web viewer"
" (default: %(default)s)")

parser.add_argument("-p", "--port", type=int, default='5000',
help="Port to serve the web viewer"
" (default: %(default)s)")
args = parser.parse_args()

if args.version:
Expand Down Expand Up @@ -208,8 +216,9 @@ def main():
database_utils.obfuscateDB(database, verbose)

print("\nThe output database is in the file:", name)
print("Use 'sqlitebrowser " + name
+ "' or other SQLITE program to view the data")
print("Use SQLITE program to view the data like 'sqlitebrowser " + name
+ "' or use the integrated web viewer in the following URL:")
web_server.start(name, args.ip, args.port)


def process_capture(ouiMap, capture, database,
Expand Down Expand Up @@ -324,7 +333,6 @@ def process_capture(ouiMap, capture, database,
hcxpcapngtool, tshark)
database_utils.setFileProcessed(cursor, verbose, captureFormat)


if __name__ == "__main__":
banner()
main()