ChocoPoC — a Python RAT Hidden in Fake GitHub PoC Exploits, with a Mapbox Dead-Drop C2
Executive Summary
ChocoPoC is a fully-functional Python RAT disclosed jointly by YesWeHack and Sekoia on 2026-07-01 that hides inside fake proof-of-concept (PoC) exploit repositories on GitHub claiming to exploit hot, freshly-disclosed CVEs. The campaign specifically targets vulnerability researchers and pentesters — the people most likely to clone and run an untrusted PoC. As of the report the malware and its servers were still live; researchers explicitly warned not to run any of the identified PoCs.
The trick is dependency-chaining: the PoC declares a dependency on a Python package named frint, which in turn drags in skytext. skytext ships a small compiled native extension — gradient.so on Linux, gradient.pyd on Windows — that executes the moment the PoC is launched. Activation is gated on the presence of a file named EXPLOIT_POC.py / exploit.py / exploit_poc.py (a filename-hash check, 0xF4835C9C), so the payload stays dormant in a sandbox that lacks the full PoC. The skytext package alone was downloaded ~2,400 times, mostly on Linux, with spikes right after major CVE disclosures.
Once live, ChocoPoC harvests browser credentials, cookies, autofill and history (Chrome, Brave, Edge, Firefox), plus local files, notes, shell history, network settings and process lists. For C2 it uses a Mapbox dataset as a dead drop — reading its orders from api.mapbox[.]com, resolving over DNS-over-HTTPS (dns.alidns[.]com, cloudflare-dns[.]com) with domain fronting so traffic looks like ordinary Mapbox API calls. Bulk exfiltration goes to a separate server at 91.132.163.78:8001. Persistence rides trojanized setuptools distutils hooks (_distutils_hack/override.py, a distutils-precedence.pth file), and the payload is heavily armored (timestomping, PEB walking, export hashing, anti-debug via CheckRemoteDebuggerPresent and hardware breakpoints, environmental gating).
Defender priority: The highest-value, lowest-FP angles are installation of the malicious packages (skytext / frint / slogsec), the compiled payload files (gradient.so / gradient.pyd), the Sekoia-published SHA-256 hashes, egress to 91.132.163.78, and git clone of the known fake-PoC repos. This is a developer-workstation threat: focus coverage on engineering, research and pentest hosts (Linux and Windows), and keep offensive-security tooling off production identities.
Source Review & Web Hunter Notes
| Tier | Source | Key Finding | Carry Fwd |
|---|---|---|---|
| 1 · Vendor | Sekoia — "Don't eat the ChocoPoCs! How vulnerability researchers were repeatedly targeted by trojanised exploits" (2026-07-01) | Primary technical analysis: dependency chain (frint→skytext), gradient.so/.pyd payload, filename-gated activation, Mapbox dead-drop C2, DoH + domain fronting, exfil IP, distutils persistence, anti-analysis, full IOC set with SHA-256 | yes |
| 1 · Vendor | YesWeHack — "Don't eat the ChocoPoCs! Vulnerability researchers were targeted by trojanised exploits" (2026-07-01) | Joint disclosure: 7 fake PoC repos tied to high-profile CVEs, ~2,400 skytext downloads, warning that servers were live | yes |
| 2 · Press | The Hacker News / BleepingComputer / Cyber Security News — ChocoPoC Mapbox dead-drop & Python RAT | Corroborate the Mapbox dead-drop, DoH resolution, credential theft, researcher targeting, prior slogsec/logcrypt.cryptography link | partial |
Every atomic IOC below (SHA-256 hashes, package names, payload filenames, exfil IP, Mapbox/DoH domains, fake-repo paths) traces to the Sekoia / YesWeHack indicator set. The package/payload names, hashes and the 91.132.163.78 exfil server are durable; api.mapbox.com and cloudflare-dns.com are legitimate services abused as carriers, so those are hunt/scoped indicators, not blanket blocks.
Hunt Brief & Attack Chain
ChocoPoC is cross-platform (Linux and Windows). The CQL below uses the CrowdStrike-normalized process/file/network events, which apply to both the Windows sensor and Falcon for Linux; confirm Linux sensor coverage on engineering/research hosts before relying on it.
Hunt hypotheses (fidelity-ordered)
- H1 (high): A host installed a malicious ChocoPoC package (
skytext/frint/slogsec) via pip. Delivery. - H2 (high): The compiled payload
gradient.so/gradient.pydwas written to disk (into askytextpackage dir). Payload drop. - H3 (high): A file matching a Sekoia-published ChocoPoC SHA-256 executed or was written. Atomic.
- H4 (high): A host connected to the exfil server
91.132.163.78(port 8001). Covers T1041. - H5 (high): A host
git cloned one of the known fake-PoC repos (lincemorado97 / ogenich / bolubey). Early warning. - H6 (medium): A Python interpreter resolved the Mapbox dead-drop / DoH carriers (
api.mapbox.com,dns.alidns.com,cloudflare-dns.com). Covers T1071.001 / T1090.004. - H7 (medium): A Python interpreter made outbound connections to a public IP shortly after running a PoC. Beacon/exfil generic.
- H8 (medium): Trojanized setuptools persistence appeared (
_distutils_hack/override.py,distutils-precedence.pth). Covers T1546.016 / T1574.006-style startup hook.
Attack chain
| Step | Behavior | Telemetry | Hunt Angle |
|---|---|---|---|
| 1 · Lure | Researcher clones a fake PoC repo for a hot CVE from GitHub | ProcessRollup2 (git) | Q5 |
| 2 · Delivery | PoC depends on frint → skytext; pip pulls the malicious packages | ProcessRollup2 (pip), FileCreateInfo | Q1 |
| 3 · Payload | skytext ships gradient.so / gradient.pyd, executed when the PoC launches | PeFileWritten / FileCreateInfo | Q2, Q3 |
| 4 · Activation gate | Runs only if EXPLOIT_POC.py / exploit_poc.py present (filename-hash 0xF4835C9C) — sandbox-evasion | ProcessRollup2 | native hunts |
| 5 · Collection | Steals browser creds/cookies/history, files, shell history, process list | File access / ProcessRollup2 | native hunts, Q7 |
| 6 · C2 (dead drop) | Reads orders from a Mapbox dataset via api.mapbox.com over DoH + domain fronting | DnsRequest, NetworkConnectIP4 | Q6, Q7 |
| 7 · Exfil | Bulk upload to 91.132.163.78:8001 | NetworkConnectIP4 | Q4 |
| 8 · Persistence | Trojanized distutils hooks (override.py, distutils-precedence.pth) re-run at Python startup | FileCreateInfo / PeFileWritten | Q8 |
Consolidated IOC Table
All values published by Sekoia / YesWeHack. Package names, payload filenames, SHA-256 hashes and the 91.132.163.78 exfil server are durable indicators. api.mapbox.com, cloudflare-dns.com and dns.alidns.com are legitimate services abused as carriers — treat them as scoped hunt indicators (correlate with a Python interpreter / PoC context), not blanket blocks.
| Type | Value | Conf | Action | Context |
|---|---|---|---|---|
| ipv4 | 91.132.163.78 (port 8001) | high | block | ChocoPoC bulk-exfil server |
| url | hxxp://91.132.163.78:8001/assets/static/bundle.ext.min.de5b2bc9.js | high | block | exfil endpoint (fake JS path) |
| sha256 | 93739477cd379adef95126b22758c0e644282d2028dd297328ce856fa111dd06 | high | detect | skytext v1.1.0 (PyPI) |
| sha256 | 17997e9e0256d0f5d5d21a4852c37f16b338e4bb9c2bec09bdfd822b24aa76b4 | high | detect | frint v0.1.2 (PyPI) |
| sha256 | 5abd45d6f4a1705dca55d882f017d4768888dce9ad99cea40b3da35c23de5cae | high | detect | slogsec (earlier campaign) |
| sha256 | 40569318e89db751ff3886b2617d990d8a343f0d1d8727b7f978a28129ca36bc | high | detect | gradient.pyd (Windows payload) |
| sha256 | 320b29844892e3c59bc6fcb07e701b2b3230a37cb4a13176174e9e294ec6d43e | high | detect | gradient.so (Linux payload) |
| package | skytext · frint · slogsec · logcrypt.cryptography | high | hunt | malicious PyPI packages |
| filename | gradient.pyd / gradient.so | high | hunt | compiled RAT payload |
| domain | api.mapbox[.]com (dead drop) | med | hunt | legit service abused as C2 carrier |
| domain | dns.alidns[.]com · cloudflare-dns[.]com | med | hunt | DoH resolvers used for covert resolution |
| repo | github.com/{lincemorado97,ogenich,bolubey}/CVE-… | high | hunt | fake PoC repos (7 known) |
| trigger | EXPLOIT_POC.py / exploit.py / exploit_poc.py (hash 0xF4835C9C) | med | hunt | activation gate (sandbox evasion) |
| persistence | _distutils_hack/override.py · distutils-precedence.pth | med | hunt | trojanized setuptools startup hook |
| env | ZEBUWIAKGPHOQAP006 · JKHWQVEKRASDF12 | med | hunt | anti-recursion env-var markers |
Affected Surface & Telemetry Matrix
| Surface | Required Telemetry | Priority | Gap Risk |
|---|---|---|---|
| Developer / research / pentest hosts (Linux) | ProcessRollup2 (Falcon for Linux) | Critical | Med — depends on Linux sensor coverage on eng hosts |
| Developer / research hosts (Windows) | ProcessRollup2 / SyntheticProcessRollup2 | Critical | Low |
| File writes (packages, gradient payload, .pth hooks) | PeFileWritten / FileCreateInfo | High | Med — .so writes vary by Linux sensor config |
| Egress (exfil IP, Mapbox dead drop, DoH) | NetworkConnectIP4, DnsRequest | High | Med — DoH + domain fronting hide the true destination |
| Browser credential stores | File access to Login Data / cookies DB | Med | High — read access not always in EDR; use native hunt (§7) |
ATT&CK Mapping
| Tactic | Technique | Observed Behavior | Coverage |
|---|---|---|---|
| Initial Access | T1195.001 Supply Chain: Software Dependencies | Fake PoC repo → frint → skytext malicious PyPI packages | Q1, Q5 |
| Execution | T1204.002 User Execution: Malicious File | Researcher runs the PoC; gradient.so/.pyd executes | Q2, Q3 |
| Defense Evasion | T1497 Virtualization/Sandbox Evasion | Payload gated on EXPLOIT_POC.py presence (hash 0xF4835C9C); anti-debug, timestomping, PEB walking | native hunts |
| Credential Access | T1555.003 Credentials from Web Browsers | Steals Chrome/Brave/Edge/Firefox creds, cookies, autofill, history | Q7, native hunts |
| Collection | T1005 Data from Local System | Files, notes, shell history, network settings, process lists | native hunts |
| Command & Control | T1071.001 Web Protocols / T1102 Web Service | Mapbox dataset dead drop via api.mapbox.com | Q6 |
| Command & Control | T1090.004 Proxy: Domain Fronting / T1071.004 DNS | DoH resolution (alidns/cloudflare-dns) + domain fronting | Q6 |
| Exfiltration | T1041 Exfiltration Over C2 Channel | Bulk upload to 91.132.163.78:8001 | Q4, Q7 |
| Persistence | T1546.016 Installer Packages / .pth startup hook | Trojanized _distutils_hack + distutils-precedence.pth run at Python startup | Q8 |
Native Audit-Log Hunts (non-CQL)
- PyPI / pip inventory: On developer, research and CI hosts, inventory installed packages and flag any presence of
skytext,frint,slogsec, orlogcrypt.cryptography(pip list/ SBOM / artifact-proxy logs). Check internal PyPI mirror download logs for these names. - Payload file sweep: Search site-packages trees for
gradient.so/gradient.pydand for adistutils-precedence.pthor modified_distutils_hack/override.pythat did not come from a clean setuptools install. - Browser credential access: On hosts that ran a suspect PoC, hunt for a Python process opening Chrome/Edge/Brave
Login Data/Network\Cookiesor the Firefoxlogins.json/key4.db— a Python interpreter touching browser secret stores is the stealer tell. - Environment markers: Check for the anti-recursion env vars
ZEBUWIAKGPHOQAP006andJKHWQVEKRASDF12in process environments on suspect hosts. - GitHub / proxy logs: Search web-proxy / git-server logs for clones of the fake-PoC repos (owners
lincemorado97,ogenich,bolubey), and rotate any credentials that were present on a host that ran one.
CrowdStrike LogScale CQL Hunt Queries
Looks for: a pip install command line referencing skytext, frint, or slogsec — direct or transitive install of the ChocoPoC delivery packages. Works on Windows and Linux.
// HUNT: pip install of a malicious ChocoPoC package // MITRE: T1195.001 // CONF: high FP: low COST: low | REQUIRES: ProcessRollup2 // FALSE POSITIVES: none expected — these are known-bad package names #event_simpleName=/ProcessRollup2|SyntheticProcessRollup2/ | CommandLine=/\b(pip|pip3|uv|python|python3)\b/i | CommandLine=/\binstall\b/i | CommandLine=/\b(skytext|frint|slogsec)\b/i | table([@timestamp, aid, ComputerName, UserName, FileName, CommandLine], limit=200)
Looks for: the compiled ChocoPoC payload gradient.so (Linux) or gradient.pyd (Windows) being written — it lands inside the skytext package directory during install. Distinctive filename → very low FP.
// HUNT: ChocoPoC compiled payload gradient.so / gradient.pyd written // MITRE: T1204.002 // CONF: high FP: low COST: low | REQUIRES: PeFileWritten / FileCreateInfo #event_simpleName=/PeFileWritten|NewExecutableWritten|FileCreateInfo/ | TargetFileName=/gradient\.(so|pyd)$/i | table([@timestamp, aid, ComputerName, TargetFileName, SHA256HashData], limit=200)
Looks for: execution or write of any Sekoia-listed ChocoPoC package or payload SHA-256. Atomic — promote straight to a Custom IOC block (see §10).
// HUNT: ChocoPoC package / payload known-bad SHA-256 (Sekoia) // MITRE: T1195.001, T1204.002 // CONF: high FP: low COST: low | REQUIRES: ProcessRollup2 / PeFileWritten #event_simpleName=/ProcessRollup2|PeFileWritten|NewExecutableWritten/ | in(SHA256HashData, values=["93739477cd379adef95126b22758c0e644282d2028dd297328ce856fa111dd06", "17997e9e0256d0f5d5d21a4852c37f16b338e4bb9c2bec09bdfd822b24aa76b4", "5abd45d6f4a1705dca55d882f017d4768888dce9ad99cea40b3da35c23de5cae", "40569318e89db751ff3886b2617d990d8a343f0d1d8727b7f978a28129ca36bc", "320b29844892e3c59bc6fcb07e701b2b3230a37cb4a13176174e9e294ec6d43e"]) | table([@timestamp, aid, ComputerName, UserName, FileName, FilePath, SHA256HashData], limit=max)
Looks for: any outbound connection to the dedicated ChocoPoC bulk-exfil server 91.132.163.78 (observed on port 8001). Dedicated actor infrastructure → any hit is high-signal.
// HUNT: ChocoPoC exfil server connection // MITRE: T1041 // CONF: high FP: low COST: low | REQUIRES: NetworkConnectIP4 #event_simpleName=/NetworkConnectIP4/ | RemoteAddressIP4=/^91\.132\.163\.78$/ | table([@timestamp, aid, ComputerName, ContextBaseFileName, RemoteAddressIP4, RemotePort], limit=200)
Looks for: a command line referencing one of the known fake-PoC repo owners (lincemorado97, ogenich, bolubey) — the earliest signal, fired at clone time before any payload runs. Distinctive strings → very low FP.
// HUNT: Clone/reference of a known ChocoPoC fake-PoC repo // MITRE: T1195.001, T1204.002 // CONF: high FP: low COST: low | REQUIRES: ProcessRollup2 #event_simpleName=/ProcessRollup2|SyntheticProcessRollup2/ | CommandLine=/(lincemorado97|ogenich|bolubey)/i | CommandLine=/(github|CVE-20)/i | table([@timestamp, aid, ComputerName, UserName, FileName, CommandLine], limit=200)
Looks for: a Python process resolving the Mapbox dead-drop (api.mapbox.com) or a DoH resolver (dns.alidns.com, cloudflare-dns.com) — the C2 carrier channel. FP: legitimate Python apps use Mapbox/Cloudflare too, so scope by the interpreter context and correlate with Q1/Q2/Q5 on the same host.
// HUNT: ChocoPoC C2 carriers resolved by a Python interpreter // MITRE: T1071.001, T1071.004, T1090.004, T1102 // CONF: medium FP: medium COST: medium | REQUIRES: DnsRequest // FALSE POSITIVES: api.mapbox.com / cloudflare-dns.com are legitimate services // TUNING: correlate with Q1/Q2/Q5 on the same aid; exclude sanctioned mapping/DoH apps #event_simpleName=/DnsRequest/ | DomainName=/^(api\.mapbox\.com|dns\.alidns\.com|cloudflare-dns\.com)$/i | ContextBaseFileName=/^(python3?|python\.exe|uv|pip3?)$/i | table([@timestamp, aid, ComputerName, DomainName, ContextBaseFileName], limit=200)
Looks for: a Python interpreter making outbound connections to non-RFC1918 destinations — useful during IR on a host that ran a suspect PoC to surface the dead-drop/exfil traffic when the domain is fronted. FP: Python legitimately calls out; scope to suspect hosts and pivot on the remote address.
// HUNT: Python interpreter outbound to a public IP (beacon/exfil, IR-scoped) // MITRE: T1041, T1071.001 // CONF: medium FP: medium COST: medium | REQUIRES: NetworkConnectIP4 // FALSE POSITIVES: Python apps make legitimate outbound calls // TUNING: run host-scoped on a suspect aid; exclude known-good internal/SaaS ranges #event_simpleName=/NetworkConnectIP4/ | ContextBaseFileName=/^(python3?|python\.exe)$/i | RemoteAddressIP4!=/^(10\.|192\.168\.|172\.(1[6-9]|2[0-9]|3[01])\.|127\.|169\.254\.)/ | table([@timestamp, aid, ComputerName, ContextBaseFileName, RemoteAddressIP4, RemotePort], limit=200)
Looks for: writes to the setuptools distutils shim (distutils-precedence.pth, _distutils_hack/override.py) that ChocoPoC trojanizes for Python-startup persistence. FP: HIGH — these files ship legitimately with setuptools, so this is hunt-only: use it to locate candidates, then confirm the file was modified outside a clean pip/setuptools install (hash it against a known-good copy). Never auto-block.
// HUNT: ChocoPoC distutils startup-hook persistence (hunt-only) // MITRE: T1546.016 // CONF: medium FP: high COST: low | REQUIRES: PeFileWritten / FileCreateInfo // FALSE POSITIVES: these files ship with setuptools — writes are normal on install // TUNING: hunt-only; confirm modification outside a pip install and hash vs known-good; do not block #event_simpleName=/PeFileWritten|NewExecutableWritten|FileCreateInfo/ | TargetFileName=/(distutils-precedence\.pth|_distutils_hack.override\.py)$/i | table([@timestamp, aid, ComputerName, TargetFileName, ContextBaseFileName], limit=200)
CrowdStrike Custom IOA Recommendations
| IOA Name | Pattern | Action | Exclusions |
|---|---|---|---|
| ChocoPoC — Malicious Package Install | pip install skytext/frint/slogsec (Q1) | Detect (high) | None expected — known-bad names |
| ChocoPoC — gradient Payload Written | gradient.so / gradient.pyd write (Q2) | Detect → Block (after baseline) | None expected — distinctive filename |
| ChocoPoC — Exfil Server | Connection to 91.132.163.78 (Q4) | Detect (high) → IOC block | None — dedicated actor infra |
| ChocoPoC — Fake-PoC Repo Clone | CmdLine lincemorado97/ogenich/bolubey (Q5) | Detect (high) | None expected |
Promotion path: validate the high-CONF/low-FP queries (Q1, Q2, Q4, Q5) against 7–14 days of baseline on developer/research hosts, confirm zero benign hits, then promote to Custom IOAs in detect mode; escalate Q2 to block once clean. Q3 hashes and the 91.132.163.78 IP → Custom IOC Management (block).
Machine-Readable IOC Appendix
One-click blocks for SOC tooling. Hashes and the exfil IP are Sekoia/YesWeHack-published; swap expirations to fit your policy. The Mapbox/DoH domains are legitimate services abused as carriers — hunt/scope, do not blanket-block.
type,value,action,severity,expiration,description,tags ipv4,91.132.163.78,block,critical,2026-12-31,ChocoPoC exfil server,tool:ChocoPoC;kind:exfil sha256,93739477cd379adef95126b22758c0e644282d2028dd297328ce856fa111dd06,prevent,critical,2026-12-31,skytext v1.1.0,tool:ChocoPoC;pkg:skytext sha256,17997e9e0256d0f5d5d21a4852c37f16b338e4bb9c2bec09bdfd822b24aa76b4,prevent,critical,2026-12-31,frint v0.1.2,tool:ChocoPoC;pkg:frint sha256,5abd45d6f4a1705dca55d882f017d4768888dce9ad99cea40b3da35c23de5cae,prevent,critical,2026-12-31,slogsec,tool:ChocoPoC;pkg:slogsec sha256,40569318e89db751ff3886b2617d990d8a343f0d1d8727b7f978a28129ca36bc,prevent,critical,2026-12-31,gradient.pyd Windows payload,tool:ChocoPoC;kind:payload sha256,320b29844892e3c59bc6fcb07e701b2b3230a37cb4a13176174e9e294ec6d43e,prevent,critical,2026-12-31,gradient.so Linux payload,tool:ChocoPoC;kind:payload
[ ChocoPoC behaviors ] - pip install of skytext / frint / slogsec / logcrypt.cryptography - gradient.so (Linux) or gradient.pyd (Windows) in a skytext pkg dir - Payload activates only if EXPLOIT_POC.py / exploit_poc.py present (filename-hash gate 0xF4835C9C) - dormant in bare sandboxes - Python process reading browser Login Data / cookies / logins.json - DNS to api.mapbox.com dead drop + DoH (alidns / cloudflare-dns) with domain fronting - Bulk exfil to 91.132.163.78:8001 (/assets/static/bundle.ext.min.de5b2bc9.js) - Persistence: _distutils_hack/override.py + distutils-precedence.pth - Env markers ZEBUWIAKGPHOQAP006 / JKHWQVEKRASDF12 - git clone of lincemorado97 / ogenich / bolubey CVE repos
Malware: ChocoPoC (Python RAT) - Sekoia / YesWeHack Prior: slogsec + logcrypt.cryptography (late-2025, same operator) Delivery: PyPI frint -> skytext -> gradient.so / gradient.pyd C2: api.mapbox.com dataset dead drop (DoH + domain fronting) Exfil: 91.132.163.78:8001 Fake PoC repos (7 known): github.com/lincemorado97/CVE-2025-64446_CVE-2025-58034 github.com/lincemorado97/CVE-2025-55182_CVE-2025-66478 github.com/lincemorado97/CVE-2025-14847 github.com/ogenich/CVE-2026-10520 github.com/ogenich/CVE-2026-48908 github.com/bolubey/CVE-2026-0257 github.com/bolubey/CVE-2026-50751
[ ] Run untrusted PoCs only in a disposable, network-isolated VM/container [ ] Inventory dev/CI hosts for skytext / frint / slogsec / logcrypt.cryptography [ ] Sweep site-packages for gradient.so / gradient.pyd + distutils-precedence.pth [ ] Block egress to 91.132.163.78; alert Python -> api.mapbox.com / DoH resolvers [ ] Internal PyPI proxy: allow-list / quarantine new/low-reputation packages [ ] Separate offensive-security tooling from production identities/SSO [ ] Rotate creds/cookies on any host that ran a suspect PoC [ ] Disable/monitor DoH from endpoints so covert resolution is visible
Detection Validation Gates
- Telemetry ready: Confirm ProcessRollup2, PeFileWritten/FileCreateInfo, DnsRequest and NetworkConnectIP4 are flowing from developer/research/pentest hosts — and that Falcon for Linux is deployed on Linux engineering hosts, since skytext was primarily downloaded on Linux.
- Benign baseline: Run Q6 and Q7 (Python egress) over 7–14 days on engineering hosts; record sanctioned Python apps that call Mapbox/Cloudflare, and exclude those before alerting.
- Positive test: In an isolated VM,
pip installa harmless package renamedskytextand confirm Q1 fires; drop a benign file namedgradient.pydinto site-packages and confirm Q2 fires; add a firewall log/route to91.132.163.78and confirm Q4 fires. - Promotion: Only promote Q1/Q2/Q4/Q5 to detect/block after zero benign hits across the baseline window. Q8 stays hunt-only (FP high).
Hardening — Tiered & Deployable
- Block egress to
91.132.163.78at firewall/proxy and load the ChocoPoC hashes (Q3) as Custom IOCs. (MITRE M1031 Network Intrusion Prevention.) - Analyst guidance + policy: untrusted PoC exploits are run only in a disposable, network-isolated VM or container — never on a workstation with production credentials or SSO cookies. (M1048 Application Isolation and Sandboxing.)
- Inventory + quarantine the malicious packages (
skytext,frint,slogsec,logcrypt.cryptography) across dev/CI hosts and your internal PyPI mirror. (M1047 Audit.)
- Internal package proxy with allow-listing: route pip through a controlled proxy (e.g. Artifactory/devpi) that quarantines new or low-reputation packages and blocks direct PyPI on managed hosts. (M1016 Vulnerability Scanning / M1051 Update Software.)
- Make DoH visible: disable or force-proxy DNS-over-HTTPS from endpoints so ChocoPoC's covert resolution (alidns/cloudflare-dns) is inspectable, and alert on non-browser processes performing DoH. (M1037 Filter Network Traffic.)
- Least-privilege browsers for researchers: use dedicated research browser profiles with no saved production credentials, so a browser-stealer yields nothing of value. (M1027 Password Policies / M1017 User Training.)
- Dedicated offensive-security enclave: segment pentest/research hosts into their own network/identity zone with monitored egress, so a compromised researcher box can't reach production or SSO. (M1030 Network Segmentation / M1026 Privileged Account Management.)
- SBOM + dependency provenance in CI so transitive pulls of packages like
frint→skytextare flagged before they land on a host. (M1016 Vulnerability Scanning.) - Phishing-resistant MFA + short-lived tokens everywhere, so stolen cookies/passwords from a stealer are low-value. (M1032 Multi-factor Authentication.)
Deployable Playbooks
P1 · Inventory hosts for the malicious ChocoPoC packages (cross-platform)
# Linux / macOS (bash) — run on dev/research hosts or via your fleet tool for p in skytext frint slogsec logcrypt.cryptography; do pip show "$p" 2>/dev/null | grep -E '^(Name|Version|Location):' && echo "FOUND: $p" done # Also sweep site-packages for the payload: find / -type f \( -name 'gradient.so' -o -name 'gradient.pyd' \) 2>/dev/null
P2 · Windows — find the gradient payload and malicious packages
# PowerShell
Get-ChildItem -Path "$env:LOCALAPPDATA\Programs\Python","C:\Python*","$env:APPDATA\Python" `
-Recurse -Include 'gradient.pyd','gradient.so' -ErrorAction SilentlyContinue |
Select-Object FullName, Length, LastWriteTime,
@{n='SHA256';e={(Get-FileHash $_.FullName -Algorithm SHA256).Hash}}
foreach ($p in 'skytext','frint','slogsec') { pip show $p 2>$null }
P3 · Block the exfil server (stopgap host firewall)
# Windows New-NetFirewallRule -DisplayName 'Block ChocoPoC exfil 91.132.163.78' ` -Direction Outbound -Action Block -RemoteAddress 91.132.163.78 # Linux (iptables) iptables -A OUTPUT -d 91.132.163.78 -j DROP # Prefer an enterprise egress/proxy block; host rules are a per-host stopgap.
P4 · Remove ChocoPoC packages + payload from a confirmed host
# After isolating the host and collecting evidence: pip uninstall -y skytext frint slogsec logcrypt.cryptography 2>/dev/null find / -type f \( -name 'gradient.so' -o -name 'gradient.pyd' \ -o -name 'distutils-precedence.pth' \) -print 2>/dev/null # Manually review/restore _distutils_hack/override.py from a clean setuptools; # then rotate every credential/cookie that was present on the host.
Containment Runbook
| Phase | Actions | Owner | Evidence |
|---|---|---|---|
| Isolate | Network-contain the host in Falcon; preserve memory, the cloned PoC repo, the Python env / site-packages, and shell history; do NOT reboot (lose volatile C2 state). | SOC | RTR capture, process tree, netconns |
| Eradicate | Uninstall skytext/frint/slogsec, delete gradient.so/.pyd and the distutils-precedence.pth hook, restore _distutils_hack from clean setuptools; block the exfil IP and ChocoPoC hashes (Q3/Q4). | IR | Removed-file hashes, pip diff |
| Credential recovery | Assume browser secrets are stolen: rotate all saved passwords, invalidate cookies/sessions, revoke tokens/SSH keys and cloud creds present on the host; force re-MFA. ChocoPoC harvests Chrome/Brave/Edge/Firefox stores wholesale. | IR + IdP | Credential-rotation log |
| Hunt-back | Sweep dev/research/CI fleet with Q1–Q8; pivot on the package/payload hashes and the exfil IP; check the internal PyPI mirror and git-server logs for other clones of the fake-PoC repos. | Threat Hunt | Fleet query results, mirror/git logs |
Detection Coverage Map
| Technique | Behavior | CQL | IOA | Coverage |
|---|---|---|---|---|
| T1195.001 | Malicious PyPI package install | Q1 | Yes | Good |
| T1204.002 | gradient.so/.pyd payload written | Q2 | Yes | Good |
| Atomic hashes | Known-bad package / payload files | Q3 | IOC | Good |
| T1041 | Exfil server connection | Q4 | Yes | Good |
| T1195.001 (early) | Fake-PoC repo clone | Q5 | Yes | Good |
| T1071.001 / T1090.004 | Mapbox dead-drop / DoH carriers | Q6 | — | Partial (scoped/FP-tuned) |
| T1041 (fronted) | Python beacon to public IP | Q7 | — | Partial (IR-scoped) |
| T1546.016 | distutils startup-hook persistence | Q8 | — | Partial (hunt-only, FP high) |
| T1555.003 | Browser credential theft | — | — | Gap — file-read of cred stores via native hunt (§7) |
| T1497 | Filename-gated sandbox evasion | — | — | Gap — in-process logic; not directly observable |
Detection is strongest at the supply-chain delivery (package install, payload write, repo clone), the atomic hashes, and the dedicated exfil IP — all low-FP. The Mapbox dead-drop is deliberately hidden behind DoH + domain fronting, so C2 detection is partial and best paired with making DoH visible (§12). Browser credential theft and the in-process sandbox gate are gaps addressed via native file-access hunts (§7) and disposable-VM policy.
Hunt Summary Ticket
TITLE: ChocoPoC trojanized-PoC Python RAT hunt
SEVERITY: Critical - live C2, targets researchers/pentesters
SCOPE: Developer / research / pentest / CI hosts (Linux + Windows);
internal PyPI mirror; egress/DoH
HYPOTHESIS: A researcher cloned a fake PoC repo; pip pulled frint->skytext;
gradient.so/.pyd stole browser creds and beaconed via a
Mapbox dead-drop, exfiltrating to 91.132.163.78:8001.
QUERIES: Q1 pkg install | Q2 gradient payload | Q3 hashes | Q4 exfil IP
Q5 fake-PoC repo | Q6 Mapbox/DoH | Q7 python beacon | Q8 .pth persist
DO FIRST: Q1, Q2, Q4, Q5 (lowest FP, structural) then Q3.
FINDINGS: <record hosts / packages / payload hashes / exfil hits here>
GAPS: Browser cred theft + sandbox gate (use §7 native hunts).
ACTIONS: Block 91.132.163.78 + hashes; quarantine skytext/frint/slogsec;
run PoCs only in disposable VMs; rotate creds on any host that
ran a suspect PoC; make DoH visible; isolate + IR.
OWNER: HuntPack
VERSION: v0.2 · 2026-07-07
Changelog
table() calls now carry an explicit row limit: the default is 200 and truncation is silent, so a capped result was indistinguishable from a complete one. Atomic-IOC sweeps (filters over 5+ hashes or C2 IPs) use limit=max so a wide infection is never silently under-scoped; behavioural hunts use limit=200, where exceeding the cap indicates the query needs tuning. Where present, event names that do not exist in the Falcon data model were corrected (e.g. ServiceInstalled is a Sysmon concept, not a Falcon event; ElfFileWritten is ELFFileWritten) — such queries could never return a row. No detection logic, fields, or IOCs changed.References
| Tier | Source | Used For | Access Date |
|---|---|---|---|
| 1 · Vendor | Sekoia — Don't eat the ChocoPoCs! (trojanised PoC exploits) | Primary analysis, dependency chain, payload, Mapbox dead-drop C2, DoH, exfil IP, persistence, full IOC set with SHA-256 | 2026-07-07 |
| 1 · Vendor | YesWeHack — Don't eat the ChocoPoCs! | Joint disclosure, fake-PoC repos, download stats, live-server warning | 2026-07-07 |
| 2 · Press | The Hacker News — New ChocoPoC RAT Targets Vulnerability Researchers via Fake PoC Exploit Repos | Corroboration of delivery, Mapbox dead-drop, credential theft, prior slogsec link | 2026-07-07 |
| Ref | MITRE ATT&CK — T1195.001 Supply Chain: Software Dependencies | Technique mapping, mitigations | 2026-07-07 |