{"id":"CVE-2026-16760","kind":"vulnerability","aliases":["CVE-2026-16760"],"related":[],"title":"fickling: pickle safety scanner trusts stdlib callables, letting malicious payloads run code","description":"In Trail of Bits fickling through 0.1.12, the _process_import() method in fickle.py adds an imported name to the likely_safe_imports set whenever the source module is present in sys.stdlib_module_names and the name is absent from the UNSAFE_IMPORTS denylist, without inspecting what the name resolves to or how it behaves. The OvertlyBadEvals analysis pass in analysis.py then unconditionally skips any call whose function name appears in likely_safe_imports, including calls that would otherwise be flagged as overtly dangerous. As a result, any callable reachable through a non-blocked standard library module inherits module-level trust regardless of its own behavior, and check_safety() returns LIKELY_SAFE with zero findings for pickle payloads that reach code execution or file write through such callables. Demonstrated primitives: dataclasses._create_fn, which passes an attacker-controlled function body to exec() and yields arbitrary code execution on Python 3.7 through 3.12 (the internal _create_fn function was removed in Python 3.13); mailcap.findmatch, which internally calls os.system() on a test command taken from an attacker-controlled caps dictionary, yielding arbitrary code execution on Python versions through 3.12 (the mailcap module was removed in Python 3.13); and readline.add_history() followed by readline.write_history_file(), which writes attacker-controlled content to an arbitrary filesystem path (POSIX platforms, where the readline module is available). fickling.load() chains check_safety() into pickle.loads() as an explicit security gate, so a LIKELY_SAFE verdict causes the payload to be deserialized and executed with the privileges of the victim process. The root cause is the inference of callable safety from module origin; it is independent of, and separately fixable from, the dotted __qualname__ resolution weakness tracked as CVE-2026-118938, which this advisory does not address. As of 2026-10-30 the three demonstrated modules were added to UNSAFE_IMPORTS on the vendor master branch (commit d011709) pending release; released versions through 0.1.12 remain affected, and the module-origin trust root cause persists, as demonstrated by the related CVE-2026-118938 remaining exploitable on that same build.","summary":"Trail of Bits fickling is a Python library that scans pickle files for danger before loading them. Through version 0.1.12 its scanner marks any callable from a standard library module as safe, so a crafted pickle that calls functions such as dataclasses._create_fn or mailcap.findmatch passes the check and runs code on the victim's machine when fickling.load() deserializes it.","severity":"high","cvss":8.8,"epss":null,"exploited":false,"exploited_at":null,"products":[],"ecosystems":[],"tags":["fickling","ml-model-scanning","package","pickle-loaders","python"],"not_affected_if":["no-untrusted-pickles","not-using-fickling-gate"],"how_to_check":"- Check the installed fickling version with `pip show fickling` or `python -c \"import fickling; print(fickling.__version__)\"`; versions through `0.1.12` are affected.\n- Search your code and CI for calls to `fickling.load(` or `check_safety(` on pickle files that come from outside your system.","fix":"- fickling: unknown (the sources say the fix is on the vendor master branch in commit d011709, pending release; no released fixed version is stated)","status":"open","published_at":"2026-10-30T22:16:54+00:00","first_seen_at":"2026-10-30T22:25:56+00:00","triaged_at":"2026-10-30T22:27:07+00:00","updated_at":"2026-10-30T22:27:07+00:00","category":"package","confidence":0.7,"triage":{"at":"2026-10-30T22:27:07Z","model":"claude-haiku-5-5","output":{"fix":"- fickling: unknown (the sources say the fix is on the vendor master branch in commit d011709, pending release; no released fixed version is stated)","reach":"local","title":"fickling: pickle safety scanner trusts stdlib callables, letting malicious payloads run code","impact":"high","summary":"Trail of Bits fickling is a Python library that scans pickle files for danger before loading them. Through version 0.1.12 its scanner marks any callable from a standard library module as safe, so a crafted pickle that calls functions such as dataclasses._create_fn or mailcap.findmatch passes the check and runs code on the victim's machine when fickling.load() deserializes it.","audience":"developers","category":"package","nicknames":[],"who_cares":["python","fickling","pickle-loaders","ml-model-scanning"],"confidence":0.7,"fix_status":"none","needs_auth":"no","worst_case":"Attacker code runs with the privileges of the victim Python process","analyst_note":"Confirm whether a released fickling version beyond 0.1.12 contains the UNSAFE_IMPORTS fix, using the vendor repository and commit d011709.","how_to_check":"- Check the installed fickling version with `pip show fickling` or `python -c \"import fickling; print(fickling.__version__)\"`; versions through `0.1.12` are affected.\n- Search your code and CI for calls to `fickling.load(` or `check_safety(` on pickle files that come from outside your system.","plain_action":"","needs_analyst":true,"not_affected_if":["no-untrusted-pickles","not-using-fickling-gate"],"who_is_affected":"Teams that use fickling to vet untrusted pickle files before loading them are exposed, because a LIKELY_SAFE verdict triggers pickle.loads() on the payload. Systems that never load pickles from outside sources, or that do not rely on fickling as their gate, are not exposed through this path. The sources say the three demonstrated modules are blocked on master but released versions remain affected.","user_interaction":"yes","needs_special_setup":"no","severity_if_unknown":"unknown"},"cost_usd":0.00084,"input_tokens":4776,"output_tokens":724,"severity_before":"high"},"analyst_status":null,"analyst_note":null,"search":"'-10':380C '-118938':370C,422C '-16760':15A,18A '-2026':14A,17A,369C,421C '-30':381C '0.1.12':48B,93C,403C '2026':379C '3.12':238C,274C '3.13':248C,282C '3.7':236C 'absent':124C 'add':100C,387C 'address':376C 'advisori':373C 'affect':405C 'analysi':143C 'analysis.py':146C 'appear':155C 'arbitrari':231C,267C,299C 'attack':223C,262C,294C 'attacker-control':222C,261C,293C 'avail':309C 'behav':140C 'behavior':193C 'bit':31B,90C 'block':180C 'bodi':226C 'branch':395C 'build':428C 'call':66B,151C,161C,252C 'callabl':7A,53B,174C,215C,348C 'cap':264C 'caus':326C,343C,413C 'chain':311C 'check':75B,195C,312C 'code':12A,78B,208C,232C,268C 'command':257C 'commit':396C 'content':296C 'control':224C,263C,295C 'craft':63B 'creat':241C 'cve':13A,16A,368C,420C 'd011709':397C 'danger':42B,169C 'dataclasses._create_fn':70B,218C 'demonstr':216C,384C,416C 'denylist':129C 'deseri':86B,331C 'dictionari':265C 'dot':362C 'exec':228C 'execut':209C,233C,269C,333C 'explicit':318C 'exploit':424C 'fickl':1A,19B,32B,91C 'fickle.py':99C 'fickling.load':85B,310C 'file':40B,211C,290C 'filesystem':300C 'find':202C 'fixabl':359C 'flag':166C 'fn':242C 'follow':286C 'function':67B,153C,225C,243C 'gate':320C 'histori':285C,289C 'import':96C,102C,108C,128C,159C,390C 'includ':160C 'independ':355C 'infer':346C 'inherit':184C 'inspect':131C 'intern':240C,251C 'let':8A 'level':187C 'librari':36B,57B,182C 'like':106C,157C,198C,323C 'load':44B 'loader':27B 'machin':83B 'mailcap':276C 'mailcap.findmatch':72B,249C 'malici':9A 'mark':51B 'master':394C 'method':97C 'ml':21B 'ml-model-scan':20B 'model':22B 'modul':58B,113C,118C,183C,186C,277C,307C,351C,385C,409C 'module-level':185C 'module-origin':408C 'name':103C,119C,122C,134C,154C 'non':179C 'non-block':178C 'origin':352C,410C 'os.system':253C 'otherwis':164C 'overt':168C 'overtlybadev':142C 'packag':24B 'pass':73B,144C,220C 'path':301C 'payload':10A,205C,328C 'pend':398C 'persist':414C 'pickl':2A,26B,39B,64B,204C 'pickle-load':25B 'pickle.loads':315C 'platform':303C 'posix':302C 'present':115C 'primit':217C 'privileg':336C 'process':95C,340C 'python':28B,35B,235C,247C,271C,281C 'qualnam':363C 'reach':207C 'reachabl':175C 'readlin':306C 'readline.add':284C 'readline.write':288C 'regardless':189C 'relat':419C 'releas':399C,400C 'remain':404C,423C 'remov':245C,279C 'resolut':364C 'resolv':135C 'result':172C 'return':197C 'root':342C,412C 'run':11A,77B 'safe':60B,107C,158C,199C,324C 'safeti':3A,196C,313C,349C 'scan':23B,38B 'scanner':4A,50B 'secur':319C 'separ':358C 'set':109C 'skip':149C 'sourc':112C 'standard':56B,181C 'stdlib':6A 'sys.stdlib':117C 'taken':258C 'test':256C 'three':383C 'track':366C 'trail':29B,88C 'trust':5A,188C,411C 'uncondit':148C 'unsaf':127C,389C 'vendor':393C 'verdict':325C 'version':47B,272C,401C 'victim':81B,339C 'weak':365C 'whenev':110C 'whose':152C 'without':130C 'would':163C 'write':212C,292C 'yield':230C,266C 'zero':201C","nicknames":[],"mitigations":[],"who_affected":"Teams that use fickling to vet untrusted pickle files before loading them are exposed, because a LIKELY_SAFE verdict triggers pickle.loads() on the payload. Systems that never load pickles from outside sources, or that do not rely on fickling as their gate, are not exposed through this path. The sources say the three demonstrated modules are blocked on master but released versions remain affected.","tier":"B","verified":{},"analyst_revisit_at":null,"fix_status":"none","audience":"developers","plain_action":null,"first_public_at":"2026-10-30T22:16:54+00:00","kev":null,"cvss_vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H","cvss_by":"aa17e1a1-c329-4d6e-a1ed-8d0188aea082","poc_count":0,"sources":[{"source":"nvd","external_id":"CVE-2026-16760","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-16760","severity":null,"published_at":"2026-10-30T22:16:54+00:00","modified_at":"2026-10-30T22:16:54+00:00","first_seen_at":"2026-10-30T22:25:56+00:00","fetched_at":"2026-10-30T22:25:56+00:00","note":null}],"documents":[],"notice":"Free to read at a human pace. An API with keys and an MCP server are in preparation; early access: hello@infrastack.ai"}