The sckit Worm: The First Supply-Chain Attack Built for AI Agent Memory
💡 Tool Tip:API Key Rotator, JSON Formatter, AI Regex Builder
Supply-chain attacks have long targeted developer tooling, but the target moved. According to Forkast's analysis, between 02:23 and 05:55 UTC on September 23, 2026, malicious versions of MemTensor's open-source memory framework MemOS, used by LLMs and AI agents and carrying roughly 11,500 GitHub stars, appeared on both npm and PyPI. The payload was a cross-platform Go-based credential stealer called sckit. It is the first documented supply-chain worm aimed specifically at agent memory infrastructure. What makes it worth studying is the technique: it used no postinstall hooks at all, hiding instead inside code paths the agent calls anyway. Here is how it worked, what it stole, and how to check whether you were exposed.
The attack stopped using postinstall hooks and hid inside normal code paths
What happened
Per Forkast, the compromise began with direct commits to MemTensor's own GitHub repositories, published through the maintainer account leason1974 without a gitHead. Clean versions appeared between malicious ones, 0.1.22 and 0.1.24 on npm, which researchers at Semgrep read as the attacker testing payload optimization while access lasted. The affected versions are npm's @memtensor/memos-cloud-openclaw-plugin 0.1.21, 0.1.23 and 0.1.25, plus PyPI's MemoryOS 2.0.34.
# The incident, in one object. Timeline, versions and C2 details are
# from Forkast's analysis of the MemTensor/MemOS compromise.
INCIDENT = {
"window_utc": "2026-09-23 02:23 -> 05:55",
"target": "MemTensor MemOS (memory framework for LLMs/agents, ~11.5k stars)",
"registries": ["npm", "PyPI"],
"payload": "sckit (cross-platform Go credential stealer)",
"affected": {
"npm": "@memtensor/memos-cloud-openclaw-plugin 0.1.21, 0.1.23, 0.1.25",
"pypi": "MemoryOS 2.0.34",
},
"c2": ["skyleen[.]fr"],
"campaign": "cloud-openclaw-semi-nuclear",
"not_after": "2026-10-23",
}Why install-time scanners missed it
sckit does not use postinstall hooks, because everyone scans those now. Instead it wires itself into functionality the agent calls anyway. On the npm side, the malicious versions add lib/sckit.js, which launches a platform-specific Go binary from a hidden .sckit/ directory at two points: when the OpenClaw agent gateway starts, and every time the memory-recall hook fires. The second trigger is the dangerous one. Each recall passes the user's current prompt text to the binary through the SCKIT_EVENT_TEXT environment variable, which StepSecurity reads to mean that anything a developer asked the agent while an affected version was loaded should be treated as exposed. On the Python side the attack is arguably more elegant: the payload triggers through memos/log.py, where configure_logging() appends a call to memos._stage0.trigger(). Because Python executes module-level statements on import, a simple import memos reaches the logging hook, which reaches the launcher. No install-time script required.
# Why install-time scanners missed it. sckit uses no postinstall
# hooks. It attaches to functionality the agent calls anyway.
# npm side: lib/sckit.js launches a Go binary from .sckit/, at two
# moments: when the OpenClaw gateway starts, and on every memory recall.
# Each recall passes the current prompt text via SCKIT_EVENT_TEXT.
# python side: import triggers everything, no install script needed.
# memos/log.py
from . import _stage0 # legitimate-looking
def configure_logging():
_stage0.trigger() # <-- the payload rides along
# Lesson: if you only scan lifecycle scripts, you miss attacks that
# ride ordinary import paths.The target was publishing tokens, the most sensitive credentials in a supply chain
What it steals, and where it goes
The sckit binary harvests credentials from 13 categories: npm tokens, PyPI tokens, GitHub and GitLab personal access tokens, AWS access keys, Hugging Face tokens, HashiCorp Vault tokens, Slack tokens, Stripe live keys, SendGrid keys, SSH keys, generic JWTs, and environment variables matching secret-shaped patterns. Captured data routes to command-and-control infrastructure at skyleen[.]fr and its subdomains. The embedded configuration, decoded from a base64 --config64 argument, names the campaign cloud-openclaw-semi-nuclear, sets $HOME as the inventory root, and configures three external fronts with /config, /status and /batch endpoints. The campaign's not_after timestamp corresponds to October 23, 2026.
# What you would grep for right now, across lockfiles, requirements
# files, poetry.lock and SBOMs. Run these across every repo and CI cache.
# Shell one-liners (safe to run, read-only)
grep -r "memos-cloud-openclaw-plugin" --include="package-lock.json" .
grep -r "MemoryOS" --include="requirements*.txt" --include="poetry.lock" .
find . -type d -name ".sckit" -not -path "*/node_modules/*"
# Python: audit an installed environment for the malicious version
def check_env(env):
risks = []
for dist in env.iter_installed():
if dist.name == "MemoryOS" and dist.version == "2.0.34":
risks.append(("pypi", dist.name, dist.version))
return risksWhy the agent stack is a good target
Forkast points to three properties. Credential density is high, because agents need secrets to function, so the machines running them tend to hold publishing tokens, cloud keys and API credentials. Few teams threat-model pip install for AI projects. And the prompts themselves carry sensitive data that conventional supply-chain attacks never touched. The sckit binary also includes functions named recursivePublish, prepareRemoteNode, prepareRemotePython and prepareRemoteWorkflow, plus embedded GitHub Actions templates that request execution on push. The design is clear: steal publishing tokens, use them to push compromised versions of other packages, repeat. As of Forkast's writing, no confirmed downstream spread had been observed. The capability exists in the code; its real-world activation had not been documented.
# Credential rotation order matters. Rotate everything reachable
# from any host that imported an affected version, registry tokens
# first, because those let the worm spread.
ROTATION_ORDER = [
"npm + PyPI publish tokens (registry first)",
"GitHub and GitLab personal access tokens",
"AWS access keys",
"HashiCorp Vault tokens",
"Hugging Face tokens",
"Stripe live keys, SendGrid keys, Slack tokens",
"SSH keys and generic JWTs",
"any env var matching secret-shaped patterns",
]
BLOCK = ["skyleen.fr", "*.skyleen.fr"] # add to DNS egress denylistMemory frameworks are now explicitly part of the agent attack surface
What to do now
If your environment could have imported an affected version, work in this order. Pin npm to 0.1.20 and PyPI to 2.0.33, or remove the packages entirely. Rotate every credential accessible from any affected host, registry tokens first because those let the worm spread, then source control, cloud, Vault and SSH keys. Block skyleen[.]fr and all subdomains at your DNS egress denylist. Finally, search lockfiles, requirements files, poetry.lock and SBOMs for the affected package names, and audit your own releases if an affected host held a publish token.
# Prevention for the next one: make installs boring again. Pin, use
# deterministic installs, and give CI publish tokens the least power
# they can possibly hold.
HARDENING = {
"pin": "exact versions in lockfiles; never auto-bump on install",
"ci": "npm ci / pip --require-hashes, not npm install",
"tokens": "scoped, short-lived publish tokens; require 2FA to publish",
"sandbox": "build agents in ephemeral containers without prod secrets",
"egress": "deny outbound by default; allowlist registries only",
"monitor": "alert on any new outbound domain, not just on CPU spikes",
}
# If a compromised host held a publish token, audit your own releases.The bigger lesson
Put this in context. This week's agent-security coverage has already moved across the kernel, the VM and the browser; the MemTensor incident adds the package ecosystem, and specifically the memory frameworks agents depend on. It also exposes a fact many teams have ignored: install-time scanning is not enough, because an attack can ride an ordinary import path. And the memory layer, which touches prompts, credentials and long-lived state at the same time, is becoming the highest-value, least-visible part of the stack. The real response is not a new scanner but making installs boring again: pin exact versions, use deterministic installs, give CI publish tokens the least power they can hold, and keep production secrets and default egress out of your build environments.
📌 Frequently Asked Questions
What actually happened?
Per Forkast, between 02:23 and 05:55 UTC on September 23, 2026, malicious versions of MemTensor's open-source memory framework MemOS appeared on npm and PyPI carrying a cross-platform Go credential stealer called sckit. It is the first documented supply-chain worm aimed specifically at agent memory infrastructure.
Which versions were affected?
According to Forkast, npm's @memtensor/memos-cloud-openclaw-plugin 0.1.21, 0.1.23 and 0.1.25, and PyPI's MemoryOS 2.0.34. Clean versions appeared between the malicious ones, which researchers read as the attacker testing payloads.
Why didn't install-time scanners catch it?
Because sckit uses no postinstall hooks. Analysis shows it triggers on the npm side via lib/sckit.js at gateway start and on every memory recall, and on the Python side via memos/log.py's configure_logging() at import time. It hides in normal code paths, so scanners that only inspect lifecycle scripts miss it.
Which credentials does it steal?
Per Forkast, sckit harvests 13 categories, including npm and PyPI tokens, GitHub and GitLab personal access tokens, AWS access keys, Hugging Face tokens, HashiCorp Vault tokens, Slack tokens, Stripe live keys, SendGrid keys, SSH keys, generic JWTs and secret-shaped environment variables, sending them to skyleen[.]fr and its subdomains.
What should I do right now?
Pin npm to 0.1.20 and PyPI to 2.0.33 or remove the packages; rotate every credential reachable from an affected host, registry tokens first; block skyleen[.]fr and its subdomains; search lockfiles, requirements files and SBOMs for the affected package names; and audit your own releases if a affected host held a publish token.