Двое специалистов из Кембриджского университета опубликовали исследование о том, что они назвали уязвимостью в большинстве современных компиляторов. По факту авторы работы предложили новый метод атак, который использует вполне законную особенность средств разработки: нашли способ, при котором просмотр исходного кода программ показывает одно, а компилируется в итоге совсем другое. Как так вышло? Все дело в волшебных управляющих символах кодировки Unicode.
Чаще всего управляющие символы не выводятся на экран (хотя некоторые редакторы их все же показывают), а модифицируют каким-то образом остальной текст. В таблице показаны коды, отвечающие за работу алгоритма, — Unicode Bidirectional, или Bidi.
Как вы, вероятно, знаете, в одних языках принято писать текст справа налево, а в других — слева направо. Когда в тексте используется только один язык, проблем не возникает, но что, если в одной строке приводятся слова на разных языках, например на английском и арабском? В таком случае коды bidi позволяют указать, в каком направлении будет отображаться текст.
Авторы исследования воспользовались этими кодами, чтобы, например, сдвинуть идентификатор комментария в коде на языке Python в конец строки, хотя на самом деле он находится в ее середине. Они применили код RLI, который сдвигает только несколько символов, а все остальные не трогает.
Справа показан вариант кода, который увидит программист во время проверки исходников; слева — как код будет выполнен. В большинстве своем компиляторы игнорируют управляющие символы. Для того, кто проверяет код, пятая строка выглядит как безобидный комментарий. На самом деле в ней скрыта функция return, которая приведет к пропуску операции списания денег со счета. В этом примере условная банковская программа выдаст деньги, но не уменьшит остаток.
Почему это опасно?
На первый взгляд кажется, что это слишком простая уязвимость — ну кто будет коварно вставлять невидимые символы в код, надеясь обмануть проверяющих? На самом деле проблема была признана достаточно серьезной, чтобы выдать ей идентификатор уязвимости (CVE-2021-42574). Перед публикацией авторы уведомили разработчиков широко используемых компиляторов и дали им время на подготовку патчей.
В исследовании указаны базовые возможности подобной атаки. Приведено два варианта ее исполнения: скрыть в комментариях что-то, по факту являющееся командой, или скрыть что-то в строке, которая, например, выводится на экран. В теории можно добиться противоположного эффекта — создать код, строка которого выглядит как команда, а по факту будет являться частью комментария и не выполнится. Наверняка возможны более «творческие» методы эксплуатации данной проблемы.
Например, с помощью такой уловки можно провести качественную атаку на цепочку поставок: подрядчик поставляет организации код, который выглядит правильным, но работает не так, как задумывалось. А потом (после релиза конечного продукта) злоумышленник может воспользоваться внесенной «альтернативной функциональностью» для атаки на клиентов организации.
Насколько это опасно в реальности?
Вскоре после публикации работы программист Расс Кокс опубликовал критический разбор атаки Trojan Source. Он, мягко говоря, не впечатлился британским исследованием. Его тезисы следующие:
- это вовсе не новая атака;
- во многих редакторах кода с подсветкой синтаксиса «невидимые» коды видны;
- делать патчи для компиляторов не обязательно — достаточно внимательно проверять код, выявляя любого рода случайные или злонамеренные баги.
Действительно, проблема с управляющими символами Unicode всплывала, например, в 2017 году. Есть схожая, также давно известная проблема с омоглифами — символами, которые выглядят одинаково, но имеют разные коды. С их помощью тоже можно попробовать протащить через ручной анализ какой-нибудь посторонний код.
Впрочем, критический разбор не отменяет существование проблемы, у его автора скорее претензии к ее излишней драматизации. Особенно — к публикации по этой теме журналиста Брайана Кребса, где использованы совсем уж мрачные эпитеты типа «весь код в опасности».
Проблема существует, но, к счастью, решается она достаточно просто. Все патчи, которые уже выпущены или в ближайшее время будут применены, заблокируют компиляцию кода при наличии таких символов. Показательный пример — в бюллетене от разработчиков компилятора Rust. Если вы пользуетесь собственными инструментами сборки софта, рекомендуем добавить аналогичную проверку на скрытые символы. В нормальной ситуации их в коде быть не должно.
Опасность атак на цепочку поставок
Многие компании раздают задачи на разработку подрядчикам или используют в своих проектах готовые модули с открытым исходным кодом. Это всегда открывает определенный простор для атаки через цепочку поставок. Злоумышленники могут скомпрометировать подрядчика или встроить код в opensource-проект и подсунуть вредоносный код в финальное программное обеспечение. В результате конечный пользователь получит ПО из доверенного источника, но все равно потеряет свои данные. Чаще всего такие «закладки» выявляются на этапе проверки кода.
Trojan Source — это пример куда более «элегантной» атаки. Не попытка протащить мегабайты вредоносного кода в конечный продукт, а потенциальная возможность допустить в ключевом месте программы «незаметную» ошибку, которую потом можно будет эксплуатировать годами, максимально усложнив ее обнаружение.
Как остаться в безопасности
Для того чтобы обезопасить процесс обработки от возможной эксплуатации Trojan Source, имеет смысл:
- обновить компиляторы применяемых вами языков программирования (если для них выпущен соответствующий патч);
- написать собственные скрипты, детектирующие ограниченный ассортимент управляющих символов в коде.
В более широком смысле борьба с потециальными атаками на supply chain требует и ручного анализа кода, и широкого спектра автоматических тестов. Никогда не помешает посмотреть на собственный код с точки зрения киберпреступника, попробовать найти ту самую простейшую ошибку, ломающую весь механизм безопасности. Если у вас нет собственных ресурсов на такого рода анализ, можно воспользоваться помощью сторонних экспертов.