Бэкдор в XZ

Перейти к навигацииПерейти к поиску
Бэкдор в XZ
Дата публикации29 марта 2024[1]
Дата открытия (изобретения)29 марта 2024[2] и 28 марта 2024[3]
Зависит отXZ Utils[вд][4][5]
Официальный сайтtukaani.org/xz-ba… (англ.)
Кем доказаноАндрес Фройнд[вд][6]
ПреступникJia Tan[вд]

Бэкдор в пакете XZ Utils для Linux был выпущен 24 февраля 2024 года[7] и обнаружен 28 марта. Внедряется в SSHD (Secure Shell Daemon) — часть OpenSSH, службу для Unix-подобных ОС, позволяющую удалённым пользователям выполнять команды консоли. «Отравленный» SSHD выполняет команды консоли от неизвестного центра, владеющего скрытым ключом. Бэкдор пассивен — центр сам должен был пройти брандмауэр и подключиться к компьютеру. Угроза была обнаружена и остановлена ещё в нестабильных выпусках, до того, как был нанесён масштабный урон.

Предварительно откомпилированный вредоносный код для архитектуры x64 тайно запрятан в динамическую библиотеку сжатия данных LibLZMA. Неизвестный хакер два года втирался в доверие, пытаясь взять на себя часть работы программиста-одиночки, глубоко знает архитектуру Linux и работу утилит сборки.

Бэкдор получил идентификатор CVE-2024-3094 и максимальный балл серьёзности 10,0.

Это классическая атака на цепочки поставки[англ.]: вместо того, чтобы атаковать сам OpenSSH, атаковали менее защищённого поставщика. Как отмечает The Economist, бэкдор в XZ Utils считается первой известной атакой на менее защищённые части критичной открытой программы, но это не значит, что атака была действительно первой — и вряд ли будет последней[8].

Инцидент раскрыл множество слабых звеньев в открытом сообществе — организационных, технических и просто ошибок. Хакер даже был вынужден спешить, пока одну из проблем не исправили[7]. Некоторые слабости были решены сразу же, а другие требуют сложного решения.

Кто уязвим и как избавиться

Бэкдор рассчитывался на Linux для x64, и успел попасть в Fedora 40[9] и в передовые нестабильные каналы обновления[9]. Об атаке также сообщили Kali Linux и OpenSUSE[9].

Не были подвержены бэкдору:

  • Другие ОС, не основанные на Unix и x64.
  • FreeBSD с консервативной политикой стабильности, другими методами сборки программ и другой стандартной библиотекой Си[10].
  • Сборки, в которых SSHD не зависит от LibSystemd: Arch Linux, Gentoo и NixOS[7].
  • Ubuntu с консервативной политикой стабильности: она не успела обновиться.

Чтобы избавиться от бэкдора, нужно откатить пакет xz-utils 5.6.0 или 5.6.1 до 5.4[9] — или обновить до будущей новой версии. Многие авторы дистрибутивов сделали пакеты-откаты — например, у OpenSUSE он 5.6.1.revertto5.4.

История

Предпосылки

Утечка 1998 года говорила, что Linux — враг Microsoft, но компания времён 2020-х даже платит программистам за ПО для Linux.

Сборка программы из человекоредактируемых исходных файлов — это всегда большая сложность: процесс длится долго, отличается для игр и библиотек, для Windows и Linux, на машине разработчика и на выпуск. Не зря инженер по выпуску[англ.] — отдельная специальность в разработке крупного ПО. Скрипты сборки не проходят такую ревизию, как исходные тексты: выходит действующая программа — и хорошо[11].

В любом ПО признак хорошего тона, а для копилефт-лицензий (GPL) даже требование — зафиксировать (например, в архиве), из каких исходных файлов собран пакет — и скрипт сборки тоже. Если процесс выпуска плохо налажен, или сборка неофициальная, в репозитории и зафиксированных исходных текстах бывает разный скрипт сборки[7]. Новаторством стало то, что эти изменения — малая часть вредоносного кода.

Репозитории библиотек, работающих с файлами какого-то формата (например, с архивами), содержат немалое количество тестовых архивов, взявшихся из разных источников — найденных у пользователей, созданных вручную или какой-то утилитой[12]. Для многих из этих файлов нет лучшего «исходника», чем сами файлы. Там и спрятали основную часть зловреда.

В 2020 году[13] веб-комикс xkcd написал, что вся инфраструктура современных IT где-то в глубине держится на программе, бескорыстно поддерживаемой парнем из Небраски[14]. Прецеденты этого были: в 2016 году турецко-американский программист Азер Кочулу, получив от авторов программы Kik Messenger требование отдать имя Kik и поняв, что NPM на их стороне, удалил из NPM все свои пакеты, включая тривиальную функцию leftpad, на несколько часов парализовав сверхпопулярный React — и, по странному совпадению, Kik Messenger тоже[15]. Именно таким — поддерживаемым одним человеком в свободное время — оказался пакет XZ Utils[англ.], позволивший открытому ПО сжимать не хуже WinRAR.

Закон Линуса действует только отчасти: чтобы найти хорошо замаскированную ошибку, нужно знание кода, и чаще на код смотрит автоматика, проверяющая критичные части ПО.

Есть специфичное для проекта GNU расширение формата ELF под названием косвенная функция (IFUNC)[16] — у некоторых важных подпрограмм вроде memcpy есть несколько версий: скажем, общая и оптимизированная под набор команд SSE4. Загрузчик вызывает специальную функцию (resolver), та проверяет возможности процессора и возвращает ссылку на одну из версий, ещё до подстановки адресов в машинный код главной программы. Прикладной программе не нужна даже перекомпиляция. Этот resolver обычно маленький (вызов функции CPUID из библиотеки языка и анализ результатов), вызывается рано (при загрузке программы) и всегда (даже если ни одной функции главной программе не нужно).

Программисты часто отказываются от библиотеки, особенно в форме динамического модуля (*.dll/*.so), по собственным причинам: слишком велика, недостаточно кроссплатформенна, или ещё по каким-то. Разработчики дистрибутивов своими силами налаживали передачу в систему информации о работе службы SSHD через функцию libsystemd.sd_notify[17], но OpenSSH упорно не принимали LibSystemd[18][a]. Хоть часть задачи — автоматический перезапуск «упавшей» службы — попытались решить окольным путём в конце 2016, но отказались из-за ненадёжности[18].

Внедрение хакера в проект

Осенью 2021 на GitHub появился пользователь с ником JiaT75, подписывавшийся как Jia Tan (Цзя Тань). Первое его изменение в библиотеке LibArchive было крайне подозрительным и могло дестабилизировать старые терминальные программы[19][20]. Тогда же он вносит первые мелкие изменения (стилевые настройки, воспроизводимость результатов) в XZ Utils[7].

Весной 2022 Цзя вносит изменение в Java-версию XZ Utils. Сразу же появляются несколько «пользователей», желавших заполучить это изменение, и Цзя в грубой форме требует, чтобы финн Лассе Коллин (Lasse Collin), единственный человек, поддерживавший проект, передал ему управление, если сам не справляется с потоком проблем. В ответ Лассе ссылается на депрессию[7].

С января 2023 Цзя всё больше и больше включается в работу над XZ[19]. В частности, он объявляет себя контактным лицом проекта.

В июне 2023 Цзя включает первую косвенную функцию — CRC64[21] для новых процессоров (Intel с 2010, AMD с 2013). Дальнейшая активность автора функции, Ханса Янсена, незначительна[22]. Польза от такой оптимизации сомнительна, ведь быстрая (не криптографическая) контрольная сумма — не самое критичное место библиотеки сжатия[23].

В январе 2024 Цзя перемещает главный сайт на GitHub Pages — единственный ресурс, к которому имеет доступ[24].

Кроме того, Цзя частично отключает OSS-Fuzz (автоматическое тестирование открытого ПО мусорными данными) и полностью — Landlock (автоматическую проверку на постороннюю функциональность)[7][23]. Landlock проверяет программу XZ, а не библиотеку, а она ничего постороннего не делала ни при каких условиях — полагают, что это на будущее[7].

Атака

23 февраля 2024 года Цзя вносит несколько тестовых архивов, два из которых содержат бэкдор[19][25], и собирает версию 5.6.0. В зафиксированных исходных текстах в скрипте сборки оказываются 26 лишних строк по сравнению с Git[17]. Сразу нашлись две ошибки, стабильность Gentoo Linux и срабатывание Valgrind (программы поиска ошибок доступа к памяти) прямо на вредоносной функции у Red Hat[7].

За две недели, пока Цзя исправлял ошибки, бэкдора не обнаружили, и 9 марта он обновляет архивы с бэкдором, описав как «взяты постоянные настройки случайности», без указания, какие[7][26] — это будет версия 5.6.1. Чтобы больше не менять эти архивы, он вносит поиск обновлений[12]. Заодно он делает пустое изменение, якобы исправляющее Valgrind[7].

Тем временем в LibSystemd появляется изменение: загружать редко нужную LibLZMA динамически, когда она нужна, чтобы уменьшить размер стартового виртуального «диска» (механизм начальной загрузки Linux, позволяющий разворачивать ПО, пока не загрузилась поддержка файловых систем). Это должно сломать бэкдор (LibLZMA выпадает из зависимостей SSHD), и хакер решает активно рекламировать пакет, чтобы попасть в стабильный канал обновлений раньше LibSystemd[7]. В конце марта упомянутый Ханс Янсен убедил Debian взять 5.6.1[7]. 28 марта Цзя рекламирует 5.6.1 перед Ubuntu — но через несколько часов Debian откатывают, а на следующий день о бэкдоре узнают все[27].

Обнаружение

28 марта 2024 года программист из Microsoft Андрес Фройнд (Andres Freund) предупреждает о бэкдоре производителей Linux[7][27], 29 марта — всех[28].

Он испытывал PostgreSQL на производительность на новейшем нестабильном выпуске Debian Linux и обнаружил замедление: подключение по SSH занимало не 0,3, а 0,8 секунды[29]. Андрес пришёл к выводу, что виноват пакет XZ Utils версий 5.6.0 и 5.6.1, вспомнил срабатывание Valgrind месяц назад и отследил почти все факты:

  • это наверняка бэкдор;
  • какая функция виновата, где спрятана и как попадает в скомпилированный пакет;
  • при каких условиях срабатывает;
  • кто хакер — по его мнению, Цзя именно хакер под прикрытием, а не взломанный разработчик.

Что делает функция и как она встала на место OpenSSL, ему понять не удалось.

После обнаружения

Лассе Коллин, руководитель проекта, сделал периодически обновляемый пресс-релиз, содержащий только сухие факты[30]. Блогеры[] похвалили его за такую реакцию.[]

GitHub закрыл проект и все его зеркала, и забанил обоих пользователей. Лассе вернул домашнюю страницу на личный сервер, и там же развернул Git-репозиторий проекта. Лассе разбанили 2 апреля, а 9 апреля проект снова переехал на GitHub. GitHub закрыл все запросы на изменения (pull requests), как будто их закрыл Лассе, а когда Лассе стал снова их открывать, чтобы проверить и внести — снова автоматически закрывал[30].

Авторы дистрибутивов откатили XZ до 5.4[9] и исключили Цзя из доверенных сборщиков.

Провели более глубокий аудит изменений Цзя и откатили многие его изменения, включая[31] LibArchive.

Началась волна сокращения цепочек поставки в критичном ПО, в частности:

  • В OpenSSH написали автономную функцию sd_notify, чтобы авторы дистрибутивов не делали патчи с посторонними зависимостями[18]. Проблема стояла с 2016 года, но занялись ею только после бэкдора. Несколько позже LibSystemd написала свой вариант автономной sd_notify[17] — для всех, кому библиотека нужна только ради этой функции.
  • Начались разговоры об отказе LD (стандартного линкера GNU) от JSON-библиотеки[17] — но поскольку ошибки в метаданных исполняемого файла Linux и неадекватная реакция ПО на них возможны уже сейчас, а атака на LibJanssen будет не скоро, оставили как есть. Альтернативные скоростные линкеры LLD и MOLD смотрят на JSON как на чёрный ящик, никак не проверяя, да и LD можно собрать в такой конфигурации[32].

Ubuntu отложила на неделю апрельскую бета-версию, и своими силами пересобрала все пакеты[33].

Эмблема авторства Цзя, ставшая эмблемой бэкдора

Новая чистая версия XZ, вышедшая 29 мая, получила номер 5.6.2[23]. Отдельные изменения:

  • Выброшены косвенные функции — выигрыш от них действительно был настолько мал, что не сто́ит усложнения проекта.
  • Запуск тестов перенесён в отдельный скрипт CMake, чтобы сборка работала и без тестов.
  • Для всех тестов прописана методика их воссоздания. Выброшены старые тесты, которые являются сами себе исходниками,— два объектных файла неизвестного происхождения. Документация в формате PDF также воссоздаётся из формата man, а не хранится в репозитории.
  • Убрана эмблема, нарисованная Цзя,— она стала эмблемой бэкдора, а не проекта в целом.

Личность Цзя

Имя Jia Cheong Tan вряд-ли бывает у жителя континентального Китая[b]. Нашли некоего Jia Tan из Калифорнии, но исследователи больше склоняются к краже имени[22].

Заявленный часовой пояс — UTC+8 (Восточная Азия), IP-адрес — сингапурский VPN, что ни о чём не говорит. Основная часть правок сделана в 12…17 часов UTC[c][22], что похоже на рабочее время UTC+2 или +3 (Европа от Греции до России, Ближний Восток, часть Африки)[34]. Девять правок сделаны с заявленным часовым поясом UTC+2/3[34] — возможно, финн Лассе принял изменения своими руками, или хакер забыл переключить часовой пояс. Работал в китайские праздники и не работал в европейские[d][34].

Принцип действия

Бэкдор устроен очень сложно. Окольными путями решаются три вопроса: как протащить вредоносный код в открытый проект; как его подключить к SSHD, который LZMA вообще не вызывает, даже в тех дистрибутивах, где есть зависимость SSHD ← LibSystemd ← LibLZMA; как подключить нужный для работы OpenSSL, не требуя его явно.

Сборка вредоносного LibLZMA

  1. Скрипт сборки, лежащий в зафиксированных исходных текстах (не в Git), находит пример плохого архива tests/files/bad-3-corrupt_lzma2.xz (без явного указания этого имени[12]), четырьмя заменами (TAB↔пробел, дефис↔подчерк) превращает его в хороший, и распаковывает более старым скомпилированным XZ[35]. На выходе получается командный файл.
  2. Этот файл распаковывает второй пример, на сей раз хорошего архива — tests/files/good-large_compressed.lzma — 1 килобайт распакованных данных пропускается, 2 читаются, 1 снова пропускается…
  3. То, что получилось, обрабатывается шифром простой замены и снова разархивируется. В результате снова получается командный файл, который и прислал Фройнд[36].
  4. В этом командном файле были какие-то скрипты на будущее — например, поиск обновлений в других тестах. А если обновлений нет — из тех же тестовых файлов сходным образом извлекается заранее откомпилированный объектный файл liblzma_la-crc64-fast.o и расшифровывается RC4, написанным на препроцессоре AWK.
  5. Кроме того, корректирует два связанных с косвенными функциями Си-файла (CRC64 и CRC32), чтобы они вызывали вредоносную функцию _get_cpuid[12], а не системную __get_cpuid[37]. Одного файла хватало, но, видимо, хакер хотел, чтобы выбор обеих косвенных функций выглядел в дизассемблере одинаково[12].

Объектный файл занимает 86 килобайт[38], что многовато для одной оптимизированной функции. В действительности функций там несколько десятков, и все переименованы так, чтобы не вызывать подозрение[39]: одна из таблиц префиксного дерева называется Llzip_decode_1.

Командные файлы Цзя полагались на ошибки в CMake[40] и Autoconf[41][17].

Подключение вредоносных функций к SSHD

На конец апреля 2024 бэкдор полностью исследован[42], несмотря на определённую защиту от исследования: например, условия x=="string" записаны не открытым текстом, а в виде префиксного дерева: findInTrie(x)==stringId[43].

В описании на сайте CVE было написано, что библиотека может «перехватывать и менять обмен данными» с LibLZMA[44], но исследователи об этом не пишут[43][42], бэкдор направлен исключительно на SSHD.

Во многих дистрибутивах SSHD неофициально подключает функцию sd_notify из библиотеки LibSystemd[17], чтобы передавать другому ПО, в каком состоянии служба. Эта библиотека совсем в других функциях полагается на LibLZMA.

Подпрограмма выбора косвенных функций crc64_resolve находится в Си-файле с оптимизированными функциями — она должна при загрузке выбрать версию функции CRC64. После изменений, внесённых в на этапе сборки, она вызывает вредоносную _get_cpuid[12], та, если функция запущена из программы /usr/sbin/sshd[28] и в переменных среды отсутствует код отключения, перекомпоновывает программу[43]:

  1. Ищет в памяти процесса образы других ELF-модулей и, не требуя явно OpenSSL, находит в памяти указатели на его шифровальные функции[43].
  2. Подменяя память функции, нужной, чтобы программа могла следить за процессом динамической компоновки, заменяет три функции OpenSSL на вредоносные[43]. Наиболее известна из них RSA_public_decrypt, попавшая в отчёт Фройнда[28], но остальные две делают то же самое[42].

Бэкдор

RSA_public_decrypt запускается ещё до того, как проверит имя пользователя — на стадии обмена ключами. Ключ шифрования и является командой для бэкдора.

Функция пробует расшифровать ключ симметричным шифром ChaCha20 (команда зашифрована, чтобы походила на ключ), делает несложную проверку заголовка, а потом проверяет электронную подпись, сделанную шифром Ed448. Если подпись сходится, команда выполняется. Подтверждена следующая вредоносная функциональность[42]:

  • Исполнить любую команду консоли функцией system(). Подменив открытый ключ на собственный, блогеры повторяли это поведение[45];
  • При следующем подключении не проверять пароль;
  • Отключить часть логов SSHD на некоторое время;
  • Включить/выключить отладочный вывод самого себя;
  • Отключить Pluggable Authentication Modules;

Поднятые вопросы

В очередной раз после Heartbleed и NPM/React получилось, что критичная инфраструктура оказалась в руках неоплачиваемого хобби-разработчика. Как отмечает эксперт Михал Залевский (Michal Zalewski)[29]:

Вывод состоит в том, что несказа́нные триллионы долларов опираются на программный код, написанный любителями. В программном обеспечении интернета могут скрываться и другие пока не обнаруженные лазейки.

Снова после утечек Сноудена поднят вопрос о государственных хакерах, внедрившихся в открытое сообщество, тем более в хакере есть признаки жителя Восточной Европы на зарплате[34]. По мнению The Economist, выдержка, проявленная Цзя, а также поддержка некоторых других аккаунтов, рекомендовавших Коллину передать свою работу новичку, указывают на операцию государственной разведслужбы[29].

Издание также отмечает, что атаки на цепочки поставки[англ.] становятся всё более частыми. Так, в 2019—2020 годах российская служба внешней разведки проникла в сети правительства США через платформу управления сетями SolarWinds Orion. Хакеры на службе правительства КНР вмешались в прошивку роутеров Cisco с целью получения доступа к экономическим, коммерческим и военным объектам США и Японии[8].

Из технических вопросов — прозрачность инфраструктуры сборки (например, почему в скрипте сборки были функции eval и AWK)[46], попавшие в открытый доступ методики атаки на чужую память.

Примечания

Комментарии
  1. Причиной указано «пустить верблюжий нос в шатёр» — поговорка, основанная на сказке: бедуин пустил в шатёр сначала нос, потом голову, и так до целого верблюда. По-русски «дай палец, руку откусит» или «коготок увяз — всей птичке пропасть».
  2. Это смесь языков Китая (мандарина, кантонского и южноминьского), к тому же в континентальном Китае любят склеивать два личных имени в одно[22][34]
  3. Статистика собрана по событиям GitHub, независимо от типа — а не по заявленному времени правок.
  4. Работал: в китайский Новый год (с 22 до 27 января 2023), День поминовения умерших (5 апреля 2023), Праздник осени (29 сентября 2023). Не работал: оба католических Рождества (25 декабря 2022/23), оба григорианских Новых года (31 декабря 2022/23, 1 января 2023/24).
Источники
  1. https://www.cve.org/CVERecord?id=CVE-2024-3094
  2. https://www.openwall.com/lists/oss-security/2024/03/29/4
  3. https://research.swtch.com/xz-timeline
  4. https://tukaani.org/xz-backdoor/
  5. https://www.cisa.gov/news-events/alerts/2024/03/29/reported-supply-chain-compromise-affecting-xz-utils-data-compression-library-cve-2024-3094 (англ.)
  6. https://www.nytimes.com/2024/04/03/technology/prevent-cyberattack-linux.html
  7. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 research!rsc: Timeline of the xz open source attack
  8. 1 2 A chilling near-miss shows how today’s digital infrastructure is vulnerable, The Economist, 4 April 2024
  9. 1 2 3 4 5 Red Hat warns of backdoor in XZ tools used by most Linux distros
  10. Disclosed backdoor in xz releases — FreeBSD not affected
  11. Understanding the Linux Backdoor: Implications for Open Source [When Penguins Cry] — YouTube
  12. 1 2 3 4 5 6 research!rsc: The xz attack shell script
  13. 2347: Dependency — explain xkcd
  14. xkcd: Dependency
  15. How one programmer broke the internet by deleting a tiny piece of code
  16. GNU indirect function | MaskRay
  17. 1 2 3 4 5 6 xz-utils backdoor situation (CVE-2024-3094) · GitHub
  18. 1 2 3 2641 — Add systemd notify code to to track running server
  19. 1 2 3 The XZ Backdoor: Everything You Need to Know | WIRED
  20. Added error text to warning when untaring with bsdtar by JiaT75 · Pull Request #1609 · libarchive/libarchive · GitHub
  21. git.tukaani.org — xz.git/commit
  22. 1 2 3 4 Evan Boehs. Everything I Know About the XZ Backdoor
  23. 1 2 3 Release XZ Utils 5.6.2 (stable) · tukaani-project/xz · GitHub
  24. git.tukaani.org — xz.git/commitdiff
  25. git.tukaani.org — xz.git/commitdiff
  26. git.tukaani.org — xz.git/commitdiff
  27. 1 2 Bug #2059417 «Sync xz-utils 5.6.1-1 (main) from Debian unstable …» : Bugs : xz-utils package : Ubuntu
  28. 1 2 3 oss-security — backdoor in upstream xz/liblzma leading to ssh server compromise
  29. 1 2 3 A stealth attack came close to compromising the world’s computers, The Economist, Apr 2nd 2024
  30. 1 2 XZ Utils backdoor
  31. tar: make error reporting more robust and use correct errno by emaste · Pull Request #2101 · libarchive/libarchive · GitHub
  32. Remove dependency on libjansson
  33. Noble Numbat Beta delayed (xz/liblzma security update) — Announcements — Ubuntu Community Hub
  34. 1 2 3 4 5 XZ Backdoor: Times, damned times, and scams
  35. xz/liblzma: Bash-stage Obfuscation Explained — gynvael.coldwind//vx.log
  36. https://www.openwall.com/lists/oss-security/2024/03/29/4/1
  37. CPUID — OSDev Wiki
  38. xzre/liblzma_la-crc64-fast.o at main · smx-smx/xzre · GitHub
  39. [WIP] XZ Backdoor Analysis and symbol mapping · GitHub
  40. Consider hardening check_c_source_compiles (#25846) · Issues · CMake / CMake · GitLab
  41. autoreconf -force seemingly does not forcibly update everything
  42. 1 2 3 4 binary-risk-intelligence/xz-backdoor at master · binarly-io/binary-risk-intelligence · GitHub
  43. 1 2 3 4 5 Kaspersky analysis of the backdoor in XZ | Securelist
  44. CVE Website
  45. revealing the features of the XZ backdoor — YouTube
  46. Three steps we could take to make supply chain attacks a bit harder — devel — Fedora Mailing-Lists