TanStack став новою жертвою атаки на ланцюг постачання

💡 Усі статті, обговорення, новини про Front-end — в одному місці. Приєднуйтесь до Front-end спільноти!

Вчора розробники TanStack повідомили, що невідомі зловмисники здійснили атаку на ланцюг постачання та скомпрометували одразу 42 пакети екосистеми, випустивши 84 шкідливі версії.

Дослідники виявили, що зловмисники не зламували особисті акаунти розробників, а використали ланцюжок вразливостей в архітектурі GitHub Actions, щоб безпосередньо опублікувати заражені версії прямо в реєстр npm.

Як все відбувалося

Атака відбулася через обхід стандартних механізмів безпеки. Хакери створили замаскований форк репозиторію TanStack/router і надіслали PR. Через використання вразливого тригера pull_request_target у файлі конфігурації bundle-size.yml, шкідливий код автоматично виконався на серверах GitHub без жодного ручного схвалення від мейнтейнерів.

Ось як виглядав цей вразливий шматок конфігурації, який дозволив виконати недовірений код із форку:

on:
  pull_request_target:
    paths: ['packages/**', 'benchmarks/**']
jobs:
  benchmark-pr:
    steps:
      - uses: actions/[email protected]
        with:
          ref: refs/pull/${{ github.event.pull_request.number }}/merge 
      - uses: TanStack/config/.github/setup@main
      - run: pnpm nx run @benchmarks/bundle-size:build 

Завдяки цьому скрипт зміг зберегти своє корисне навантаження у загальний кеш pnpm-store під виглядом звичайних залежностей. Коли один із головних розробників злив у гілку main цілком безпечний PR від іншого контриб’ютора, процес релізу відновив цей отруєний кеш. Шкідливий код розпакувався, прочитав оперативну пам’ять процесу GitHub Worker, витягнув звідти свіженький OIDC токен і самостійно відправив заражені апдейти до npm, повністю оминувши офіційний крок публікації.

Хронологія зараження:

  • 10 травня о 17:16 зловмисник створює форк репозиторію та додає туди масивний JS-файл розміром близько 30 тисяч рядків, сховавши його виконання за тегом [skip ci].
  • 11 травня об 10:49 UTC відкривається підроблений PR з назвою «WIP: simplify history build». У найближчі двадцять хвилин автор робить кілька force-push операцій, щоразу запускаючи процес перевірки розміру бандлу, який успішно і непомітно отруює кеш для головної гілки.
  • Об 11:31 зловмисник робить ще один force-push, повертаючи свій PR до стану чистої гілки main. Він миттєво закриває PR і видаляє свою гілку. Це перетворило шкідливий запит на «порожній» (ніби жодних файлів не змінювалося), повністю приховавши сліди в інтерфейсі GitHub. Але отруєний кеш вже лишився в системі.
  • Того ж дня о 19:15 легітимний розробник Manuel зливає інший PR із виправленнями CSS, що запускає офіційний реліз.
  • Вже о 19:20 реєстр npm отримує першу партію з десятків заражених версій, таких як @tanstack/[email protected]. Ще через шість хвилин, після другого легітимного злиття коду, публікується друга хвиля малварі.
  • О 19:50 (всього через 20 хвилин після публікації) дослідник безпеки під ніком carlini помічає аномалію та публікує розбір відбитків вірусу. Команда починає екстрене відкликання доступу та видалення кешів.

Як працює вірус

Як відомо, вірус поширювався через запис в optionalDependencies. Якщо ви заглянете в маніфест зараженого пакета, то побачите там ось такий відбиток:

"optionalDependencies": {
  "@tanstack/setup": "github:tanstack/router#79ac49eedf774dd4b0cfa308722bc463cfe5885c"
}

Коли ви запускаєте npm install, pnpm install або yarn install, пакетний менеджер розв’язує цю залежність, завантажує ізольований коміт з мережі форків і виконує обфускований файл router_init.js розміром 2.3 МБ. Оскільки все це відбувається на етапі життєвого циклу prepare, шкідливий скрипт стартує автоматично і непомітно.

Сам троян націлений на масовий збір секретів. Він активно сканує типові системні директорії та змінні оточення у пошуках облікових даних AWS IMDS та Secrets Manager, метаданих GCP, токенів сервісних акаунтів Kubernetes, Vault, вмісту файлу ~/.npmrc, а також токенів GitHub та приватних SSH-ключів.

Злив викрадених даних відбувається через мережу завантаження файлів децентралізованого месенджера Session (домени filev2.getsession.org та seed.getsession.org). Завдяки наскрізному шифруванню та відсутності єдиного командного сервера, заблокувати цей трафік класичними методами за IP майже неможливо.

Але найгірше тут те, що малваря вміє самопоширюватися. Вона сканує реєстр npm, знаходить інші пакети, які підтримує розробник-жертва, і автоматично намагається перевипустити їх із цією ж самою шкідливою ін’єкцією.

Що тепер робити

Якщо ви встановлювали будь-які пакети з родини @tanstack/* вчора (зверніть увагу, що пакети query, table, form, virtual та store підтверджено залишилися чистими), вам потрібно перевіритися.

Шукайте у своїх lock-файлах згадки підозрілих optionalDependencies із посиланням на github:tanstack/router#79ac49eedf774dd4b0cfa308722bc463cfe5885c або наявність прихованого файла router_init.js.

Оскільки скрипт працює на рівні системи і краде абсолютно всі ключі доступу, до яких може дотягнутися, простого видалення папки node_modules чи відкоту версії в package.json не буде достатньо. Будь-який хост або CI-сервер, де відбулося встановлення в цей день, апріорі вважається скомпрометованим. Вам необхідно замінити всі локальні ключі, SSH-сертифікати, хмарні токени та доступи до баз даних, які можна було дістати з вашого пристрою.

👍ПодобаєтьсяСподобалось4
До обраногоВ обраному1
LinkedIn
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter

Підписатись на коментарі