In the early hours of 23 September 2026 UTC, three malicious versions of @memtensor/memos-cloud-openclaw-plugin were published to npm, and a malicious MemoryOS 2.0.34 followed on PyPI at 05:25 UTC. Both belong to MemTensor's MemOS memory layer for AI agents. Socket found that each bundled a Go binary called sckit that harvested credentials and secret environment variables and reported to servers under skyleen.fr. The malicious versions have since been removed from both registries.
What's affected
| Registry | Package | Malicious | Last known good |
|---|---|---|---|
| npm | @memtensor/memos-cloud-openclaw-plugin | 0.1.21, 0.1.23, 0.1.25 | 0.1.20 |
| PyPI | MemoryOS | 2.0.34 | 2.0.33 |
OSV tracks them as MAL-2026-16476 (npm, alias GHSA-mhjf-v53x-7p87) and MAL-2026-16475 (PyPI, alias PYSEC-2026-3987).
Socket's timeline shows an odd pattern on npm. Between the malicious releases, versions 0.1.22 and 0.1.24 were published that match 0.1.20 apart from version strings. When we checked on 18 October, the npm registry no longer listed 0.1.21, 0.1.23 or 0.1.25, and latest pointed to 0.1.24. PyPI's latest is 2.0.33, and 2.0.34 is gone.
How the payload runs, per Socket:
- npm plugin:
sckitstarts when the OpenClaw gateway starts, and again on every memory recall, with the user's prompt passed to it in theSCKIT_EVENT_TEXTenvironment variable. Prompts sent while a bad version was loaded should be treated as exposed. - PyPI package: starts on
import memos, with no API call needed. - Both: runs detached in the background with the host's full environment, and looks for credential files and secrets for npm, PyPI, GitHub, GitLab, AWS, Vault, SSH and other services. Strings in the binary suggest it could republish packages with stolen registry tokens.
Socket notes the npm releases came from an existing maintainer account but carried no gitHead, so they were not built by the project's CI. The malicious code first appeared in commits that no branch or tag references. How the attacker got access has not been published.
How to check if you're exposed
Search lock files, requirements files and SBOMs for both names:
grep -rnE 'memos-cloud-openclaw-plugin|"0\.1\.2[135]"' --include=package-lock.json --include=pnpm-lock.yaml --include=yarn.lock .
grep -rniE '^memoryos([=<>~ ]|$)' --include='requirements*.txt' --include=poetry.lock --include=uv.lock .
pip show MemoryOS 2>/dev/null | grep -i '^version'
Then look for the runtime pieces on hosts and containers that ran either package:
pgrep -fl sckit
ls -la ~/.openclaw/.cache/runtime/ ~/.memos/.cache/runtime/ 2>/dev/null
find / -path '*/.sckit/*' -name sckit 2>/dev/null | head
python3 -c "import memos, os; print(os.listdir(os.path.dirname(memos.__file__)))" 2>/dev/null | grep -E '_stage0|_sckit|_pypi_bridge'
In DNS, proxy and egress logs, search for skyleen.fr and any subdomain from 23 September onward.
What to do now
- Pin to the known-good versions Socket names.
0.1.20for the npm plugin and2.0.33for MemoryOS. Socket says0.1.24matches 0.1.20 apart from version strings, but it was published inside the attack window, so prefer 0.1.20 until the maintainers publish a verified release. Avoid floating ranges that could resolve to a version you haven't reviewed. - Treat hosts that loaded a bad version as compromised. That includes CI runners and containers. Kill
sckitprocesses and delete the package directories and the.cache/runtimefolders above. - Rotate every secret reachable from the user's home directory and environment. Registry tokens, Git hosting tokens, cloud keys, Vault tokens, SSH keys and
.envvalues. - Check your own publishing. If an affected host held npm or PyPI tokens, review your packages for releases you didn't make.
- Block
skyleen.frand alert on historical hits. - Review what users sent through the plugin. If people pasted secrets or customer data into prompts while a bad version ran, handle that as a data exposure.
Why it matters
AI agent plugins sit in an unusually sensitive spot. A memory plugin sees what users type, runs inside a long-lived gateway process with the developer's environment, and is often installed quickly to try something new. Here the attacker didn't need to search the disk for interesting data. The host program handed every prompt to the payload.
The missing gitHead is a useful signal. A release built by a project's CI normally records the commit it came from, and provenance attestations make that verifiable. A sudden release from a maintainer's laptop, with no matching commit on any branch, deserves a pause before you install it. Automated checks can catch that difference if you look for it.
Try it yourself
For one npm dependency you rely on, compare the last three versions' registry metadata:
npm view some-package@1.2.3 gitHead dist.attestations --json
Note which releases have a gitHead and an attestation and which don't. A sudden change in how a package is published is worth an alert, even before anyone labels it malicious.
Click through it at your own pace, or open it full screen.
Quick check
When did the PyPI payload run?
On import memos. The user didn't need to call any API.
Why should prompts be considered exposed for the npm plugin?
Socket found the plugin launched sckit on every memory recall and passed the user's prompt to it in an environment variable.
What publishing detail set the malicious npm releases apart?
They had no gitHead, meaning they were not built from a commit by the project's CI workflow, and the malicious commits were not on any branch or tag.