Абстрактная техническая иллюстрация на тёмно-синем фоне: небольшой плоский векторный куб-пакет слева от центра, от которого тонкая янтарная линия загибается вниз к компактному изображению рабочей станции разработчика; веер тонких бирюзовых и стально-синих нитей расходится вправо от рабочей станции к множеству маленьких круглых узлов на нестрогой сетке, два из них загибаются обратно вверх к кубу-пакету, а один узел в крайней правой части расположен отдельно и не соединён. Люди, логотипы или текст отсутствуют. Логотип OrcaRouter наложен в правом нижнем углу.
AI Safety Incidents

npm SDK Tensorlake был отравлен, чтобы украсть учетные данные AI-агентов — и стереть ваш домашний каталог, если вы отозвали токен

Автор

Alistair Wren

Дата публикации

Новые модели · 20Все модели →
Бенчмарки: Artificial Analysis · обновляется ежедневно
Назад ко всем статьям

8 октября 2026 года отравленный релиз tensorlake — npm SDK для платформы Tensorlake, который выполняет недоверенный код, сгенерированный LLM, в изолированных песочницах, — попал в реестр npm. Версия 0.5.144 была опубликована в 01:12:07 UTC; исследователи Socket сообщают, что заметили её примерно одиннадцать минут спустя. Это был неавторизованный релиз. Tensorlake заявляет, что злоумышленник отправил код напрямую в GitHub-репозиторий компании и опубликовал его через собственный процесс выпуска проекта, поэтому пакет имел корректные сведения npm о происхождении — подписанную аттестацию, указывающую, где он был собран, для исходного кода, который уже был отравлен. Компания утверждает, что версия была доступна примерно 1 час 40 минут, с 01:12 до 02:54 UTC, прежде чем npm удалил её.

Полезной нагрузке не требовался импорт и не требовался работающий агент. Хук preinstall в манифесте пакета запускал node lib/setup.mjs, обфусцированный загрузчик, который установил среду выполнения Bun на машину и использовал её для запуска lib/Math_Symbol.js, похититель учётных данных и самораспространяющийся червь размером 856 501 байт. Любой, кто выполнил обычную установку этой одной версии, запустил её. Поскольку это был latest тег пакета, именно эта версия была той, которую установка по умолчанию выбирала, пока она была активна.

Ценность этой записи не в размере полезной нагрузки — о семействе Shai-Hulud уже писали и раньше. Дело в списке целей. Наряду с токенами npm и GitHub, учётными данными AWS, секретами Kubernetes и Vault, SSH-ключами, файлами .env, паролями из браузеров и криптовалютными кошельками, эта сборка целенаправленно собирала конфигурацию инструментов для разработки с ИИ и файлы MCP — связанные с Claude Code, Cursor, Kiro, Windsurf и Zed, по данным Socket. Она также записывала свои собственные .claude/settings.json и .vscode/tasks.json в доступные ей репозитории, чтобы код запустился снова, когда разработчик в следующий раз откроет этот проект в Claude Code или VS Code. Это атака на цепочку поставок, нацеленная прямо на машины, на которых люди создают агентов.

Как релиз утек из аккаунта мейнтейнера

Socket, SafeDep и Aikido 8 октября опубликовали каждый свой анализ, и их описания механизма совпадают. Уровень достоверности этой записи в архиве — A, она не отмечена как оспариваемая, и ничто из найденного в источниках не противоречит ни одному из них.

В разборе SafeDep отслеживаются восемь коммитов, внесённых в ветку main репозитория в период с 01:20 по 03:57 UTC 7 октября, приписываемых учётной записи администратора репозитория и сделанных через веб-интерфейс GitHub. Коммит, который архив называет первым вредоносным, — это 41b38f0, ревизия, затрагивающая четыре файла, от 03:44:48 UTC 7 октября, сообщение которой — "Add files via upload", и которая имеет verified-подпись GitHub — GitHub подписывает коммиты, сделанные в его собственном веб-редакторе, поэтому "verified" отражает среду редактирования, а не личность. Этот коммит не затронул ни одного файла, кроме typescript/lib/Math_Symbol.js и typescript/lib/setup.mjs, согласно GitHub API: полезная нагрузка, и ничего больше.

SafeDep предлагает интерпретацию поведения оператора, обозначенную как его собственный вывод: первое изменение preinstall-хука нарушило синтаксис JSON файла, и через девятнадцать минут последовал коммит с исправлением, что, по его описанию, согласуется с тем, что человек редактировал файлы в браузере, а не сценарий автоматизированного конвейера. Ни одна публикация Socket, SafeDep, Aikido, StepSecurity или Tensorlake не называет метод, с помощью которого был захвачен аккаунт мейнтейнера.

Злоумышленнику не потребовался токен npm. SafeDep сообщает, что аккаунт запустил проектный publish_npm.yaml рабочий процесс вручную 8 октября в 00:08 UTC; журнал сборки показывает файлы полезной нагрузки внутри опубликованного tarball. Релиз использовал npm trusted publishing, а родственные tensorlake-native-* пакеты, опубликованные из того же запуска, имеют действительный провенанс SLSA — провенанс, который действительно валиден для сборки отравленного исходного кода. Релиз был выпущен через двенадцать минут после запуска рабочего процесса.

В архиве зафиксировано, что репозиторий оставался скомпрометированным примерно 20 часов до срабатывания публикации — этот промежуток приведённая ниже хронология разбирает коммит за коммитом — что согласуется с собственным заявлением Tensorlake о том, что атакующий опубликовал «через наш собственный релизный процесс».

Что делала полезная нагрузка после запуска

A screenshot of a card titled 'A credential stealer with an AI-tooling list', kicked 'tensorlake@0.5.144 · what the payload collected', subtitled 'Per Socket, SafeDep and Tensorlake's advisory — grouped by what each collector reached for'. Three columns of paired labels sit side by side. 'Publishing and source control' lists npm tokens from .npmrc, the token API and the OIDC exchange; GitHub tokens with a repository and workflow permission check; GitHub Actions secrets read from Runner.Worker memory; and cryptocurrency browser wallets across 14 extensions. 'AI agent tooling' lists ~/.claude.json for Claude Code settings; ~/.kiro/settings/mcp.json for Kiro MCP config; .cursor, Windsurf and Zed config and MCP files; and .claude/settings.json and .vscode/tasks.json written back into reachable repositories. 'Cloud, cluster and endpoint' lists AWS via STS, SSM Parameter Store and Secrets Manager; Kubernetes service-account tokens and kubeconfigs; HashiCorp Vault at 127.0.0.1:8200; SSH and GPG keys, .env files and Docker and CLI files; the Wallets row, wallet.dat, Exodus, Electrum and MetaMask; and saved passwords via a downloaded HackBrowserData binary. A footer reads 'Collector list per Socket's initial assessment, SafeDep's decoded payload analysis and the Tensorlake advisory (GHSA-8g63-53c9-65j2). Socket flags AI development tooling for Claude Code, Cursor, Kiro, Windsurf and Zed by name.'

Оценка Socket выявляет сбор данных из локальных файлов, сред CI, источников Kubernetes и Vault, а также эксфильтрацию, закрепление и выполнение удалённо предоставленного кода. SafeDep воспроизвела декодированную полезную нагрузку: lib/setup.mjs загружает Bun 1.3.13 из официального релиза на GitHub и использует его для запуска червя, и он завершает работу на раннерах — цель — машина разработчика, а не сборочный агент. SafeDep также обнаружила, что полезная нагрузка содержит обычную копию того же загрузчика, которую она внедряет в другие пакеты и репозитории.

Полезная нагрузка проходит через три слоя обфускации, которые SafeDep декодировала без выполнения образца: массив строк обфускатора, пользовательский шифр, использующий соль svksjrhjkcejg — идентичную той, о которой сообщалось в более раннем keyv-компрометации — и AES-256-GCM для одиннадцати встроенных файлов, включая скрипты, хуки и два открытых ключа RSA. Её первая строка содержит тег сборки WORMTAG='tensrlake', с опечаткой, по которой SafeDep отличает эту сборку от продолжающегося повторного заражения.

Помимо конфигурации AI-инструментов, список собираемых данных широк и конкретен: ~/.claude.json и ~/.kiro/settings/mcp.json для настроек агентов; SSH-ключи, файлы облачных CLI, .npmrc, .env, файлы Docker и Kubernetes, а также история команд оболочки; wallet.dat плюс данные Exodus, Electrum и Ledger Live и расширения браузерных кошельков, такие как MetaMask и Phantom; AWS STS, SSM Parameter Store и Secrets Manager; секреты GitHub Actions, считываемые из памяти процесса Runner.Worker; и токены служебных аккаунтов Kubernetes и токены Vault. Он загружал бинарный файл для каждой платформы с iseekaigogo[.]com/hbd/<platform> и запускал его с аргументами "dump -b all -c password -f json" — аргументами, соответствующими инструменту HackBrowserData, хотя SafeDep отмечает, что сам бинарный файл не был восстановлен, и называет идентификацию предположительной. Червь, добавляет SafeDep, завершает работу при русских локалях.

A screenshot of a card titled 'Persistence, resolution, and the wipe trigger', kicked 'tensorlake@0.5.144 · staying power', subtitled 'The release was removed in under two hours; the implant it installed was built to outlive that.' Three columns sit side by side. 'Persistence: gh-token-monitor' lists a systemd user service on Linux, a LaunchAgent com.user.gh-token-monitor on macOS, a scheduled task with a PowerShell monitor on Windows, and files left behind in ~/.local/bin, ~/.config and %LOCALAPPDATA%. 'Endpoint resolution order' lists, in numbered order, the primary endpoint iseekaigogo[.]com:443/router; an Ethereum contract 0xb614155Fd88...1Df091B9; the public RPC endpoints that contract is read through, given as ~30 by Socket and 35 by SafeDep; signed GitHub commits found by the string thebeautifulmarchoftime; and public dead-drop repositories described as 'Shai-Hulud: Here We Go Again'. 'The dead-man switch' lists a poll of api.github.com/user every 60s, for 24 hours, firing on a 40x response to that token, with the effect rm -rf ~/ (advisory: home dir), armed only if the account has no orgs, and the order that matters: remove monitor, then revoke. A footer reads 'Per Socket's blog and SafeDep's analysis, with the C2 endpoint, the contract and the gh-token-monitor service confirmed in Tensorlake's advisory. Socket and SafeDep give different counts of the public RPC endpoints the contract is read through.'

Персистентность сделана намеренно. Socket писала о полезной нагрузке: «Такое сочетание расширяет риск за пределы одного украденного ключа API», — добавляя, что «персистентность может сохранить доступ атакующего после удаления затронутой зависимости». SafeDep сообщает, что червь коммитит файлы хуков Claude Code и VS Code, указывая в качестве git-автора claude, а также создаёт рабочий процесс «Run Copilot», который выгружает все секреты репозитория, а затем удаляет запуск и ветку. В рекомендации Tensorlake разработчикам, проверяющим свои журналы, советуют искать ветку с именем dependabot/github_actions/format/setup-formatter и коммиты, автор которых указан под той же идентичностью.

Распространение идёт на украденных токенах, а не на второй уязвимости. Socket сообщает, что червь перечисляет пакеты, связанные с публикующим идентитетом жертвы, формирует provenance Sigstore и перепубликует отравленные версии. SafeDep добавляет деталь: внутри CI он добавляет git-зависимость и подписывает свой результат с помощью Fulcio и Rekor — так что provenance у пакета, который он публикует следующим, так же валиден, каким был provenance у 0.5.144.

Двадцать часов перед публикацией

Разрыв между первым вредоносным коммитом и релизом — это та часть хронологии, которая объясняет, как отравленный пакет стал носителем действительного провенанса. Все времена указаны в UTC; время коммитов — по данным SafeDep и GitHub API, а время публикации, удаления и выпуска рекомендаций — Tensorlake.

A screenshot of a card titled 'Compromised source in the repo, then one manual publish', kicked 'tensorlake@0.5.144 · the ~20-hour window', subtitled 'All times UTC. Commit times per SafeDep's timeline and the GitHub API; publish and removal times per the Tensorlake advisory.' Three columns sit side by side. '7 October 2026 — source, main branch' lists 01:20 setup.mjs and Math_Symbol.js added; 01:24 the preinstall script added; 01:43 package.json JSON repaired; 02:13 to 03:44 four loader and payload revisions; 03:44 commit 41b38f0, the payload commit; 03:57 the version set to 0.5.144. '8 October 2026 — release and removal' lists 00:08 publish_npm.yaml started by hand; 01:12:07 version 0.5.144 published to npm; 01:22:02 issue #1014 opened by StepSecurity; 01:23:10 flagged by Socket, about eleven minutes in; 02:54 the version removed, after roughly 1h 40m live; 20:24 version 0.5.145 published next. '9 October 2026 — disclosure closed' lists advisory GHSA-8g63-53c9-65j2; pull request #1016, source reverted and 0.5.145 cut; issue #1014 closed as completed; 0.5.143 as the last version stated unaffected; and no usage seen, per Tensorlake's user-agent telemetry. A footer reads 'The ~20-hour gap between the first rogue commit and the publish is SafeDep's commit timeline, and matches the archive's own upstream record.'

Сопоставьте данные: файлы полезной нагрузки попали в основную ветку репозитория 7 октября, версия была установлена на 0.5.144 в 03:57 того утра, и ничего не публиковалось до тех пор, пока аккаунт вручную не запустил рабочий процесс выпуска в 00:08 на следующий день. Скомпрометированный исходный код пролежал в ветке примерно двадцать часов, прежде чем собственный конвейер проекта собрал его. В этом и состоит вся атака на путь выпуска — никакой поддельной аттестации, никакого повторно опубликованного tarball. Сведения о происхождении были точны: они относились к сборке кода, которого вообще не должно было быть в ветке.

Выключатель мертвеца

Та часть этого инцидента, которая не имеет прецедентов в предыдущих компрометациях npm, — это то, что происходит после того, как жертва реагирует правильно. Если украденный токен GitHub принадлежит аккаунту без организаций, сообщает SafeDep, червь устанавливает gh-token-monitor: сервис, который опрашивает api.github.com/user с помощью этого украденного токена каждые 60 секунд в течение 24 часов. Когда GitHub возвращает 40x — как это происходит в момент отзыва токена — монитор запускает предоставленный злоумышленником обработчик и удаляет домашний каталог пользователя.

Socket описывает тот же компонент как использующий монитор PowerShell, который сохраняется через запланированное задание ONLOGON, выполняющее предоставленный злоумышленником обработчик через Invoke-Expression, и отмечает, что код содержит буквальную строку "IfYouRevokeThisTokenItWillWipeTheComputerOfTheOwner", встречавшуюся в более ранних волнах Shai-Hulud. SafeDep называет этот обработчик как rm -rf ~/. Формулировка Socket явно указывает, что это срабатывает независимо от того, где происходит отзыв; монитор локален, а отзыв — нет. Все четыре источника — Socket, SafeDep, StepSecurity и собственное уведомление Tensorlake — приводят один и тот же порядок операций: сначала удалите монитор, затем отзовите.

Что установлено, а что лишь выведено

Архив фиксирует отсутствие подтверждённого вреда для этой записи, и источники подтверждают это, а не просто не противоречат этому.

• Tensorlake заявляет об отсутствии использования этой версии. В своём уведомлении компания сообщает, что отслеживает использование SDK через заголовки user-agent и «не видела никакого использования этой версии с момента её выпуска». Это заявление поставщика о его собственной телеметрии, а не проверенный аудитом подсчёт.

• Цифра загрузок — это не число заражений. Socket сообщает, что у пакета примерно 12 000 загрузок в неделю и отмечает, что это описывает общее использование пакета, а не загрузки вредоносной версии; SafeDep приводит около 106 000 загрузок в месяц для того же пакета. Ни то, ни другое не доказывает, что 0.5.144 где-либо запускалась. Окно помеченного релиза Socket и двухчасовое окно Tensorlake согласуются с версией, которая была удалена до того, как успела распространиться.

• Собственные ошибки полезной нагрузки могли ограничить её. SafeDep обнаружила две: путь распространения npm требует ./dist/Math_Symbol.js на диске и останавливается без него, а внедрённый загрузчик ищет ai_init.js, тогда как червь записывает math_init.js. SafeDep называет эти находки ограничениями распространения, и в статье они рассматриваются как трактовка кода в рамках одного анализа, а не как гарантия того, что ничего не распространилось.

• Аттестация происхождения подлинна. Это не спорное утверждение: npm provenance фиксирует, где был собран пакет и каким рабочим процессом, и в данном случае он зафиксировал правду — собственный релизный workflow проекта, собиравший отравленный исходный код. В бюллетене Tensorlake это сказано дословно: «пакет несёт действительные данные npm provenance». Socket формулирует ту же мысль прямо, отмечая, что provenance «говорит, где был собран пакет. Он не говорит, что код безопасен».

Некоторые детали, находящиеся в обращении, — это утверждения, основанные на одном анализе, и воспринимать их следует именно так. Конкретный механизм, с помощью которого атакующий получил учётную запись мейнтейнера, не был опубликован ни одной из фирм, проводивших расследование, ни Tensorlake. Адрес Ethereum-контракта и его роль в разрешении эндпоинта фигурируют в отчётах SafeDep и Socket (Socket утверждает, что полезная нагрузка разрешает свой эндпоинт через Ethereum-контракт, используя примерно 30 публичных RPC-эндпоинтов с резервным механизмом на GitHub, тогда как SafeDep указывает количество RPC на уровне 35 и сообщает, что 8 октября контракт вернул iseekaigogo.com), а Tensorlake подтверждает эндпоинт и контракт в своём уведомлении о безопасности. Точное количество RPC в двух отчётах различается; сама схема — нет.

Об одном пробеле в публичных данных стоит сказать прямо. Ни один из источников, рассмотренных для этой статьи, не сообщает о подтверждённом заражении ниже по цепочке — другого пакета или репозитория, отравленного червём с использованием учётных данных, украденных из этого. Код распространения существует, и SafeDep с ним ознакомилась; неизвестно, привёл ли он ко второй жертве.

Что изменилось после этого?

Реакция была быстрее и более скоординированной, чем инцидент.

• Версия исчезла. По данным Tensorlake, она была доступна с 01:12 до 02:54 UTC 8 октября; SafeDep сообщает, что npm удалил её. При прямом запросе в реестр npm 10 октября для этой статьи, 0.5.144 больше не появляется среди опубликованных версий пакета, а последний тег пакета — 0.5.148. В своём уведомлении Tensorlake указывает разработчикам на 0.5.143 как на последнюю незатронутую версию.

• Рекомендация опубликована вместе с исправлением. В рекомендации Tensorlake GHSA-8g63-53c9-65j2 указано, что в PyPI и crates.io tensorlake пакеты, а также tensorlake CLI не были затронуты — затронут был только пакет npm — и что версии 0.5.143 и более ранние не затронуты. SafeDep сообщает, что сопровождающие откатили исходный код в пул-реквесте #1016 и выпустили 0.5.145.

• Ветка раскрытия информации на удивление полна. Исследователь из StepSecurity Ашиш Курми открыл задачу на GitHub #1014 в 01:22:02 UTC 8 октября — примерно через десять минут после публикации — указав на собственный разбор StepSecurity того, что она называет червём «hostage token». Оливер Смит из Aikido прокомментировал, что полезная нагрузка была внесена в коммите 41b38f0 и идентифицировал её как новый инфостилер Shai-Hulud. Мейнтейнер Дэвид Лоренцо — идентификатор мейнтейнера, указанный в npm-пакете как calavera — ответил 9 октября подтвердив рекомендацию по безопасности и закрыв задачу как выполненную в тот же день. Сам заголовок задачи отражает доказательства: он начинался как отчёт о «возможной вредоносности», а к моменту закрытия был переименован так, чтобы описывать полезную нагрузку как вредоносную без оговорок.

• Кампания, к которой это относится, крупнее, чем этот пакет. Socket, SafeDep и Aikido все относят релиз Tensorlake к волне ChainDrop / Mini Shai-Hulud, впервые задокументированной в начале августа 2026 года, жертвами которой в npm стали keyv, cacheable и связанные пакеты. В рекомендации Tensorlake указано то же самое, со ссылкой на Socket, SafeDep и Aikido.

Рекомендация Socket для всех, кто установил эту версию, состоит в том, что хост следует считать скомпрометированным: проверьте файлы блокировки, манифесты и журналы сборки на наличие этой версии; удалитеgh-token-monitor сервис до отзыва любого токена; затем смените учётные данные с чистой машины. В рекомендациях Tensorlake указан тот же порядок действий и предлагается проверить реестры пакетов и репозитории, в которые раскрытые токены могли записывать данные, на наличие неожиданных релизов. В статье Aikido советуют задать ignore-scripts=true в .npmrc, чтобы заблокировать хуки установки, и считать любую машину, на которой выполнялся этот хук, полностью скомпрометированной.

Это одна запись: о ней сообщили другие, и здесь она приводится со ссылкой на них. Остальные задокументированные инциденты с агентами — каждый со своими источниками, степенью серьёзности, уровнем уверенности и отметкой об оспаривании — находятся в архиве инцидентов ИИ — так что компрометацию цепочки поставок, нацеленную на инструментарий агентов, можно рассмотреть рядом со всем остальным, что действительно произошло.