Risky Business: Self-Deleting npm Packages Explained

Risky Business: The Anti-Proctoring Packages That Delete Themselves

TL; DR

The packages delete themselves. Over fifteen days in August, one npm operator published nine packages under nine names from seven accounts and withdrew eight of them personally. The window each was left standing fell from thirteen hours to seventeen minutes. The technique is not theirs alone: seven further packages from unrelated operators, carrying entirely different payloads, were withdrawn by their own publishers at a median of 37.8 minutes. The artifact is gone before most defensive processes run, and the install has already happened.

Everything the registry exposes rotates; the payload does not. All nine shipped a byte-identical bin/ directory, the same SHA-256 digests across three package names and two accounts, fifteen days apart. Content identity is the only tracking signal this operator did not break, and the single package they never withdrew is the reason any of it could be examined at all.

The people installing it are not victims, and are badly exposed anyway. This tool is not aimed at developers: it defeats remote exam proctoring through hidden screen capture, on-screen text extraction and answers returned to an overlay, so whoever runs npm install is the intended beneficiary rather than the target. They also run an unsigned binary that decrypts their browser cookie store, from an account that deletes itself within the hour. They cannot receive a fix, cannot audit later what they ran, and cannot report harm without disclosing why they installed it.

Ecosistema: npm, same pattern observed on PyPI. Período: June–August 2026.

La medida

Dwell time is the interval between a package appearing and disappearing, computed from the registry’s own metadata. These are measurements, not estimates.

PREMIUM Published (UTC) Left standing
moidev 2026-08-16 16:31 5h 58m
moidevx 2026-08-17 03:24 13h 23m
moidevz 2026-08-26 03:38 12h 56m
moideva 2026-08-26 18:58 1h 33m
amicat 2026-08-28 20:48 17m
bmcat 2026-08-28 21:17 47m
eyevox 2026-08-28 21:44 21m
moidevh 2026-08-31 03:22 47m

Read in publication order, the window contracts by more than a factor of forty — from 13h 23m at the widest to 17 minutes at the narrowest. The first four packages average eight and a half hours; the last four average thirty-three minutes. On 28 August, three packages were published and withdrawn inside a single 77-minute span.

The contraction is not gradual drift. It is concentrated between 26 and 28 August, and what changed was the operator’s estimate of how long a package can safely stand.

Every withdrawal here is publisher-initiated. The registry document for each shows an empty maintainer list beside a time.unpublished record — the signature of an unpublish rather than a takedown. A ninth package, eyiouss, was never withdrawn at all; it is the reason any of this could be examined.

A second withdrawal regime exists and should not be confused with this one. Where only the payload version is pulled and the package survives, the median is roughly 73 hours across 31 packages. Those replacements arrive in scripted batches and leave an empty husk — a package.json declarando un principal that ships no file — whose only function is to keep the name held. That regime is housekeeping on a schedule. This one is a tuned exposure window.

What Survives the Deletion

Nine of these packages are one operator: moidev, moidevx, moidevz, moideva, moidevh, amigo, bmcat, ojo y eyiouss. The names share no convention — anyone tracking the moidev stem would have missed more than half of them — and they were published from seven separate accounts, nearly all registered on disposable-email domains.

What they share is a manifest entry with no reason to exist:

"bin": { "": "bin/kalamasha-tool.js" } }

We retained copies of five before removal. Across all five, the entire compartimiento/ directory is byte-identical:

Archivo SHA-256
bin/kalamasha-tool.js 0c293be90c10c8a7618d25f4c4811fa63612b7bc5199b86e10bb31eb36c716dd
bin/uia_extract.exe e2fda5aa8397799669f29258f69e803cf05d322c1d93269eef6754ca024c3865
bin/chrome_cookies.ps1 21b61ec810331850d72423c40c5448e6cd310c4cfad6aed54f92cd7e7cac5f3a
bin/stealth_capture.ps1 c9baf5897c03b941c54f736df5af9243ad28794a9e69746d41d4f4a34065589c
bin/uia_extract.py c20b38c63efd9221930095c54b42cdd5adffa1972c44c4578226cbe1e4dbac3e
bin/uia_get_text.ps1 4a3f3ada0bdf13aa8e89edb8af817c7816296147dae5be25f169ff960d28a87c

Two further signals agree. Three of the packages were published at 03:24, 03:38 and 03:22 UTC on 17 August, 26 August and 31 August — a sixteen-minute band across fifteen days and three separate accounts. And two of those accounts were registered on the same disposable email domain, tying them together independently of the code.

Las cuentas

These are the npm publisher accounts that shipped the payload, with the package each uploaded and how long it was left standing:

Publisher account PREMIUM Email domain Left standing
letol39119 moidev:1.0.0 hutdot.com (disposable) 5h 58m
so1d1a7i27 moidevx:1.0.0 ogzmail.com (disposable) 13h 23m
lololololol435678 eyiouss:3.0.21, 3.0.22, 4.0.1 ogzmail.com (disposable) still published
tagadob568 moidevz:1.0.0 ebflyai.com (disposable) 12h 56m
tayihim489 moideva:1.0.0 prodbits.com (disposable) 1h 33m
sahihai123 amicat:1.0.0, bmcat:2.0.9, eyevox:1.0.0 consumer webmail 17m / 47m / 21m
heytoshan moidevh:1.0.0 consumer webmail 47m

so1d1a7i27 y lololololol435678 compartir la ogzmail.com registration domain — the link that ties the account still hosting a live copy to the account that published under the moidev stem two weeks earlier. sahihai123 published and withdrew three separate package names inside a single 77-minute window on 28 August. heytoshan registered, published moidevh, and withdrew it 47 minutes later; it remains that account’s only publication.

Every one of these accounts is email-unverified, carries no verified source-control link, and has published no unrelated project. Six of the seven no longer host anything.

One package was never withdrawn. eyiouss:4.0.1 has been published since 16 August and was still resolvable at the time of writing. Eight artifacts were unrecoverable; the campaign was reconstructed entirely from the one that stayed. An operator who deletes reflexively will still, eventually, leave one standing.

Qué hace la herramienta

eyiouss:4.0.1 describes itself on npm as a “High-performance DOM utility and diagnostic bridge for modern web applications”. It is a screen-capture and answer-retrieval tool built to run underneath remote exam proctoring, and the source says so in its own words.

The launcher copies the Electron runtime under a false filename before running it, and names what it is avoiding:

// 🛡️ STEALTH BINARY NAME: Must NOT collide with real Testpad!    const STEALTH_NAME = 'SearchApp.exe';    

The same file’s header calls it an Anti-Proctor Ghost Watchdog and implements a respawn loop that backs off further the faster it is killed. Elsewhere the process raises its own priority to fight proctoring CPU throttling, pushes the exam application out of the foreground when it takes focus, and terminates a Safe Exam Browser child process with taskkill /F /T.

Capture runs through a bundled PowerShell script invoked with the execution policy disabled and the window suppressed, with on-screen text harvested in parallel through Windows UI Automation. The queued images and text are submitted to a language-model session in an embedded browser and the answer rendered in an always-on-top overlay. That connection is deferred deliberately, and the code gives the reason: to avoid permanent NETSTAT exposure.

The package also ships chrome_cookies.ps1, headed CHROME COOKIE EXTRACTOR, which decrypts Chrome and Edge cookies via DPAPI and reads the cookie database from hasta four browser profiles. Stated precisely: we did not find that script invoked by name from any shipped JavaScript file. What is present and reachable is the consuming half, which injects stored session cookies into the embedded browser.

The Position of the People Who Install It

Nobody installs this by accident. It has no function outside a monitored exam, and its output is an answer overlay on the user’s own screen. The deceptive package description is not aimed at the installer at all — it is cover directed at the registry and at anyone auditing the machine afterwards. The installer knows what they are getting; the description exists so the grabar of the install does not reflect it.

The parties harmed are third parties who never touched npm: the institution running the exam, the other candidates, and everyone downstream who relies on the credential meaning something.

But the installer is not therefore safe, and this is the part worth spelling out, because the disappearing-package model itself is what creates the exposure.

They are running an unsigned binary from nobody. uia_extract.exe ships compiled, with no source and no build provenance, from an account that no longer exists. The same file, by hash, has been republished by letol39119, so1d1a7i27, lololololol435678, tagadob568, tayihim489, sahihai123 y heytoshan across fifteen days. There is no maintainer to ask, no repository to read, and no history to check — every identity that shipped it deleted itself.

They have granted it exactly the capabilities malware needs. A hidden process, running under a false name, that decrypts the browser cookie store, reads the screen continuously, terminates security software and respawns when killed. Today the cookie decryption is scoped to one vendor’s domains and serves the user’s own session. That scope is a one-line change, and nothing about the delivery model would surface it if it changed — a new name, a new account, a new upload.

They cannot receive a fix. The package is unpublished within the hour. There is no update path, no advisory, and no way to learn later that the version they installed differed from the one their friend installed. Whatever is on the machine stays there.

They cannot audit what they ran. Their lockfile now references a package that no longer resolves. If they later wanted to establish what was on their machine — after an incident, or a dispute — the registry has no record to give them.

And they cannot report harm. A user compromised by this tool cannot file an abuse report without disclosing why they installed it. That is not incidental. Registry abuse reports are a main channel by which short-lived packages are caught, and this operator’s user base is structurally incapable of using it. The only remaining adversary is automated scanning — which a seventeen-minute window is calibrated against.

The victim structure and the dwell time are the same fact seen twice. It also explains the anomaly in the timeline: if withdrawal were driven by fear of complaints, the long-lived popular package would go first. It did not, because the complaints never come.

The Same Pattern, a Different Operator

The proctoring operator is not alone in this, and the clearest parallel runs on an unrelated payload.

Between 24 and 27 August, four npm packages appeared under names borrowed from two-factor authentication tooling — auth-otp, Secretokey-2fa, Secretokey2fa y 2fa-Secretokey — carrying sixteen versions between them. Each was published from its own freshly registered account (beayilol, jeanpaul686g, zzzalipay, dluuuuu), all email-unverified, none with a verified source-control link, none publishing anything else. Every one was withdrawn by its own publisher.

The payload has nothing to do with exam software. It reads browser credential stores and Discord tokens, and walks an Xbox Live authentication ladder against the Minecraft launcher to capture a refresh token. Here the installer is the victim in the ordinary sense — which is what makes the comparison useful, because the delivery behaviour is identical regardless.

The four cycles ran in publication order at 274.9, 37.8, 37.6 and 35.7 minutes: one long first attempt, then convergence on a figure that holds. Three further packages belong to other operators again and show the same withdrawal behaviour.

PREMIUM Operador Left standing
octopus-action recon beacon 2.5 min
mt-ts-serverless-starter recon beacon 9.6 min
2fa-Secretokey 2FA cover names 35.7 min
Secretokey2fa 2FA cover names 37.6 min
Secretokey-2fa 2FA cover names 37.8 min
auth-otp 2FA cover names 274.9 min
hex-encode-utils otros 724.2 min

Seven packages, median 37.8 minutes, none of them related to exam software.

Three unrelated payloads, three unrelated sets of accounts, one shared technique.

Why the Window Is Closing

Something changed in the registry’s environment during 2026, and the obvious candidate is that scanning got faster.

Until recently, malicious packages were found principally after the fact: by a user noticing something, by a downstream report, or by a periodic sweep. Detection latency was measured in days. That is no longer the only regime. Near-real-time analysis of newly published packages — Xygeni’s own Malware Early Warning among a growing set of such systems across the industry — closes the gap between publication and inspection to something much shorter.

An operator facing that has two options. Make the payload harder to recognise, or shorten the time it is available to be looked at. The second is cheaper, requires no engineering, and the registry provides the mechanism for free: within npm’s 72-hour unpublish window a publisher may remove a package unilaterally, and the name can be reused afterwards. Withdrawal costs the operator nothing.

The timing fits. Both operators above converge on a figure and hold it rather than drifting, which is what a tuned parameter looks like rather than a matter of convenience. And the parameter they converged on — tens of minutes — sits precisely where it would need to sit to beat an automated inspection pass while still leaving the package installable long enough to reach its audience.

This remains a hypothesis. Timing data cannot separate a response to scanning from a response to registry enforcement, or from ordinary operational maturation as these operators learn from one another. What can be said without inference is narrower and still useful: the exposure window is now short enough that any control depending on inspecting a package after publication is racing a clock the operator sets. Controls that do not depend on inspection — quarantine by age, disabled install scripts — are unaffected by that race, which is the practical reason to prefer them.

Qué hacer al respecto

Quarantine by age. Nothing in the dwell-time table survived thirteen hours. A policy that no dependency version is installable until it has existed for 72 hours would have excluded every one of them without detecting anything.

Disable install scripts by default. –ignorar-scripts removes the delivery mechanism the credential-stealing packages in this corpus depend on. It does not help against a tool the user runs deliberately, which is precisely the distinction worth understanding.

Treat disappearance as a security event. Alert when an installed dependency stops resolving. Your lockfile, not the registry, is where the evidence still lives.

Track content, not names. The six digests above match regardless of package name, publisher account or version string — every field this operator demonstrated they will change. A registry-side rule that a newly registered account withdrawing its only package within an hour is a reportable pattern would have fired on every package here, on metadata alone.

And if you are considering installing something in this category: the properties that make it hard for a registry to catch are the same properties that leave you with no recourse. There is no publisher to hold responsible, no update path, and no record of what you ran.

indicadores

Tipo Valor
Archivo bin/kalamasha-tool.js — non-standard bin entry point
SHA-256 0c293be90c10c8a7618d25f4c4811fa63612b7bc5199b86e10bb31eb36c716dd
SHA-256 e2fda5aa8397799669f29258f69e803cf05d322c1d93269eef6754ca024c3865
Proceso SearchApp.exe written beside a legitimate Electron runtime
Comportamiento powershell -ExecutionPolicy Bypass -File con windowsHide: true
Comportamiento taskkill /F /T targeting a Safe Exam Browser process
metadatos Empty maintainer list beside a time.unpublished grabar
Cuentas letol39119, so1d1a7i27, lololololol435678, tagadob568, tayihim489, sahihai123, heytoshan
Registración de ogzmail.com, hutdot.com, ebflyai.com, prodbits.com — disposable-mail domains

Referencias

  • npm unpublish policy — the 72-hour window within which a publisher may remove a package without intervention.
sca-tools-software-herramientas-de-analisis-de-composicion
Priorice, solucione y proteja sus riesgos de software
Obtén tu cuenta gratuita.
Sin tarjeta de crédito.

Asegure el desarrollo y entrega de software

con la suite de productos Xygeni