Trojan Source: невидимые закладки в исходном коде

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

Двое специалистов из Кембриджского университета опубликовали исследование о том, что они назвали уязвимостью в большинстве современных компиляторов. По факту авторы работы предложили новый метод атак, который использует вполне законную особенность средств разработки: нашли способ, при котором просмотр исходного кода программ показывает одно, а компилируется в итоге совсем другое. Как так вышло? Все дело в волшебных управляющих символах кодировки Unicode.

Управляющие символы Unicode, позволяющие изменить направление текста внутри одной строки.

Управляющие символы Unicode, позволяющие изменить направление текста внутри одной строки. Источник.

Чаще всего управляющие символы не выводятся на экран (хотя некоторые редакторы их все же показывают), а модифицируют каким-то образом остальной текст. В таблице показаны коды, отвечающие за работу алгоритма, — Unicode Bidirectional, или Bidi.

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

Авторы исследования воспользовались этими кодами, чтобы, например, сдвинуть идентификатор комментария в коде на языке Python в конец строки, хотя на самом деле он находится в ее середине. Они применили код RLI, который сдвигает только несколько символов, а все остальные не трогает.

Пример уязвимого кода на Python  c использованием bidi-кодов.

Пример уязвимого кода на Python c использованием bidi-кодов. Источник.

Справа показан вариант кода, который увидит программист во время проверки исходников; слева — как код будет выполнен. В большинстве своем компиляторы игнорируют управляющие символы. Для того, кто проверяет код, пятая строка выглядит как безобидный комментарий. На самом деле в ней скрыта функция return, которая приведет к пропуску операции списания денег со счета. В этом примере условная банковская программа выдаст деньги, но не уменьшит остаток.

Почему это опасно?

На первый взгляд кажется, что это слишком простая уязвимость — ну кто будет коварно вставлять невидимые символы в код, надеясь обмануть проверяющих? На самом деле проблема была признана достаточно серьезной, чтобы выдать ей идентификатор уязвимости (CVE-2021-42574). Перед публикацией авторы уведомили разработчиков широко используемых компиляторов и дали им время на подготовку патчей.

В исследовании указаны базовые возможности подобной атаки. Приведено два варианта ее исполнения: скрыть в комментариях что-то, по факту являющееся командой, или скрыть что-то в строке, которая, например, выводится на экран. В теории можно добиться противоположного эффекта — создать код, строка которого выглядит как команда, а по факту будет являться частью комментария и не выполнится. Наверняка возможны более «творческие» методы эксплуатации данной проблемы.

Например, с помощью такой уловки можно провести качественную атаку на цепочку поставок: подрядчик поставляет организации код, который выглядит правильным, но работает не так, как задумывалось. А потом (после релиза конечного продукта) злоумышленник может воспользоваться внесенной «альтернативной функциональностью» для атаки на клиентов организации.

Насколько это опасно в реальности?

Вскоре после публикации работы программист Расс Кокс опубликовал критический разбор атаки Trojan Source. Он, мягко говоря, не впечатлился британским исследованием. Его тезисы следующие:

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

Действительно, проблема с управляющими символами Unicode всплывала, например, в 2017 году. Есть схожая, также давно известная проблема с омоглифами — символами, которые выглядят одинаково, но имеют разные коды. С их помощью тоже можно попробовать протащить через ручной анализ какой-нибудь посторонний код.

Впрочем, критический разбор не отменяет существование проблемы, у его автора скорее претензии к ее излишней драматизации. Особенно — к публикации по этой теме журналиста Брайана Кребса, где использованы совсем уж мрачные эпитеты типа «весь код в опасности».

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

Опасность атак на цепочку поставок

Многие компании раздают задачи на разработку подрядчикам или используют в своих проектах готовые модули с открытым исходным кодом. Это всегда открывает определенный простор для атаки через цепочку поставок. Злоумышленники могут скомпрометировать подрядчика или встроить код в opensource-проект и подсунуть вредоносный код в финальное программное обеспечение. В результате конечный пользователь получит ПО из доверенного источника, но все равно потеряет свои данные. Чаще всего такие «закладки» выявляются на этапе проверки кода.

Trojan Source — это пример куда более «элегантной» атаки. Не попытка протащить мегабайты вредоносного кода в конечный продукт, а потенциальная возможность допустить в ключевом месте программы «незаметную» ошибку, которую потом можно будет эксплуатировать годами, максимально усложнив ее обнаружение.

Как остаться в безопасности

Для того чтобы обезопасить процесс обработки от возможной эксплуатации Trojan Source, имеет смысл:

  • обновить компиляторы применяемых вами языков программирования (если для них выпущен соответствующий патч);
  • написать собственные скрипты, детектирующие ограниченный ассортимент управляющих символов в коде.

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

Советы