Вход на сайт

Просмотр новости

Найдите то, что Вас интересует

Как в официальный CLI OVHcloud подложили стилер и почему спас только прописанный хеш

Дата публикации: 20-10-2026 05:41:42

19 октября в пяти уже выпущенных релизах официального CLI OVHcloud полтора часа лежал стилер вместо настоящих бинарников. Хеши на странице релиза подменили вместе с архивами, install.sh их не проверяет вовсе. Поймал атаку клиент, у которого в CI был жёстко прописан SHA-256, а пользователей Homebrew защитил хеш в cask. Я прошёл по следам сам: аккаунт, через который залили файлы, угнали ещё в марте, загрузчик в его репозиториях спрятан в 9123 невидимых символах Unicode, а адрес следующей стадии он берёт из memo в блокчейне Solana. Там же нашлась чужая запись «I know you» — и уязвимость самого тайника. Читать далее

Основное содержимое страницы с новостью.

Время на прочтение6 мин

Охват и читатели4.8K

19 октября в официальном CLI OVHcloud полтора часа раздавался стилер. Первым это заметил клиент OVH, у которого в CI был жёстко прописан SHA-256 одного архива. Сама OVH узнала о подмене из его отчёта: через час после подмены сборка у клиента упала, и он завёл issue #300.

Про этот случай уже написали французские СМИ и OpenSourceMalware. Я прошёл по их следам сам: GitHub API, репозитории взломанного аккаунта, блокчейн Solana. Нашлось кое-что, чего в пересказах нет.

Что произошло

ovhcloud — Go-утилита, которой управляют серверами, VPS, Public Cloud и доменами OVH из терминала. Ключи API она хранит в ~/.ovh.conf или в переменных окружения, то есть на машине разработчика или в CI.

С 15:47 до 16:12 по Москве (12:47–13:12 UTC) через аккаунт HassenBF в пяти уже выпущенных релизах, с v0.11.0 по v0.15.0, заменили бинарники. Новых версий не появилось и тегов не добавилось. GitHub Actions не запускались. Файлы просто перезалили через Releases API поверх старых, последним шёл checksums.txt, чтобы хеши на странице релиза совпадали с подменой.

Подменили не всё. Из десяти файлов релиза заменены сборки для macOS (обе), Linux x86_64 и arm64, Windows x86_64 и файл с хешами. Сборки для i386, Windows arm64 и WASM не тронули — самые редкие платформы. Это видно и сейчас: у v0.11.0–v0.15.0 на странице осталось ровно по четыре файла, остальные шесть OVH удалила.

В 17:21 по Москве (14:21 UTC) подменённые файлы удалили. Вечером вышла v0.16.0, уже с включёнными у GitHub immutable releases и аттестацией релиза. Октав Клаба, основатель OVH, написал в X, что злоумышленник воспользовался GitHub-аккаунтом одного из разработчиков, а бинарник ищет на диске секреты. Качество бинарника он назвал посредственным, но рабочим.

Почему спас только прописанный хеш

Проверка по checksums.txt из того же релиза здесь бесполезна: файл с хешами подменили вместе с архивами. Официальный install.sh не проверяет хеши вообще:

$ grep -ci 'sha256\|shasum\|hash' install.sh
0

Скрипт берёт архив по адресу releases/download/<тег>/<файл> и сразу ставит. В этом окне малварь получили все, кто запускал curl … install.sh | sh, скачивал архив со страницы релиза или тянул его в CI без своего хеша.

С Homebrew вышло иначе. Cask в ovh/homebrew-tap хранит SHA-256 каждого архива, а в окне атаки его никто не правил: последний коммит перед инцидентом — 16 сентября, к выходу v0.15.0. Хеш в нём для Linux_arm64 тот же 5c2beb56…, что был у клиента в CI. Подменённый архив brew должен был отвергнуть по несовпадению хеша.

Не задело и тех, кто собирал из исходников, ставил через go install или брал Docker-образ ovhcom/ovhcloud-cli: его OVH собирает своим конвейером из исходников.

Перезаливка архивов в старом релизе для проекта была штатной операцией. В .goreleaser.yaml так и написано:

release:
  mode: keep-existing
  replace_existing_artifacts: true

Поймал подмену пользователь alink-tothepast. В его CI был вписан хеш ovhcloud-cli_Linux_arm64.tar.gz версии 0.15.0: 5c2beb56…. После подмены архив стал 3833daff…, и сборка упала. Обновлять хеш вслепую он не стал и вытащил из нового бинарника метаданные сборки:

mod   github.com/ovh/ovhcloud-cli  v0.15.1-0.20261013083505-5ead8ef9b43d+dirty
build vcs.revision=5ead8ef9b43d354dfb5caadff803b11afc8c5c58
build vcs.modified=true

Go вшивает это в каждый бинарник. Для сравнения я скачал оригинальный архив v0.15.0 под i386, его не подменяли, и посмотрел то же самое в контейнере с Go (сам бинарник при этом не запускается):

mod   github.com/ovh/ovhcloud-cli  v0.15.0
build -trimpath=true
build vcs.revision=760fd72de93cbea3b507eb36dc0095991e4048e5
build vcs.modified=false

У настоящего релиза версия из тега, коммит 760fd72, на который указывает тег, и чистое дерево. У подмены псевдоверсия от коммита 13 октября, который вышел уже после v0.15.0, и vcs.modified=true: обычный go build в рабочей копии с незакоммиченными правками. Похоже, вредонос дописали локально: в истории репозитория его нет.

Проверить сохранившийся бинарник можно, не запуская его:

go version -m /path/to/ovhcloud

Не запускайте ovhcloud version на подозрительной копии: это и есть запуск стилера. Признак подмены — бинарник, скачанный как официальный релиз (v0.11.0–v0.15.0), в котором стоит псевдоверсия v0.15.1-0.20261013083505-5ead8ef9b43d+dirty. Если вы собирали CLI сами, такая строка ничего не значит.

Аккаунт угнали за семь месяцев до атаки

HassenBF — настоящий контрибьютор OVH: его пулреквесты есть в ovh/ovhcloud-docs и ovh/manager. Публичным членом организации он не значится, коммитов в ovhcloud-cli у него нет. Право на запись в репозиторий у аккаунта тем не менее было: без него Releases API не даёт загружать файлы. Откуда оно взялось, OVH пока не объяснила.

OpenSourceMalware заметили, что 5 марта 2026 года в личных репозиториях этого аккаунта за 42 секунды переписали историю трёх проектов. В два добавили preinstall.js и строчку "preinstall": "node preinstall.js" в package.json, в третьем тот же декодер встроили прямо в src/index.js. Шестого марта так же заразили четвёртый. Почерк GlassWorm: последний коммит подменяют через amend, автора и дату сохраняют и делают force push.

Файл до сих пор лежит в HassenBF/treasure-hunt-game. Я скачал его как текст и ничего не запускал. SHA-256 совпадает с индикатором из разбора OSM (43df42fd…). В редакторе это одна строка на 181 символ: декодер и eval. На самом деле символов в файле 9304. Остальные 9123 — невидимые variation selectors Unicode (U+FE00–U+FE0F и U+E0100–U+E01EF), каждый кодирует один байт. На ревью такой файл выглядит почти пустым.

Байты раскодировались в первый этап: AES-256-CBC, ключ и IV лежат там же открытым текстом. Я расшифровал его через openssl enc -d и получил второй этап, 4407 байт, хеш тоже совпал с разбором. Во втором этапе есть строки ru_RU, ru-RU и Europe/Moscow: на системах с русской локалью и подходящим часовым поясом загрузчик молча выходит. Адрес следующей стадии он берёт из блокчейна.

Тайник в Solana

Загрузчик опрашивает публичные RPC-узлы Solana, берёт транзакции кошелька BjVeAjPrSKFiingBn4vZvghsGj9KCE8AJVtbc9S8o8SC и читает поле memo у самой свежей. В memo лежит JSON с base64-ссылкой на следующую стадию. Удалить такую запись нельзя, а адрес сервера оператор меняет одной транзакцией.

Историю кошелька может посмотреть любой: вызов getSignaturesForAddress у публичного RPC. Я посмотрел:

  • 71 транзакция, в 70 из них есть memo;

  • первая 27 ноября 2025 года, последняя 29 мая 2026 года;

  • 68 записей со ссылкой на следующую стадию, одна с конфигом (25 февраля, c2server на порту 5000 и адрес для DHT) и одна чужая, о ней ниже;

  • за полгода адреса вели на 12 разных IP;

  • больше всего записей указывают на 45.32.151[.]157 (19) и 45.32.150[.]97 (11), последняя — на 137.184.198[.]91.

Самое забавное нашлось 29 мая. В 19:42 UTC в memo появилась запись {"link":"SSBrbm93IHlvdQ=="}. В base64 это «I know you». Транзакцию оплатил другой кошелёк, не тот, с которого оператор публикует ссылки. Ровно через минуту, в 19:43, оператор записал рабочую ссылку обратно.

Причина в коде загрузчика:

memo = signatures.filter(x => x?.memo)[0].memo

Отправителя он не проверяет и берёт первую попавшуюся memo. Значит, кто угодно может за пару центов подсунуть всем заражённым машинам свою запись. Кто-то так и сделал, похоже исследователь. После 29 мая записей не было: оставшиеся четыре с лишним месяца до атаки на CLI тайник никто не трогал.

Все 12 IP из истории тайника
45.32.151[.]157   19
45.32.150[.]97    11
217.69.2[.]135    10
217.69.11[.]57     9
217.69.11[.]99     6
137.184.198[.]91   5
217.69.0[.]159     3
162.33.177[.]177   2
45.77.60[.]153     1
45.76.44[.]240     1
45.32.147[.]205    1
217.69.11[.]60     1

Делал ли загрузку в CLI тот же оператор, доказать нельзя. Связывает их только общий аккаунт, а публичных копий подменённого бинарника не осталось: OVH удалила файлы, разбора самого стилера пока никто не опубликовал. Известно другое: аккаунт был скомпрометирован ещё в марте, а в октябре у него оставалось право записи в репозиторий популярного CLI.

Если вы ставили CLI 19 октября

Под удар попали машины, где с 15:47 до 17:21 по Москве ставили ovhcloud через install.sh или скачивали архив со страницы релиза: macOS, Linux x86_64/arm64, Windows x86_64. OVH просит сменить все секреты, которые лежат на диске такой машины. Кроме ключей API OVH это SSH-ключи, ~/.kube/config, .npmrc, .netrc, .env, токены других облаков. Менять их стоит с другой машины, а потом уже ставить v0.16.0. У новой версии можно проверить аттестацию:

gh release verify-asset v0.16.0 ovhcloud-cli_Linux_x86_64.tar.gz -R ovh/ovhcloud-cli

Это аттестация релиза: она подтверждает, что файл тот самый, что опубликован в неизменяемом релизе. Как именно его собрали, она не доказывает.

OVH обещает подробный постмортем «в ближайшие дни». Главные вопросы к нему: как атакующий получил доступ к аккаунту HassenBF и что с этим аккаунтом происходило между мартом и октябрём.

Схожие новости

#Наименование новостиТональностьИнформативностьДата публикации
1Вы восстановили взломанный Proxmox из бэкапа. А атакующий уже снова внутри07.7416-10-2026
2Один адрес на все хранилища, или как я перестал бояться переездов S305.318-10-2026
3Как атака БПЛА на Яндекс чуть не уничтожила мою инди-игру012.7820-10-2026
4TeamPCP: как команда хакеров-любителей «Дюны» закинула в наши Node.js-пакеты червей Shai-Hulud0829-06-2026
5Ты не найдёшь эту ошибку. Потому что её нет в твоём коде. Как Self-describing API спасает от чужих рефакторингов5807-07-2026
6Как я ловлю галлюцинации LLM в коде: пайплайн проверок в CI07.7419-10-2026
7Бэкап SQLite прошёл integrity_check, но потерял 90% данных: проверяю пять способов копирования08.9816-10-2026
8Kubernetes просто, часть 7: ConfigMap и Secret — как настраивать приложение, не пересобирая его каждый раз012.8620-10-2026
9Cloudflare открыла hash_in_range() для поэтапного распределения HTTP-трафика015.2503-10-2026
10Хакеры усилили атаки на корпоративную почту01020-10-2026

Классификация: Пресс-релизы. Схожих патентов: 0. Схожих новостей: 10. Тональность: 0. Информативность: 7.72. Источник: habr.com.