astropod Contact Us sign in

MemTensor versions Contained a Credential Stealer Published With Tokens Taken From Its Own Release Pipeline

Published on gastropod.io on 09-27-2026

MemTensor's npm and PyPI Releases Contained a Credential Stealer Published With Tokens Taken From Its Own Release Pipeline


Tags: vulnerability-intelligence, software-supply-chain-security, npm, pypi, credential-theft, ci-cd-security, ai-agents


On September 23, an attacker published malicious versions of two packages from MemTensor, the developer of the MemOS memory framework for LLMs and AI agents: @memtensor/memos-cloud-openclaw-plugin 0.1.21, 0.1.23 and 0.1.25 on npm, and MemoryOS 2.0.34 on PyPI. Each version bundles sckit, a Go credential stealer compiled for Windows, Linux and macOS (The Hacker News). The npm plugin connects OpenClaw agents to MemOS Cloud memory. The main MemOS repository on GitHub has about 11,500 stars (SC Media).

According to The Hacker News, the attacker took the publish tokens from MemTensor's own GitHub Actions release pipelines by pushing commits that made the release workflow expose the npm or PyPI token. The npm releases came from the same maintainer account as MemTensor's earlier legitimate releases, but without a gitHead, the commit hash npm writes into package metadata when a release is published from a git checkout. SC Media reported that the releases lacked that field and therefore did not come from the project's normal CI workflow.

A report filed on the plugin's GitHub repository (issue #173) compared the npm artifacts with the source. No tag or branch in the repository included the code in the three releases it examined: the newest tag was v0.1.20, and the newest commit on its branches was dated August 14. Version 0.1.20 unpacked to 271,704 bytes in 18 files. Version 0.1.21, published at 02:23 UTC, unpacked to 43,654,487 bytes in 24 files, about 160 times larger. The additions were lib/sckit.js and six platform-specific binaries of roughly 7.4 MB each. Version 0.1.22, published at 03:45 UTC, restored the clean 0.1.20 content. Version 0.1.23 reintroduced the payload four minutes later. When the report was filed, npm's latest tag pointed to 0.1.23, so a default install received it.

Both packages start the payload at runtime. The npm plugin launches sckit when the OpenClaw gateway starts and again on every memory-recall event, passing it the full process environment and, during recall, the user's prompt text. The PyPI package launches the binary as soon as an application imports the memos module. The gateway process handles user input and can inherit the credentials available to the account that runs it, on a developer workstation or in an automated job. Install-script controls, including npm v12's default refusal to run dependency install scripts without approval, do not apply to either trigger.

The sckit binary searches the user's home directory for credential files such as .npmrc, .vault-token and SSH keys, and it reads environment variables that hold tokens, passwords, API keys and connection strings, including NPM_TOKEN and PYPI_API_TOKEN. Its target list includes AWS keys, GitHub and GitLab tokens, and Hugging Face, HashiCorp Vault, Slack, Stripe and SendGrid keys. It sends what it finds to servers under skyleen[.]fr, accepts signed tasks from its command server, and contains templates for installing itself into npm packages, Python packages and GitHub Actions workflows (The Hacker News). A second function in lib/sckit.js reads NPM_TOKEN and NODE_AUTH_TOKEN directly. The operator can use any publish token the implant collects to repeat this attack against another project. The Hacker News reported that it was unclear whether any packages beyond MemTensor's were affected.

The Injective SDK theft in July, which I have already covered here, also used the project's own release automation. In that case the trusted-publisher pipeline built and published a release from the attacker's commits. MemTensor's pipeline exposed its tokens, and the attacker published from outside CI. In both cases the malicious release came from an account with a record of legitimate releases, so the publisher field matched every release before it.

The malicious versions are no longer available on either registry. The latest clean releases are 0.1.24 on npm and 2.0.33 on PyPI, and 0.1.20 is the last npm release with a matching tag in the repository. Treat any host that loaded plugin 0.1.21, 0.1.23 or 0.1.25, or imported MemoryOS 2.0.34, as compromised. Stop any running sckit process and delete the implant's state directory: $HOME/.openclaw/.cache/runtime for the npm plugin and $HOME/.memos/.cache/runtime for MemoryOS, according to the configurations Socket decoded from both builds. Rotate every secret reachable from that user's home directory and environment, block skyleen[.]fr and its subdomains, and check DNS and egress logs for connections to it since September 23. If the host held npm or PyPI publish tokens, review your own packages for versions you did not publish.

Finding the machines that ran it

Rotating the right credentials requires a list of hosts: every developer laptop, CI runner and agent server that pulled one of the four malicious versions between 02:23 UTC on September 23 and their removal. npm and PyPI publish download counts, not a record of which of your machines downloaded what. A lockfile records what a project resolved, but it records neither the machine that installed it nor the date.

Gastropod proxies npm and PyPI behind one URL and records every pull with the package PURL and digest, who pulled it, the client, the source IP and the time. For this incident, you search that record for pkg:npm/%40memtensor/memos-cloud-openclaw-plugin at 0.1.21, 0.1.23 or 0.1.25 and pkg:pypi/memoryos at 2.0.34. The results list the people and runners whose secrets need rotation, across both ecosystems. Clean and malicious versions alternated on the same day, so results from a search by package name or publish date overstate the exposure; the version-level record separates 0.1.22 and 0.1.24 from the three builds that contained sckit. The dependency graph shows which internal services depend on either package, directly or through another dependency, and a block rule at intake keeps all four versions out of new builds while teams rotate credentials.

The product page has screenshots of the audit record and dependency graph. If you set one npm or pip client's registry to Gastropod through app.gastropod.io/start, Gastropod builds the same record from your own pulls.

Sources

← news