Contents
Campaign Identifier: Gotenberg-RCE-Cryptomining-107.175.69.137
Last Updated: September 16, 2026
Threat Level: HIGH
1. Executive Summary
One disclosed CVE turned into a 198-host compromise in under an hour, and the obvious network signature for this exact injection never fires on real traffic. That is the whole report in two sentences, a near-zero-effort exploit chain and a detection trap that catches anyone who builds a rule from the vendor advisory alone.
The vulnerability is CVE-2026-42589, an unauthenticated remote code execution flaw in
Gotenberg, an open-source document-conversion service. The payload does not go in
a metadata value, where any defender reading a request would expect an injection. It goes in
the metadata key, splitting ExifTool’s argument parser and reaching a Perl eval through the
-if flag. This exact payload, down to its placeholder value, is copied verbatim from the
vendor’s own security advisory (HIGH-to-DEFINITE). The barrier to entry for this attack is zero,
and the working exploit sits in a public document anyone can read, so expect unrelated copycats
to reach for the same primitive, not only this operator. What is not copied is the campaign built
around it: 206 candidates probed, 198 confirmed exploitable, and a cryptominer running on
somewhere between 148 and 151 of them, the whole thing inside a 54-minute window on 2026-08-31.
Then the finding that matters most for a defender: the obvious detection signature for this
injection never fires on a real request. The metadata field travels as JSON, so the injected
newline reaches the wire as the two-byte escape sequence backslash-then-n, and Gotenberg converts
it back to a real newline only after its own server has parsed it, after any network sensor has
already seen and passed the packet. Anyone who writes a rule for this CVE straight from the
advisory will build one that parses cleanly, loads without error, and never once matches. Section 7
gives the rule that actually works, and warns why the escaped bytes must never be “corrected” back
to a literal newline.
This campaign is also not the operator’s main business. The exploit chain and the cryptominer are the smallest, most fully automated line inside a larger criminal enterprise that also runs mass web-application exploitation against named commercial platforms, an Android device-farm operation, and a Telegram account farm selling verified accounts and OTP-bypass inventory as a commercial product. I am not naming any of the targeted platforms in this report; what matters for a defender is the shape of the operation, not who else got hit.
The threat in clear terms
If your organization ran an internet-facing Gotenberg instance inside the affected version band during the campaign window, the operator gained:
- Unauthenticated remote code execution through the metadata-write endpoint, with no authentication and no user interaction required (DEFINITE, the exploit chain reproduces cleanly and is measured against captured traffic).
- A cryptominer install, in one of two modes: an ephemeral drop that a reboot clears, or a persistent root-owned systemd service that does not (DEFINITE, both variants recovered from the operator’s own staged payload).
- A working blind-RCE confirmation channel the operator can reuse against any other exploitable service, since the escalation ladder, the timing-based confirmation, and the out-of-band callback tagging are all generic infrastructure, not Gotenberg-specific code (HIGH, read directly from the operator’s own scripts).
- A demonstrated willingness to take resources already claimed by a different intruder: the deploy script kills a named list of rival miners, including one already running on at least one victim, before installing its own (DEFINITE, observed in real deploy output from real victims).
Classification
This is HIGH-severity, unauthenticated mass exploitation of a document-conversion service, delivering a commodity cryptominer, XMRig 6.26.0, unmodified. It is not a targeted intrusion against any single organization. If Gotenberg was internet-facing and unpatched in your environment during the campaign window, code execution as the Gotenberg service account is a DEFINITE conclusion rather than a risk to model.
Risk Rating: HIGH
| Risk Factor | Score | Justification |
|---|---|---|
| Data Exfiltration | 5/10 | The confirmed campaign, mining, exfiltrates nothing. The wider apparatus includes a live, unscoped credential-harvesting surface and data-theft tooling naming commercial platforms, but zero of those show content-level access; the risk is real but unconfirmed. |
| System Compromise | 9/10 | Unauthenticated remote code execution, confirmed on 198 of 205 probed hosts (96.6%), with a persistent, root-privileged install mode available. |
| Persistence Difficulty | 7/10 | Two install modes exist. The one the observed campaign actually deploys clears on reboot; a second, shipped in the same package, installs a root systemd unit with Restart=always that does not. "Reboot and it is gone" is the wrong advice for an unknown share of installs. |
| Evasion Capability | 5/10 | The binary itself carries no packing or custom obfuscation. The operational blind spot is structural instead: the natural detection signature for this exploit never matches real traffic, which functions as evasion without the operator doing anything to earn it. |
| Lateral Movement | 3/10 | Nothing in the recovered tooling moves laterally from a compromised Gotenberg host. This is scan-confirm-deploy against internet-facing instances, not an intrusion that pivots once inside a network. |
| Detection Difficulty | 8/10 | MEASURED, not inferred: a signature written by reading the operator's own source code fails against real captured traffic, because the injection travels JSON-escaped on the wire and is only decoded after any sensor has already seen it. |
| OVERALL RISK | 7.3/10 | HIGH |
This assessment rests on the operator’s own recovered scripts and configuration files, a reproduction of the exploit chain verified against captured traffic, and passive infrastructure enrichment. Confidence levels are stated throughout to separate what was directly observed from what I am inferring.
2. What This Campaign Is
Classification and Identification
| Attribute | Value | Confidence |
|---|---|---|
| Vulnerability | CVE-2026-42589, Gotenberg ExifTool metadata-key injection, unauthenticated RCE (CVSS 9.8) | DEFINITE |
| Payload family | XMRig 6.26.0, stock and unmodified | DEFINITE (static analysis) |
| Campaign type | Automated mass exploitation against internet-facing instances of one vulnerable software version band | DEFINITE |
| Operator sophistication | LOW on vulnerability research; MODERATE-HIGH on operational engineering, never averaged | see Section 9 |
| Status | The campaign-specific listeners are offline; the underlying host remains live and administered | DEFINITE |
A second CVE identifier exists for a related bug, and this campaign is not that one.
CVE-2026-40281 also targets Gotenberg’s ExifTool integration, but it injects into the metadata
value, reaching ExifTool pseudo-tags like -FileName and -SymLink for arbitrary file
rename, overwrite, and symlink creation (CVSS 10.0, fixed in 8.31.0). I fetched both advisories
directly (GitHub Security Advisory GHSA-rqgh-gxv4-6657 for CVE-2026-42589, and GHSA-q7r4-hc83-hf2q
for CVE-2026-40281) rather than trust a secondary summary, because the two bugs are frequently discussed
together and conflating them would misdirect a defender’s patching priority. This campaign’s
injection is in the metadata key, reaches ExifTool’s -if flag, and evaluates arbitrary Perl.
Every detection in this report and its companion detection file cites CVE-2026-42589, and that
citation is correct.
Why this is HIGH-severity, unauthenticated mass exploitation
- No authentication of any kind is required. The vulnerable endpoint,
POST /forms/pdfengines/metadata/write, accepts the injection from any client that can reach it. - The confirmation rate on probed, live targets was 96.6% (198 of 205), which is not a scan result. It is a compromise count, and I return to why that number is credible in Section 3.
- Full code execution as the Gotenberg service account follows, not a denial-of-service or an information leak. Everything downstream, the miner, the recon, the rival displacement, follows from that.
3. Campaign Scale, Timing, and Escalation Discipline
The numbers, and why I trust them
| Figure | Value | Basis |
|---|---|---|
| Targets listed | 206 | Row count of the operator's own target list, byte-verified |
| Targets probed alive | 205 | 1 target was dead at probe time |
| RCE confirmed | 198 (96.6% of probed) | Timing side-channel verdict, row count matched against the out-of-band sweep results |
| Distinct confirmed IPs | 196 | Two hosts each contributed two vulnerable ports |
| Called back, own address | 167 OBSERVED | Direct IP match in the callback log |
| Called back, shared egress | 17 INFERRED | Tag matched, but from a gateway or NAT address rather than the host's own |
| Never called back | 14 | Absent under every callback channel the operator built, including three retry transports |
| Miner install verified | 148 to 151 | See the derivation below; 28 hosts are permanently unattributable |
The callback total should never be written as 184 alone: the honest sentence names both populations, 167 confirmed from the host’s own address and a further 17 inferred through shared provider egress, because the two are different grades of evidence and collapsing them into one number overstates the weaker of the two.
These are not estimates: every count above is a row count from a surviving artifact whose parse I verified byte-for-byte, and two of these numbers moved during this investigation.
Two figures that changed during the investigation, and why the current ones are trustworthy
The miner-install figure was originally recorded as 158. A blind re-derivation from the raw callback log, run without being shown the existing table, credited at most 147 to 149 installs, a gap of nine to eighteen hosts. That gap does not resolve to a disagreement; it resolves to a genuine limitation in the surviving evidence. The main deploy wave’s own target-list ordering was never preserved on disk, so the index that ties one deploy callback to one specific host is unrecoverable for a meaningful fraction of the campaign. Every reconstructed ordering I tested, and every one a prior pass tested, rejected at over 90% mismatch against the observed timing. Timing correlation shows the deploy wave was dispatched roughly in the recorded order (r = 0.985), but the log’s one-second timestamp resolution cannot disambiguate individual hosts within that order.
There is also a direct, byte-level explanation for part of the gap: the operator’s own callback listener writes two unlocked fields per request from a threaded server handling dozens of simultaneous connections, and I found proof that this raced under load. One recorded response body ends mid-token and runs directly into a different connection’s success marker with no line break between them. The operator built a pipeline good enough to confirm 198 exploits in 54 minutes and instrumented it with a listener that loses its own data under the exact load it was designed to create.
Given that, 148 is the defensible floor, 151 is the ceiling once two genuinely ambiguous multi-port hosts are credited, and 28 hosts are permanently unattributable: 14 with no callback under any channel, and 14 more where code execution is certain but no deploy record survives to credit an install either way. A further group of 15 hosts shows a statistically suggestive pattern (a 93% local tag-match rate against a 34.6% baseline for the shared-egress mechanism) that would push the range toward 163 to 166 if credited. I am deliberately not crediting it: suggestive is not verified, and this case has already had one number published on weaker grounds than that.
The callback total moved for a related reason: a full recount against the raw log settled on 184, split as stated above.
The window: 54 minutes, and the deliberate ordering around it
The campaign itself ran from 2026-08-31 02:50:03 to 03:44:22 UTC, read directly from the first and last genuine lines of the operator’s own callback log. I am treating the timezone as the writing host’s own system clock, since nothing in the corpus independently confirms it, but that caveat does not touch the duration, which is internally consistent across every timestamped artifact I checked.
The operator did not fire this blind. Before any exploit ran, the payload itself was staged five days in advance: the miner binaries carry a build timestamp of 2026-08-26. Then, eleven minutes into the campaign, both deployed miner configuration files were regenerated live, before the mass deployment wave actually ran. I read that as a premeditated operation with a live tuning step in the middle of execution, not an improvised one; the timestamps themselves are directly observed, the reading of intent behind them is mine.
The stages ran in a fixed, escalating order, and each one gates the next:
- Liveness and fingerprinting against all 206 candidates, through a local SOCKS5 proxy.
- Timing-based RCE confirmation, the mechanism Section 7 covers in full.
- Out-of-band callback, injecting a command that curls back to the operator’s listener with a tag built from the victim’s own hostname and numeric user ID, which is what makes 198 simultaneous callbacks individually attributable to specific hosts.
- A three-transport retry (wget, then curl, then
nc) for any host that did not call back on the first attempt, with each transport tagging its own callback so the log itself records which binary existed on that host. - A pre-deployment recon pass, checking for rival miner processes, free space in
/tmp, and, critically, actually touching/tmp/.wtto confirm the directory is writable before spending a payload there. - Deployment, only after every prior gate passed.
The writability probe is the detail that tells me this operator does not waste effort. I found
direct proof of why it matters: on the one host where the operator worked interactively rather
than through the automated pipeline, the same recon logic hit touch: cannot touch
'/etc/.wtest': Permission denied and recorded the failure rather than attempting a doomed write.
A payload deployment attempt against a read-only target produces nothing but a wasted deploy slot
and a callback that never resolves; this operator pays a cheap recon cost specifically to avoid
that outcome at scale.
This ordering, in my reading, is the single clearest piece of evidence for real operational discipline in this campaign, and it stands in genuine tension with the operator’s own front door: a reverse-shell collector on port 13337 sat unauthenticated and reachable from the internet for the entire campaign, and the log on it is full of unrelated scanner noise, meaning anyone who happened to connect during the window could have watched the operator’s own interactive sessions. Careful about targeting, careless about their own exposure. Section 9 carries that same split forward as two separate capability axes rather than averaging it into one adjective.
205 targets were fingerprinted, timed, and had a version band selected before this 54-minute window even started, and that reconnaissance did not require a separate host. The operator’s own box shows an unbroken tenancy record stretching back roughly eleven weeks before the campaign, easily enough time to have run that reconnaissance quietly on this same host, with nothing surviving to prove it either way. No separate recon host is evidenced.
What the version band does and does not tell me
The target list spans Gotenberg 8.17.0 through 8.30.1. The lower bound is not a deliberate choice: exactly one target sits at 8.17.0, with the population rising smoothly from there, and a single host at the floor is the signature of “this is simply the oldest version still running in the population that was scanned,” not evidence the operator tested compatibility down to that exact release. There is nothing here to read as operator skill.
The upper bound is more interesting, and I am leaving it as an open question rather than forcing an answer the evidence does not support. CVE-2026-40281’s own advisory states that the key-sanitization fix which closes this campaign’s vulnerability landed in version 8.30.1, precisely the top of the operator’s target band, and the largest single-version bucket in it. Two readings are both consistent with what survives: either the operator picked a band that stops exactly where the public fix does, which would make the ceiling derivable from release notes rather than private knowledge, or the fix did not fully close key injection and those top-band targets were independently exploitable regardless of the operator’s intent. The outcome data leans against neither cleanly: a 96.6% confirmation rate is hard to square with 30 genuinely patched targets failing to confirm, but the version strings themselves may be unreliable banner reads rather than ground truth, so I am not treating that lean as an answer. The join that would resolve this, target version against per-host verdict, cannot be run from what survives in this corpus, so I am reporting it as NOT CHECKED rather than guessing in either direction.
4. The Payload: Cryptomining, Install Modes, and Rival Displacement
The miner itself is not the interesting part, and that is itself a finding
The dropped payload is stock, unmodified XMRig 6.26.0, an 8.35 MB Alpine-compiled Linux ELF
binary. The pool address and payout wallet are not compiled in; they live entirely in the dropped
configuration file, which is why the binary is byte-identical across both drop locations while the
two configs differ. Both configs pay the same Monero wallet through the same public MoneroOcean
pool, and both carry the identical rig identifier worker-01 on every single deployment, which is
a real operational weakness on the operator’s own side: their pool dashboard cannot distinguish one
infected host from another.
The binary hash attributes nothing, which matters for how this report’s indicators should be used. The exact sha256 has 133 submissions from 107 independent, unrelated sources on VirusTotal, re-verified live at publication. XMRig is free, open-source software, and running the stock binary unmodified commits the operator to no supplier relationship and provides no distinguishing fingerprint. The Monero wallet is the one genuinely operator-specific pivot in this entire case, and as of this investigation it is a controlled zero everywhere I can search it: no VirusTotal object and no Hunt.io indexed post references it. It is clean, not because nobody has looked, but because nobody else appears to be using it.
XMRig is free software used by the large majority of Linux cryptojacking activity industry-wide; running it unmodified is a default choice, not evidence of a supplier relationship for the payload itself. This operator takes a widely available commodity payload and invests their engineering effort in the delivery pipeline around it, which the escalation ladder in Section 3 already demonstrates in detail, and which Section 6 shows running through shared, not exclusive, infrastructure.
Two install modes, and the one that gets missed matters more
Both modes ship in the same recovered package, and the contrast between them is the single most actionable fact in this report: “reboot and it’s gone” is true for one and false for the other, so every affected host needs both checked, not just whichever one turns up first. Open either card below for the confirmed-install status, the exact systemd unit, and the cleanup steps that mode specifically needs.
Ephemeral drop
confirmed this campaign
/tmp/polkitd/
Runs aswhoever got code execution
Survives rebootNo
Configdonate-level: 1
This is the only mode the deployment script actually installs. Reconstructing it from its own strings, then checking every recoverable triage report for a systemd-enabling command, returns zero hits against a working control. Kill any process running /tmp/polkitd/polkitd and remove /tmp/polkitd/; a reboot alone already clears this mode.
148 to 151 of 198 confirmed hosts
Persistent systemd install
shipped, reach not measured
/usr/bin/polkitd.d/
Runs asroot
Survives rebootYes
Configdonate-level: 0
The systemd unit ships in the same recovered package as the ephemeral drop. Nothing in the observed campaign's own telemetry installs it, and nothing rules it out on a per-host basis either, so the honest range is 148 to 151 confirmed ephemeral installs with this mode's reach unmeasured, not a majority-and-minority split between the two. If present, disable and remove the systemd-polkitd (or systemd-vconsole-setup) unit, delete /usr/bin/polkitd.d/, and reload the systemd daemon; a reboot alone leaves this running as root.
systemd-polkitd.service, Restart=always, RestartSec=10s, User=rootOne recovered host also carried a watchdog script that should be removed alongside either mode.
The recovered package contains only Linux ELF binaries. That follows directly from the target rather than a limit on the tool: Gotenberg ships as a containerized Linux service, every recovered victim callback shows a Linux container environment, and XMRig itself ships Windows builds, so the Linux-only payload is a choice about who was being hit, not evidence of a second, unseen Windows operation running in parallel.
The operator displaces other criminals, deliberately
The deployment script’s first action on any target is to kill a named list of competing miner processes before installing its own. This is not merely a capability sitting in the script: two recorded deploy outputs show the kill command successfully terminating a rival’s process, so at least one of these victims was already compromised by a different actor and this operator took the machine over rather than sharing it.
The detail worth carrying forward is that the kill list includes polkitd, the exact process name
this operator’s own miner masquerades as. They know their chosen mask collides with a common
rival’s naming convention and kill it anyway without hesitation, which tells me this is not their
first encounter with that collision. It is also a direct warning for anyone responding to this
campaign: the mere presence of a process named polkitd is not by itself proof of this
operator’s compromise, since it is both this operator’s mask and a name their own tooling expects
to find and remove from someone else.
Separately, on the one host where the operator worked interactively, I found two other criminal operations present simultaneously: a distinct miner paying three different wallets to a different pool, and an unrelated multi-architecture dropper fetching payloads with no connection to this operator’s own infrastructure. At least three separate operations were active on that single machine at once, which is a reasonable expectation for any internet-facing host that stayed vulnerable long enough; a responder on such a host should expect more than one actor’s activity.
5. The Wider Enterprise: Data Theft, Account Farms, and AI Touchpoints
This campaign is the smallest thing this operator does
Reading the operator’s full working directory rather than just the Gotenberg-specific files, the cryptomining campaign this report is named for occupies roughly 3% of the recovered file inventory. The larger share belongs to a shared mass web-application exploitation engine targeting other named commercial platforms, and a separately branded, commercially structured account-management and OTP-resale product running on Telegram, a market structure that closely matches USENIX Security 2025’s peer-reviewed DarkGram study of Telegram-based account and OTP markets. I am naming no platform, no domain, and no organization in this report. What follows is shape and count, because that is what the evidence actually supports publishing, and because a count is more honest than a name I cannot back with content-level proof.
| Line of business | Confidence | What the evidence actually shows |
|---|---|---|
| Mass web-app exploitation (the shared engine) | HIGH that the tooling exists; INFERRED that it serves one purpose throughout | The largest single share of the recovered inventory, feeding both the data-theft and cryptomining lines |
| Cryptomining on compromised hosts | DEFINITE | The subject of this report, and the smallest, most fully automated line |
| Data-theft tooling against named commercial platforms | HIGH that platforms were targeted; INFERRED on what any dump actually contains | The dump content died with the host before anyone read it |
| Telegram account-management and OTP-resale product | HIGH on existence and commercial shape; MODERATE on actual scale | 136 extracted routes describe a real inventory-management system; no pricing, buyer, or completed-sale record survives |
| Channel-push advertising and monetization | MODERATE-HIGH on capability; INFERRED on whether it was ever monetized | Real routes for paid channel promotion and username brokering exist in the same panel |
| Android device farms | Existence OBSERVED; function UNRESOLVED | Separate panel software with no API surface connecting it to the account-management product |
One operator, or one enterprise, is the better-supported reading, and I hold that at MODERATE, not higher. Nothing in this investigation reaches even MODERATE confidence on a named threat actor; this is an unknown operator. Section 9 carries the full reasoning behind the one-operator read; the short version is that the strongest evidence is structural rather than stylistic: the cryptomining campaign’s own working files sit physically inside the same directory tree as the Telegram service and card-shop toolchains, which ties them to one point of control regardless of who typed which script.
The account farm is a commercial product, not a script someone left running
The clearest evidence of real investment in this case sits outside the cryptomining campaign entirely. The operator’s own account-management panel software describes a managed inventory business, not a dump-and-sell operation, a structure consistent with Group-IB’s Anatomy of a Fraud Operation and Europol’s Internet Organised Crime Threat Assessment 2023, and I hold that at HIGH confidence from the route names alone; whether the underlying accounts are farmed from compromised sources or legitimately provisioned is a separate, unresolved question. A separate Android device-farm control panel exists in the same directory, the shape Group-IB’s Cloud Phones: The Invisible Threat documents in this same criminal niche: its existence is OBSERVED, its function UNRESOLVED, and I found no connection between it and the account-management product.
The 136 extracted routes behind the commercial-product reading
I extracted 136 distinct routes from the operator’s own account-management panel software, and they describe a managed inventory business rather than a dump-and-sell operation: bulk session-file import, batch identity manipulation across accounts (avatar, profile, recovery email, forced device removal), a commercial OTP inventory system with sold and out-of-stock states and bulk export, and a separate monetization suite for paid channel promotion and username brokering, mechanics documented in Telegram’s own official platform materials on channel monetization and collectible usernames. A dump-and-sell model does not need lifecycle management this thorough; retaining and operating inventory does, and compromised or resold social-platform accounts of this kind are commonly funneled into malvertising and further phishing, per Push Security and Check Point Research.
I am holding two claims about this product apart on purpose, because collapsing them would overstate what I actually know. The commercial-inventory shape of the API is directly observed and I hold it at HIGH: the route names themselves describe exactly this kind of system. Whether the underlying accounts are farmed from compromised sources or legitimately provisioned through bulk SIM registration is a separate question that the intake tooling alone cannot answer, because legal SMS-verification services run structurally identical sold and out-of-stock inventory models over properly registered numbers. I found nothing in the recovered evidence that resolves this either way.
A separate Android device-farm control panel exists in the same operator’s directory, with its own UI referencing device counts, online status, and accessibility permissions on managed devices. I looked for a direct connection between this and the Telegram account farm, since the two would naturally complement each other, and found none: no shared API endpoint, no shared data format, nothing beyond both existing on the same box. I am reporting the device farm as a real, separate capability whose actual purpose I cannot currently determine.
The named platforms were targeted, not compromised
Reading the operator’s own scraped material against a corrected standard matters here. Seven externally identifiable commercial platforms turn up by name in the operator’s scraped frontend bundles, and I want to be precise about what that evidence actually proves: zero of the seven show any content-level access. The only artifact recovered per platform is a copy of that platform’s own publicly served frontend code, which by definition anyone can fetch without authenticating. Three of the seven additionally show sustained, iterated attack tooling built specifically against them, and one of those three includes several megabytes of files named as extraction outputs. None of that tooling’s actual output survives; the files were catalogued by a crawler and their content is gone.
The honest sentence for any of the seven is that a public frontend was found in the operator’s own directory, and for three of them, that attack tooling referencing the platform also existed. Neither is proof that any data was actually taken, and I am not implying otherwise. Several of what I initially counted as separate victim platforms turned out to be the same site, scraped multiple times under different internal labels, based on matching file sizes and build signatures. I did not independently verify that with a direct byte comparison, so I am holding it as an inference rather than a confirmed fact; the true count of distinct named platforms is smaller than the raw file count first suggested.
The AI angle, and why it points the opposite way from where the case started
An AI agent framework driving this exploitation is refuted, not merely unconfirmed, on three independent checks. The file the claim rested on was never fetched or read at all; the specific popularity figure cited for the framework in question is off by roughly a factor of ten against the framework’s own real numbers; and a full keyword sweep of every script that actually produced the 198 confirmed exploits found zero model API calls, zero API keys, and zero prompt or chat artifacts of any kind. Two independent checks, one on code structure and one testing the framework claim directly against a primary source, reached the same conclusion from different evidence: this is ordinary, competently engineered scripted exploitation of one vulnerability, not anything agent-orchestrated.
What I found instead is more interesting, and it points the opposite direction. The operator does not appear to use AI tooling to attack; the operator attacks AI infrastructure. One victim’s own callback log shows a single probe attempt against two LLM gateway endpoints discovered on that host, and the attempt failed outright, with every injected command line recording no execution. Separately, one of the scraped storefronts in the operator’s own directory sells shared logins for a commercial AI-subscription service, which is a data-theft target, not the operator’s own infrastructure. A third, much thinner data point exists inside the operator’s own Telegram panel: two API wrapper functions for saving and testing an AI provider connection, which tells me a bring-your-own-key AI integration exists in that panel’s settings, and nothing more. None of these three threads support any claim that this operator uses AI to build or run their own attacks, and I am keeping all three separate rather than merging them into one storyline that overstates any of them individually.
6. Infrastructure
The operator’s host, and the ColoCrossing/RackNerd distinction
The operator’s own infrastructure sits at 107.175.69.137, in AS36352, registered today as
AS-COLOCROSSING, under HostPapa. I re-verified the ASN and current owner live against VirusTotal at
publication and it matches exactly what passive registry data showed during the investigation.
The IP’s retail brand is commonly associated with RackNerd, a separate company that leases capacity inside ColoCrossing’s data centers and does not hold its own ASN, which is why a RackNerd-sold instance shows ColoCrossing as the network owner in registry data. Whether this specific instance is RackNerd-sold rather than sold by another reseller on the same ColoCrossing infrastructure is not independently confirmed.
What is actually running on the host, port by port
I tested this from two independent network paths that agree exactly on every result, including matching HTTP response codes, which rules out path-specific filtering as an explanation for anything below.
| Port | Service | State | Detail |
|---|---|---|---|
| 22 | SSH | Open | Banner dates the box to an Ubuntu 24.04 build |
| 80 | HTTP | Closed | Refused on both paths |
| 443 | HTTPS | Open | Fails without the correct hostname in the TLS handshake; succeeds cleanly with it |
| 2096 | cPanel webmail | Open | Actively administered: carries its own auto-renewing, short-lived certificate |
| 8080 | Former open directory | Gone | Consistent with the last successful crawl of its content |
| 8088 / 8443 | Unlabeled | Open | Both fronted by the same reverse proxy as the rest of the live host |
| 12577 | Unlabeled | Gone now | Carried a spoofed-identity certificate for exactly five days starting the campaign date; see below |
The server fronting every live port is Caddy, doing hostname-based routing with a working default page. Every path I tried on the live ports returned an empty body, and nothing malicious, victim-identifying, or otherwise content-bearing was recoverable from any of them. Whether real content sits behind an unguessable path is a genuine open question I cannot close from here, not a resolved negative.
Port 12577 is worth naming even though I cannot publish it as a distinguishing indicator. For exactly the five days spanning the campaign, this port carried a certificate whose subject claimed to be a well-known CDN provider but was issued by an unrelated certificate authority, a combination that never happens legitimately and is consistent with a self-signed or spoofed-identity proxy tool. Its cryptographic fingerprint, though, shares a component with a fingerprint this publication has already separately identified as ubiquitous across millions of unrelated devices, so I cannot use it to identify what tool actually ran there. I am recording it only as an unattributed lead: some proxy or tunneling tool ran on this box for exactly the campaign’s active window and was torn down afterward.
The host was declared dark, and that was wrong in an instructive way
An earlier check concluded the operator’s host had gone fully dark, all six ports it tested closed or filtered. That conclusion does not hold up: the host is live and administered, and the corrected statement, the one that should be used anywhere this campaign’s collection status comes up, is that the operator’s exposed directories and campaign-specific listeners went offline sometime between the end of the campaign and roughly a week later. The underlying machine did not, and it remains live and actively administered as of this report.
Why a real control produced a wrong dark-check, and the one-line rule it teaches
The evidence that the host is live: captured application-layer banners, an SSH version string, and an HTTP error response are all genuine service replies, not bare timestamps claiming a port was “last seen” at some point, and a scanner cannot fabricate a banner. The earlier check’s own control was real and still produced the wrong conclusion, because it tested reachability to a hyperscale, universally-peered target rather than to a target that shares the operator’s own hosting class: a cheap, single-tenant VPS on a provider with every incentive to treat automated crawler traffic differently from ordinary internet traffic. A control has to vary the same property the measurement actually depends on: proving a path reaches the easiest possible target on the internet proves nothing about whether it reaches the hardest one.
Tenancy history, and what it does and does not tell me
Passive host-key history shows four distinct tenancy periods on this IP going back to late 2024, with the current tenant holding it continuously since roughly two and a half months before the campaign, unbroken through today. That timeline is what let me retire my own recon-host hypothesis in Section 3: eleven weeks is easily enough time to have quietly run the reconnaissance this campaign clearly required, on this same box, with nothing surviving to prove it happened here specifically.
I want to name a limitation in this finding rather than let the tenancy argument read as more solid than it is. The continuity claim rests on a third party’s own internal tracking of the host’s cryptographic key identity, and I could not independently reproduce it. I captured the live host’s actual key fingerprints myself and they do not match the tracking service’s own opaque identifier for the current era, which is not itself troubling, since that identifier does not appear to be a standard fingerprint format to begin with, but it does mean this case is trusting a third party’s internal bookkeeping for a fact that several other findings, including the next one, lean on.
A co-located domain, and a grade I am actively pulling back rather than defending
A hostname resolving exclusively to this IP surfaced during the investigation, styled like a payment or identity-verification page, with a currently valid certificate and an empty default response on every path tried. An earlier pass graded this domain’s connection to the operator at HIGH confidence, reasoning that the domain’s presence sat inside an otherwise unbroken tenancy window.
I am grading that connection NOT CHECKED, not HIGH, for four reasons, each of which independently would have been enough on its own. The domain’s DNS record first appeared two days after the campaign ended, the dynamic-DNS naming-pattern base rate behind the earlier grade was never measured, and the earlier HIGH grade was conditional on a tenancy-continuity claim already flagged above as unverified. Caddy, the software serving this domain, can also issue a valid certificate for any hostname pointed at the box with zero configuration, so a valid certificate alone does not tie the domain to the operator.
Four reasons the co-located domain is NOT CHECKED, not HIGH: post-campaign DNS birth, unmeasured base rate, an unclosed conditional, and Caddy on-demand TLS
First, the domain’s own DNS record first appeared two days after the campaign ended, not during it and not before it. What spans the campaign continuously is the host’s tenancy, not this specific domain. A domain created two days after a campaign ends, on a box whose tenant did not change, is evidence about who held the box at two points in time. It is not, by itself, evidence that the same person created the campaign and chose this domain name.
Second, grading a shared hosting artifact this way depends on how common the naming pattern is among unrelated users of the same free dynamic-DNS service, and that narrower rate was never measured. An unmeasured denominator produces a NOT CHECKED grade, not a default HIGH.
Third, the HIGH grade was conditional on the same tenancy-continuity claim already flagged above as independently unverified. A conditional grade resting on an unclosed condition was never a settled HIGH to begin with.
Fourth, the software serving this domain, Caddy, has a feature that will automatically issue a valid certificate for any hostname pointed at it, with zero configuration from whoever runs the box. If that feature is active here, and I could not test it without sending an unauthorized request to a live host, a valid certificate for this domain would prove only that someone, anyone, pointed DNS at this IP. It would not prove the box’s own controller chose or even knows this name exists.
I found one additional related domain during this same pass, sitting behind a different provider’s proxy rather than resolving to this IP directly, managed inside the same DNS account as the first. That does suggest one deliberately operated cluster of names rather than an accidental default page someone stumbled onto, which raises my confidence that whoever runs this DNS zone is doing so on purpose. It does not raise my confidence that this zone’s owner is the Gotenberg operator specifically, because the entire link back to this operator still runs through the same unverified tenancy claim, and the second domain never touches this operator’s IP, ASN, or anything else in the recovered corpus at all.
Where the 196 confirmed victim hosts are hosted, by provider only
The confirmed victims sit on mainstream, responsive cloud and telecom providers, and hosting concentrates fast across a small number of provider groups. Nothing in that pattern suggests a bulletproof-hosting relationship: this operator took whatever was vulnerable and reachable rather than selecting targets by where they were hosted. The victim population is a function of who exposed a vulnerable Gotenberg instance to the internet, not of who they bought hosting from.
I reviewed the larger provider groups and found none with a history in bulletproof-hosting literature. The smaller single-IP tail was not individually reviewed, so treat that as unchecked rather than clear.
What the shared miner infrastructure does and does not establish
The stock miner’s own relationship graph on VirusTotal connects to a set of other files and domains through a shared mining pool and a shared cryptojacking installer family. I want to be precise about what that connection is worth. Contact with the public mining pool alone is evidence of using the same commercial service every other unrelated cryptojacking operation on that pool also uses; it is not evidence of a relationship to this specific operator, and I confirmed that by finding four entirely unrelated, independently published campaigns dating back to 2024 that use the identical pool endpoints with zero connection to this case. A shared installer family that serves the same miner binary to two other domains also shows multiple independent submitters over time, and one of the domains in that same cluster serves a completely unrelated scanning tool to what appears to be a different customer entirely. My reading is that this is a shared drop layer serving several unrelated cryptojacking operators, not infrastructure this specific operator built or exclusively controls, and I am not treating the shared file match as a link to any of them.
The negative results, stated with their controls
The Monero payout wallet returns zero results everywhere I searched it, across two independent enrichment sources, and I validated both search tools against a known-positive query in the same session before trusting either negative. This remains the cleanest operator-specific pivot in the entire case; it has simply not surfaced anywhere else that is indexed yet. I did not re-run the mining pool endpoints as bare searches beyond what already established their broad, unrelated ubiquity, since doing so would only refine how common they are, not change whether they constitute a link.
Live re-verification at publication
The co-located domain still resolves to the operator’s host as of publication, with no reputation signal either way. I did not re-check whether the host still answers on the same live ports, so every port and banner above is dated to the observation window rather than to the publication date. The two-path, twice-controlled results in this section rest on direct, dated observation rather than on a third party’s report.
7. The Exploitation Mechanism, and the Detection Signature That Never Fires
This is the report’s most important technical finding, and it is a negative result: the obvious detection rule for this exact injection does not work, and reading the operator’s own source code would never have told me that.
How the injection actually works
Gotenberg hands user-supplied PDF metadata to ExifTool for writing. The operator’s request is an
ordinary multipart form POST to /forms/pdfengines/metadata/write, carrying a PDF file and a
metadata field that is a JSON object. The injection does not sit in a metadata value, which is
where a defender reviewing the endpoint would look first. It sits in the metadata key:
Title\n-if\nsystem('sleep {N}')||1\n-Comment
The embedded newlines are the whole mechanism. When Gotenberg passes this key to ExifTool,
ExifTool’s argument parser splits on the newlines, so -if stops being part of a literal key
name and becomes ExifTool’s own command-line flag. -if evaluates its argument as Perl, so
system('sleep 5')||1 runs an arbitrary shell command and the trailing ||1 keeps the
expression truthy, so ExifTool’s processing continues normally and nothing about the response
looks wrong. A q()-wrapped variant, system(q(...))||1, is the operator’s own fix for payloads
containing single quotes, which tells me this was iterated on rather than written once and left
alone.
I confirmed by direct comparison that this is not the operator’s own discovery. I fetched
Gotenberg’s own GitHub Security Advisory for CVE-2026-42589 and read its published
proof-of-concept payload against the operator’s own. They match almost element for element: the
same key-position injection shape, the same ||1 truthiness suffix, the same trailing dummy
-Comment key, and even the advisory’s own placeholder metadata value, the literal character
"x", which I confirmed on the captured wire traffic. That value has no technical reason to
match by chance. Only the command inside system() differs, and that is exactly what changes
when a defender’s demonstration marker becomes an attacker’s operational timing probe. I hold
this at DEFINITE on the comparison itself; the one honest limitation is that only one public
proof-of-concept repository exists to compare against, so I cannot rule out a shared, unpublished
upstream source for both.
The confirmation method is also not the operator’s own idea
Confirming the injection worked without leaving a payload behind is a genuinely elegant choice, but it is not operator engineering. The operator times a baseline metadata write against an injected one and calls the target confirmed when the delta is at least 70% of the injected sleep duration, so a five-second sleep needs a 3.5-second delay to count. I found the identical sleep-based blind-timing confirmation technique, with a duration-threshold verdict, published in a public proof-of-concept repository’s own Nuclei detection template, dated via its own repository metadata to roughly three months before the operator staged this campaign’s payload. The operator did not invent a stealthier way to confirm remote code execution. They read one that was already public, indexed, and three months old, and used it as written.
That distinction matters for how I read this operator’s overall capability. Section 9 keeps the two axes separate: the vulnerability-research side of this operation is copied, at or below the public floor. What is not copied is everything built around it, which is the subject of the rest of this report.
Why the obvious detection signature never fires
The obvious detection signature, built straight from the operator’s own scripts, is a regex on
the raw injection bytes: \n-if\n[^\n]*system\s*\(. Against real traffic, it does not match a
single request.
The reason is structural rather than a typo in the regex.
The metadata field travels as a JSON string, so before it ever leaves the client, JSON encoding
escapes the embedded newline to the two-byte ASCII sequence backslash and n (hex 5c 6e).
Gotenberg’s server only converts that escape sequence back into a real newline character after
its own HTTP layer has already parsed the request body, which is after any network sensor sitting
in front of it has already seen and passed the packet unmodified. A rule written against a literal
0x0A newline byte, whether as a raw Suricata content match or a PCRE \n, will parse cleanly,
load without error, and never once fire on real traffic. Both natural readings of the regex return
zero matches against the operator’s own real request, measured directly against captured traffic
rather than inferred from the source alone.
The controls that proved it
Six legitimate metadata-write requests should never match this signature, and none of them did: a
single-key write, a multi-key write, a value containing a literal embedded newline, a key
containing the word “system” with no -if shape at all, and two fields exercising unicode and
heavy punctuation. One of the six, a metadata value carrying embedded newlines, triggers an
unrelated HTTP 500 from Gotenberg’s own ExifTool wrapper. That failure is real and reproducible,
but it carries neither -if nor system(, so it is a separate, unrelated bug in how Gotenberg
handles that specific input shape, not a detection false positive.
The corrected anchor, matched against the JSON-escaped wire form and measured, not inferred:
content:"|5c 6e 2d 69 66 5c 6e 73 79 73 74 65 6d 28|"
This is the literal hex-escaped form of \n-if\nsystem(, matched against the HTTP request body on
POSTs to /forms/pdfengines/metadata/write. It survives every transport variant that could be
reproduced: a different sleep duration, and the base64-transported deploy shape the operator uses
for the actual miner drop. The three-transport retry ladder’s exact command shape is not
byte-confirmed on the wire, though every script that builds this key produces the same invariant
\n-if\nsystem( prefix regardless of what the command inside it does, so I expect the anchor holds
there too (MODERATE on that one variant specifically, HIGH on the anchor generally).
One warning has to travel with this rule wherever it goes. The Suricata content match
above is a hex byte sequence, not readable text, and it looks wrong to anyone who has not read this
section. An analyst “tidying” |5c 6e 2d 69 66 ...| back into a literal newline byte, or into a
PCRE \n, produces a rule that parses cleanly, passes every syntax check, and never fires on real
traffic again. This is, in my judgment, the single most likely way this finding gets quietly
destroyed in a future edit, so the companion detection file carries the warning as a load-bearing
comment directly above the rule, not as a note someone can miss.
What a defender actually sees, and its limits
HTTP status code never distinguishes a confirmed injection from an ordinary metadata write; both return 200. Latency is the only observable signal, and the 70%-of-sleep threshold holds with real margin against a measured clean baseline of roughly 250 to 650 milliseconds on this endpoint. Gotenberg’s own structured access log never records the metadata payload on a request that parses as valid JSON, so content-based detection is not possible from Gotenberg’s own logs at all; it requires visibility at the network or reverse-proxy layer, wherever the raw request body is actually captured.
A signature derived by reading source code, and never checked against real traffic, looks finished. It parses cleanly and passes every syntax check. Only a test against real captured bytes catches that it never fires. The only version of this signature worth deploying is the one measured in this section against actual wire traffic, not one built from the advisory or the operator’s own scripts alone.
8. MITRE ATT&CK Mapping
All rows below are HIGH confidence or better unless explicitly marked
(MODERATE). The Confidence Summary near the end of this report organizes findings by confidence level for the higher-level view.
| Tactic / Technique | Name | Evidence |
|---|---|---|
| Reconnaissance / T1595.002 | Vulnerability Scanning | 206 candidates fingerprinted for Gotenberg version band before any exploit ran |
| Initial Access / T1190 | Exploit Public-Facing Application | CVE-2026-42589 metadata-key injection, 198/205 confirmed |
| Execution / T1059.004 | Unix Shell | system('sleep N')\|\|1 and payload commands run via ExifTool -if Perl eval |
| Discovery / T1082 | System Information Discovery | Pre-deploy check of /tmp free space and writability (touch /tmp/.wt) |
| Discovery / T1057 | Process Discovery | Recon greps for rival miner processes before spending a deploy |
| Defense Evasion / T1036.004 | Masquerade Task or Service | Miner process and systemd unit both named polkitd; a second VT-recorded alias uses systemd-logind (MODERATE) |
| Persistence / T1543.002 | Systemd Service | Root systemd-polkitd.service, Restart=always, shipped in the package; per-host deployment is NOT CHECKED (MODERATE) |
| Command and Control / T1071.001 | Web Protocols | Out-of-band callback listener, HTTP GET tagged with victim hostname and UID |
| Command and Control / T1105 | Ingress Tool Transfer | Deploy script base64-encodes and transfers the miner package to each confirmed host |
| Impact / T1496 | Resource Hijacking | XMRig cryptomining verified installed on 148 to 151 confirmed hosts |
9. Threat Actor Assessment
No named actor, and that is the honest answer
Nothing in this investigation reaches even MODERATE confidence on a named threat actor, a known group, a handle, or an overlap with a previously tracked operation. I am reporting this as an unknown operator, and the rest of this section is about the shape of that operator rather than their identity.
Nothing here revises or contradicts anything I have published before, and a reader should not go looking for an earlier piece that this one supersedes.
One operator, at MODERATE, not higher
The AI-premise refutation in Section 5 governs how this assessment should be read: this is ordinary, competently engineered, scripted mass exploitation of one disclosed vulnerability, not anything agent-orchestrated. I hold “one operator” at MODERATE confidence.
Two style-based arguments for one author, and why neither can carry the verdict: an unmeasured drift pattern, and a language symmetry that fits two operators as well as one
The strongest style-based argument for a single author was a shared helper function that drifted inconsistently across every Gotenberg-specific script, which reads as one person iterating solo rather than a team coordinating changes. That reading is plausible, but the underlying pattern, regenerate-with-drift bookkeeping, is also simply the most common shape any re-runnable batch job takes regardless of who is writing it, and it was never measured for how distinctive that specific pattern actually is against other operators’ tooling. An unmeasured pattern cannot carry the verdict.
Two supporting observations from language analysis, that the Chinese and English toolsets read at matching levels of formality and that the operator code-switches naturally between them, are equally consistent with one bilingual person and with two closely collaborating, comparably fluent operators splitting the work by language. Neither observation can discriminate between those two possibilities, and I should not have let it carry the weight it originally did.
What actually holds the verdict is structural rather than stylistic, and it is the one piece of evidence in this section that does not depend on a judgment call about writing style. The Gotenberg campaign’s own target list, state file, and deployment results sit physically inside the same directory tree as the Telegram service and card-shop toolchains covered in Section 5. That co-location ties every toolchain in this investigation to one point of control, regardless of whose hands actually typed each one. It is enough, on its own, to hold “one controlling entity” at MODERATE. It is not enough to hold it any higher.
One clean, unmoved finding: neither language shows LLM authorship or non-native strain
This answers a specific question I set out to test directly, and I hold the answer at HIGH, measured over a meaningful sample rather than a couple of lines: does either the Chinese or English tooling read as more formal, more textbook, or less naturally fluent than the other, in a way that would suggest LLM generation or a non-native writer? It does not.
The English and Chinese comparison this HIGH rests on, and why matching casualness in both languages is itself the finding
The English scripts are uniformly terse and casually slang-inflected, with the kind of mid-thought, self-questioning code comments a formal or generated writer would be unlikely to produce. The Chinese material, checked with a round-trip encoding verification to rule out corruption, shows the identical profile: colloquial gray-market slang, a contracted spoken-register phrasing rather than the formal written equivalent, and native-typist punctuation inconsistencies. Neither language is more polished than the other. Both read as native, casual, first-draft developer prose, and that symmetry is itself the answer. This is the one finding I would call least likely to move with more evidence.
Capability: two axes, never averaged into one adjective
Averaging these into “medium sophistication” would be wrong. Vulnerability research and operational engineering point in genuinely different directions here, with distinct evidence and distinct confidence, and averaging them would misrepresent both.
Weak at finding vulnerabilities, strong at running the operation. Expect a copied public exploit rather than a novel one, and expect the compromise itself to be careful, staged and thorough.
Vulnerability research Confidence: HIGH-to-DEFINITE The exploit primitive is copied from the public advisory, not discovered.
The injection shape, the truthiness suffix, the trailing dummy key and the vendor advisory's own placeholder value all match verbatim. The timing-confirmation method is separately traceable to a public proof-of-concept published three months before the campaign. This operator did not discover anything.
Held just short of flat DEFINITE only because a single comparison point cannot formally exclude every implausible coincidence.
Operational engineering Three of four originally claimed elements survive a working prior-art search.
The writability probe, a relative rather than fixed timing threshold, and a three-transport retry ladder survive. A dedicated Chinese-language security search, run with working controls proving it could surface real content, removed the fourth: per-host callback tagging built from shell command substitution is foundational, widely taught technique in that literature, not an operator contribution.
Held at MODERATE-HIGH rather than higher because three-of-four standing against a working search is evidence of absence, not a positive discovery. Recovery of the operator's own reconnaissance notes, or a private or paywalled writeup covering the writability check or the retry ladder, would lower this toward the copied-technique reading already applied to callback tagging.
The fuller reasoning, the one alternative excluded, and what would move either rating are in the surrounding text.
The honest, non-averaged statement is this: this operator did not need to be, and was not, a vulnerability researcher for this campaign. They read a disclosed advisory closely, picked up a publicly documented confirmation technique and a foundational tagging convention, and built a reliable, proxied, 198-host automated pipeline around all three, with genuine engineering on the parts that stayed undocumented anywhere I could search. That is a real, narrower capability than “they wrote this exploit,” and it is the capability I documented here.
The one alternative I could rule out, and the ones I could not
Authorized security testing does not survive, and I am excluding it rather than merely leaving it unsupported. Nothing in the readable corpus shows client-facing reporting, scoped-engagement artifacts, or any authorization trail, and what the deployment scripts actually do is install a cryptominer paying the operator’s own wallet while killing a named list of competing miners already running on the box. An authorized tester does not compete with other criminals for a client’s compute and walk away with the mining proceeds.
Three alternatives I could not kill, and I am naming them rather than smoothing past them:
- One operator versus two closely collaborating operators with indistinguishable habits. Register and habit analysis cannot resolve this from text alone; the structural directory tie survives this alternative, but it proves common control, not a single pair of hands, and I am not claiming it does.
- The shared helper function’s drift as solo hand-editing versus one person repeatedly prompting an LLM to regenerate the same file. Both produce a nearly identical drift signature. This does not revive the falsified agent-framework premise, which was specifically about whether an autonomous agent orchestrated the exploitation itself, but it does mean I cannot rule out LLM-assisted authorship of individual files, and I am not claiming to.
- The device-farm and Telegram account-management business as the same operational pipeline as this campaign, versus two business lines sharing a host only incidentally. The device-farm panel’s own API surface has no endpoint connecting it to the Telegram service’s OTP or SIM data anywhere I could find. The link that exists is conceptual and structural, not a direct code path, and I am not asserting these are one pipeline.
What would change each of these judgments
| Finding | Confidence | What would change it |
|---|---|---|
| One operator (one controlling entity) | MODERATE | A second structural tie, such as a shared credential or a second campaign reusing this exact directory layout, would raise it. Content from any unrecovered file showing two distinct authorship voices would lower it. |
| Neither language shows LLM or non-native authorship | HIGH | Nothing currently visible would move this; I named the predicted signature in advance and found none of it. A much larger sample of the operator's own prose in either language, showing a different register, is the one thing that could. |
| Vulnerability research is copied, not discovered | HIGH-to-DEFINITE | A second, independent public source confirming the same copy would move this to flat DEFINITE. |
| Operational engineering exceeds the public baseline | MODERATE-HIGH | Recovery of the operator's own reconnaissance notes, or a private or paywalled writeup covering the writability check or the retry ladder, would lower this toward the copied-technique reading already applied to callback tagging. |
| Authorized testing is excluded | DEFINITE | Nothing I can foresee would revive it; a wallet-funded miner deployment with a rival-miner kill list is not consistent with any authorized-testing framing I can construct. |
Why I am not designating a new tracking identifier for this operator
I am not assigning a tracking identifier for this operator, even though the case technically clears the designation gates on a literal count. Most of the qualifying characteristics are absence-of-evidence findings rather than a positive, unique signature. The “one operator” premise a designation would follow sits at MODERATE for reasons of evidentiary rigor, not new contrary evidence, and building on it invites exactly the kind of drift that later forces a designation to be retired. The strongest distinctive fact, three business lines sharing one directory footprint, is tied to this specific, now-dark box, and would not by itself let a future campaign on different infrastructure be recognized as the same operator.
10. Indicators of Compromise
Full detail lives in the machine-readable feed referenced below; this section highlights what actually distinguishes this operator from the surrounding commodity noise.
The one genuinely operator-specific indicator
| Type | Value | Confidence | Note |
|---|---|---|---|
| Monero address | 456UWvWXto1PacXMu689Mghh2QWQg2amvapezv3HWucT2KiKz86VQYJZ9cHGha6NbuTyqrrRDrJKSPB2eS7BNwkhSuw5QQU |
DEFINITE | Identical across both dropped configs. Returns a controlled zero everywhere searchable as of this report. This, not the miner hash, is the pivot worth tracking. |
Network indicators
| Type | Value | Confidence | Note |
|---|---|---|---|
| IPv4 | 107.175.69.137 |
DEFINITE | The operator’s own host. Served the exposed directory, the campaign toolkit, the OOB listener, and an unauthenticated reverse-shell collector. |
| Port | 18888 |
DEFINITE | Out-of-band callback listener; victim callbacks tagged with hostname and UID |
| Port | 19999 |
DEFINITE | Campaign toolkit and miner payload staging |
| Port | 13337 |
DEFINITE | Unauthenticated reverse-shell collector, internet-reachable throughout |
| HTTP request | POST /forms/pdfengines/metadata/write with a JSON metadata key carrying the wire-escaped sequence \n-if\nsystem( |
DEFINITE | The exploitation signature, measured against captured traffic; see Section 7 |
Host indicators
| Type | Value | Confidence | Note |
|---|---|---|---|
| Path | /tmp/polkitd/ |
DEFINITE | Ephemeral drop, cleared by reboot |
| Path | /usr/bin/polkitd.d/ |
DEFINITE | Persistent install directory; survives reboot |
| Systemd unit | systemd-polkitd.service |
DEFINITE | Root, Restart=always; the reason “reboot and it is gone” is wrong advice for this mode |
| Process name | polkitd |
DEFINITE | The miner’s masquerade name. Also appears on the operator’s own rival-miner kill list, so its presence alone does not confirm this operator specifically |
| Process name | systemd-logind |
INSUFFICIENT | A second VirusTotal-recorded alias for the same binary hash, with zero ties to this host, wallet, or corpus. Watchlist only |
| XMRig rig ID | worker-01 |
DEFINITE | Identical on every deployment; low fidelity alone |
Commodity, explicitly non-attributing
| Type | Value | Note |
|---|---|---|
| SHA256 | b20f39fc00d242e706b6c30367ad811c676e0575050a4ec2f30104b696944b49 |
Stock, unmodified XMRig 6.26.0. 133 VirusTotal submissions from 107 unrelated sources, re-verified at publication. Do not use this hash to tie a host or campaign to this operator. |
| Mining pools | gulf.moneroocean.stream:10032, ca.moneroocean.stream:10032 |
Public, shared infrastructure used by unrelated operators. Hunt for it; never blocklist it as this operator’s own infrastructure. |
Full Feed Location
Complete machine-readable indicators, including the hunt-only shared infrastructure and every excluded victim address, are maintained separately in the full IOC feed. No indicators are embedded directly in this report body beyond the highlights above; the feed is canonical, and it excludes every victim address by design.
11. Detection and Response Guidance
Network-based detection
The only reliable signature keys on the wire-escaped injection bytes, not on a raw newline. Full
Suricata and Sigma rules are in
the companion detection file,
and both carry the same load-bearing warning this report does: the hex byte sequence in the rule is
the intentional JSON-escaped form of
\n-if\nsystem(, and correcting it to a literal newline produces a rule that parses cleanly and
never fires. Where request-body visibility is unavailable, a secondary hunting rule watches for
latency spikes on Gotenberg’s metadata-write endpoint against its own established baseline, at the
cost of a high false-positive rate against genuinely slow or malformed documents.
The Suricata rule loads and parses cleanly, but replay against this campaign’s captured traffic is NOT CHECKED. The companion detection file’s Coverage Gaps section records that limit.
Host-based detection
Check for both install modes independently, since only one leaves an obvious trace after a reboot.
Look for a process or systemd unit named polkitd, keeping in mind this operator’s own tooling
also kills a rival using that same name, and separately for the systemd-polkitd.service unit and
the /usr/bin/polkitd.d/ directory specifically. A single confirmed MINER_OK-style ephemeral
drop does not rule out the persistent mode also being present on the same host, since nothing in
the observed campaign’s own telemetry can distinguish the two per host.
The full rule set, and the one gap left open
The complete rule set lives in the companion detection file: eleven rules in total, four Sigma Detection rules, three Sigma Hunting rules, one Suricata Detection signature, and three Suricata Hunting signatures. The Sigma rules cover the exploit itself at the ExifTool argv-split, the daemon-name-versus-location mismatch that both miner install modes produce, the systemd wrapper shape behind the persistent install, the rival-miner kill sweep, and the base64-staged deploy. The Suricata rules cover the wire signature plus the operator’s own callback lane.
No YARA rule is shipped for the miner binary: the coverage gap is explained in that file rather than left silent, since the miner is a commodity payload that a byte-pattern rule would only ever match across an unrelated population, not this operator specifically. Three further candidates were considered and deliberately cut, with the reasoning recorded there: a writability probe too ubiquitous to carry signal, a payload-fetch pair whose only anchors are atomic indicators already in the feed, and a watchdog script seen on a single host whose behavior was never captured.
Threat hunting approaches
Hunt 1: Confirm scope of the persistent install
Join any evidence of the ephemeral drop against a live check for the systemd-polkitd.service
unit and /usr/bin/polkitd.d/, since the campaign’s own telemetry cannot answer this and every
confirmed host should be checked directly.
Hunt 2: Identify the confirmation timing pattern historically
Filter historical access logs for the metadata-write endpoint for requests whose response time sits at or above 70% of a suspiciously round sleep duration, against an established clean baseline for that endpoint.
Hunt 3: Check for co-tenant compromise
Where this campaign’s miner is found, check for evidence of other, unrelated cryptomining or dropper activity on the same host. This operator’s own kill-list behavior and the interactive session recovered in this investigation both show other criminal activity coexisting on the same compromised machines.
Response action categories
As a third-party intelligence provider, I am naming action categories here, not procedures specific to any organization’s tooling, and I am ordering them by what this specific threat actually requires rather than by a generic incident-response template.
The fact that matters most is the one from Section 4: this operator ships two install modes, and
only one of them clears on a reboot. Checking for the ephemeral /tmp/polkitd/ drop and declaring
a host clean misses the persistent, root-owned systemd service entirely, so both modes have to be
checked on every affected host before anything is called resolved. Past that, patch Gotenberg
beyond the top of the version band this campaign actually confirmed against, and treat the
Gotenberg service account’s own credentials and any secrets co-located with it as exposed rather
than merely at risk, since the operator had unauthenticated code execution as that account.
12. Recommendations
Immediate actions
If infection is confirmed, isolate the host, patch or take the Gotenberg instance offline until patched, and check for the persistent systemd install specifically rather than assuming a reboot resolved it. If infection is only suspected, deploy the provided detection rules and review historical logs for the latency pattern in Section 11’s second hunt.
Short-term detection improvements
- Deploy the wire-escaped Suricata and Sigma rules from the companion detection file, with their load-bearing comments intact
- Enable request-body logging on any reverse proxy or WAF in front of a Gotenberg deployment, since Gotenberg’s own application log cannot see this injection at all
- Alert on the specific latency signature in Section 11 only where body-level visibility is genuinely unavailable, given its high false-positive rate
Prevention control categories
- Patch Gotenberg past the version that closes CVE-2026-42589, and separately confirm the fix actually blocks metadata-key injection rather than trusting the version number alone
- Do not expose a document-conversion service’s metadata-write endpoint directly to the internet where a reverse proxy with body inspection can sit in front of it instead
- Apply outbound network controls that would catch an unexpected out-of-band callback from a document-processing service, which has no legitimate reason to make arbitrary outbound connections
Long-term strategic changes
- Treat any internet-facing document, image, or media-processing service as a high-value target for this same injection-and-timing-confirmation pattern, since the technique itself is generic and not specific to Gotenberg
- Build detection validation into any workflow that derives a signature from source code alone: exactly that kind of signature failed silently here until it was tested against real captured traffic
Control effectiveness against this threat
| Control category | Effectiveness | Priority |
|---|---|---|
| Reverse proxy or WAF with request-body inspection | HIGH | CRITICAL |
| Timely patching of internet-facing document-processing services | HIGH | CRITICAL |
| Outbound network egress controls | HIGH | HIGH |
| Host-based detection for both miner install modes | HIGH | HIGH |
| Reliance on Gotenberg’s own application log alone | LOW (cannot see this injection) | n/a |
13. Confidence Summary and Evidence Gaps
| Finding | Confidence | What would change it |
|---|---|---|
| The exploitation mechanism, the CVE identity, and the wire-escaping detection failure | DEFINITE | A Gotenberg deployment that un-escapes before the sensor boundary; none is possible given the documented endpoint contract |
| The miner is stock XMRig; its hash attributes nothing; the wallet is operator-specific | DEFINITE | The wallet surfacing in an unrelated corpus, which would make it shared rather than unique |
| The AI-agent-driven premise the case opened on is refuted | DEFINITE | Recovery of the one unfetched file the original claim rested on, if it contained agent configuration; its bytes did not survive |
| The host's attack surface is gone but the machine is live and administered | DEFINITE | The captured banners ceasing across independent paths, tested with a control matched to the host's own hosting class |
| 198 of 205 probed targets confirmed exploitable inside 54 minutes | DEFINITE | A surviving copy of the target list showing a materially different probe-to-confirm split |
| The corpus coverage gap is a collection failure, not a triage failure | DEFINITE | Enumeration of one unopened archive that could move up to three files into the held-but-untriaged bucket |
| 184 confirmed hosts called back, split 167 observed and 17 inferred; 14 never did | HIGH | A fresh derivation from the raw callback log printing every host and donor explicitly |
| The seven named platforms were targeted, not compromised; zero show content-level access | HIGH | Recovery of any captured content from those platforms' own systems, which does not currently exist anywhere reachable |
| Vulnerability-research capability is copied from the public advisory, at or below the public floor | HIGH-to-DEFINITE | A second independent public source further confirming the copy would move this to flat DEFINITE |
| Neither the Chinese nor English tooling shows LLM authorship or non-native strain | HIGH | Nothing currently visible; a much larger sample showing a different register in either language |
| Victim scope for the credential-harvesting surface is permanently unknowable from this corpus | HIGH as a negative | A snapshot not currently mounted holding the missing bytes; verified absent three ways with a working control |
| 148 to 151 confirmed miner installs, 28 permanently unattributable | MODERATE | Recovery of the main deploy wave's own target-list ordering, which would resolve the index-to-host mapping |
| Operational engineering exceeds the documented public baseline, on three of four originally claimed elements | MODERATE | Any of the three surviving elements appearing in published tooling in a language the searches did not reach |
| One operator (one controlling entity) rather than several | MODERATE | A measured denominator showing the shared bookkeeping habit is genuinely distinctive, or any artifact placing two hands on one toolchain |
| This is one multi-line criminal enterprise rather than several unrelated operations sharing a host | MODERATE | Evidence of data, credential, or customer flow between the lines, which no lane observed either way |
| The two install modes exist; which hosts received which is not measurable | HIGH on existence, NOT CHECKED on the split | A per-host record carrying an install-mode field, or a callback body distinguishing the two |
| The co-located domain is meaningful operator linkage | INSUFFICIENT / NOT CHECKED | A measured base rate for the naming pattern, or independent verification of the underlying tenancy claim; either alone would move it off NOT CHECKED |
| Attribution to any named actor | INSUFFICIENT | Any infrastructure, code, or TTP overlap with a previously tracked actor; none currently exists |
The two gaps that matter most, in plain terms
The gap between “148 to 151 confirmed installs” and “198 confirmed exploited” is the single most important number in this report to hold apart correctly. Every one of the 198 got code execution. Fewer than that have a verified miner running, because the operator’s own callback infrastructure lost data under load and the main deployment’s target ordering was never preserved. That is a genuine evidentiary limit, not a rounding choice, and I would rather publish an honest range with its derivation than a single confident number I cannot actually defend.
The second gap is the co-located domain, graded NOT CHECKED rather than HIGH for the four reasons given in Section 6. That is a deliberate withdrawal, not a hedge.
What is missing
- Whether the persistent systemd install mode reached any of the confirmed hosts. The observed campaign’s own deployment script only ever installs the ephemeral mode; a per-host telemetry record carrying an install-mode field would close this, and none currently exists.
- What the Android device-farm capability actually connects to. Its existence is confirmed; its function, and whether it feeds the Telegram account farm at all, is not, and closing it would need backend content this investigation never recovered.
- The exact scope of the credential-harvesting surface. Two live private keys are confirmed; the files that would establish how far that surface actually reaches were fetched once, hashed, and never retained, and are permanently unrecoverable.
- Whether the top of the operator’s target version band reflects deliberate targeting or an incomplete patch. The join that would settle this cannot be run from what survives in this corpus; it is reported as an open, NOT CHECKED question rather than resolved in either direction.
- The cryptographic verification of the host’s tenancy continuity, which several findings in Section 6 lean on and which rests on a third party’s internal tracking that I could not independently reproduce against the live host’s own key fingerprints.
The source base, in short
Most of what this report rests on comes from artifacts recovered directly off the operator’s own infrastructure: their own scripts, their own configuration files, and their own logs, which is the strongest source base available for a case like this. The exploit-provenance and prior-art claims in Sections 3 and 9 are the exception, resting on primary vendor advisories and public repository metadata I fetched and read directly rather than took on faith. Where the infrastructure section’s tenancy-continuity claim and a third party’s passive enrichment data disagreed with what I could verify myself, I have said so explicitly rather than picked the more convenient reading, and the domain co-location grade in Section 6 is the direct consequence of following that disagreement through rather than around it.
Glossary
| Term | Definition |
|---|---|
| Blind RCE | remote code execution where the response gives no direct evidence the injected command ran, confirmed instead through a side channel like timing or an out-of-band callback. |
| Out-of-band (OOB) callback | a connection an exploited host makes back to infrastructure the attacker controls, used to confirm code execution when the original request’s response reveals nothing. |
| Argv splitting | an injection technique where embedded delimiter characters, here newlines inside a metadata field, cause a program to parse part of an intended data value as a separate command-line argument. |
| Systemd unit | a Linux service definition that controls how a program starts, restarts, and persists across reboots; the mechanism behind this campaign’s persistent miner install. |
| RDAP | Registration Data Access Protocol, the modern replacement for WHOIS, used here to identify hosting providers and abuse contacts for the confirmed victim population. |
© 2026 Joseph, The Hunters Ledger. Licensed under CC BY 4.0, free to republish and adapt, including commercially, with attribution to The Hunters Ledger and a link to the original.