Running the script will :
-Scan for the Cameras
-Create HTML table with dynamically sortable columns, just click on the column heading to sort asc/desc
-Startup a small webserver
-Launch your browser to load the HTML file.
-Don't forget to change the passwords/ip range
Code: Select all
import os
import requests
from requests.auth import HTTPDigestAuth
import ipaddress
import sys
import threading
import time
import json
import webbrowser
from http.server import SimpleHTTPRequestHandler, HTTPServer
# Replace with your camera's username
USERNAME = "admin" # Replace with your actual username
# List of possible passwords to try
PASSWORDS = [
"admin", # First password to try
"admin", # Second password to try
# Add more passwords here if needed
]
# Define the network range (e.g., 192.168.0.1/24)
NETWORK_RANGE = "192.168.0.1/24"
discovered_cameras = [] # Store discovered camera information
scanning = False # Flag to control the throbber
# Function to get the MAC address of the camera
def get_mac_address(camera_ip, password):
try:
NETWORK_URL = f"http://{camera_ip}/cgi-bin/configManager.cgi?action=getConfig&name=Network"
response = requests.get(NETWORK_URL, auth=HTTPDigestAuth(USERNAME, password), timeout=5)
if response.status_code == 200:
mac_start = response.text.find("PhysicalAddress=")
if mac_start != -1:
mac_address = response.text[mac_start + 16:mac_start + 33].strip()
return mac_address
except requests.exceptions.RequestException:
pass
return None
# Function to check if a camera is online and accessible
def check_camera(camera_ip):
for i, password in enumerate(PASSWORDS, 1):
try:
CAMERA_URL = f"http://{camera_ip}/cgi-bin/magicBox.cgi?action=getSystemInfo"
response = requests.get(CAMERA_URL, auth=HTTPDigestAuth(USERNAME, password), timeout=5)
if response.status_code == 200:
mac_address = get_mac_address(camera_ip, password)
system_info = response.text.strip()
# Parse the response text to extract the specified fields
fields = {
"appAutoStart": None,
"deviceType": None,
"hardwareVersion": None,
"processor": None,
"serialNumber": None,
"updateSerial": None,
"updateSerialCloudUpgrade": None
}
for line in system_info.splitlines():
key, sep, value = line.partition('=')
if key in fields:
fields[key] = value.strip()
if fields["serialNumber"] is not None:
system_info = "Camera Detected"
discovered_cameras.append({
"ip": camera_ip,
"password_used": f"pass{i}",
"mac_address": mac_address,
"processor": fields["processor"],
"deviceType": fields["deviceType"],
"serialNumber": fields["serialNumber"],
"system_info": system_info,
})
'''
"hardwareVersion": fields["hardwareVersion"],
"updateSerial": fields["updateSerial"],
"updateSerialCloudUpgrade": fields["updateSerialCloudUpgrade"],
"appAutoStart": fields["appAutoStart"]
'''
return
except requests.exceptions.RequestException:
pass
# Function to ping a single camera
def ping_camera(camera_ip):
return os.system(f"ping -c 1 {camera_ip} > /dev/null 2>&1") == 0
threads_left = 0
def check_ip(ip):
global threads_left
camera_ip = str(ip)
if ping_camera(camera_ip):
check_camera(camera_ip)
threads_left -= 1
def throbber():
global threads_left
while scanning:
for char in "|/-\\":
print(f"\rScanning waiting for {threads_left} threads to finish... {char}", end="", flush=True)
time.sleep(0.1)
print("\rScan complete! ")
# Scan the network and check for reachable devices
def scan_network():
global threads_left
ts = time.time()
global scanning
network = ipaddress.IPv4Network(NETWORK_RANGE, strict=False)
scanning = True
threads = []
ct = 0
for ip in network.hosts():
ip_thread = threading.Thread(target=check_ip, args=(ip,))
ip_thread.start()
threads.append(ip_thread)
threads_left += 1
ct += 1
print(f"[{ct}] Scanning threads launched in parallel, be patient for roughly 15 seconds give or take...")
throbber_thread = threading.Thread(target=throbber)
throbber_thread.start()
for t in threads:
t.join()
print(f"\r\n[{ct}] Threads finished locating {len(discovered_cameras)} cameras in {round(time.time() - ts)} seconds flat....")
scanning = False
# Save discovered cameras to a JSON file
with open('discovered_cameras.json', 'w') as json_file:
json.dump(discovered_cameras, json_file, indent=4)
# Launch the browser to display the HTML file
webbrowser.open('http://localhost:8000')
# Wait for all threads to finish
# HTML content to display the discovered cameras
HTML_CONTENT = """
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Discovered Cameras</title>
<style>
table {
width: 100%;
border-collapse: collapse;
}
th, td {
padding: 8px;
text-align: left;
border-bottom: 1px solid #ddd;
}
th {
cursor: pointer;
}
th.sort-asc::after {
content: " ▲";
}
th.sort-desc::after {
content: " ▼";
}
tr:nth-child(even) {
background-color: #f2f2f2;
}
tr:hover {
background-color: #ddd;
}
</style>
</head>
<body>
<h1>Discovered Cameras</h1>
<!--
<th onclick="sortTable(7)">Hardware Version</th>
<th onclick="sortTable(8)">Update Serial</th>
<th onclick="sortTable(9)">Update Serial Cloud Upgrade</th>
<th onclick="sortTable(10)">App Auto Start</th>
-->
<table id="cameraTable">
<thead>
<tr>
<th onclick="sortTable(0)">IP Address</th>
<th onclick="sortTable(1)">Password Used</th>
<th onclick="sortTable(2)">MAC Address</th>
<th onclick="sortTable(3)">Processor</th>
<th onclick="sortTable(4)">Device Type</th>
<th onclick="sortTable(5)">Serial Number</th>
<th onclick="sortTable(6)">System Info</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
<script>
//<td>${camera.hardwareVersion}</td>
/// <td>${camera.updateSerial}</td>
// <td>${camera.updateSerialCloudUpgrade}</td>
// <td>${camera.appAutoStart}</td>
document.addEventListener('DOMContentLoaded', function() {
fetch('discovered_cameras.json')
.then(response => response.json())
.then(data => {
const tableBody = document.querySelector('#cameraTable tbody');
data.forEach(camera => {
const row = document.createElement('tr');
row.innerHTML = `
<td>${camera.ip}</td>
<td>${camera.password_used}</td>
<td>${camera.mac_address}</td>
<td>${camera.processor}</td>
<td>${camera.deviceType}</td>
<td>${camera.serialNumber}</td>
<td>${camera.system_info}</td>
`;
tableBody.appendChild(row);
});
sortTable(0, true); // Default sort by IP Address descending
});
});
function sortTable(columnIndex, descending = false) {
const table = document.getElementById('cameraTable');
const tbody = table.tBodies[0];
const rows = Array.from(tbody.rows);
const th = table.tHead.rows[0].cells[columnIndex];
rows.sort((a, b) => {
const aText = a.cells[columnIndex].textContent.trim();
const bText = b.cells[columnIndex].textContent.trim();
return aText.localeCompare(bText, undefined, {numeric: true});
});
if (descending) {
rows.reverse();
}
rows.forEach(row => tbody.appendChild(row));
Array.from(th.parentNode.cells).forEach(cell => cell.classList.remove('sort-asc', 'sort-desc'));
th.classList.add(descending ? 'sort-desc' : 'sort-asc');
}
</script>
</body>
</html>
"""
class CustomHandler(SimpleHTTPRequestHandler):
def do_GET(self):
if self.path == '/':
self.send_response(200)
self.send_header('Content-type', 'text/html')
self.end_headers()
self.wfile.write(HTML_CONTENT.encode('utf-8'))
elif self.path == '/discovered_cameras.json':
self.send_response(200)
self.send_header('Content-type', 'application/json')
self.end_headers()
with open('discovered_cameras.json', 'r') as json_file:
self.wfile.write(json_file.read().encode('utf-8'))
else:
self.send_error(404, "File not found")
def run_server():
server_address = ('', 8000)
httpd = HTTPServer(server_address, CustomHandler)
print("Serving on port 8000...")
httpd.serve_forever()
# Main function to run the scan
if __name__ == "__main__":
try:
scan_network()
print("\nScan complete. Discovered devices:")
for cam in discovered_cameras:
print(f"\nCamera IP: {cam['ip']}\nPassword Used: {cam['password_used']}\nMAC Address: {cam['mac_address']}\nSystem Info:\n{cam['system_info']}")
# Run the server to display the HTML content
run_server()
except KeyboardInterrupt:
print("\n\nScan aborted by user. Exiting script gracefully...")
sys.exit(0)
-