mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-06-11 05:34:55 +03:00
c47eed6a64
- Fix 25 shell=True subprocess calls with list-based commands - Fix 49 verify=False in defensive skills (env-var override) - Add timeout to 231 HTTP/subprocess/socket calls - Fix 6 SQL injection patterns with whitelist validation - Replace 8 __import__() with standard imports - Remove 701 unused imports across 442 files - Add authorized-testing disclaimers to all offensive skills - Complete 11 incomplete skill directories - Expand 10 stub SKILL.md files with full content - Fix 2 YAML parse errors in frontmatter - Fix 5 pre-existing syntax errors - Convert 22 hardcoded paths/ports to environment variables - Back up 21 redundant skill pairs to .bak - Fix 2 global declaration errors - 724/724 skills with full folder anatomy (SKILL.md + agent.py + api-reference.md + LICENSE) - 0 compile errors across all 724 agent.py files
187 lines
7.7 KiB
Python
187 lines
7.7 KiB
Python
#!/usr/bin/env python3
|
|
"""BLE security assessment using bleak for device scanning and GATT enumeration."""
|
|
|
|
import argparse
|
|
import asyncio
|
|
import json
|
|
import sys
|
|
import time
|
|
|
|
from bleak import BleakClient, BleakScanner
|
|
|
|
|
|
SENSITIVE_SERVICE_UUIDS = {
|
|
"0000180d-0000-1000-8000-00805f9b34fb": "Heart Rate",
|
|
"00001810-0000-1000-8000-00805f9b34fb": "Blood Pressure",
|
|
"00001808-0000-1000-8000-00805f9b34fb": "Glucose",
|
|
"00001809-0000-1000-8000-00805f9b34fb": "Health Thermometer",
|
|
"0000180f-0000-1000-8000-00805f9b34fb": "Battery Service",
|
|
"0000180a-0000-1000-8000-00805f9b34fb": "Device Information",
|
|
"00001812-0000-1000-8000-00805f9b34fb": "Human Interface Device",
|
|
"00001802-0000-1000-8000-00805f9b34fb": "Immediate Alert",
|
|
}
|
|
|
|
SENSITIVE_CHAR_UUIDS = {
|
|
"00002a37-0000-1000-8000-00805f9b34fb": "Heart Rate Measurement",
|
|
"00002a35-0000-1000-8000-00805f9b34fb": "Blood Pressure Measurement",
|
|
"00002a18-0000-1000-8000-00805f9b34fb": "Glucose Measurement",
|
|
"00002a1c-0000-1000-8000-00805f9b34fb": "Temperature Measurement",
|
|
"00002a19-0000-1000-8000-00805f9b34fb": "Battery Level",
|
|
"00002a29-0000-1000-8000-00805f9b34fb": "Manufacturer Name",
|
|
"00002a26-0000-1000-8000-00805f9b34fb": "Firmware Revision",
|
|
"00002a28-0000-1000-8000-00805f9b34fb": "Software Revision",
|
|
"00002a25-0000-1000-8000-00805f9b34fb": "Serial Number",
|
|
}
|
|
|
|
VULNERABLE_DEVICE_PATTERNS = [
|
|
"ITAG", "SmartLock", "BLE_Door", "FitBand", "iTag",
|
|
"CC2541", "HM-10", "JDY-08", "AT-09", "MLT-BT05",
|
|
]
|
|
|
|
|
|
async def scan_devices(scan_time: float) -> list:
|
|
devices = await BleakScanner.discover(timeout=scan_time, return_adv=True)
|
|
results = []
|
|
for addr, (device, adv_data) in devices.items():
|
|
name = adv_data.local_name or device.name or "Unknown"
|
|
vuln_match = None
|
|
for pattern in VULNERABLE_DEVICE_PATTERNS:
|
|
if pattern.lower() in name.lower():
|
|
vuln_match = pattern
|
|
break
|
|
results.append({
|
|
"address": str(addr),
|
|
"name": name,
|
|
"rssi": adv_data.rssi,
|
|
"service_uuids": [str(u) for u in (adv_data.service_uuids or [])],
|
|
"manufacturer_data": {str(k): v.hex() for k, v in (adv_data.manufacturer_data or {}).items()},
|
|
"known_vulnerable_pattern": vuln_match,
|
|
})
|
|
results.sort(key=lambda d: d["rssi"], reverse=True)
|
|
return results
|
|
|
|
|
|
async def enumerate_gatt(device_address: str) -> dict:
|
|
findings = []
|
|
services_info = []
|
|
total_chars = 0
|
|
async with BleakClient(device_address, timeout=15.0) as client:
|
|
if not client.is_connected:
|
|
return {"error": f"Failed to connect to {device_address}"}
|
|
for service in client.services:
|
|
svc_uuid = str(service.uuid)
|
|
svc_name = SENSITIVE_SERVICE_UUIDS.get(svc_uuid, service.description or "Unknown")
|
|
is_sensitive_svc = svc_uuid in SENSITIVE_SERVICE_UUIDS
|
|
chars_info = []
|
|
for char in service.characteristics:
|
|
total_chars += 1
|
|
char_uuid = str(char.uuid)
|
|
props = char.properties
|
|
char_name = SENSITIVE_CHAR_UUIDS.get(char_uuid, char.description or "Unknown")
|
|
is_sensitive_char = char_uuid in SENSITIVE_CHAR_UUIDS
|
|
char_entry = {
|
|
"uuid": char_uuid,
|
|
"name": char_name,
|
|
"properties": list(props),
|
|
"handle": char.handle,
|
|
}
|
|
if is_sensitive_char and ("read" in props):
|
|
findings.append({
|
|
"severity": "high",
|
|
"finding": f"{char_name} readable without encryption",
|
|
"uuid": char_uuid,
|
|
"service": svc_name,
|
|
"properties": list(props),
|
|
"remediation": "Enable encryption requirement on characteristic",
|
|
})
|
|
if "write-without-response" in props and is_sensitive_svc:
|
|
findings.append({
|
|
"severity": "critical",
|
|
"finding": f"{char_name} writable without response in sensitive service",
|
|
"uuid": char_uuid,
|
|
"service": svc_name,
|
|
"properties": list(props),
|
|
"remediation": "Remove write-without-response or require authenticated pairing",
|
|
})
|
|
if "write" in props and not is_sensitive_svc:
|
|
findings.append({
|
|
"severity": "medium",
|
|
"finding": f"{char_name} writable without known authentication",
|
|
"uuid": char_uuid,
|
|
"service": svc_name,
|
|
"properties": list(props),
|
|
"remediation": "Verify write access requires bonded connection",
|
|
})
|
|
chars_info.append(char_entry)
|
|
services_info.append({
|
|
"uuid": svc_uuid,
|
|
"name": svc_name,
|
|
"sensitive": is_sensitive_svc,
|
|
"characteristics": chars_info,
|
|
})
|
|
severity_weights = {"critical": 10, "high": 7, "medium": 4, "low": 1}
|
|
risk_total = sum(severity_weights.get(f["severity"], 0) for f in findings)
|
|
risk_score = min(10.0, round(risk_total / max(len(findings), 1), 1))
|
|
return {
|
|
"services_found": len(services_info),
|
|
"characteristics_found": total_chars,
|
|
"services": services_info,
|
|
"findings": findings,
|
|
"risk_score": risk_score,
|
|
}
|
|
|
|
|
|
async def run_audit(device_address: str, scan_time: float) -> dict:
|
|
scan_results = await scan_devices(scan_time)
|
|
target = None
|
|
for dev in scan_results:
|
|
if dev["address"].upper() == device_address.upper():
|
|
target = dev
|
|
break
|
|
if not target:
|
|
return {"error": f"Device {device_address} not found in scan", "scanned_devices": len(scan_results)}
|
|
gatt_result = await enumerate_gatt(device_address)
|
|
return {
|
|
"assessment_type": "ble_security_audit",
|
|
"target_device": target,
|
|
**gatt_result,
|
|
}
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description="BLE Security Assessment Tool")
|
|
parser.add_argument("--action", choices=["scan", "enumerate", "audit"],
|
|
required=True, help="Action to perform")
|
|
parser.add_argument("--scan-time", type=float, default=10.0,
|
|
help="BLE scan duration in seconds")
|
|
parser.add_argument("--device-address", type=str, default=None,
|
|
help="Target BLE device address (MAC or UUID)")
|
|
parser.add_argument("--output", type=str, default=None,
|
|
help="Output JSON file path")
|
|
args = parser.parse_args()
|
|
|
|
if args.action in ("enumerate", "audit") and not args.device_address:
|
|
print(json.dumps({"error": "Device address required for enumerate/audit"}))
|
|
sys.exit(1)
|
|
|
|
start = time.time()
|
|
if args.action == "scan":
|
|
result = asyncio.run(scan_devices(args.scan_time))
|
|
output = {"action": "scan", "devices_found": len(result), "devices": result}
|
|
elif args.action == "enumerate":
|
|
result = asyncio.run(enumerate_gatt(args.device_address))
|
|
output = {"action": "enumerate", "target": args.device_address, **result}
|
|
elif args.action == "audit":
|
|
output = asyncio.run(run_audit(args.device_address, args.scan_time))
|
|
|
|
output["elapsed_seconds"] = round(time.time() - start, 2)
|
|
report = json.dumps(output, indent=2)
|
|
if args.output:
|
|
with open(args.output, "w") as f:
|
|
f.write(report)
|
|
print(report)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|