{"id":"MAL-2026-17196","kind":"package","aliases":["MAL-2026-17196"],"related":[],"title":"Malicious code in donutpromotion (PyPI)","description":"`donutpromotion` presents itself as a Minecraft SMP donation and rank-promotion helper. The published 0.1.0 sdist instead executes an encrypted downloader before setuptools processes the package.\n\nThe automated package report identified a critical `py-download-and-execute` signal at `donutpromotion/telemetry.py:99`: downloaded or embedded bytes are decrypted and passed to `exec()` with unrestricted builtins. Review of the cited path confirms the install trigger. When `setup.py` runs during build or installation, lines 6-7 import and call `donutpromotion.telemetry.check()` before `setup()` runs.\n\n`telemetry.py:19` hardcodes `https://thisisafalsepositive.st/cdn/v2/promotion/manifest.json`. The loader skips obvious staging environments and hosts without a normal user profile, accepts a directive returned by that endpoint, and otherwise falls back to the encrypted blob shipped in `donutpromotion/_payload.py`. Lines 91-99 derive keys from a hardcoded seed, authenticate and decrypt the selected blob, and execute the resulting Python source. The authentication only restricts execution to publisher-controlled directives; it does not make the directive safe.\n\nOffline static decryption of the embedded fallback, without executing it, reveals a second-stage downloader. It checks Windows BIOS strings for VMware, VirtualBox, QEMU, Xen and other virtualisation products; searches the process list for Wireshark, Procmon, Process Hacker, IDA, x64dbg and other analysis tools; calls `IsDebuggerPresent`; creates a one-time marker; and sleeps for a random interval. If those checks pass, it downloads `https://thisisafalsepositive.st/cdn/v2/9f4e7a2c1b8d.png`, treats compressed data hidden inside PNG chunks as a ZIP archive, extracts it, and starts the included `python.exe AppHost/main.py` as a detached, hidden background process.\n\nThe automated sandbox completed without observing the second-stage request; the source explicitly avoids staged environments and hosts without a normal user profile. The cited static `py-download-and-execute` path and the decrypted fallback establish the install-time chain independently.\n\nEvidence: `setup.py:6-7` calls the loader during build/install; `donutpromotion/telemetry.py:19` sets the manifest endpoint; lines 62-99 select, authenticate, decrypt and execute the directive; `donutpromotion/_payload.py` contains the encrypted fallback.\n\n---\n_-= Per source details. Do not edit below this line.=-_\n\n## Source: amazon-inspector (ada85a54b2cc7d9770d7276da4b2e9983c29ad0e8e2187737415073b4f80d947)\nsetup.py imports donutpromotion.telemetry and invokes check() during pip install. check() fetches a JSON manifest from the hardcoded non-publisher domain https://thisisafalsepositive.st/cdn/v2/promotion/manifest.json, extracts an 'r' field, and if the fetch fails falls back to a ~4.4KB base64-encoded encrypted PAYLOAD embedded in _payload.py. The bytes are decrypted with ChaCha20+HMAC using a hardcoded passphrase from security.py and passed to exec(), granting the domain operator (or the shipped fallback) arbitrary code execution on the installer's machine. Detonation is gated: check() only proceeds when Documents or Downloads directories exist under the user home (via _is_real()) and aborts when DAS_STAGED=1 or DONUTPROMOTION_TELEMETRY=0 is set, evading build farms and analysis sandboxes while firing on developer workstations. setup.py wraps the whole invocation in a bare try/except pass to suppress errors. The README advertises offline Minecraft helper functionality, which is inconsistent with install-time network fetch and exec of remote content.\n\n## Source: kam193 (26201e7959d7a736440fa6fb22ba7b9911c269b08460374d0a2db307ba6b0273)\nDuring installation, obfuscated code is used to fetch code hidden in an image containing a Python application with a native extension module. This module holds an obfuscated infostealer which collects sensitive data and exfiltrates them to a C2 server retrieved from transaction history in the Polygon blockchain.\n\n\n---\n\nCategory: MALICIOUS - The campaign has clearly malicious intent, like infostealers.\n\n\nCampaign: 2026-09-donutautosellsrc\n\n\nReasons (based on the campaign):\n\n\n - infostealer\n\n\n - Downloads and executes a remote executable.\n\n\n - obfuscation\n\n\n - malware\n\n\n - native-extension\n\n\n - steganography\n\n\n - c2-in-blockchain\n\n\n - The package contains code to detect if it is running in a sandbox environment.","summary":null,"severity":"critical","cvss":null,"epss":null,"exploited":false,"exploited_at":null,"products":[{"name":"donutpromotion","fixed":[],"affected":"= 0.1.0","ecosystem":"pypi"}],"ecosystems":["pypi"],"tags":["malicious","package"],"not_affected_if":[],"how_to_check":null,"fix":null,"status":"open","published_at":"2026-09-27T08:55:47+00:00","first_seen_at":"2026-10-26T20:52:03+00:00","triaged_at":"2026-10-26T21:08:21+00:00","updated_at":"2026-10-26T21:08:21+00:00","category":"package","confidence":null,"triage":null,"analyst_status":null,"analyst_note":null,"search":"'-17196':8A,11A '-2026':7A,10A '-7':89C,310C '-99':135C,324C '/cdn/v2/9f4e7a2c1b8d.png':238C '/cdn/v2/promotion/manifest.json':101C '/cdn/v2/promotion/manifest.json,':374C '0':459C '0.1.0':30C '1':455C '19':317C '26201e7959':509C '4.4':388C '6':88C '62':323C '91':134C '99':57C 'abort':451C 'accept':115C 'ada85a54b2cc7d9770d7276da4b2e9983c29ad0e8e2187737415073b4f80d947':350C 'advertis':488C 'amazon':348C 'amazon-inspector':347C 'analysi':214C,466C 'apphost/main.py':257C 'applic':527C 'arbitrari':423C 'archiv':249C 'authent':142C,155C,326C 'autom':43C,265C 'avoid':278C 'back':125C,385C 'background':262C 'bare':480C 'base64':391C 'base64-encoded':390C 'bio':190C 'blob':129C,147C 'bloc':557C 'build':84C,463C 'build/install':315C 'builtin':70C 'byte':61C,399C 'c2':548C 'call':92C,216C,311C 'chacha20':403C 'chain':306C 'check':188C,232C,356C,360C,434C 'chunk':245C 'cite':74C,289C 'code':2A,424C,514C,519C 'collect':540C 'complet':267C 'compress':240C 'confirm':76C 'contain':333C,524C 'content':506C 'control':162C 'creat':218C 'critic':48C 'd7a736440fa6fb22ba7b9911c269b08460374d0a2db307ba6b0273':510C 'das':453C 'data':241C,542C 'decrypt':63C,144C,173C,299C,327C,401C 'deriv':136C 'detach':260C 'detail':339C 'deton':431C 'develop':471C 'direct':117C,163C,169C,331C 'directori':441C 'document':438C 'domain':371C,417C 'donat':22C 'donutpromot':4A,15C,457C 'donutpromotion':12A 'donutpromotion.telemetry':353C 'donutpromotion.telemetry.check':93C 'donutpromotion/_payload.py':132C,332C 'donutpromotion/telemetry.py':56C,316C 'download':36C,51C,58C,186C,235C,293C,440C 'edit':342C 'embed':60C,176C,395C 'encod':392C 'encrypt':35C,128C,335C,393C 'endpoint':121C,321C 'environ':107C,280C 'error':485C 'establish':301C 'evad':462C 'evid':308C 'exec':67C,414C,503C 'execut':33C,53C,149C,158C,179C,295C,329C,425C 'exfiltr':544C 'exist':442C 'explicit':277C 'extens':531C 'extract':250C,375C 'fail':383C 'fall':124C,384C 'fallback':177C,300C,336C,422C 'farm':464C 'fetch':361C,382C,501C,518C 'field':378C 'fire':469C 'function':492C 'gate':433C 'grant':415C 'hacker':209C 'hardcod':98C,140C,367C,407C 'helper':27C,491C 'hidden':242C,261C,520C 'histori':553C 'hmac':404C 'hold':535C 'home':446C 'host':109C,282C 'ida':210C 'identifi':46C 'imag':523C 'import':90C,352C 'includ':255C 'inconsist':495C 'independ':307C 'infosteal':538C 'insid':243C 'inspector':349C 'instal':78C,86C,304C,359C,428C,498C,512C 'install-tim':303C,497C 'instead':32C 'interv':229C 'invoc':477C 'invok':355C 'isdebuggerpres':217C 'json':363C 'kam193':508C 'kb':389C 'key':137C 'line':87C,133C,322C,345C 'list':204C 'loader':103C,313C 'machin':430C 'make':167C 'mal':6A,9A 'malici':1A,13B 'manifest':320C,364C 'marker':223C 'minecraft':20C,490C 'modul':532C,534C 'nativ':530C 'network':500C 'non':369C 'non-publish':368C 'normal':112C,285C 'obfusc':513C,537C 'observ':269C 'obvious':105C 'offlin':171C,489C 'one':221C 'one-tim':220C 'oper':418C 'otherwis':123C 'packag':14B,41C,44C 'pass':65C,233C,412C,482C 'passphras':408C 'path':75C,296C 'payload':394C 'payload.py':397C 'per':337C 'pip':358C 'png':244C 'polygon':556C 'present':16C 'proceed':436C 'process':39C,203C,208C,263C 'procmon':207C 'product':200C 'profil':114C,287C 'promot':26C 'publish':29C,161C,370C 'publisher-control':160C 'py':50C,292C 'py-download-and-execut':49C,291C 'pypi':5A 'python':152C,526C 'python.exe':256C 'qemu':195C 'r':377C 'random':228C 'rank':25C 'rank-promot':24C 'readm':487C 'real':449C 'remot':505C 'report':45C 'request':274C 'restrict':157C 'result':151C 'retriev':550C 'return':118C 'reveal':181C 'review':71C 'run':82C,96C 'safe':170C 'sandbox':266C,467C 'sdist':31C 'search':201C 'second':184C,272C 'second-stag':183C,271C 'security.py':410C 'seed':141C 'select':146C,325C 'sensit':541C 'server':549C 'set':318C,461C 'setup':95C 'setup.py':81C,351C,473C 'setup.py:6':309C 'setuptool':38C 'ship':130C,421C 'signal':54C 'skip':104C 'sleep':225C 'smp':21C 'sourc':153C,276C,338C,346C,507C 'stage':106C,185C,273C,279C,454C 'start':253C 'static':172C,290C 'string':191C 'suppress':484C 'telemetri':458C 'telemetry.py:19':97C 'thisisafalsepositive.st':100C,237C,373C 'thisisafalsepositive.st/cdn/v2/9f4e7a2c1b8d.png':236C 'thisisafalsepositive.st/cdn/v2/promotion/manifest.json':99C 'thisisafalsepositive.st/cdn/v2/promotion/manifest.json,':372C 'time':222C,305C,499C 'tool':215C 'transact':552C 'treat':239C 'trigger':79C 'try/except':481C 'unrestrict':69C 'use':405C,516C 'user':113C,286C,445C 'via':447C 'virtualbox':194C 'virtualis':199C 'vmware':193C 'whole':476C 'window':189C 'wireshark':206C 'without':110C,178C,268C,283C 'workstat':472C 'wrap':474C 'x64dbg':211C 'xen':196C 'zip':248C","nicknames":[],"mitigations":[],"who_affected":null,"tier":null,"verified":{},"analyst_revisit_at":null,"first_public_at":"2026-09-27T08:55:47+00:00","kev":null,"cvss_vector":null,"cvss_by":null,"poc_count":0,"sources":[{"source":"osv-malicious","external_id":"MAL-2026-17196","url":"https://osv.dev/vulnerability/MAL-2026-17196","severity":"critical","published_at":"2026-09-27T08:55:47+00:00","modified_at":"2026-10-25T23:45:03+00:00","first_seen_at":"2026-10-26T20:52:03+00:00","fetched_at":"2026-10-26T20:52:03+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"}