Back
Adversary Intelligence
Table of Content
No items found.

Executive Summary

An operator has spent the summer of 2026 publishing malicious packages to the public npm registry under names that belong to somebody else. Not names resembling popular libraries, which is the usual pattern, but the internal package names of one company. We track the activity as TXTBOOK. It began on PyPI, moved into npm with its execution trigger rebuilt for the new ecosystem, and now runs to 993 npm packages.

CloudSEK's supply chain monitoring surfaced them and from those packages we pivoted to the rest: the staging infrastructure, the command and control estate, the third stage implant, and finally the target.

What matters is who the 993 packages are named after. They reproduce the internal, private package namespace of a single organisation: Tinkoff, the Russian financial group now trading as T-Bank. The names cover the group's BNPL product, its business banking platform, its analytics client libraries, and the proprietary counterparts of its own published open source projects. This is a dependency confusion operation aimed at one company, run at a scale normally associated with indiscriminate registry flooding.

The targeting is not inferred from names alone. The second stage loader carries a hardcoded, obfuscated table of hostnames, and three of its nine entries are victim infrastructure rather than attacker infrastructure. All three belong to the T-Bank group. Before contacting its own command and control servers, the loader resolves the group's internal artifact repository and proceeds only if it answers. This ensures that the malware has been run inside the bank’s internal network. The malware carries its own definition of the target.

A small secondary cluster of online betting names is present in the same advisory data. It is four packages out of 993, none of which were held in our corpus, and it is reported here as an observation rather than a co-equal target.

TXTBOOK's delivery technique remains its most distinctive feature. Rather than downloading a binary over HTTP, the loader reassembles a native executable from several hundred DNS TXT records, a few hundred bytes per record. To a network monitor the activity is a burst of DNS lookups. The third stage that arrives by this route is a Sliver implant with an effectively complete command set, and its command and control protocol has now been reversed from captured traffic, recovered key material and static analysis of the binary.

Key Findings

  • The campaign comprises 993 packages, and the naming is a private namespace reconstruction of one organisation. Strict prefix matching attributes 850 of 993 to T-Bank; the remainder are generic banking-platform names (claims, deposits, cards, certificates, business process management) that are consistent with the same source. Seven names, under one percent, point anywhere else.
  • The loader carries a hardcoded target gate. Its obfuscated hostname table decodes to nine entries on Windows, macOS and Linux ARM64 alike, of which three are victim infrastructure: the group's Nexus artifact repository, its Linux package repository, and the alerting endpoint of CloudPayments, a payment processor T-Bank has wholly owned since 2023. The malware defines its own target.
  • The squat list leaks an internal service catalogue. Package names of the form statist-browser-typed-client-* embed internal service paths such as mb.product.payments, sme.rko.conversionpayments.web, investaccounting.events, leasing.admin.events and dwh.chimera.base. These are not guessable strings; they indicate the operator worked from a real inventory of the target's internal systems.
  • The operator squats the proprietary siblings of the target's public open source. T-Bank publishes the Tramvai application framework and the Taiga UI design system openly; the campaign registers tramvai-* and taiga-ui-proprietary-* names. Public repositories give an attacker an accurate, free guess at private package names.
  • A previously undocumented version band is the largest we hold. Published versions were understood to fall into four generations. A 20.x band accounts for 199 of 331 verified archives, more than the 33.x, 34.x and 35.x bands combined, and reassembles the same command and control batch, so it belongs to the same infrastructure era.
  • The third stage is a Sliver implant, and its protocol is now fully understood. The implant encrypts with age rather than Sliver's native scheme, authenticates its server with an Ed25519 minisign signature on every response, and selects one of five traffic encoders per request. Beacon traffic was decrypted in the analysis environment by substituting a controlled key into the running process, which yielded the plaintext of the command and control exchange.
  • The campaign runs two separate operator servers. Two independent cryptographic identities, an age recipient key and a minisign signing key, partition the platform builds identically: Windows and Linux ARM64 to one server, Linux x64 to another. Both are fronted by the same Cloudflare Workers estate, so shared front end infrastructure does not imply a shared back end.
  • Obfuscation was applied inconsistently. The Windows and macOS builds have their string literals encrypted; neither Linux build does. The Linux implants carry the operator's keys, their own private keys and their command and control hostname in plaintext on disk, which makes captured server to implant traffic for those builds decryptable with no runtime analysis at all.

Required Actions

  • Treat this as a targeted campaign against a named organisation, not as registry flooding. If you operate any part of the T-Bank estate, or build software that resolves dependencies inside it, assume the private namespace is known to the operator and enumerate which of the 993 names exist internally.
  • Configure registry scoping so private package names cannot resolve to the public registry. Dependency confusion is defeated by resolution policy, not by blocklists; the 993 names in Appendix A are the symptom.
  • Block the DNS staging zones and the Cloudflare Workers command and control estate listed under Indicators of Compromise, and hunt DNS telemetry for sequentially numbered TXT lookups that reassemble into an executable.
  • Pivot on the operator's minisign server keys rather than on hostnames. A minisign key identifies a Sliver server installation and survives infrastructure rotation, campaign renaming and rebuilds.
  • Treat the presence of any listed package in a lockfile as a compromise and rotate every credential available to the affected build environment.

Analysis

How the Campaign Unfolded

The operation did not begin with npm. It was first documented on PyPI in July 2026, as the campaign tracked 2026-07-andreiiiiiii_i and, before that, as activity around a configuration client package. The move into npm reused the infrastructure wholesale: the same staging domains, the same command and control estate, the same third stage. What changed was the execution trigger, which was rebuilt for the new ecosystem. On PyPI the operator installs a path-configuration file that the interpreter runs at startup. On npm it appends a required call to the package entry point, which runs on import. Both achieve code execution without declaring an install hook. That is a deliberate port, not a reused payload.

Publishing proceeded in waves rather than continuously, and the version numbers separate them cleanly. Each wave changed the thing that had made the previous one detectable. The earliest packages avoided npm lifecycle scripts altogether. A later wave reintroduced a postinstall hook. The most recent returned to import-time execution and added per-package payload variation, so that no two packages share a hash and the loader filename differs in every one.

The supporting infrastructure was provisioned in batches ahead of the packages that would use it. The command and control estate spans roughly two dozen Cloudflare accounts in two clearly separate naming batches, and a second staging domain was registered, shipped inside live packages, and left dormant, resolving but serving nothing. The operator was building the next stage of the campaign while the current one was still running.

Registry enforcement has not displaced them. When npm removed nine of the first ten names on 1 August 2026, publishing resumed within roughly a day, and by 3 August three of those names carried live malicious versions again. The publishing accounts explain why: they are disposable, and almost every one holds a single package, so removing an account removes a package rather than an operator.

When What the operator did
When July 2026 What the operator did Operation documented on PyPI; infrastructure and third stage established
When What the operator did Third stage compiled, per the build timestamp in its own signing material
When July 2026 What the operator did Expansion into npm; execution trigger rebuilt for the ecosystem
When Ongoing What the operator did Publishing in waves, each altering what made the previous wave detectable
When What the operator did Nine of the first ten names removed by npm
When What the operator did Publishing resumes, roughly 25 hours after removal
When What the operator did Staging infrastructure serving a valid native executable
When Pre-positioned What the operator did Second staging domain registered and shipped, still dormant
Table 1. The campaign's own chronology. Every entry is derived from the packages, the infrastructure or the implant rather than from reporting about them.

CloudSEK's supply chain monitoring surfaced the first ten packages of the npm arm, and those names are what made the rest reachable. Everything that follows, the staging channel, the native loader, the implant and the target, was recovered by following them.

Tradecraft Evolution

Published version numbers cluster into distinct bands, and the bands correspond to changes in execution technique. The earliest avoid npm lifecycle scripts entirely, triggering from a single line appended to the package entry point so that importing the module suffices. A later band reintroduces a postinstall hook. The most recent returns to import-time execution and adds per-package payload variation, defeating hash-based clustering.

Version band Archives Characteristics
Version band 5.x – 9.x Archives Characteristics Shared setup.js loader, byte-identical payload across packages
Version band 11.x – 12.x Archives Characteristics postinstall hook reintroduced
Version band 20.x Archives 199 Characteristics Largest band held; same C2 batch as 33–35.x. Not previously documented.
Version band 33.x – 34.x Archives 23 Characteristics Randomised loader filename, per-package payload
Version band 35.x Archives 98 Characteristics Current generation, fragment-assembled indicators
Table 2. Archive counts are those confirmed in the corpus sweep, so they reflect our holdings rather than the operator's total output.

The 20.x band is a correction to the previously published four-generation model. It is the largest single band in our holdings and reassembles the same command and control account batch as the 33.x to 35.x bands, so it belongs to the same infrastructure era rather than to an earlier one. Any taxonomy of this campaign built on the four generations alone is incomplete.

Malware Analysis

Following the packages meant following the payload, and the payload did not cooperate. Each step below depended on the one before it, so the order is the order of work. The chain has four stages: an npm package that executes on import, a loader that retrieves its payload over DNS, a native second stage, and a Sliver implant. Recovering the fourth stage required building a purpose-made detonation environment, reading keys out of the running process, and modifying that process in memory while it ran.

Building the Analysis Environment

The sample refuses to run in commercial sandboxes, so a bespoke environment was built. A Windows 10 guest runs under a QEMU/KVM hypervisor on an isolated virtual bridge with no uplink. A local service answers every DNS query and accepts every TCP connection, minting per-name TLS certificates from a certificate authority already trusted in the guest's machine store, so the implant's HTTPS beacons complete a handshake and expose their payloads at the capture layer. Packet capture happens on the host, outside the guest's reach.

Countermeasure How the environment defeats it
Countermeasure Hypervisor detection How the environment defeats it SMBIOS vendor, product and serial strings present a Dell OptiPlex; hypervisor CPU flag disabled
Countermeasure Disk enumeration check How the environment defeats it Registry disk-enum keys populated with a consumer SSD model and serial
Countermeasure Physical-memory floor How the environment defeats it Guest allocated 4.6 GB, above the sample's minimum
Countermeasure Timing checks How the environment defeats it Full hardware virtualisation rather than emulation
Countermeasure TLS inspection avoidance How the environment defeats it Lab certificate authority pre-trusted at machine level, with a published revocation list so revocation checking succeeds
Table 3. The sample self-terminates in five commercial sandboxes. Each check is answered rather than patched out, so the sample follows its normal execution path.
Figure 1. The analysis guest. An ordinary Windows 10 desktop is the objective: every environmental check the sample performs returns the answer it expects.

Detonation, and a Loader That Cannot Run Its Own Payload

The first detonation produced no beacon. A memory capture taken during execution explained why: the third stage payload was resident in memory still compressed, its packer section names intact and no runtime initialised. The loader maps its payload reflectively, allocating memory and parsing the executable header by hand, but it performs no decompression, and the Windows third stage is packed.

Figure 2. The operational consequence is severe: an analyst who detonates the loader observes a failed load and concludes the chain is inert.

This is the most likely explanation for the sparse public behavioural record on this family. The third stage must be executed directly to be observed at all.

Recovering the Loader's Configuration

The loader stores its hostname table obfuscated rather than as plaintext strings, using a single-byte XOR key that differs per build. Recovery needs nothing more sophisticated than trying all 256 keys and scoring each result by how many well formed hostnames appear, without reference to any previously recorded key. Nine hostnames emerged. Six are the operator's own infrastructure. The other three are not, and they are what the rest of this report turns on.

Identifying the Third Stage

Sliver is an open source adversary emulation framework, a legitimate red team tool in the same category as Cobalt Strike, and routinely repurposed by criminal operators.

An operator runs a Sliver server. Each compiled implant is a full remote access agent that beacons to it and executes tasks on demand.

Finding one at the end of a dropper tells you the objective was hands on keyboard access. The npm packages are only the delivery mechanism.

The implant is a statically linked Go binary with no import table beyond the bare runtime, no package paths and no program strings, because it was compiled with an obfuscator. Conventional string-based capability profiling returns nothing. What survives is Go's reflection metadata, which the runtime requires in order to marshal messages and which therefore cannot be stripped.

Probing that metadata for the framework's command-message type names recovered 79 of 82. The three absent names are spelling variants, not missing capabilities, so the implant carries an effectively complete command set.

Capability group Confirmed present
Capability group Execution Confirmed present In-process .NET assembly execution, DLL sideloading and spawning, native and WebAssembly extensions
Capability group Privilege and identity Confirmed present Five token and privilege escalation primitives, process migration, memory dumping
Capability group Collection Confirmed present Full filesystem and in-memory file operations, screenshot capture, registry access including offline hive reading
Capability group Lateral movement Confirmed present Complete SSH client; Kerberos implementation supporting constrained-delegation abuse
Capability group Tunnelling Confirmed present SOCKS proxy, port forwarding, WireGuard
Capability group Control Confirmed present Service control, pivot listeners, session and beacon management
Table 4. Capability inventory recovered from reflection metadata. Presence of a message type is strong evidence a handler is compiled in; absence proves nothing, because a name may straddle an obfuscation boundary.

Why the Keys Are Not on Disk

The obfuscator encrypts every string constant in the binary and decrypts each one in code, at the moment it is used.

Alice ships a program containing a secret. Rather than storing the secret she stores a recipe for rebuilding it, run only when the secret is needed and discarded straight after.

So searching the file on disk for keys, hostnames or protocol strings returns nothing, however the search is transformed. The secrets exist only inside the running process.

This was confirmed exhaustively rather than assumed. The operator key, the implant keys, the protocol identifiers and even the command and control hostname are all absent from the unpacked binary under single-byte XOR, additive and subtractive constants, position-dependent XOR, and repeating-key XOR up to period eight. A full pairwise search, testing every offset in the file against an index of every eight-byte window for the relation ciphertext XOR key equals plaintext, also returned nothing.

Reading the Keys Out of Live Memory

If the secrets exist only at runtime, the runtime is where they must be read. The guest's entire physical memory is a single mapping inside the hypervisor process on the host, and guest physical addresses are a fixed offset within it, so the host can read guest memory directly while the guest runs.

age is a file encryption format built on X25519 key agreement and ChaCha20-Poly1305. Bob publishes a public key, anyone may encrypt to it, and only Bob's private key opens the result.

The implant carries the operator's public key and seals every beacon to it. Whoever captures that traffic holds ciphertext they cannot open, because the private half never leaves the operator's server.

The implant has its own key pair too, which lets the server encrypt replies to it. Recovering that private key opens the operator to implant direction only.

Scanning the guest's memory for the format's key encoding recovered both the operator's public key and the implant's own key pair. Note that a search for conventional padded base64 finds nothing here, because the format uses unpadded encoding, which is why the keys had not surfaced earlier.

Figure 3. Holding the implant's private key does not decrypt its beacons: those are sealed to the operator, and unwrapping fails on every captured message. Confirmed by attempt, not assumed.

Substituting a Key in the Running Process

The implant encrypts to whichever public key it holds in memory. Replace that value with a key we control and the next message is sealed to us instead of to the operator.

Alice writes to Bob using an address book. Edit one line of that book and she keeps writing exactly as before, but the letters arrive at our address. Her own keys are untouched.

This reads the implant's traffic. It does not impersonate the operator or issue commands, and it needs access to the running process, so it is an analysis technique rather than a remote attack.

Two representations of the operator key exist in memory: the human-readable encoded string that the implant parses, and the raw decoded bytes derived from it. Only the encoded string is authoritative. The decoded bytes are a by-product regenerated on every use, so overwriting them achieves nothing durable, which cost considerable time to establish. Overwriting the single encoded string, with the guest paused and the target verified before and after the write, causes every subsequent beacon to be sealed to a key we hold.

The Decrypted Beacon

With beacons sealed to a controlled key, the plaintext of the command and control exchange becomes readable. Each beacon carries a freshly generated symmetric session key wrapped in the framework's session-initialisation message. Every beacon carried a new one, which established that no session was ever completed during analysis: the implant was repeatedly restarting a handshake that the isolated environment never answered.

Figure 4. An early reading treated the two-byte marker as a field separator. It is a protobuf tag, and misreading it shifts every subsequent offset.

The Transport Layer

Before encryption is even reached, each message is wrapped in one of five interchangeable transport encoders, chosen at random per request. The choice is communicated to the server in a single-letter query parameter whose value is a numeric nonce; the encoder identifier is that nonce modulo 65537. Random letters are spliced into the decimal value before transmission, so the digits must be extracted before the modulus is taken.

Encoder ID Appearance on the wire
Encoder Base64 ID 909 Appearance on the wire Custom 64-character alphabet, unpadded; contains + - and _ together, and omits lowercase g
Encoder Gzip ID 17894 Appearance on the wire Ordinary compressed stream
Encoder Hex ID 51439 Appearance on the wire Lowercase hexadecimal
Encoder English ID 52716 Appearance on the wire Space-separated English words from a 512-word table, two words per byte value
Encoder PNG ID 61017 Appearance on the wire A genuine, valid PNG image carrying the payload in its pixels
Table 5. Encoder identifiers are randomised per build, which makes this set of five values a build fingerprint independent of any hostname.
Figure 5. A real captured beacon, rendered as an image. This 10x10 pixel PNG is a structurally valid image file that passes file-type validation, and its 300 bytes of pixel data are the encrypted beacon. Uploads of images to script paths are the tell, not the file format.

The image encoder is not simple pixel packing. Payload bytes are written down columns rather than along rows, and the values zero and one are escape-encoded before packing, because a raw zero byte would be lost to the image encoder's padding. A decoder that reads rows in the obvious order recovers only the first thirty bytes and appears to have found a truncated message.

How the Server Authenticates Itself

The implant checks an Ed25519 signature on every server reply before it attempts decryption, using a verification key compiled into the binary.

Bob reads only letters bearing Alice's seal, and he checks the seal before opening the envelope. A forged letter is discarded unread, and copying Alice's letters still does not let anyone write new ones.

Traffic is authenticated one way and encrypted both ways. Implant to server messages carry no signature. Server to implant messages must.

The verification keys were recovered from the same memory image, and they are the most durable indicator in this report. A signing key is generated once per server installation and signs every implant that server builds, so it identifies the operator's infrastructure independently of any hostname.

Locating the Cryptography Without Symbols

Function names are destroyed by the obfuscator, so the cryptographic routines were located structurally instead. Two properties survive obfuscation: the constants a cipher must use, and the arithmetic a construction must perform. The authenticated-encryption routines are identifiable from length arithmetic alone, because sealing appends a sixteen-byte authentication tag and opening removes one.

Figure 6. Disassembly of the two authenticated-encryption primitives. The sign of the 0x10 adjustment identifies the direction with no symbols present. The repeated comparison against a value at r14+0x10 is the language runtime's stack-growth check, which reliably marks genuine function entry points.

Measuring the proportion of indirect calls established that only names and strings are obfuscated, and that control flow is intact: 0.2 percent in the cryptographic routines and 2.09 percent across a sample of the code section, consistent with ordinary compiled Go rather than with flattened control flow, which would show fifteen to forty percent.

Two Operator Servers

Extracting the signing key from each platform build partitions the campaign, and the encryption keys partition it identically. Two independent cryptographic identities agreeing on the same split is strong evidence of two distinct server installations.

Attribute Server A Server B
Signing key ID Server A 262CA2380CC0AB31 Server B 68BAEB7614479037
Encryption key Server A age1pzpn8l7...ssd8l2g9 Server B age1rnmw52sl...jd38qrzjrnh
Builds Server A Windows x64, Linux ARM64 Server B Linux x64
Table 6. Both servers sit behind the same Cloudflare Workers estate, so shared front end infrastructure is not evidence of a shared back end.

Obfuscation was applied inconsistently across the four builds, and the inconsistency is itself useful. String-literal encryption was applied to the Windows and macOS builds but to neither Linux build. The Linux implants therefore carry the operator's keys, their own private keys and their command and control hostname in plaintext on disk. Captured server to implant traffic for those builds is decryptable offline with no runtime analysis whatsoever, and the macOS build is the only one whose server cannot be attributed by this method.

Who Is Being Targeted

Recovering the loader's configuration in the previous section produced something the package names alone could not: a list of hostnames the malware carries but does not own. Three of them belong to the victim. That is where the campaign stops looking like registry spam and starts looking like an operation with an address.

The 993 package names are the primary evidence, and they are unusually legible. A conventional typosquat imitates something popular: a character transposition of a widely installed library, or a plausible-sounding utility. These names do neither. They are internally structured, product-specific and organisationally coherent, in the way that a company's private package registry is coherent.

Namespace familyPackagesWhat it corresponds to
Namespace familybnpl-* Packages156 What it corresponds toBuy-now-pay-later product line
Namespace familydolyame-* Packages133 What it corresponds toDolyame, T-Bank's consumer BNPL brand
Namespace familydevplatform-* Packages108 What it corresponds toInternal developer platform
Namespace familybigops-* Packages97 What it corresponds toInternal operations tooling
Namespace familyboxy-* Packages70 What it corresponds toInternal component framework
Namespace familycheckout-* Packages61 What it corresponds toPayment checkout components
Namespace familyclaims-* Packages41 What it corresponds toInsurance and disputes
Namespace familytinkoff-* Packages38 What it corresponds toExplicit corporate prefix
Namespace familystatist-*, tinkoff-statist-* Packages54 What it corresponds toAnalytics client libraries
Namespace familybpm-foundation-* Packages25 What it corresponds toBusiness-process platform
Namespace familybeaver-ui-* Packages22 What it corresponds toInternal design system
Namespace familytwork-* Packages21 What it corresponds toBusiness-banking data services
Table 7. The twelve largest namespace families, covering 805 of 993 packages. The distribution is that of one company's internal registry, not of a public ecosystem.

The malware names its own target

Name analysis establishes intent but not much more, so the second line of evidence matters more. The second stage loader stores its hostnames as an obfuscated table rather than as plaintext strings. Recovering the table by exhaustive key search, rather than by reusing a previously recorded key, yields the same nine entries from the Windows, macOS and Linux ARM64 builds. Six are attacker infrastructure. Three are not.

Figure 7. The loader's decoded hostname table. XOR keys 0x9C (Windows, macOS) and 0x0E (Linux ARM64) were recovered independently of any prior analysis.

All three victim entries belong to one corporate group. CloudPayments is a Russian payment processor that T-Bank acquired in stages, taking a 55 percent holding in October 2017, 95 percent by August 2019 and full ownership in January 2023. It reads as an unrelated third party only if the ownership is not known.

The gate is also behavioural, not merely declarative. Across three independent detonations, nexus.tcsbank.ru was the first DNS query the sample issued, resolved twice, before any command and control host was contacted. The loader proceeds down the list only until one entry answers, which is why the second and third victim hostnames appear in traffic only when the first is unavailable. The malware is asking whether it has landed inside the target network.

An internal service catalogue in the package names

The analytics client family is the most revealing part of the squat list. These packages follow a rigid convention in which the trailing segment is an internal service path, and the paths are far too specific to be invented.

Figure 8. Nine of 54 analytics client names. The segments after the prefix are internal service identifiers spanning mobile banking, business banking, leasing, insurance, data warehousing and internal messaging.

A name such as beaver-ui-drawer could be guessed. A path such as sme.rko.conversionpayments.web could not. The operator worked from a real inventory of the target's internal systems, obtained before the packages were published. How that inventory was obtained is not established by this analysis, but the plausible sources are narrow: a leaked lockfile or build manifest, an exposed internal registry index, a public code-search hit on a private configuration file, or a former insider.

Squatting the proprietary siblings of published open source

T-Bank maintains a visible open source presence, including the Tramvai application framework and the Taiga UI design system. The campaign registers names in both lineages, among them tramvai-module-feature-toggle, tramvai-tinkoff-module-legacy-popup and taiga-ui-proprietary-navigation. The last is the most instructive: the public design system is Taiga UI, and the name asserts the existence of a proprietary counterpart.

This is a low-cost, high-yield reconnaissance route that generalises well beyond this victim. An organisation's public repositories reveal its internal naming conventions, its module boundaries and often the names of private packages referenced in configuration. Any company publishing open source under a corporate identity should assume the private namespace adjacent to it is inferable.

How Far the Campaign Reaches

Knowing who the operator was aiming at changes the question from what the ten packages were to how many more of them exist. The answer came from sweeping the package corpus for the loader's structure rather than for any name or hostname.

The expansion happened in three steps, and the provenance of each matters when weighing the total.

StagePackagesVersionsSource
StageInitial detection Packages10 Versions58 SourceCloudSEK supply chain monitoring
StageExtended tracking Packages18 Versions71 SourceContinued monitoring of the cluster
StageAdvisory correlation Packages811 Versions974 SourceCross-reference with published advisories
StageCorpus sweep Packages993 Versions1,156 SourceCloudSEK npm package corpus
Table 8. The ten packages from the initial detection are what made the rest reachable; every later step pivoted from indicators derived from them.

The final step swept all 190,045 npm archives held in CloudSEK's package corpus for the loader's structural signature. Because the loader assembles its hostnames from string fragments at runtime, a search for whole hostnames finds nothing; the sweep therefore keyed on fragment-safe substrings combined with the campaign's characteristic file layout. Every archive that matched was then opened and confirmed by reassembling the fragments back into command and control hostnames, which is the part that cannot occur by coincidence.

Each of the 187 new packages was verified individually rather than in aggregate. Every one yields at least one campaign hostname when the fragmented string literals in its loader are reassembled, and none was accepted on file layout alone. The loader filename is randomised per package across at least fifteen forms in this set, among them _adapter.js, _bridge.js, _init.js, _compat.js, _platform.js, _runtime.js and setup.js, so filename heuristics do not cluster them.

Figure 9. Confirmation is by reassembly, not by keyword match.

The resulting figure is a floor rather than a census. It counts only what our corpus already held, and the analytics client names are formulaic enough that the operator's published set is very likely larger still.

Indicators of Compromise

Operator Server Signing Keys

These are the highest-value pivots in this report. A minisign key identifies a Sliver server installation, not a campaign, and therefore clusters samples across infrastructure rotation, renaming and rebuilds.

Target Gate Hostnames

These are victim infrastructure, not attacker infrastructure. They should be used to recognise the campaign's targeting, and must not be blocked or sinkholed as though they were malicious.

Command-and-Control and Staging

Build Fingerprint

Impact

For the named target the exposure is direct. A dependency confusion attack succeeds when a build system resolves a private package name against the public registry, which happens through misconfiguration rather than through user error. Any build that resolved one of the 993 names would have executed native code on import, with the resulting Sliver implant providing an effectively complete post-exploitation capability inside a build environment, which is typically among the most credential-rich contexts an organisation operates.

For everyone else the applicable lesson is the reconnaissance, not the payload. The operator assembled a detailed inventory of a target's internal package and service names before publishing anything, and part of that inventory was inferable from the target's own public open source repositories. Any organisation with a visible open source presence and a private registry shares this exposure.

Nothing in this analysis evidences a successful compromise. Publication of a squatted name demonstrates intent and capability; it does not demonstrate that any build resolved it.

Recommendations

Immediate

  • Enforce registry scoping so that private namespaces cannot fall through to the public registry. This is the control that defeats the entire class of attack; blocklisting 993 names does not.
  • Enumerate which of the Appendix A names exist as private packages in your estate. Any match indicates the operator held accurate internal knowledge.
  • Block the DNS staging zones and treat sequential numbered TXT lookups as hostile regardless of the zone involved.

Detection Engineering

  • Alert on packages whose entry point requires a randomised underscore-prefixed module inside a swallowed try/catch. The filename varies; the structure does not.
  • Treat runtime string assembly of hostnames from array fragments as a strong signal in package content. It exists to defeat exactly the indicator matching most tooling performs.
  • Hunt on the minisign server keys and the encoder-ID fingerprint rather than on hostnames. Both survive infrastructure rotation.

Strategic

  • Assume your public repositories disclose your private namespace. Review what internal package and service names are inferable from published code, configuration and documentation.
  • Defensively register or reserve internal package names in public registries, prioritising those inferable from published open source.

Conclusion

TXTBOOK was first assessed as a dropper distinguished by DNS payload staging. That was correct but incomplete. DNS staging is how the payload arrives. The campaign around it is a sustained dependency confusion operation against a single financial group, run at a scale that resembles indiscriminate flooding without being indiscriminate.

The distinction has practical consequences. Read as registry flooding, the appropriate response is to block a list of names. Read as targeted dependency confusion, the appropriate response is to fix dependency resolution policy, audit which internal names are publicly inferable, and treat the package list as a symptom. Only the second response addresses the next 993 names.

The reconnaissance deserves more attention than the payload. Internal service paths embedded in the squat list indicate access to a real inventory of the target's systems, and a further portion of that inventory was inferable from the target's own published open source at no cost and no risk. The expensive part of this campaign was the reconnaissance, and it is repeatable against any organisation that publishes code under a corporate identity.

The initial ten packages surfaced by CloudSEK's supply chain monitoring are what made the remainder reachable. Every subsequent finding, the staging infrastructure, the command and control estate, the third stage implant, the two operator servers and the 993 package inventory, was reached by pivoting from that seed.

Appendix A: Complete Package Inventory

All 993 package names attributed to this campaign, grouped by namespace family and ordered by family size. Names marked with a dagger were newly identified in the corpus sweep described under How Far the Campaign Reaches, and do not appear in previously published advisory data.

bnpl  (156)

bnpl-api   bnpl-blocks-analytics   bnpl-blocks-atom-bnpl-action-card   bnpl-blocks-atom-bnpl-anchor-menu
bnpl-blocks-atom-bnpl-badge   bnpl-blocks-atom-bnpl-base-popup   bnpl-blocks-atom-bnpl-breadcrumbs
bnpl-blocks-atom-bnpl-button   bnpl-blocks-atom-bnpl-card   bnpl-blocks-atom-bnpl-carousel-line
bnpl-blocks-atom-bnpl-dangerously-html   bnpl-blocks-atom-bnpl-dolyame-button   bnpl-blocks-atom-bnpl-dropdown
bnpl-blocks-atom-bnpl-email-form   bnpl-blocks-atom-bnpl-fade-overflow   bnpl-blocks-atom-bnpl-feedback
bnpl-blocks-atom-bnpl-image   bnpl-blocks-atom-bnpl-image-popup   bnpl-blocks-atom-bnpl-info-card
bnpl-blocks-atom-bnpl-integrations-breadcrumbs   bnpl-blocks-atom-bnpl-link-avatar   bnpl-blocks-atom-bnpl-navigation-arrow
bnpl-blocks-atom-bnpl-news-card   bnpl-blocks-atom-bnpl-no-index-link   bnpl-blocks-atom-bnpl-notification
bnpl-blocks-atom-bnpl-popup   bnpl-blocks-atom-bnpl-product-card   bnpl-blocks-atom-bnpl-promocode
bnpl-blocks-atom-bnpl-radio   bnpl-blocks-atom-bnpl-range   bnpl-blocks-atom-bnpl-review
bnpl-blocks-atom-bnpl-reviews-counter   bnpl-blocks-atom-bnpl-skeleton   bnpl-blocks-atom-bnpl-small-image-card
bnpl-blocks-atom-bnpl-star-rating   bnpl-blocks-atom-bnpl-store-button   bnpl-blocks-atom-bnpl-store-button-set
bnpl-blocks-atom-bnpl-text   bnpl-blocks-atom-bnpl-toggle   bnpl-blocks-atom-container
bnpl-blocks-atom-desktop-bnpl-dangerously-html   bnpl-blocks-atom-desktop-bnpl-highlighted-text
bnpl-blocks-atom-desktop-bnpl-text   bnpl-blocks-atom-icon-loader   bnpl-blocks-atom-image-gallery
bnpl-blocks-component-story-name   bnpl-blocks-data-schema-generator   bnpl-blocks-design-system
bnpl-blocks-desktop-bnpl-action-panel   bnpl-blocks-desktop-bnpl-anchor-title   bnpl-blocks-desktop-bnpl-article-author
bnpl-blocks-desktop-bnpl-breadcrumbs   bnpl-blocks-desktop-bnpl-button-set   bnpl-blocks-desktop-bnpl-card-gallery
bnpl-blocks-desktop-bnpl-card-panel   bnpl-blocks-desktop-bnpl-category-gallery   bnpl-blocks-desktop-bnpl-container
bnpl-blocks-desktop-bnpl-documents   bnpl-blocks-desktop-bnpl-faq   bnpl-blocks-desktop-bnpl-feedback
bnpl-blocks-desktop-bnpl-floating-button   bnpl-blocks-desktop-bnpl-footer   bnpl-blocks-desktop-bnpl-grid-container
bnpl-blocks-desktop-bnpl-header   bnpl-blocks-desktop-bnpl-header-catalog   bnpl-blocks-desktop-bnpl-header-search
bnpl-blocks-desktop-bnpl-image-plus-text   bnpl-blocks-desktop-bnpl-image-with-caption
bnpl-blocks-desktop-bnpl-info-slider   bnpl-blocks-desktop-bnpl-navigation   bnpl-blocks-desktop-bnpl-news-cards
bnpl-blocks-desktop-bnpl-notification   bnpl-blocks-desktop-bnpl-partners   bnpl-blocks-desktop-bnpl-picture-gallery
bnpl-blocks-desktop-bnpl-popup   bnpl-blocks-desktop-bnpl-reviews   bnpl-blocks-desktop-bnpl-separator
bnpl-blocks-desktop-bnpl-shopping-banner   bnpl-blocks-desktop-bnpl-shopping-header   bnpl-blocks-desktop-bnpl-tab-buttons
bnpl-blocks-desktop-bnpl-table   bnpl-blocks-desktop-bnpl-text-block   bnpl-blocks-desktop-bnpl-title
bnpl-blocks-desktop-large-bnpl-reviews   bnpl-blocks-feature-partners   bnpl-blocks-fonts
bnpl-blocks-independent-bnpl-anchor   bnpl-blocks-independent-bnpl-button   bnpl-blocks-independent-bnpl-cards
bnpl-blocks-independent-bnpl-code-text   bnpl-blocks-independent-bnpl-faq   bnpl-blocks-independent-bnpl-features
bnpl-blocks-independent-bnpl-info-slider   bnpl-blocks-independent-bnpl-items   bnpl-blocks-independent-bnpl-main-banner
bnpl-blocks-independent-bnpl-main-title   bnpl-blocks-independent-bnpl-mobile-application
bnpl-blocks-independent-bnpl-navigation   bnpl-blocks-independent-bnpl-origination   bnpl-blocks-independent-bnpl-picture-gallery
bnpl-blocks-independent-bnpl-preset-container   bnpl-blocks-independent-bnpl-product-grid
bnpl-blocks-independent-bnpl-scheme   bnpl-blocks-independent-bnpl-search   bnpl-blocks-independent-bnpl-separator
bnpl-blocks-independent-bnpl-table   bnpl-blocks-independent-bnpl-text-block   bnpl-blocks-independent-bnpl-tiles
bnpl-blocks-independent-bnpl-title   bnpl-blocks-independent-bnpl-video   bnpl-blocks-markdown
bnpl-blocks-mobile-bnpl-anchor-title   bnpl-blocks-mobile-bnpl-article-author   bnpl-blocks-mobile-bnpl-breadcrumbs
bnpl-blocks-mobile-bnpl-button-set   bnpl-blocks-mobile-bnpl-card-gallery   bnpl-blocks-mobile-bnpl-card-panel
bnpl-blocks-mobile-bnpl-category-gallery   bnpl-blocks-mobile-bnpl-container   bnpl-blocks-mobile-bnpl-documents
bnpl-blocks-mobile-bnpl-faq   bnpl-blocks-mobile-bnpl-feedback   bnpl-blocks-mobile-bnpl-footer
bnpl-blocks-mobile-bnpl-grid-container   bnpl-blocks-mobile-bnpl-header   bnpl-blocks-mobile-bnpl-header-search
bnpl-blocks-mobile-bnpl-image-plus-text   bnpl-blocks-mobile-bnpl-image-with-caption   bnpl-blocks-mobile-bnpl-link-group
bnpl-blocks-mobile-bnpl-navigation   bnpl-blocks-mobile-bnpl-news-cards   bnpl-blocks-mobile-bnpl-notification
bnpl-blocks-mobile-bnpl-partners   bnpl-blocks-mobile-bnpl-picture-gallery   bnpl-blocks-mobile-bnpl-popup
bnpl-blocks-mobile-bnpl-reviews   bnpl-blocks-mobile-bnpl-separator   bnpl-blocks-mobile-bnpl-shopping-banner
bnpl-blocks-mobile-bnpl-shopping-header   bnpl-blocks-mobile-bnpl-tab-buttons   bnpl-blocks-mobile-bnpl-table
bnpl-blocks-mobile-bnpl-text-block   bnpl-blocks-mobile-bnpl-title   bnpl-blocks-playwright
bnpl-blocks-post-css   bnpl-blocks-sanitizer   bnpl-blocks-scroll   bnpl-blocks-seo-tools
bnpl-blocks-shops-api   bnpl-blocks-stories   bnpl-blocks-stories-of-block   bnpl-blocks-types-gen
bnpl-blocks-utils   bnpl-core   bnpl-forms-test-utils   bnpl-molecule-mobile-bnpl-onboarding

dolyame  (133)

dolyame-boxy-atom-bnpl-badge   dolyame-boxy-atom-bnpl-button   dolyame-boxy-atom-bnpl-card
dolyame-boxy-atom-bnpl-dangerously-html   dolyame-boxy-atom-bnpl-dolyame-button   dolyame-boxy-atom-bnpl-email-form
dolyame-boxy-atom-bnpl-image-card   dolyame-boxy-atom-bnpl-info-card   dolyame-boxy-atom-bnpl-popup
dolyame-boxy-atom-bnpl-store-button   dolyame-boxy-atom-bnpl-store-button-set   dolyame-boxy-atom-bnpl-text
dolyame-boxy-atom-container   dolyame-boxy-atom-desktop-bnpl-container   dolyame-boxy-atom-desktop-bnpl-dangerously-html
dolyame-boxy-atom-desktop-bnpl-highlighted-text   dolyame-boxy-atom-desktop-bnpl-text
dolyame-boxy-atom-icon-loader   dolyame-boxy-block   dolyame-boxy-component-story-name
dolyame-boxy-desktop-bnpl-button-set   dolyame-boxy-desktop-bnpl-card-gallery   dolyame-boxy-desktop-bnpl-card-panel
dolyame-boxy-desktop-bnpl-footer   dolyame-boxy-desktop-bnpl-header   dolyame-boxy-desktop-bnpl-image-plus-textdolyame-boxy-desktop-bnpl-picture-gallery   dolyame-boxy-desktop-bnpl-text-block   dolyame-boxy-desktop-bnpl-title
dolyame-boxy-fonts   dolyame-boxy-independent-bnpl-breadcrumbs   dolyame-boxy-independent-bnpl-button
dolyame-boxy-independent-bnpl-cards   dolyame-boxy-independent-bnpl-code-text   dolyame-boxy-independent-bnpl-documents
dolyame-boxy-independent-bnpl-features   dolyame-boxy-independent-bnpl-info-images   dolyame-boxy-independent-bnpl-info-slider
dolyame-boxy-independent-bnpl-items   dolyame-boxy-independent-bnpl-main-banner   dolyame-boxy-independent-bnpl-mobile-application
dolyame-boxy-independent-bnpl-navigation   dolyame-boxy-independent-bnpl-open-api   dolyame-boxy-independent-bnpl-origination
dolyame-boxy-independent-bnpl-partners   dolyame-boxy-independent-bnpl-picture-gallery
dolyame-boxy-independent-bnpl-preset-container   dolyame-boxy-independent-bnpl-scheme
dolyame-boxy-independent-bnpl-separator †   dolyame-boxy-independent-bnpl-swagger   dolyame-boxy-independent-bnpl-table
dolyame-boxy-independent-bnpl-text-block   dolyame-boxy-independent-bnpl-tiles †   dolyame-boxy-independent-bnpl-title
dolyame-boxy-independent-bnpl-video   dolyame-boxy-markdown   dolyame-boxy-mobile-bnpl-button-set
dolyame-boxy-mobile-bnpl-card-gallery   dolyame-boxy-mobile-bnpl-card-panel   dolyame-boxy-mobile-bnpl-footer
dolyame-boxy-mobile-bnpl-header   dolyame-boxy-mobile-bnpl-image-plus-text   dolyame-boxy-mobile-bnpl-picture-gallery
dolyame-boxy-mobile-bnpl-popup   dolyame-boxy-mobile-bnpl-text-block   dolyame-boxy-mobile-bnpl-title
dolyame-boxy-scroll   dolyame-boxy-stories   dolyame-boxy-stories-of-block   dolyame-ui-accordion
dolyame-ui-action   dolyame-ui-avatar   dolyame-ui-badge   dolyame-ui-button   dolyame-ui-calendar
dolyame-ui-carousel   dolyame-ui-chart   dolyame-ui-checkbox   dolyame-ui-clickable   dolyame-ui-codemods
dolyame-ui-colors   dolyame-ui-confirmation   dolyame-ui-constants   dolyame-ui-container
dolyame-ui-draggable   dolyame-ui-dropdown   dolyame-ui-editor   dolyame-ui-filter   dolyame-ui-flag
dolyame-ui-footer   dolyame-ui-form †   dolyame-ui-grid   dolyame-ui-group   dolyame-ui-icon
dolyame-ui-input   dolyame-ui-island   dolyame-ui-label   dolyame-ui-layout   dolyame-ui-link
dolyame-ui-list   dolyame-ui-loader   dolyame-ui-menu   dolyame-ui-mixins   dolyame-ui-money
dolyame-ui-navigation   dolyame-ui-notification   dolyame-ui-pagination   dolyame-ui-pie
dolyame-ui-popover   dolyame-ui-popup   dolyame-ui-portal   dolyame-ui-radio   dolyame-ui-react-dom-wrapper
dolyame-ui-react-version-switch   dolyame-ui-react-wrapper   dolyame-ui-scroll   dolyame-ui-select
dolyame-ui-skeleton   dolyame-ui-slider   dolyame-ui-stepper   dolyame-ui-storybook-menu
dolyame-ui-styles   dolyame-ui-swiper   dolyame-ui-table   dolyame-ui-tabs   dolyame-ui-tag
dolyame-ui-test   dolyame-ui-text   dolyame-ui-themes   dolyame-ui-toggle   dolyame-ui-tooltip
dolyame-ui-types   dolyame-ui-utils

devplatform  (108)

devplatform-api-clients   devplatform-api-endpoint   devplatform-api-v2-endpoint   devplatform-api-v2-resource-mock
devplatform-api-v2-resources   devplatform-api-v2-resources-metadata   devplatform-auth-client
devplatform-cli   devplatform-cli-contracts   devplatform-cli-plugin-generator   devplatform-cli-plugin-lint
devplatform-cli-plugin-test   devplatform-cli-spa   devplatform-confirm-input   devplatform-cra-template
devplatform-create-nx-spa   devplatform-data-table   devplatform-di   devplatform-eslint-plugin-nx
devplatform-hooks   devplatform-http-client   devplatform-humanize-network-error   devplatform-i18n
devplatform-iam-client   devplatform-integrations-tests-lib   devplatform-jscodeshift-plugin
devplatform-jscodeshift-transforms   devplatform-jscodeshift-utils   devplatform-markdown
devplatform-npm-versions-checker   devplatform-nx-devkit   devplatform-nx-gle   devplatform-nx-husky
devplatform-nx-pvm   devplatform-nx-react   devplatform-nx-spa   devplatform-nx-stylelint
devplatform-nx-svelte   devplatform-nx-ts   devplatform-po-declaration-generator   devplatform-po-loader
devplatform-po-transformer   devplatform-prettier-config   devplatform-react-form   devplatform-react-hooks
devplatform-react-micro-frontend   devplatform-react-query-s3-client   devplatform-react-rest-client
devplatform-react-scripts   devplatform-react-sentry   devplatform-react-utils   devplatform-rest-client
devplatform-rest-resources-v1   devplatform-rest-resources-v2   devplatform-s3-client
devplatform-s3-resources   devplatform-s3-uploader   devplatform-select-fields   devplatform-select-user
devplatform-snarkdown   devplatform-spa   devplatform-spa-cli   devplatform-spa-devtools
devplatform-spa-di   devplatform-spa-feature-toggle   devplatform-spa-plugin-ability   devplatform-spa-plugin-analytics
devplatform-spa-plugin-auth   devplatform-spa-plugin-beaver   devplatform-spa-plugin-cobrowsing
devplatform-spa-plugin-devtools   devplatform-spa-plugin-dom-render   devplatform-spa-plugin-error-boundary
devplatform-spa-plugin-history   devplatform-spa-plugin-i18next   devplatform-spa-plugin-i18next-instance
devplatform-spa-plugin-location   devplatform-spa-plugin-module   devplatform-spa-plugin-module-loader
devplatform-spa-plugin-notifier   devplatform-spa-plugin-query   devplatform-spa-plugin-root-devtools
devplatform-spa-plugin-root-sentry   devplatform-spa-plugin-router   devplatform-spa-plugin-s3
devplatform-spa-plugin-s3-feature-toggle   devplatform-spa-plugin-s3-router   devplatform-spa-plugin-sentry
devplatform-spa-plugin-suspense   devplatform-spa-plugin-thermostat   devplatform-spa-route-tree
devplatform-spa-testing   devplatform-spa-tokens   devplatform-spa-ui   devplatform-spa-use-track
devplatform-sre-devplatform-sre-core   devplatform-supafetch   devplatform-table   devplatform-test-pvm-is-even
devplatform-test-pvm-is-odd   devplatform-translate   devplatform-ui-kit   devplatform-ui-notification †
devplatform-utils   devplatform-vite-plugin-external   devplatform-vite-plugin-gle   devplatform-vite-plugin-preserve-css-modules
devplatform-zod-wip-api

bigops  (97)

bigops-alerts-widget   bigops-api   bigops-api-customer   bigops-api-mobile   bigops-auth
bigops-auth-cache   bigops-auth-interceptor   bigops-auth-provider-interceptor   bigops-auth-utils
bigops-awesome-viewer   bigops-backend   bigops-call-history   bigops-chat-files-hub-client
bigops-chat-messages   bigops-chat-tmsg   bigops-chat-transfer   bigops-chats   bigops-chats-client
bigops-cobrowsing   bigops-cobrowsing-client   bigops-communication-client   bigops-create-manifest †
bigops-customer   bigops-customer-processing-client   bigops-data-storage   bigops-eslint
bigops-eslint-config   bigops-external-auth   bigops-figma-to-html   bigops-file-storage
bigops-frontend-bigops-frontend-core   bigops-header-tabs   bigops-info-notices   bigops-informer
bigops-insite   bigops-jsonpath-mapper   bigops-legacy-telephony-panel   bigops-legacy-utils
bigops-logs-player   bigops-microfrontend   bigops-nitro-client   bigops-nitro-events-table
bigops-notes   bigops-offers   bigops-opening-file-handler   bigops-operator-status   bigops-outgoing-call
bigops-plugin-notification-builder   bigops-procedure-file-handlers   bigops-procedure-player
bigops-product-list   bigops-products   bigops-products-accounts-and-cards   bigops-products-bnpl
bigops-products-deposits   bigops-products-insurance   bigops-products-investment   bigops-products-loans
bigops-products-mobile   bigops-products-savings-accounts   bigops-products-timeline †
bigops-products-timeline-commons   bigops-products-travel   bigops-rx-post-message   bigops-shared-ui
bigops-statements-timeline   bigops-status-selection   bigops-storio   bigops-storio-ngrx
bigops-storio-ngrx-component-store   bigops-storio-schematics   bigops-storio-store-adapter
bigops-stylelint   bigops-tasks   bigops-tasks-client   bigops-tcrm-auth   bigops-tcrm-identity-auth
bigops-tcrm-permissions   bigops-telephony   bigops-telephony-client   bigops-telephony-ui
bigops-telephony-ui-adapter   bigops-timeline   bigops-timeline-ui   bigops-timers   bigops-tinkoff-telephony-mock
bigops-tslint   bigops-ui-kit   bigops-ui-kit-styles   bigops-ui-themes   bigops-umf-statist
bigops-videocalls   bigops-voximplant   bigops-watchdog-angular   bigops-watchdog-worker
bigops-watermark   bigops-web-analytics

boxy  (70)

boxy-adapter-tramvai   boxy-add-link   boxy-add-script   boxy-bus-providers   boxy-client-loader
boxy-constants   boxy-contract   boxy-cook   boxy-debugger   boxy-demo-tokens   boxy-devtools
boxy-devtools-tokens   boxy-docusaurus-plugin-content-docs-light   boxy-esm-shims   boxy-expander-contract
boxy-fixture   boxy-fixture-allure †   boxy-fixture-paperclip   boxy-fixture-pendel   boxy-fixture-pfa
boxy-fixture-sb   boxy-get-retry-url   boxy-global-modules-webpack-plugin   boxy-lazy-loader
boxy-lcd   boxy-legacy-providers   boxy-live   boxy-loader   boxy-make-tokens-map   boxy-maker
boxy-maker-extendable-minihost   boxy-maker-legacy   boxy-maker-page-readiness   boxy-maker-readiness
boxy-maker-scroll   boxy-maker-skeleton   boxy-maker-sticky   boxy-maker-wave   boxy-mif-tokens
boxy-mm-advisor   boxy-mm-server-loader   boxy-mmb-deps-pvm-plugin   boxy-mmb-types   boxy-mops
boxy-page-story   boxy-pkg-deps-hash   boxy-pkg-path   boxy-render-unwrap   boxy-rspack-optimize-sharing-runtime
boxy-scroll-providers   boxy-search-workspace-substrings   boxy-semver   boxy-service
boxy-sitree   boxy-sticky-providers   boxy-story-header   boxy-storybook-addon-changelog
boxy-temper   boxy-test-block   boxy-test-block-fixture   boxy-test-host   boxy-test-maker
boxy-tokens   boxy-tree-providers   boxy-use-calls   boxy-use-what-changed   boxy-validate-implicit-deps
boxy-webpack-test-utils   boxy-wrapper-rspack-plugin   boxy-wrapper-webpack-plugin

checkout  (61)

checkout-accounts-holder   checkout-base-analytics-data   checkout-cashback-logic   checkout-checkout-test-block-aff
checkout-commit-pos-order-am   checkout-common-tokens †   checkout-confirm-pos-order-sms-am
checkout-context   checkout-create-draft-agreement-am   checkout-create-pos-order-am †
checkout-create-token   checkout-desktop-accounts   checkout-desktop-bnpl   checkout-desktop-cashback
checkout-desktop-input-card   checkout-desktop-legal   checkout-desktop-pay-button   checkout-desktop-pay-widget
checkout-desktop-total   checkout-dolyame-logic   checkout-format-money   checkout-get-installment-client-agreement-am
checkout-iframe   checkout-input-card-logic   checkout-input-email-logic   checkout-legal-logic
checkout-mobile-accounts   checkout-mobile-accounts-old   checkout-mobile-bnpl   checkout-mobile-cashback
checkout-mobile-dolyame   checkout-mobile-input-card   checkout-mobile-input-email   checkout-mobile-legal
checkout-mobile-pay-button †   checkout-mobile-pay-widget   checkout-mobile-promocode
checkout-mobile-total   checkout-molecule-desktop-status-modal   checkout-molecule-mobile-status-sheet
checkout-parameters   checkout-pay-button-logic   checkout-pay-widget-logic   checkout-post-installment-client-agreement-accept-am
checkout-postpone-until-render   checkout-promocode-logic   checkout-public-tokens   checkout-resend-pos-order-sms-am
checkout-select-pos-offer-am   checkout-storage-service   checkout-storybook-default   checkout-subscription-holder
checkout-test-component   checkout-test-utils   checkout-todo-test   checkout-total-logic
checkout-types   checkout-use-boolean   checkout-use-disable-interaction   checkout-use-notifications
checkout-use-toasts

claims  (41)

claims-accounts-lib   claims-api-adapters   claims-api-clients-shared   claims-auth-api-adapters
claims-auth-domain   claims-block-visibility   claims-claims-domain   claims-constants
claims-create-client-validator   claims-domain   claims-enums   claims-fixtures-shared
claims-format-date   claims-format-masked-phone-number   claims-format-money   claims-get-base-url
claims-get-error-string   claims-get-indexed-selector   claims-get-percentage-value   claims-historical-claims-api-adapters
claims-historical-claims-domain   claims-is-mobile-layout   claims-jira-service   claims-microgen-templates
claims-navigate   claims-page-maker-types   claims-patch-url   claims-payout-offer-api
claims-payout-offer-api-adapters   claims-payout-offer-domain   claims-person-api-adapters
claims-person-domain   claims-policies-api-adapters   claims-policies-domain   claims-policy-details-api-adapters
claims-policy-details-domain   claims-shared   claims-types   claims-use-base-analytics
claims-use-callback-once   claims-use-visual-viewport-resizing

tinkoff  (38)

tinkoff-boxy-atom-text-link †   tinkoff-boxy-desktop-features-banner †   tinkoff-boxy-desktop-icons-horizontal †
tinkoff-boxy-desktop-mgm-product-filter †   tinkoff-boxy-desktop-two-panel-right-image †
tinkoff-boxy-form-desktop-sme-registration-ooo †   tinkoff-boxy-gitlab-labels   tinkoff-boxy-independent-kasko-form †
tinkoff-boxy-mobile-documents †   tinkoff-boxy-mobile-separator †   tinkoff-boxy-mobile-vivid-heading †
tinkoff-cache-path †   tinkoff-codeceptjs-storyshots †   tinkoff-codeceptjs-storyshots-alpha †
tinkoff-component-infopanel   tinkoff-component-limits †   tinkoff-component-page-loader †
tinkoff-fb-app-frame-page-height-dippy †   tinkoff-fb-fieldset-car-reference-kasko †   tinkoff-fb-rf-add-application †
tinkoff-fb-service-prefill-profile-contact †   tinkoff-mutual-mgm-form †   tinkoff-pfp-atom-desktop-carousel
tinkoff-pfp-atom-styles-tiles †   tinkoff-pfp-block-desktop-tabs †   tinkoff-pfp-block-mobile-advert-footer †
tinkoff-pfp-block-mobile-panels †   tinkoff-pfp-integration-mobile-slider-icons †   tinkoff-pfpa-tools †
tinkoff-pwa-confac-types †   tinkoff-statist-web-typed-client-test.golden.retriever   tinkoff-terminal-kit-carousel †
tinkoff-terminal-kit-datetime †   tinkoff-terminal-kit-test-commons †   tinkoff-test-app-child-app †
tinkoff-ui-action   tinkoff-ui-angular-addon-wysiwyg †   tinkoff-volna-zustate †

tinkoff-statist-browser-typed-client  (28)

tinkoff-statist-browser-typed-client-art.apps.reporegistry †   tinkoff-statist-browser-typed-client-automlplatform.nlppl.classica †
tinkoff-statist-browser-typed-client-cardsmobile.events.promotest   tinkoff-statist-browser-typed-client-coretech.statist.mobile.ci †
tinkoff-statist-browser-typed-client-dss.insurance.service †   tinkoff-statist-browser-typed-client-dwh.chimera.base †
tinkoff-statist-browser-typed-client-eventea.projects.finhealthwebmicroblocks †   tinkoff-statist-browser-typed-client-eventea.projects.smartcam †
tinkoff-statist-browser-typed-client-investaccounting.events.templatepage.mainpage †   tinkoff-statist-browser-typed-client-investing.product.pulse †
tinkoff-statist-browser-typed-client-itsa.candy.selfservicesupport.frontend.events †   tinkoff-statist-browser-typed-client-itsa.corporatemessenger.clientv1.web.events †
tinkoff-statist-browser-typed-client-jumptaxi.feature.contacts   tinkoff-statist-browser-typed-client-leasing.admin.events †
tinkoff-statist-browser-typed-client-mb.product.restrictions.arrestments †   tinkoff-statist-browser-typed-client-mb.product.tgeofencing †
tinkoff-statist-browser-typed-client-mb.reliability.android.events †   tinkoff-statist-browser-typed-client-sme.compliance.web.events †
tinkoff-statist-browser-typed-client-sme.platform.mobile.dynamicteasers.common †   tinkoff-statist-browser-typed-client-sme.platform.mobile.employees.common.events †
tinkoff-statist-browser-typed-client-sme.platform.web.companyprofile.metrics †   tinkoff-statist-browser-typed-client-sme.reporting.reporting †
tinkoff-statist-browser-typed-client-sme.rko.authorization.common †   tinkoff-statist-browser-typed-client-sme.rko.conversionpayments.web †
tinkoff-statist-browser-typed-client-sme.rko.origsmartphonepaytb.common.mobile.events †
tinkoff-statist-browser-typed-client-sme.rko.ta.ios.events †   tinkoff-statist-browser-typed-client-sme.users.origination.web
tinkoff-statist-browser-typed-client-test.softwarecenter.metrics †

statist-browser-typed-client  (26)

statist-browser-typed-client-automlplatform.nlppl.searchy †   statist-browser-typed-client-coretech.web.metrics †
statist-browser-typed-client-ddp.mentat.ui.web   statist-browser-typed-client-eventea.projects.pfpacquiring †
statist-browser-typed-client-hra.workplacer.events †   statist-browser-typed-client-investing.product.loginandauthorization
statist-browser-typed-client-itsa.digitalinterview.events †   statist-browser-typed-client-jumptaxi.feature.contacts †
statist-browser-typed-client-mb.product.analytics †   statist-browser-typed-client-mb.product.mclaccount †
statist-browser-typed-client-mb.product.operations †   statist-browser-typed-client-mb.product.payments †
statist-browser-typed-client-mb.product.sme.cards   statist-browser-typed-client-mb.reliability.android.metrics †
statist-browser-typed-client-nfs.grocery.mobile.events †   statist-browser-typed-client-risktech.uwfrontantifraud.events
statist-browser-typed-client-rubliq.platform.keycloak †   statist-browser-typed-client-sme.platform.mobile.voip.common.events
statist-browser-typed-client-sme.platform.web.productsnavigation.events †   statist-browser-typed-client-sme.platform.web.teasers †
statist-browser-typed-client-sme.rko.finance.web †   statist-browser-typed-client-sme.rko.tariffs.web †
statist-browser-typed-client-sme.salary.web.metrics †   statist-browser-typed-client-social.shorts.editor †
statist-browser-typed-client-test.jumpwork.circuitbreaker †   statist-browser-typed-client-twork.tsales.nitro.metrics †

bpm-foundation  (25)

bpm-foundation-ai-assistant   bpm-foundation-analytics   bpm-foundation-api   bpm-foundation-app-configs
bpm-foundation-auth   bpm-foundation-base-configs   bpm-foundation-bundle-optimization
bpm-foundation-common   bpm-foundation-date-range   bpm-foundation-delegate-docs   bpm-foundation-feature-flags
bpm-foundation-feedback   bpm-foundation-husky   bpm-foundation-linters   bpm-foundation-nest-keycloak-connect
bpm-foundation-oauth-client   bpm-foundation-rate-us   bpm-foundation-renovate   bpm-foundation-sentry
bpm-foundation-statist-client   bpm-foundation-storage   bpm-foundation-tcs-configs   bpm-foundation-test-jasmine
bpm-foundation-test-jest   bpm-foundation-testing

beaver-ui  (22)

beaver-ui-breadcrumbs †   beaver-ui-date-range-picker †   beaver-ui-form †   beaver-ui-form-modal †
beaver-ui-form-object   beaver-ui-grid †   beaver-ui-header †   beaver-ui-hooks   beaver-ui-icon-lock
beaver-ui-items-with-more †   beaver-ui-layout †   beaver-ui-list   beaver-ui-multi-select-with-all
beaver-ui-object-card   beaver-ui-pagination   beaver-ui-popover-card   beaver-ui-search-dropdown
beaver-ui-side-navigation   beaver-ui-smart-filter   beaver-ui-split-view   beaver-ui-subheader
beaver-ui-table

twork  (21)

twork-data-services-aggregator-api-v2-data-view-company-company-profile-mf-data-transformer †
twork-data-services-aggregator-company-sme-main-timeline-loader-with-customers †   twork-data-services-aggregator-sme-task-info †
twork-data-services-counterfree †   twork-data-services-customer-api-v2-customer-vip-status †
twork-data-services-eacq-company-service-v2-api-v1-identifiers-crm †   twork-data-services-getting-arrests †
twork-data-services-invest-box-account †   twork-data-services-ng14-aggregator-api-v2-data-view-user-b2b-create-deal-mf-config †
twork-data-services-product-design-data   twork-data-services-proxy-b2b-crm-api-v1-partners-companies-info †
twork-data-services-proxy-invest-symbols-list †   twork-data-services-proxy-prime-api-v1-account-overdraft-info †
twork-data-services-role-app †   twork-data-services-sme-agent-company-relation †   twork-data-services-sme-operations-authorizations †
twork-mf-e2e-nitro †   twork-mf-sandbox †   twork-products-taiga2-products-investment †
twork-products-taiga2-products-mobile   twork-products-taiga2-products-timeline †

delivery  (14)

delivery-ci-microforms   delivery-ci-notify   delivery-ci-pipeline-rules   delivery-ci-quality
delivery-ci-sage   delivery-ci-storybook   delivery-ci-storyshots   delivery-ci-tech-holder
delivery-ci-unit   delivery-ci-update-gitlab   delivery-ci-upgrade-form   delivery-ci-upgrade-from
delivery-ci-validate   delivery-ci-validate-tinkoff-fb

pfp  (11)

pfp-block-independent-iframe †   pfp-block-mobile-past-meetup-list †   pfp-block-mobile-steps †
pfp-block-mobile-vacancy-description   pfp-forms-independent-sme-glossary-anchor †   pfp-forms-insurance-health †
pfp-forms-mobile-sme-group-tiles †   pfp-forms-sme-loan †   pfp-forms-sme-registration-ooo †
pfp-forms-sme-sitebuilder †   pfp-integration-mobile-heading

cardsmobile  (9)

cardsmobile-cardsmobile-core   cardsmobile-collection †   cardsmobile-command-utils   cardsmobile-data-utils
cardsmobile-event-emitter   cardsmobile-http   cardsmobile-notifications   cardsmobile-rs-api
cardsmobile-storage

cobrowsing  (9)

cobrowsing-cobrowsing-core   cobrowsing-configs   cobrowsing-decorators   cobrowsing-exception-filter
cobrowsing-k8s-module   cobrowsing-logger   cobrowsing-redis-module   cobrowsing-test-utils
cobrowsing-utils

accounts  (8)

accounts-appointment †   accounts-card-design-picker †   accounts-constants †   accounts-ddos-shield †
accounts-final-form †   accounts-forms †   accounts-limits †   accounts-loading-state †

cards  (6)

cards-forms-cards-core-clone   cards-forms-clone-cards-core-clone   cards-forms-clone-credit-clone
cards-forms-clone-debit-clone   cards-forms-credit-clone   cards-forms-debit-clone

ded  (6)

ded-aa-common-ded-aa-common-core   ded-aa-liza-ded-aa-liza-core   ded-aa-liza-ui-components
ded-ps-ded-ps-core   ded-ps-events-ded-ps-events-core   ded-pwa-bnpl-forms-demo

sme  (6)

sme-auth-core †   sme-crm-services-sme-crm-services-core †   sme-foundation-frame-manager †
sme-rko-finance-front-shared-entity-groups-models †   sme-scripts-cli †   sme-scripts-shared-library-webpack-plugin †

constructor  (5)

constructor-blocks-common   constructor-blocks-landings   constructor-blocks-mailings
constructor-blocks-renderer-landings   constructor-blocks-renderer-mailings

eacq  (5)

eacq-acq-menu   eacq-acq-menu-core   eacq-api   eacq-auth   eacq-payform-core †

hubert  (5)

hubert-application-get-document-preview-am   hubert-appointment-v2-task-create-am †   hubert-document-actual-insurance-rules-am †
hubert-react-query   hubert-verify-primary-email-am †

fb  (4)

fb-cards-form-no-resident-information †   fb-forms-form-boilerplate-contacts †   fb-hr-sites--boxified-form-meetup-subcribe †
fb-insurance--boxified-form-vzr-test †

pfa  (3)

pfa-autotests-reporter   pfa-errors †   pfa-prettier-config †

tcb-web  (3)

tcb-web-copy-to-clipboard †   tcb-web-header   tcb-web-images

a.poltoradnev  (2)

a.poltoradnev-package-b †   a.poltoradnev-package-c †

bpm  (2)

bpm-ng-security   bpm-renovate-renovate-config

certificates  (2)

certificates-revocation-sw   certificates-revocation-utils

deposits  (2)

deposits-common   deposits-overnight

dp  (2)

dp-owners   dp-web-header

eventea  (2)

eventea-diag †   eventea-router

platform  (2)

platform-ui-codemods †   platform-ui-island

saas  (2)

saas-f-lego-landings †   saas-f-testing †

specials  (2)

specials-mvno-client †   specials-obid-webpack †

sso  (2)

sso-tramvai-lib-roles †   sso-tramvai-module-context-auth †

time  (2)

time-linters-webapp-eslint-config †   time-webkit-tag †

tramvai  (2)

tramvai-module-feature-toggle †   tramvai-tinkoff-module-legacy-popup †

trapp  (2)

trapp-check-logs †   trapp-configuration

travel (2)

travel-core-typings-reducers †   travel-core-utils-object †

aasp (1)

aasp-tent-aasp-tent-core †

astra (1)

astra-core

astrabet (1)

astrabet-templates-kit

betweb (1)

betweb-openapi-axios

bi  (1)

bi-core-bi-core-core

blocks  (1)

blocks-sahred-atom-mobile-app-bar-action

bs  (1)

bs-bs-core

caen  (1)

caen-caen-core

candy  (1)

candy-candy-core

capacity  (1)

capacity-capacity-core

cbp  (1)

cbp-exchange-government-cbp-exchange-government-core

cnb  (1)

cnb-cnb-core

contract  (1)

contract-contract-builder

csc  (1)

csc-csc-core

custom  (1)

custom-plugins-request-factory

dc  (1)

dc-dc-core

digital-interview  (1)

digital-interview-digital-interview-core †

dippy  (1)

dippy-invertor

dlp  (1)

dlp-dlp-core

docflow  (1)

docflow-cryptopro

docsmate  (1)

docsmate-docsmate-core

dwhetlfront  (1)

dwhetlfront-dwhetlfront-core

dws  (1)

dws-frontend-dws-frontend-core

dwsai  (1)

dwsai-dwsai-core

evo  (1)

evo-web-base-analytics-data †

finance  (1)

finance-business-company-id-models †

form  (1)

form-bnpl-dolyame-component-object

fry  (1)

fry-page-maker-types †

invest  (1)

invest-module-cookie †

lifestyle  (1)

lifestyle-test-utils †

nxify  (1)

nxify-unic †

olimpbetkz  (1)

olimpbetkz-api

peter  (1)

peter-desktop-peter-big-column

prokit  (1)

prokit-core †

pvm  (1)

pvm-autodoc †

scandoc  (1)

scandoc-scandoc-core †

shopping  (1)

shopping-shared-atom-mobile-cart-counter †

sotqa  (1)

sotqa-test †

statist  (1)

statist-statist-core †

taiga  (1)

taiga-ui-proprietary-navigation †

tailwind  (1)

tailwind-hide-scrollbar

tms  (1)

tms-x-headers †

tui  (1)

tui-react-mobile-styles †

volna  (1)

volna-boxy-di-test †

vvvedernikov  (1)

vvvedernikov-test-another-test †

Appendix B: Publisher Accounts

Every package in Appendix A was queried against the public npm registry to recover its publishing account. The result is a fleet of single-use accounts sharing one disposable mail domain and one rigid naming convention: a twelve-character lowercase alphanumeric username, with the mailbox local part identical to the username.

PropertyObserved
Accounts recovered Observed153 disposable, plus one legacy account
Mail domain Observedweb-library.net (all 153)
Username pattern Observed^[a-z0-9]{12}$ (153 of 153)
Mailbox equals username Observed153 of 153
Packages per account Observed130 accounts hold 1; 21 hold 2; 2 hold 3
Legacy account Observedalekseil-spam <[email protected]>
Table 10. One account per package is the operator's default. Registry enforcement against an individual account therefore removes almost nothing.

Two detections follow directly and neither depends on a package name. Any npm account whose username matches the twelve-character pattern and whose registered mailbox is that same string at web-library.net should be treated as operator-controlled. The domain itself is a high-confidence indicator: it appears as the registered mailbox for 153 accounts publishing packages that impersonate one bank's private namespace.

Coverage is partial and the figure should be read as such: 178 of the 993 names resolve to one of these accounts today. Packages the registry has since removed are now held by npm's own security account, and unpublished names return nothing at all, so the fleet is certainly larger than 153. The list below gives every account recovered, with its mailbox.

Disposable publisher accounts (153), all @web-library.net

[email protected]   [email protected]   [email protected]
[email protected]   [email protected]   [email protected]
[email protected]   [email protected]   [email protected]
[email protected]   [email protected]   [email protected]
[email protected]   [email protected]   [email protected]
[email protected]   [email protected]   [email protected]
[email protected]   [email protected]   [email protected]
[email protected]   [email protected]   [email protected]
[email protected]   [email protected]   [email protected]
[email protected]   [email protected]   [email protected]
[email protected]   [email protected]   [email protected]
[email protected]   [email protected]   [email protected]
[email protected]   [email protected]   [email protected]
[email protected]   [email protected]   [email protected]
[email protected]   [email protected]   [email protected]
[email protected]   [email protected]   [email protected]
[email protected]   [email protected]   [email protected]
[email protected]   [email protected]   [email protected]
[email protected]   [email protected]   [email protected]
[email protected]   [email protected]   [email protected]
[email protected]   [email protected]   [email protected]
[email protected]   [email protected]   [email protected]
[email protected]   [email protected]   [email protected]
[email protected]   [email protected]   [email protected]
[email protected]   [email protected]   [email protected]
[email protected]   [email protected]   [email protected]
[email protected]   [email protected]   [email protected]
[email protected]   [email protected]   [email protected]
[email protected]   [email protected]   [email protected]
[email protected]   [email protected]   [email protected]
[email protected]   [email protected]   [email protected]
[email protected]   [email protected]   [email protected]
[email protected]   [email protected]   [email protected]
[email protected]   [email protected]   [email protected]
[email protected]   [email protected]   [email protected]
[email protected]   [email protected]   [email protected]
[email protected]   [email protected]   [email protected]
[email protected]   [email protected]   [email protected]
[email protected]   [email protected]   [email protected]
[email protected]   [email protected]   [email protected]
[email protected]   [email protected]   [email protected]
[email protected]   [email protected]   [email protected]
[email protected]   [email protected]   [email protected]
[email protected]   [email protected]   [email protected]
[email protected]   [email protected]   [email protected]
[email protected]   [email protected]   [email protected]
[email protected]   [email protected]   [email protected]
[email protected]   [email protected]   [email protected]
[email protected]   [email protected]   [email protected]
[email protected]   [email protected]   [email protected]
[email protected]   [email protected]   [email protected]


Related Blogs