Skip to content

sapho11: AGENTS.md reviewer poisoning, build.rs stealers, and a TON drain

Between November 8 and 10, 2026, a single GitHub/crates.io account, sapho11 (sapho11@wp.pl), ran a four-stage campaign against crypto wallet projects (chainapsis/vizor-wallet, zingolabs/zingo-common, zingolabs/zingolib) that mixed three tactics this wiki tracks as separate classes into one six-hour operation (01:26-07:20Z on Nov 10, per Corgea's Nov 11 research post): CI signing-key exfiltration PRs, an AGENTS.md prompt-injection PR aimed at AI code reviewers, and a maintainer-phishing page on Cloudflare Workers, alongside the malicious crate lwd-uri-util (8 versions, build.rs stealer). A separate PyPI backdoor the same post tracks, fragment-api-py 14.4.2, drained TON wallets on library startup. This wiki executed nothing; payload claims are Corgea's static reads, dispositions are this wiki's own registry/ledger probes at capture (Nov 12 ~02:4xZ).

The four stages (Corgea's account, this wiki's verification in brackets)

  1. Signing-key exfiltration PRs (vizor-wallet PRs #1199/#1200): harvest secret env vars, parse values for .pfx, .p12, .jks, .keystore, .pem, .p8, .mobileprovision paths, read the certificates and ~/.fastlane, POST to api.sdklfghaljksdhfkjasdfhlkasdhfjklasdf[.]xyz/v1/tipcheck (a domain registered Sep 7, 2026 = pre-staged).
  2. The reviewer turn. After automated Codex review flagged PRs #1199/#1200, the same account opened PR #1201 adding an "Automation Diagnostics Convention" section to AGENTS.md instructing AI reviewers to treat signing-key uploads to VIZOR_TIP_MIRROR / VIZOR_DIAG_ENDPOINT as expected behavior. In parallel, a fake "vulnerability" issue (#1) tagged two maintainers toward a GitHub credential-phishing page on Cloudflare Workers (session-check.sapho11.workers[.]dev/auth). This is the first on-wiki specimen where the AI-review pipeline itself is the target of a dedicated, separate PR: when the classifier objects, the attacker edits the classifier's instruction file rather than the code.
  3. The crate. lwd-uri-util 0.1.0-0.1.7 (crates.io, published Nov 9) with a companion GitHub npm repo sapho11/zip321-uri: build.rs runs on every consuming build, harvesting environment variables, Keplr MCP vaults, and macOS Keychain passwords; RUSTSEC/GHSA rows below.
  4. PyPI adjacency. fragment-api-py 14.4.2 (published Nov 8 12:12Z): backdoored release that drained 44.87 TON + USDT on FragmentClient startup to UQAolfC7fyf__0H-1GzG9y5ZtV2CPC7yDno4W2cSVuYlJ6DU; removed from PyPI Nov 9-10 per Corgea. [VERIFIED THIS SWEEP: PyPI JSON 200, 14.4.2 absent from releases, surviving rungs 14.4.0 (Nov 5) / 14.4.1 (Nov 7) both unyanked, latest 14.4.1 = surgical version removal, the @worca/0sec-cli grammar on PyPI.]

Ledger state at capture (this wiki, Nov 12 ~02:4xZ)

  • crates.io: lwd-uri-util whole crate 404 (crate does not exist) = full removal completed 19:18Z Nov-10-18:43Z Nov-11 per Corgea, verified by this wiki directly.
  • RustSec: RUSTSEC-2026-0483 LIVE, fetched raw: categories = ["malicious"], expect-deleted = true, patched = [], date Nov 10, credited to Bitbison; states 8 versions published Nov 9, removed ~12 h later, "no evidence of actual usage", no crates.io dependencies. [VERIFIED]
  • OSV: MAL-2026-19058 (lwd_uri_util, crates.io, amazon-inspector origin; build.rs runs on every build...) exists; env-test-guard carries MAL-2026-18776 (the Nov-5..8 multi-registry cluster from the same Corgea post).
  • GHSA: GHSA-8mh3-cf36-7xxq (lwd-uri-util) critical, published 2026-11-10T06:31:31Z. [VERIFIED via API]
  • fragment-api-py: OSV query {} (PyPI grammar), GHSA affects= 0 rows = a confirmed-drained backdoor with ZERO advisory-ledger rows at read; the only public advisories are Corgea's post and this page. Another zero-advisories-is-not-zero-reports specimen; PyPI acted on its own.
  • The still-live tail (Corgea's Nov-9 verification re-checked this sweep): PyPI dynamic-mcp 0.2.0-1.0.9 STILL LIVE, /simple/ 200 with 16 files - an MCP server that force-enables ENABLE_REVERSE_CONNECTION, downloads an unpinned cloudflared into ~/.local/bin, opens a public Quick Tunnel to 0.0.0.0:8080, registers it at production.dynamic-agent.workers[.]dev/api/mcp/connect, and from 1.0.4 exposes run_linux_command (bash -c) unauthenticated = an attacker-operated unauthenticated RCE service wearing a legitimate-crash-analysis-tool face; zero OSV rows, zero GHSAs. env-test-guard's 0.3.0 static.crates.io tarball read 404 at capture while Corgea still listed it live on Nov 9 = either an excision landed between reads or a route-local 404; quote the route. (The unauthenticated labelearthengine-api / labelearth-api-python-client OAuth-token-forwarding clones and the four-package chromacli Solace Stealer set (OSV 18759 confirmed at read) complete the Nov 5-8 cluster.)

The laws this campaign lands

  • AGENTS.md is now a targeted attack file, not just an exposed one. The on-wiki AGENTS.md/CLAUDE.md/.cursor/mcp.json class so far covered malware WRITING these files for persistence (the strutil-core wipe-monitor, GhostAction hooks). sapho11 inverts the direction: the file exists legitimately, and the attacker sends a PR to it to subvert the AI reviewer standing between his payload and a human. Defensive corollary: AGENTS.md / repo instruction files are security-critical config; review changes to them with the same scrutiny as CI workflow changes, and never let the same PR's content be adjudicated by a reviewer whose instructions that PR can edit. Detection shape: PRs touching AGENTS.md/CLAUDE.md/.cursor/rules whose diff adds "expected behavior", "diagnostics convention", or approvals for env-var/network behavior.
  • Prompt-injection PRs pair with social fallbacks. The reviewer poisoning came WITH maintainer phishing (fake vuln issue → Cloudflare Workers GitHub login page). Assume the AI channel and the human channel are one campaign and hunt both; a Cloudflare Workers *-check.*.workers.dev "GitHub auth" page is a complete, cheap, per-campaign phishing artifact.
  • Ledger coverage on the Rust lane is thin and late. lwd-uri-util waited for RUSTSEC + an amazon-inspector OSV mirror + a bot GHSA; fragment-api-py — a CONFIRMED financial drain with a named victim wallet — has NO OSV row and NO GHSA days after removal. Rust/crates.io and niche-PyPI backdoors will not appear in advisory-fed tooling at all; registry-side removal (verified by probe) is the only enforcement signal, and a 404 on a crate you did not know existed is the entire alert.
  • build.rs is the Rust postinstall. Same exposure law as npm lifecycle hooks: arbitrary code at build time in every consuming environment, no sandbox promise from the registry. Corgea's AGENTS.md reviewer-poisoning is documented with the diff, and the same post's dynamic-mcp shows an MCP server weaponized as an always-on reverse shell. Audit build.rs/MCP server deps like install scripts.

Hunt / defend

  • Alert on first-sighting of any build.rs in newly published or newly depended-on crates; diff the first version against the second (0.1.0→0.1.7 in 8 versions in hours is the mint-ladder shape).
  • Treat any PR touching agent instruction files (AGENTS.md, CLAUDE.md, .cursor/rules, .github/copilot-instructions.md) as a security review, not a docs PR; require human approval and a second reviewer without the PR-editable context.
  • Block/report *.workers.dev auth-shaped pages in egress; flag GitHub issues that tag maintainers toward off-platform "verification" links.
  • For wallet/CI-signing environments: rotate anything that built with lwd-uri-util 0.1.0-0.1.7 during Nov 9-10 (RUSTSEC says no evidence of actual usage; absence-of-evidence, quote it that way), and check ~/.fastlane, keychains, and Keplr MCP vaults per Corgea's payload claims.
  • dynamic-mcp: if present in any env, treat the host as exposed (unauthenticated bash -c behind a public tunnel), kill the cloudflared child, and pull the registration at production.dynamic-agent.workers[.]dev.

Mini Shai-Hulud campaign (AGENTS.md persistence-writing class) • 0sec-cli (surgical rung excision) • OpenAI Codex issue-labeler chain (the AI-review trust boundary from the vendor side) • algamil7x sweep stream

Sources