Конкурс Underhanded C 2015: как скрывают уязвимости в коде ядерной верификации

Underhanded C, ежегодный конкурс, где программисты пишут код, выглядящий честным и безопасным, но содержащий скрытые уязвимости. В 2015 году конкурс спонсировала Nuclear Threat Initiative (NTI), организация по снижению угрозы ядерного оружия. Задача была реалистичной: создать программу для верификации ядерного вооружения, сравнивающую спектры излучения и выявляющую подделки.

Организаторы получили свыше 40 работ высокого качества. Около трети использовали одну технику, отравление NaN (Not a Number). В C переменная может получить значение NaN при вычислениях с неопределённым результатом (sqrt(-1.0) или 0/0). Ключевое свойство: любое сравнение с NaN возвращает false, это можно использовать для обхода проверок.

Пример атаки: функция вычисляет корреляцию спектров. Если поддельный источник сконструирован так, что все значения ниже базового уровня, вычитание даст ноль везде, корреляция будет 0/0 = NaN, и сравнение (correlation < threshold) вернёт false вместо true, проверка не сработает.

Другие победители применили изощренные подходы: логарифмирование функции Пуассона (NaN при значениях ~1686), манипуляцию количеством процессоров для race conditions, ввод невалидных символов через sscanf для создания NaN.

Организаторы подчеркнули, что победившие атаки были физически реалистичны. Например, добиться экстремального значения спектра можно, упаковав поддельный источник короткоживущим радионуклидом с одним мощным пиком или установив внутри генератор рентгеновского излучения. Конкурс демонстрирует, что в безопасности-критичных системах даже тщательный code review может пропустить такие баги.

Ключевые факты

  • Отравление NaN, распространённая техника для обхода проверок: NaN всегда сравнивается как false, что можно использовать для подделки результата
  • Победители конкурса использовали реалистичные сценарии атак: физически достижимые спектры излучения, системные манипуляции (количество CPU, часы), невалидные входные данные
  • Уязвимость Philippa Klenze использовала логарифм вероятности из распределения Пуассона, NaN возникает при значениях ~1686+, что достижимо упаковкой поддельного источника коротким изотопом
  • Баги удалось замаскировать в неподозрительных местах: logging-коде, обработке ошибок, выборе статистического метода
  • Результаты подчёркивают необходимость глубокого понимания граничных случаев в C и культуры постоянного скептицизма при ревью security-critical кода

Почему это важно

Ядерная верификация, это система, от которой зависит международная безопасность. Даже малейшая ошибка в программе может привести к тому, что поддельное оружие будет принято за подлинное. Конкурс Underhanded C демонстрирует, насколько легко скрыть критическую уязвимость в коде, который проходит поверхностный review. Баг может быть замаскирован в совершенно банальных местах, в logging, в обработке ошибок, в выборе статистического метода. Это подчёркивает необходимость не просто тестирования, но глубокого понимания всех граничных случаев и подводных камней языка программирования.

Кому это важно

Разработчикам безопасности-критических систем (ядерные системы, медицина, финансы, авиация); security researchers и auditors, которые проверяют такой код; авторам компиляторов и языков программирования; организациям, внедряющим международные стандарты проверки (МАГАТЭ, NTI); преподавателям информатики и security-инженерам; любым программистам на C, которые хотят понять подводные камни языка и как их можно использовать против безопасности.

Как это применить

При ревью кода обратить особое внимание на floating-point операции, логарифмы, деления, корни, они могут незаметно произвести NaN. Проверить, что сравнения с результатами таких операций правильно обрабатывают NaN (например, используя isnan()). Тестировать граничные случаи: экстремально большие/малые значения, нулевые значения, пустые массивы. Использовать статические анализаторы, которые ловят опасные паттерны (деление на переменную без проверки). Для security-critical кода использовать formal verification, а не просто code review. Документировать предположения о диапазонах входных данных и проверять их в runtime.

Можно ли доверять

Да, это официальные результаты конкурса Underhanded C, учреждённого в 1999 году. Судьями были признанные эксперты в области безопасности и языков программирования. Спонсорство Nuclear Threat Initiative подтверждает серьёзность и релевантность проблемы. Примеры кода, реальные работы участников конкурса, прошедшие полный процесс оценки. Организаторы также провели live AMA на Reddit для обсуждения деталей.

Риски и подводные камни

NaN-отравление, не единственный способ скрыть баг; есть множество других (race conditions, buffer overflows, integer overflows, undefined behavior). Knowing о NaN-трюке не гарантирует, что вы поймёте все способы его применения в конкретном коде. Реалистичная атака требует понимания физики (спектры излучения) или системных деталей (количество CPU), что может быть не очевидно при поверхностном ревью. Environment-triggered атаки требуют physical access или privileges, что усложняет анализ реализма. Полное избежание требует не просто знаний, но культуры постоянного скептицизма и paranoia.

«Вычисление с NaN-значением часто приводит к NaN; любое сравнение с NaN возвращает false.»

— Из описания уязвимостей конкурса Underhanded C 2015