Статья Как Mozilla упустила (не)очевидную уязвимость

Admin

Администратор

Как Mozilla упустила (не)очевидную уязвимость​

В 2021 году всем хорошим багам нужно цепляющее название, и у этой уязвимости появилось имя BigSig. Сначала объясню, как она нашлась, а затем попытаюсь понять, почему её так долго упускали.

Анализ​

Network Security Services (NSS) — популярная кросс-платформенная криптографическая библиотека от Mozilla. Когда проверяется зашифрованная цифровая подпись ASN.1, в NSS создаётся структура VFYContext для хранения необходимых данных — открытого ключа, хеш-алгоритма и самой подписи.
Код:
struct VFYContextStr {
   SECOidTag hashAlg; /* the hash algorithm */
   SECKEYPublicKey *key;
   union {
       unsigned char buffer[1];
       unsigned char dsasig[DSA_MAX_SIGNATURE_LEN];
       unsigned char ecdsasig[2 * MAX_ECKEY_LEN];
       unsigned char rsasig[(RSA_MAX_MODULUS_BITS + 7) / 8];
   } u;
   unsigned int pkcs1RSADigestInfoLen;
   unsigned char *pkcs1RSADigestInfo;
   void *wincx;
   void *hashcx;
   const SECHashObject *hashobj;
   SECOidTag encAlg;    /* enc alg */
   PRBool hasSignature;
   SECItem *params;
};
Структура VFYContext из NSS

Сигнатура максимального размера, которую обрабатывает эта структура, равна наибольшему элементу объединения, здесь это RSA в 2048 байтов, то есть 16 384 бита. Это достаточно много, чтобы вместить сигнатуры даже невероятно больших ключей. А что, если сделать сигнатуру больше этой? Произойдёт повреждение памяти. Да, так есть. Ненадёжная сигнатура просто копируется в этот буфер фиксированного размера, перезаписывая соседние элементы произвольными данными, которые контролируются злоумышленником.

Баг прост в воспроизведении и влияет на несколько алгоритмов. Проще всего показать RSA-PSS:
Код:
# We need 16384 bits to fill the buffer, then 32 + 64 + 64 + 64 bits to overflow to hashobj,
# which contains function pointers (bigger would work too, but takes longer to generate).
$ openssl genpkey -algorithm rsa-pss -pkeyopt rsa_keygen_bits:$((16384 + 32 + 64 + 64 + 64)) -pkeyopt rsa_keygen_primes:5 -out bigsig.key
# Generate a self-signed certificate from that key
$ openssl req -x509 -new -key bigsig.key -subj "/CN=BigSig" -sha256 -out bigsig.cer
# Verify it with NSS...
$ vfychain -a bigsig.cer
Уязвимость BigSig за три простых команд

Код, который вызывает повреждение, зависит от алгоритма. Вот код для RSA-PSS. Баг заключается в том, что проверки границ просто нет вообще: sig и key — это большие двоичные объекты произвольной длины, контролируемые злоумышленником, а cx->u — это буфер фиксированного размера:
Код:
case rsaPssKey:
               sigLen = SECKEY_SignatureLen(key);
               if (sigLen == 0) {
                   /* error set by SECKEY_SignatureLen */
                   rv = SECFailure;
                   break;
               }

               if (sig->len != sigLen) {
                   PORT_SetError(SEC_ERROR_BAD_SIGNATURE);
                   rv = SECFailure;
                   break;
               }

               PORT_Memcpy(cx->u.buffer, sig->data, sigLen);
               break;
Уязвимость вызывает ряд вопросов:
  • Связана ли она с недавним изменением кода или это регрессия, которая проявилась только сейчас? Нет, исходный код был проверен поддержкой ECC 17 октября 2003 года, но его нельзя было использовать до рефакторинга, проведённого в июне 2012 года. В 2017 году была добавили поддержка RSA-PSS, при этом допустили ту же ошибку.
  • Много ли времени требуется, чтобы сгенерировать ключ, который вызывает баг? Нет, в приведённом примере генерируются реальный ключ и сигнатура, но это может быть мусор: переполнение происходит до проверки сигнатуры. Несколько килобайтов символа А работают без проблем.
  • Требуется ли для доступа к уязвимому коду сложное состояние, с которым у техник тестирования и статических анализаторов были бы трудности при синтезе, например хеши или контрольные суммы? Нет, должен быть правильный DER, вот и всё.
  • Уязвимости трудно достичь в смысле кода? Нет, в Firefox этот путь кода не используется для сигнатур RSA-PSS, но точка входа по умолчанию для проверки сертификата в NSS — CERT_VerifyCertificate() — уязвима.
  • Она характерна исключительно для алгоритма RSA-PSS? Нет, она влияет и на сигнатуры DSA.
  • Этой уязвимостью нельзя воспользоваться или же она имеет ограниченное воздействие? Нет, может быть затёрт элемент hashobj. Этот объект содержит указатели функции, которые сразу же используются.
Это не сбой процесса: вендор всё сделал правильно. В Mozilla работают специалисты мирового класса по обеспечению безопасности. Они первыми внедрили программу вознаграждений за обнаруженные уязвимости, вкладываются в обеспечение безопасной работы с памятью, автоматизацию тестирования безопасности и покрытие кода тестами.

NSS был одним из первых проектов, включённых в oss-fuzz: по крайней мере поддерживался официально с октября 2014 года. В Mozilla сами также проводят автоматизацию тестирования безопасности NSS с помощью libFuzzer и представили собственную коллекцию методов-модификаторов, а также основу корпуса покрытия. Есть обширный тестовый комплект и ночные сборки ASAN.

Я в общем скептически отношусь к статическому анализу, но это похоже на простую недостающую проверку границ, которую должно быть легко найти. Coverity отслеживает NSS по крайней мере с декабря 2008 года и тоже, кажется, не смогла обнаружить уязвимость.

До 2015 года в Google Chrome использовали NSS и поддерживали собственную инфраструктуру тестового комплекта и автоматизации тестирования безопасности, независимую от Mozilla. Сегодня в платформах Chrome используется BoringSSL, но порт NSS ещё поддерживается.
  • Было ли в Mozilla хорошее тестовое покрытие уязвимых областей? Да.
  • Были ли в корпусе автоматизации тестирования безопасности Mozilla / Chrome / oss-fuzz соответствующие входные данные? Да.
  • Есть ли метод-модификатор, способный расширить эти ASN1_ITEM? Да.
  • Является ли это внутриобъектным переполнением или другой формой повреждения, которую ASAN было бы трудно обнаружить? Нет, это классическое переполнение буфера, которое ASAN может обнаружить легко.

Как я нашёл баг?​

Экспериментировал с альтернативными методами измерения покрытия кода, чтобы узнать, можно ли их как-то использовать на практике в автоматизации тестирования безопасности. В технике тестирования, с помощью которой удалось обнаружить эту уязвимость, применялось сочетание двух подходов: покрытие стека и выделение объектов.

Покрытие стека​

Самый распространённый метод измерения покрытия кода — покрытие блоков или покрытие границ, когда доступен исходный код. Интересно, всегда ли этого достаточно? Например, возьмём простую таблицу диспетчеризации с сочетанием надёжных и ненадёжных параметров, как показано в листинге:
Код:
#include <stdio.h>
#include <string.h>
#include <limits.h>
 
static char buf[128];
 
void cmd_handler_foo(int a, size_t b) { memset(buf, a, b); }
void cmd_handler_bar(int a, size_t b) { cmd_handler_foo('A', sizeof buf); }
void cmd_handler_baz(int a, size_t b) { cmd_handler_bar(a, sizeof buf); }
 
typedef void (* dispatch_t)(int, size_t);
 
dispatch_t handlers[UCHAR_MAX] = {
    cmd_handler_foo,
    cmd_handler_bar,
    cmd_handler_baz,
};
 
int main(int argc, char **argv)
{
    int cmd;
 
    while ((cmd = getchar()) != EOF) {
        if (handlers[cmd]) {
            handlers[cmd](getchar(), getchar());
        }
    }
}
Покрытие команды bar — это надмножество команды foo, поэтому ввод, содержащий foo, будет отброшен при минимизации корпуса. Есть уязвимость, недоступная через команду bar, которая может быть не обнаружена никогда. Покрытие стека корректно сохранит оба ввода1.

Чтобы решить эту проблему, я отслеживал стек вызовов во время выполнения.

Наивная реализация слишком медленная, но после многих оптимизаций я создал практичную библиотеку, достаточно быструю, чтобы интегрировать её в автоматизацию тестирования безопасности, ориентированную на покрытие. Я тестировал, как она работает с NSS и другими библиотеками.

Выделение объектов​

Многие типы данных создаются из записей меньшего размера. Файлы PNG состоят из фрагментов, файлы PDF — из потоков, файлы ELF — из разделов, а сертификаты X.509 — из элементов ASN.1 TLV. Если в технике тестирования заложено представление о базовом формате, то с её помощью можно выделить эти записи и извлечь те, что приводят к обнаружению новой трассировки стека.

Техника тестирования, которую я использовал, способна выделять и извлекать интересные новые идентификаторы объекта ASN.1, последовательности SEQUENCE, целые числа INTEGER и т. д. После извлечения они могут случайным образом комбинироваться или вставляться в данные шаблона. На самом деле идея не новая, новая — реализация. Планирую в будущем сделать этот код общедоступным.

Работают ли эти подходы?​

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

Что в итоге?​

Как обширная, настраиваемая автоматизация тестирования безопасности с впечатляющими показателями покрытия не выявила этот баг?

Что пошло не так?​

1. Нет сквозного тестирования​

NSS — модульная библиотека. Многоуровневый дизайн отражён в подходе автоматизации тестирования безопасности, ведь каждый компонент тестируется независимо. Например, декодер QuickDER проходит расширенное тестирование, но при использовании техники тестирования объекты просто создаются, отбрасываются и никогда не используются:
Код:
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
char *dest[2048];

for (auto tpl : templates) {
PORTCheapArenaPool pool;
SECItem buf = {siBuffer, const_cast<unsigned char *>(Data),
static_cast<unsigned int>(Size)};

PORT_InitCheapArena(&pool, DER_DEFAULT_CHUNKSIZE);
(void)SEC_QuickDERDecodeItem(&pool.arena, dest, tpl, &buf);
PORT_DestroyCheapArena(&pool);
}
При использовании техники тестирования QuickDER объекты просто создаются и отбрасываются. Так проверяется синтаксический анализ ASN.1, но не корректность работы с получаемыми объектами других компонентов.
С этой техникой тестирования можно было создать SECKEYPublicKey, который бы достал в уязвимый код. Но баг так и не был бы обнаружен, ведь результат никогда не использовался для проверки сигнатуры.

2. Произвольные ограничения по размеру​

Для ввода автоматизированного тестирования задаётся произвольное ограничение в 10 000 байтов. В NSS такого ограничения нет: у многих структур этот размер возможно превысить. В случае с этой уязвимостью ошибки происходят на границах, поэтому ограничение следует выбирать с умом.
Приемлемым вариантом может быть 224–1 байтов, т. е. максимально возможный сертификат, предоставляемый сервером во время согласования рукопожатия TLS.
Хотя в NSS обрабатываются объекты даже большего размера, TLS задействовать нельзя, а это снижает общую степень серьёзности любых пропущенных уязвимостей.

3. Метрики и заблуждения​

Все техники тестирования NSS представлены в объединённых показателях покрытия oss-fuzz, а не в их индивидуальных покрытиях. Эти данные оказались неверными, так как уязвимый код проходит расширенное автоматизированное тестирование, но с помощью техник тестирования, которые не могли генерировать соответствующие входные данные.
Почему? Потому что в техниках тестирования типа tls_server_target используются фиксированные, жёстко заданные сертификаты. При этом выполняется код, относящийся к проверке сертификата, но проводится автоматизированное тестирование лишь сообщений TLS и изменений состояния протокола.

Что всё-таки сработало?​

  • Благодаря дизайну библиотеки проверки корректности сертификатов mozilla::pkix не было допущено ухудшение ситуации с этим багом. К сожалению, она не используется вне Firefox и Thunderbird.
Сложно сказать, удача это или нет. Вероятно, RSA-PSS в mozilla::pkix будет разрешён.

Рекомендации​

Эта проблема свидетельствует о том, что даже в очень хорошо поддерживаемом C/C++ могут быть фатальные, простейшие ошибки.

Краткосрочные рекомендации​

  • Увеличить максимальный размер объектов ASN.1, создаваемых с помощью libFuzzer, с 10 000 до 224–1 = 16 777 215 байтов.
  • При использовании техники тестирования QuickDER должны вызываться соответствующие API с любыми успешно созданными объектами, прежде чем они будут уничтожены.
  • Показатели покрытия кода oss-fuzz нужно разделить по технике тестирования, а не по проекту.

Решение​

Эта уязвимость, CVE-2021-43527, закрыта в NSS 3.73.0. Если вы вендор, распространяющий NSS в своих продуктах, то вам, скорее всего, потребуется заменить библиотеку или применить патч.
 
Похожие темы
Admin Интересно «Пароль01» и дырявый VPN. Как пустить хакеров в сеть, чтобы они сломали вообще всё (пошаговая инструкция). Новости в сети 0
Admin Статья Как "Казаки" паттерны мошенников-"Разбойников" вычисляют, вооружаясь технологиями. Анонимность и приватность 0
Admin Интересно Ваш сервер — их притон: как группа UAT-7290 сдает ваши сервера в аренду своим друзьям. Дорого. Новости в сети 0
Admin Интересно Как стать «богом» в Linux, просто правильно подгадав время. Спойлер: вам понадобится Chronomaly. Новости в сети 0
Admin Статья Как оставаться незаметным в 2025 году – простые правила оперативной безопасности для всех. Анонимность и приватность 0
Admin Статья HTTP Request Smuggling в 2025: Как обходить современные WAF Уязвимости и взлом 0
Admin Статья Криптография в малвари: Как работают вымогатели (Ransomware). Полезные статьи 0
Admin Статья Право на root. Как повышают привилегии в Linux. Уязвимости и взлом 0
Admin Статья Как простой баг повреждения памяти ядра Linux приводит к полной компрометации системы(Часть 2) Уязвимости и взлом 0
Admin Статья Как простой баг повреждения памяти ядра Linux приводит к полной компрометации системы(Часть 1) Уязвимости и взлом 0
Admin Статья Почему ваш «Windows» прокси палится как Linux: Глубокий разбор TCP Window Size, о котором молчат. Анонимность и приватность 0
Admin Интересно Старый конь борозды не испортит. Как сертификат десятилетней давности помог хакерам проникнуть в госучреждения Азии. Новости в сети 0
Admin Статья Direct Syscalls vs EDR: Как заставить Windows выполнять ваши команды в обход хуков защитного ПО Вирусология 0
Admin Интересно Gemini лезет из каждой дыры Chrome? Вот как убить все ИИ-кнопки и вернуть нормальный браузер. Новости в сети 0
Admin Интересно «Здравствуйте, я журналист, заполните анкету». Как хакеры из КНДР «разводят» южнокорейских экспертов. Новости в сети 0
Admin Статья Гейминг как источник данных: OSINT в виртуальных мирах OSINT 0
Admin Статья Крипто-детектив: Идем по следу транзакций. Как деанонить блокчейн. OSINT 0
Admin Интересно Семь миллионов долларов за одну ночь. Рассказываем, как пострадали пользователи Trust Wallet и что делать сейчас. Новости в сети 0
Admin Интересно Казалось, что летим, а на деле — ползём. Как ИИ-помощники незаметно крадут время у профессиональных кодеров. Новости в сети 0
Admin Статья Анонимные мессенджеры: Как общаться, не оставляя следов Анонимность и приватность 0
Admin Интересно Охотник стал добычей. Как «безопасники» ловят вирусы, пытаясь скачать инструменты для их поиска. Новости в сети 0
Admin Интересно Цифровое чудо на Рождество. Как ученым удалось восстановить UNIX V4 с ленты 1970-х годов. Новости в сети 0
Admin Статья Взгляд с другой стороны: как Linux админ ловит вас Полезные статьи 0
Admin Статья Как отслеживается e-mail? OSINT 0
Support81 «Менеджер» с архивом и черным ходом через Yandex. Как группировка APT31 годами шпионила за российскими IT-компаниями Новости в сети 1
Support81 От 314 до 968 млрд рублей. Как российский рынок кибербезопасности станет монополией за 6 лет Новости в сети 0
Support81 Перевод крупной суммы по СБП на свой же счёт будет расцениваться банком как подозрительный Новости в сети 0
Support81 Перехват DNS – что это за атака и как она работает? Новости в сети 0
Support81 Суверенный Рунет. Мишустин подписал постановление о том, как им будут управлять (и от чего защищать) Новости в сети 0
Support81 $120000000 испарились за утро: как хакерам удалось обойти 10 аудитов и причем здесь ракетная программа КНДР Новости в сети 0
Support81 Одна буква — миллионные потери. Как русская «Е» обманула разработчиков и присвоила их крипту Новости в сети 0
Support81 «Ага, туннель! Придушим». Ваш VPN тоже лагает на 4G? Объясняем, как операторы видят ваш трафик (и что с этим делать) Новости в сети 0
Support81 «Магический пакет» творит чудеса: как хакеры превратили Linux-сервер в невидимку Новости в сети 0
Support81 Касперский против ChatGPT: как антивирус вычислил вредонос, написанный ИИ Новости в сети 0
Support81 Не Таиланд, а рабство в Мьянме: как туристический рай стал перевалочной базой для похитителей россиян Новости в сети 0
Support81 Оригинальный соучредитель Tesla, управлявший компанией до прихода Маска, заявил, что Cybertruck выглядит как «мусорный контейнер» Новости в сети 0
wrangler65 Как стать хакером для «самых маленьких» Ч.2 Полезные статьи 0
wrangler65 Как стать хакером для «самых маленьких» Ч.1 Полезные статьи 0
Support81 Как война в Украине стала полигоном для наркокартелей Новости в сети 0
Support81 Вайб-кодинг звучал как шутка, пока Opal от Google не начал делать сайты по вашему описанию Новости в сети 0
Support81 Серые токены, чёрные схемы: как российский бизнес уходит в крипту до принятия закона Новости в сети 1
Support81 Нажали Play — хакер уже в системе. Как работает звуковой троян Новости в сети 0
Support81 Белым по белому: как стать «гением» в науке с помощью ChatGPT Новости в сети 0
Support81 Телефон против владельца: как Android помогает хакерам воровать криптовалюту Новости в сети 0
Support81 Операторы хакерского форума BreachForums, как сообщается, арестованы во Франции Новости в сети 0
Support81 Российская ИБ стала как медицина в глубинке — врач один, а если заболеет, то все умрут Новости в сети 0
Support81 Важно! Мобильный аудит Wi-Fi сетей: как быстро найти уязвимости с помощью Stryker Уязвимости и взлом 0
wrangler65 Интересно Как МВД России ищет киберпреступников и как оставаться анонимным в 2025 Анонимность и приватность 0
Support81 JPEG, пицца и разоблачения: как Error Level Analysis считывает ложь по пикселям Новости в сети 0
Support81 Ваш VPN — это троян. Он выглядит как защита, но работает как слежка Новости в сети 0

Название темы