Files
proxy-god/proxy_chain_manager/fingerprint.py
Indiana Holmes 8f012402a6 Harden Windows Server compatibility and system-wide proxy.
Fix exit-IP checks for HTTP-only proxies, apply proxy via WinINet Connections blob and WinHTTP, improve VPN detection on legacy Server, add SOCKS5 host:port:user:pass exit parsing, and add win_compat probe for PowerShell 2.0 hosts.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-16 18:03:30 -07:00

230 lines
7.5 KiB
Python

"""Device fingerprint audit and OS-level hardening helpers."""
from __future__ import annotations
import logging
import random
import re
import string
import subprocess
import winreg
from dataclasses import dataclass, field
from .firewall import is_admin
from .mac_spoof import list_nics
from .win_compat import probe as _win_probe
log = logging.getLogger(__name__)
_WEBRTC_CHROME = r"SOFTWARE\Policies\Google\Chrome"
_WEBRTC_EDGE = r"SOFTWARE\Policies\Microsoft\Edge"
_WEBRTC_VALUE = "DefaultWebRtcIpHandlingPolicy"
_WEBRTC_DISABLE = 2 # disable_non_proxied_udp
@dataclass
class FingerprintAudit:
lines: list[str] = field(default_factory=list)
hostname: str = ""
machine_guid: str = ""
username: str = ""
macs: list[str] = field(default_factory=list)
def _run_ps(script: str, timeout: float = 15.0) -> str:
try:
r = subprocess.run(
["powershell", "-NoProfile", "-Command", script],
capture_output=True,
text=True,
timeout=timeout,
creationflags=getattr(subprocess, "CREATE_NO_WINDOW", 0),
)
return (r.stdout or "").strip()
except Exception as e:
log.debug("fingerprint ps: %s", e)
return ""
def get_computer_name() -> str:
try:
import os
return os.environ.get("COMPUTERNAME", "") or ""
except Exception:
return ""
def get_machine_guid() -> str:
try:
with winreg.OpenKey(
winreg.HKEY_LOCAL_MACHINE,
r"SOFTWARE\Microsoft\Cryptography",
) as key:
val, _ = winreg.QueryValueEx(key, "MachineGuid")
return str(val)
except OSError:
return ""
def random_hostname(prefix: str = "PC") -> str:
suffix = "".join(random.choices(string.ascii_uppercase + string.digits, k=7))
return f"{prefix}-{suffix}"[:15]
def set_computer_name(name: str) -> tuple[bool, str]:
"""Set NetBIOS / computer name (Admin). Reboot may be required for all apps."""
if not is_admin():
return False, "Administrator required to change computer name."
name = re.sub(r"[^A-Za-z0-9\-]", "", name)[:15]
if len(name) < 1:
return False, "Invalid hostname."
code = subprocess.run(
["powershell", "-NoProfile", "-Command",
f'Rename-Computer -NewName "{name}" -Force -ErrorAction Stop'],
capture_output=True,
text=True,
timeout=30,
creationflags=getattr(subprocess, "CREATE_NO_WINDOW", 0),
).returncode
if code != 0:
# NetBIOS name via WMI (often works without full rename)
wmi = (
f'$n = Get-WmiObject Win32_ComputerSystem; '
f'$r = $n.Rename("{name}"); if ($r.ReturnValue -ne 0) {{ exit $r.ReturnValue }}'
)
code = subprocess.run(
["powershell", "-NoProfile", "-Command", wmi],
capture_output=True,
text=True,
timeout=30,
creationflags=getattr(subprocess, "CREATE_NO_WINDOW", 0),
).returncode
if code == 0:
return True, f"Computer name set to {name} (some apps need reconnect/reboot)."
return False, "Could not change computer name."
def disable_ipv6_on_adapters() -> tuple[list[str], list[str]]:
"""Disable IPv6 binding on up physical adapters. Returns (adapter names, log lines)."""
if not is_admin():
return [], ["IPv6 disable skipped (not Admin)."]
if not _win_probe().has_net_cmdlets:
return [], [
"IPv6 disable skipped — requires PowerShell 3.0+ "
"(Windows 8 / Server 2012+). Detected legacy PowerShell."
]
script = (
"Get-NetAdapter | Where-Object { $_.Status -eq 'Up' } | "
"ForEach-Object { $_.Name }"
)
names = [n.strip() for n in _run_ps(script).splitlines() if n.strip()]
logs: list[str] = []
changed: list[str] = []
skip = ("virtual", "vmware", "hyper-v", "loopback", "bluetooth")
for name in names:
if any(h in name.lower() for h in skip):
continue
cmd = (
f'Disable-NetAdapterBinding -Name "{name}" -ComponentID ms_tcpip6 -Confirm:$false '
f'-ErrorAction SilentlyContinue'
)
subprocess.run(
["powershell", "-NoProfile", "-Command", cmd],
capture_output=True,
timeout=20,
creationflags=getattr(subprocess, "CREATE_NO_WINDOW", 0),
)
changed.append(name)
logs.append(f"IPv6 disabled on {name}")
return changed, logs
def enable_ipv6_on_adapters(adapters: list[str]) -> list[str]:
if not is_admin() or not adapters:
return []
if not _win_probe().has_net_cmdlets:
return []
logs: list[str] = []
for name in adapters:
cmd = (
f'Enable-NetAdapterBinding -Name "{name}" -ComponentID ms_tcpip6 -Confirm:$false '
f'-ErrorAction SilentlyContinue'
)
subprocess.run(
["powershell", "-NoProfile", "-Command", cmd],
capture_output=True,
timeout=20,
creationflags=getattr(subprocess, "CREATE_NO_WINDOW", 0),
)
logs.append(f"IPv6 re-enabled on {name}")
return logs
def apply_webrtc_hardening(enable: bool) -> tuple[bool, str]:
"""Chrome/Edge: disable WebRTC non-proxied UDP (Admin, HKLM policies)."""
if not is_admin():
return False, "Administrator required for browser WebRTC policy."
paths = [_WEBRTC_CHROME, _WEBRTC_EDGE]
try:
for path in paths:
if enable:
try:
key = winreg.CreateKeyEx(
winreg.HKEY_LOCAL_MACHINE, path, 0, winreg.KEY_SET_VALUE
)
except OSError:
continue
with key:
winreg.SetValueEx(key, _WEBRTC_VALUE, 0, winreg.REG_DWORD, _WEBRTC_DISABLE)
else:
try:
with winreg.OpenKey(
winreg.HKEY_LOCAL_MACHINE, path, 0, winreg.KEY_SET_VALUE
) as key:
try:
winreg.DeleteValue(key, _WEBRTC_VALUE)
except OSError:
pass
except OSError:
pass
return True, (
"WebRTC hardened (Chrome/Edge: non-proxied UDP disabled)."
if enable
else "WebRTC policy removed."
)
except OSError as e:
return False, str(e)
def audit_device() -> FingerprintAudit:
"""Collect identifiers sites and trackers often fingerprint."""
import getpass
import os
host = get_computer_name()
guid = get_machine_guid()
user = getpass.getuser()
macs = [f"{n.name}: {n.mac}" for n in list_nics()]
lines = [
f"Computer name: {host or ''}",
f"Windows username: {user}",
f"MachineGuid: {guid[:8]}{guid[-4:]}" if len(guid) > 12 else f"MachineGuid: {guid or ''}",
f"Network adapters ({len(macs)}):",
]
lines.extend([f"{m}" for m in macs[:8]] or [" • (none detected)"])
if len(macs) > 8:
lines.append(f" … and {len(macs) - 8} more")
lines.append("")
lines.append("Browser fingerprint (Canvas/WebGL/fonts) is not changed by this app.")
lines.append("Use hardened browser profiles + proxy chain for web traffic.")
lines.append("WebRTC toggle here affects Chrome/Edge system policy only.")
return FingerprintAudit(
lines=lines,
hostname=host,
machine_guid=guid,
username=user,
macs=[n.mac for n in list_nics()],
)