Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added certificates parsing, Python Flask web viewer and update requir… #57

Open
wants to merge 5 commits into
base: dev
Choose a base branch
from
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 @@

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']))

Check notice on line 376 in utils/database_utils.py

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

utils/database_utils.py#L376

line too long (518 > 159 characters) (E501)
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)

Check warning on line 26 in utils/web_server.py

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

utils/web_server.py#L26

Possible SQL injection vector through string-based query construction.
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 @@
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 '',

Check notice on line 421 in utils/wifi_db_aircrack.py

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

utils/wifi_db_aircrack.py#L421

continuation line unaligned for hanging indent (E131)
'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 '',

Check notice on line 423 in utils/wifi_db_aircrack.py

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

utils/wifi_db_aircrack.py#L423

continuation line unaligned for hanging indent (E131)
'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 '',

Check notice on line 426 in utils/wifi_db_aircrack.py

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

utils/wifi_db_aircrack.py#L426

continuation line unaligned for hanging indent (E131)
'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 '',

Check notice on line 428 in utils/wifi_db_aircrack.py

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

utils/wifi_db_aircrack.py#L428

continuation line unaligned for hanging indent (E131)
'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 '',

Check notice on line 430 in utils/wifi_db_aircrack.py

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

utils/wifi_db_aircrack.py#L430

continuation line unaligned for hanging indent (E131)
},
'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 '',

Check notice on line 434 in utils/wifi_db_aircrack.py

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

utils/wifi_db_aircrack.py#L434

continuation line unaligned for hanging indent (E131)
'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 '',

Check notice on line 436 in utils/wifi_db_aircrack.py

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

utils/wifi_db_aircrack.py#L436

continuation line unaligned for hanging indent (E131)
'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 '',

Check notice on line 439 in utils/wifi_db_aircrack.py

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

utils/wifi_db_aircrack.py#L439

continuation line unaligned for hanging indent (E131)
'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 '',

Check notice on line 441 in utils/wifi_db_aircrack.py

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

utils/wifi_db_aircrack.py#L441

continuation line unaligned for hanging indent (E131)
'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 '',

Check notice on line 443 in utils/wifi_db_aircrack.py

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

utils/wifi_db_aircrack.py#L443

continuation line unaligned for hanging indent (E131)
}
}
if cert_info not in certs_info:
certs_info.append(cert_info)
if verbose:
print("Certificate information:", cert_info)
Dismissed Show dismissed Hide dismissed
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)
Dismissed Show dismissed Hide dismissed
except Exception as error:
errors += 1
print("Error in parse_certificates (CAP): ", error)
print(".cap Certificates done, errors", errors)
Dismissed Show dismissed Hide dismissed

# 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 @@
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)

Check warning on line 112 in utils/wifi_db_database.sql

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

utils/wifi_db_database.sql#L112

Expected TSQL Keyword to be capitalized
);


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 @@
"extension is provided, all types will be added. "
"This option supports the use of "
"wildcards (*) to select multiple files or folders.")

Check notice on line 90 in wifi_db.py

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

wifi_db.py#L90

Trailing whitespace
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 @@
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 @@
hcxpcapngtool, tshark)
database_utils.setFileProcessed(cursor, verbose, captureFormat)


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