How the Noir kit uses DNS TXT records and DoH as a dynamic infrastructure control plane
Executive summary
- We found a cryptocurrency wallet-drainer kit, self-branded as Noir, that uses DNS TXT records to locate its hosted pool; currently these run on Cloudflare Pages. The loader races three public DNS-over-HTTPS (DoH) services and uses the first valid answer.
- The architecture separates long-lived lure pages from short-lived operational infrastructure. Changing one TXT record can repoint deployed lures without rebuilding them, while a 60-second cache and self-healing retry logic help the kit recover when a pool host is retired.
- The drainer does not need a seed phrase, password, or private key. It tailors content to the user after receiving initial read permissions, developing a plan for the specific wallet. Then it obtains spending permissions that can look less alarming than a direct transfer and performs the theft from attacker-controlled infrastructure.
- The source is unusually self-documenting. Plain-English comments describe bugs, fixes, operator support, and design decisions. It seems likely this software is created and maintained with AI assistance.
- DoH provides threat actors with a highly resilient command-and-control (C2) mechanism. Risk averse networks may want to block access to DoH and fully monitor their DNS.
The wallet drainer was interesting. The fact that it found its location via DNS was more interesting to us.
While investigating a fake crypto voting page, we found a loader for a wallet-drainer kit that calls itself Noir. The lure looked like the familiar sort of thing: a token vote, airdrop, listing, decentralized exchange clone, or eligibility check that invites the visitor to connect a wallet. Underneath, however, the page did not contain a durable link to the drainer infrastructure. Before the operational flow could begin, the loader queried a DNS TXT record through public DoH services to learn which backend pool was active. See Figure 1.

Figure 1. The attack flow and DNS C2 element of Noir crypto draining service.
That design turns DNS into a small but effective control plane. The lure is the durable entry point. The TXT record supplies the current pool. The pool serves the interface and connects the victim to the backend that assesses the wallet, prepares the requests, and captures the resulting permissions. If a pool is blocked or removed, the operator can change the DNS answer instead of replacing every lure already deployed.
Noir is a useful case study because it combines several developments that defenders will continue to encounter elsewhere: public DoH used outside the enterprise resolver path, DNS records used as live configuration, cloud-hosted infrastructure that can be replaced quickly, per-victim decision-making, and code that arrives with unusually candid documentation from its own developer.
We’ve seen DNS TXT records used for a control plane many times over the years. Most notably by Detour Dog, who used it to forward users through an affiliate marketing platform, as well as for the distribution of information stealers. A wide range of other actors have used both TXT and other record types for C2, including the sophisticated remote access system, Decoy Dog.
The Victim Does Not Hand Over a Wallet
The easiest way to misunderstand a wallet drainer is to imagine a victim typing a seed phrase into a form. Noir does not require that. The victim can lose assets without surrendering a password, private key, or recovery phrase, and without approving a transaction that plainly says “send this amount to this address.”
The interaction begins with a standard wallet-connection flow. Connecting initially shares the public wallet address, which alone is not enough to steal funds. The kit then uses that address, the blockchain network, and an estimate of the portfolio value to request a plan from its backend. The backend can inspect what the wallet holds and return a sequence tailored to that victim rather than using one generic set of prompts.
The important distinction is between transferring an asset and granting permission to spend it. Legitimate decentralized applications routinely request approvals or typed-data signatures to perform actions on a user’s behalf. Noir uses the same underlying concepts for a different purpose. Depending on the wallet’s capabilities, the victim may see a gasless signature request, a bundled confirmation containing multiple approvals, or individual approval transactions. A signature request can appear less consequential than an explicit transfer because the theft itself is not the transaction being shown at that moment.
Each captured signature or approval is submitted to the backend immediately. The kit does not need to wait for the visitor to complete the entire sequence. Its own internal log language describes this as “draining at capture.” The attacker-side relayer can then exercise the granted permission and execute the transfer from its infrastructure. The victim’s wallet may show the earlier permission, but not a straightforward outbound transaction initiated from the wallet for the eventual theft.
The kit also cleans up after itself. Once the flow finishes, it disconnects the WalletConnect session, so the malicious application no longer remains visible in the wallet’s connected-applications list. The combination matters: a familiar connection prompt, a request that does not look like a direct payment, attacker-side execution, and removal of the most obvious lingering clue.
A TXT Record Points to the Current Drainer
In the samples we reviewed, the attack begins with JavaScript file embedded in the lure page. That script contains a decimal character-code array, which is decoded and executed from the primary page, creating a loader for the drainer. For example, in one case:
- A URL hosted on listchoiseopenleaderboardseptember[.]netlify[.]app
- Loaded a script called k1xfns97l5w.5cgsbfh2.js
- Which contained a character-code array that was used to complete the loader initialization
Once running, the loader races three DoH queries in parallel for the TXT record at _r.noir[.]black. The observed providers were Google Public DNS and two Cloudflare endpoints, including direct access to 1.1.1.1. The response includes the current drainer pool domain. For example, render-984.pages[.]dev was one of the returned domains.
The loader also queries an API endpoint on noir[.]black for a “road” configuration. That response can provide an independently resolved fallback pool, which gives the kit another path on networks where direct DoH fails or is blocked. The client performs the DNS race and configuration request concurrently, a choice documented in the source as a latency improvement. The returned mode determines whether the malicious flow is imported into the lure page or displayed through a transparent, full-viewport frame that appears to belong to the page the visitor intentionally opened.
Once the road directions are returned, a short script at the drainer pool domain, e.g., render-984.pages[.]dev, executes and effectively wires the drainer engine to the original page. See Figure 2.

Figure 2. The script from the domain found in the dynamic DNS TXT record. It loads the engine via index.js and constructs the per-deployment configuration including a WalletConnect project ID. This script was observed on render-984.pages[.]dev.
Noir caches the pool hostname in local storage for 60 seconds, matching the TXT record’s 60-second TTL. The comments explain why: an earlier, longer cache continued sending visitors to dead infrastructure after rotation. If the current host fails its readiness check, the loader removes the cached value and retries, preferring the server-provided fallback.
The Source Explains Itself
The most entertaining part of the investigation was not obfuscation. It was documentation: it resembles an internal engineering log accidentally, or indifferently, shipped with production code.
We found extensive plain-English comments describing design decisions, dated defects, support reports, and corrective changes. The comments discuss dead pool hosts after TXT rotation, a wallet prompt that exposed the canonical origin, pages that turned white when framed incorrectly, and a requirement that closing the flow always carry a reason. They refer to a “founder,” distinguish the visitor from the operator page, and include numbered fix-list language. The kit even provides a debug mode with an on-screen console and a way for an operator to copy a trace from a victim’s phone for support. See Figure 3 for comments found in the script hosted at render-984.pages[.]dev.

Figure 3. Example commentary included the source code on render-984.pages[.]dev
It reads like modern AI-assisted development: verbose explanatory prose wrapped around implementation, explicit reasoning about edge cases, and comments that restate intent for the next developer or model to consume. A human developer could have produced it. But most trained developers aren’t telling their life story in their code comments. See Figure 4 for more examples of comments found in the code.

Figure 4. Various comments found in pieces of the Noir Drainer kit scripts
The code and infrastructure both point toward a shared service used across many lure themes rather than a single actor. The notes identify fake community votes, airdrops, eligibility checks, exchange clones, and brand-specific spoofs that reuse the same engine. Operator-specific configuration identifiers connect the lure to the backend, while shared infrastructure and a common wallet-connection project identifier provide useful clustering signals. There appear to be service tickets being addressed in comments.
Also pointing to a service is the variety of lures. We have seen:
- Fake token “Community Voting” pages impersonating CoinMarketCap, DexScreener, OKX
- Fake airdrop/claim pages impersonating Uniswap, Lido, Hyperliquid, Morpho, Ondo, LayerZero, MegaETH
- Fake DEX/swap clones
- “ETH Airdrop Eligibility Check”
- “Desktop Browser Required” gate pages
- brand spoofs: Polymarket, SpaceX/Ondo tokenized-RWA, Grass, Venice, FoxFi
Indicators
_r.noir[.]black
noir[.]black
render-984.pages.dev
listchoiseopenleaderboardseptember[.]netlify[.]app

