>
>
>
Атака Trojan Source: скрытые уязвимости

Гость
Статей: 23

Атака Trojan Source: скрытые уязвимости

Мы представляем новый тип атаки для внедрения в исходный код вредоносных изменений, по-разному выглядящих для компилятора и человека. Такая атака эксплуатирует тонкости стандартов кодирования символов, таких как Юникод, и позволяет создавать исходный код, токены которого логически кодируются в порядке, отличном от того, в котором они отображаются. Это приводит к уязвимостям, незаметным при просмотре кода. Атаки 'Trojan Source', как мы их называем, представляют непосредственную угрозу как для производителей программного обеспечения, так и для каждого в цепочке поставок. В данной работе мы представляем примеры Trojan Source атак в C, C++, C#, JavaScript, Java, Rust, Go и Python. В данной работе мы рассмотрим эффективные средства защиты на уровне компиляторов, а также опишем иные системы контроля для снижения рисков, которые можно использовать в редакторах, репозиториях и конвейерах сборки при обновлении компиляторов для предотвращения данной атаки.

Мы опубликовали и перевели эту работу с разрешения правообладателя. Авторы – Николас Баучер (nicholas.boucher@cl.cam.ac.uk ) и Росс Андерсон (ross.anderson@cl.cam.ac.uk). Оригинал опубликован на сайте arXivLabs.

I. Введение

Что если можно было бы заставить компиляторы создавать бинарные файлы, логика которых не соответствует той, что видна в исходном коде? Мы продемонстрируем, что многие современные компиляторы подвержены этой атаке. Более того, злоумышленники могут использовать эту атаку в своих целях.

Здесь перечислены тонкости современных стандартов кодирования символов, таких как Юникод, которые могут быть использованы для написания кода, выглядящего для компилятора и разработчика по-разному. При помощи этого хакеры могут незаметно изменять логику приложения и намеренно внедрять уязвимости.

Факт того, что надежные компиляторы создают бинарные файлы, которые правильно реализуют алгоритмы, определенные в исходном коде, является основополагающим в сфере программного обеспечения. Известно, что вредоносные компиляторы могут создавать бинарные файлы, содержащие уязвимости [1]; в связи с этим были предприняты серьезные меры по проверке компиляторов и смягчению возможных негативных последствий их использования. Однако, насколько нам известно, создание уязвимых бинарных файлов с помощью немодифицированных компиляторов путем манипулирования кодировкой невредоносного исходного кода до сих пор не изучалось.

Рассмотрим случай, при котором злоумышленник хочет внедрить уязвимости в программное обеспечение вышестоящего сервера. Такое недавно произошло с SolarWinds [2]. Существуют два метода, которые злоумышленники могут использовать для достижения своей цели. Первый – это привлечение лица, имеющего доступ к конфиденциальной информации, готового внести уязвимости в код системы ПО. Второй – внесение скрытых уязвимостей в проекты с открытым исходным кодом. Чтобы предотвратить или снизить вероятность таких атак, разработчикам необходимо хотя бы единожды выполнять проверку кода или безопасности каждого внесенного изменения. Однако даже такой контроль можно обойти. Уязвимости могут остаться незаметны при просмотре кода – они могут скрываться в кодировке текста.

Такая атака вполне осуществима, что мы и продемонстрируем далее.

Основные цели данной работы:

  • Определить новый класс уязвимостей, которые мы называем Trojan Source атаками. Они используют вредоносные, но семантически допустимые модификации исходного кода для внедрения невидимых уязвимостей программного обеспечения.
  • Предоставить примеры Trojan Source атак в C, C++, C#, JavaScript, Java, Rust, Go и Python.
  • Описать эффективные средства защиты, которые должны применять компиляторы, а также другие средства защиты, которые можно использовать в редакторах, репозиториях и конвейерах сборки.
  • Задокументировать согласованный процесс раскрытия информации, который мы проводили для оглашения этой уязвимости во всех отраслях программирования.
  • Поднять вопрос о критериях надежности компилятора.

Предварительные сведения

A. Надежность компиляторов

Компиляторы переводят текст, написанный на языке программирования высокого уровня, в низкоуровневое представление, например, архитектурно-зависимые машинные команды или переносимый байт-код. Их задача – соблюдать официальные языковые спецификации, отклонения от которых считаются ошибками.

С 1960-х годов [4] велись исследования формальных методов математического доказательства того, что выходные данные компилятора правильно реализуют предоставленный исходный код [5], [6]. Многие расхождения между логикой исходного кода и логикой компилятора возникают из-за оптимизаций компилятора, о которых бывает трудно рассуждать [7]. Эти оптимизации также могут вызывать побочные эффекты, которые сказываются на безопасности [8].

Б. Кодировки текста

Цифровой текст хранится в виде закодированной последовательности числовых значений или кодовых позиций, которые соответствуют визуальным глифам определенной спецификации. Раньше преобладали спецификации с одним сценарием, такие как ASCII. Современные текстовые кодировки стандартизированы вокруг Юникода [9] (согласно данным W3Techs, в 2021 году 97% из 10 миллионов наиболее посещаемых веб-сайтов используют UTF-8 кодировки Юникода).

На момент написания работы Юникод насчитывает 143 859 символов в 154 различных сценариях в дополнение к многочисленным наборам символов, не являющихся рукописными (например, emoji), плюс множество управляющих символов. Хотя его спецификация обеспечивает соотнесение числовых кодовых позиций и символов, бинарное представление этих кодовых точек определяется тем, какая именно кодировка используется. Одной из наиболее распространенных является UTF-8.

Текст отображается при помощи интерпретации закодированных байтов в виде цифровых кодовых позиций в соответствии с выбранной кодировкой. Затем происходит поиск символов в соответствующей спецификации. Далее разрешаются все управляющие символы и, наконец, отображаются глифы, предусмотренные для каждого символа в выбранном шрифте.

В. Атаки через цепочки поставок

Атаки через цепочки поставок – это атаки, во время которых злоумышленник намеренно пытается создать уязвимости в развернутых приложениях, операционных системах и компонентах ПО [10]. Появившись однажды, такие уязвимости, скорее всего, сохранятся в поврежденной экосистеме, даже если позже для них сделают патчи [11]. После ряда атак на многие компании и правительственные ведомства атаки через цепочки поставок привлекли пристальное внимание Белого дома [12].

Злоумышленники могут внедрять уязвимости в атаки через цепочки поставок, изменяя исходный код, компрометируя системы сборки или совершая атаки на выпущенное ПО [13], [14]. Распространение атак снижается за счет действий производителей ПО, подписывающих бинарные файлы, поэтому злоумышленники предпочитают атаковать на ранних стадиях разработки. Атаки на вышестоящее ПО, например, на популярные пакеты программ, могут повлиять на множество зависимых продуктов, потенциально ставя под угрозу целые экосистемы. Поскольку атаки через цепочки поставок затрагивают различные организации, их моделирование и снижение рисков атак требуют учета технических, экономических и социальных факторов [15].

ПО с открытым исходным кодом предоставляет злоумышленникам возможность, с помощью которой они могут запустить атаки через цепочки поставок [16], и входит в OWASP Top 10 уязвимостей безопасности в веб-приложения [17].

III. Методология атаки

А. Переупорядочивание

Интернационализированные текстовые кодировки должны поддерживать языки, имеющие направление письма слева направо, такие как английский и русский. А также языки, имеющие направление письма справа налево, например, иврит и арабский. При сочетании сценариев с разными порядками написания, должен существовать фиксированный способ разрешения конфликтующей направленности письма. Для Юникода это реализовано алгоритмом обработки двунаправленного текста (Bidirectional или Bidi Algorithm) [3].

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

В таблице I приведен список символов переопределения Bidi, имеющих отношение к этой атаке. Следует отметить символы LRI и RLI, которые форматируют последующий текст слева направо и справа налево соответственно, и оба завершаются символом PDI.

Таблица I. Форматирование символов Юникода, имеющих отношение к перераспределению атак. Полный список можно найти в спецификации BIDI [3].

Обозначение 

Кодовая позиция 

Название 

Описание 

LRE 

U+202A 

Left-to-Right Embedding

Пытается обработать последующий текст слева направо.

RLE 

U+202B 

Right-to-Left Embedding

Пытается обработать последующий текст справа налево.

LRO 

U+202D 

Left-to-Right Override

Принудительно обрабатывает последующий текст слева направо.

RLO 

U+202E 

Right-to-Left Override

Принудительно обрабатывает последующий текст справа налево.

LRI 

U+2066 

Left‑to‑Right Isolate

Принудительно обрабатывает последующий текст слева направо, не затрагивая соседний текст.

RLI 

U+2067 

Right‑to‑Left Isolate 

Принудительно обрабатывает последующий текст справа налево, не затрагивая соседний текст.

FSI 

U+2068   

First Strong Isolate 

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

PDF 

U+202C 

Pop Directional Formatting 

Завершает действие ближайшего символа LRE, RLE, LRO или RLO.

PDI 

U+2069 

Pop Directional Isolate

Завершает действие ближайшего символа LRI или RLI.

Символы Bidi позволяют отображать даже символы, принадлежащие только одному скрипту (single-script characters), в порядке, отличном от их логической кодировки. Этот факт ранее использовался для маскировки расширений файлов вредоносных программ, распространяемых по электронной почте [18] и для создания вредоносных примеров для конвейеров машинного обучения на основе NLP [19].

В качестве примера рассмотрим следующую последовательность символов Юникода:

RLI a b c PDI

которая будет отображаться как:

c b a

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

Б. Перетасовка изолятов

В спецификации Bidi изоляты (isolates) – группы символов, которые обрабатываются как единое целое; т.е. все изоляты будут перемещены как единый блок, когда порядок отображения будет переопределен.

Изоляты могут быть вложенными. Например, рассмотрим последовательность символов Юникода:

RLI LRI a b c PDI LRI d e f PDI PDI

которые будут отображаться как:

d e f a b c

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

В. Действия с компилятором

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

Мы можем эксплуатировать это отличие для создания злонамеренно закодированного текста, который по-разному выглядит для человека, просматривающего код, и компилятора.

Г. Соблюдение синтаксиса

Большинство тщательно проработанных языков программирования не допускают наличие произвольных управляющих символов в исходном коде, поскольку они будут рассматриваться как токены, влияющие на логику. Таким образом, случайное размещение символов переопределения Bidi в исходном коде обычно приводит к синтаксической ошибке компилятора или интерпретатора. Чтобы предотвратить появление таких ошибок, можно применять два основных принципа языков программирования:

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

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

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

Д. Новая атака через цепочку поставок

Суммируя вышесказанное, мы приступаем к рассмотрению новой атаки через цепочку поставок. Используя символы переопределения Юникода Bidi в комментариях и строках, злоумышленник может создать синтаксически корректный исходный код на большинстве современных языков, для которых порядок отображения символов представляет логику, отличающуюся от реальной логики. По сути, программа А превращается в программу Б.

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

Еще больше беспокоит тот факт, что символы переопределения Bidi сохраняются при применении copy-and-paste в большинстве современных браузеров, редакторов и операционных систем. Любой разработчик, копирующий код из ненадежного источника в защищенную кодовую базу, может непроизвольно внедрить незаметную уязвимость. Такое копирование кода является значительным источником существующих эксплойтов безопасности [20].

Е. Применимость

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

IV. Методы эксплуатации

Существует множество способов внедрения уязвимостей в исходный код. Основной принцип в каждом из них один: используются символы Bidi для создания синтаксически корректного изменения порядка символов исходного кода на языке перевода.

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

А. Ранние возвраты

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

Рассмотрим, например, случай со строками документации (docstrings) – формальными комментариями, которые призваны документировать назначение функции. Такие комментарии считаются хорошей практикой при разработке программного обеспечения. В языках, где документационные строки могут быть расположены в определении функции, злоумышленнику нужно только найти подходящее местоположение, чтобы написать слово return (или его языковой эквивалент) в документационном комментарии, а затем изменить порядок выполнения так, чтобы оператор return выполнялся сразу после комментария.

#!/usr/bin/env python3
bank = { 'alice': 100 }

def subtract_funds(account: str, amount: int):
    ''' Subtract funds from bank account then RLI''' ;return
    bank[account] -= amount
    return

subtract_funds('alice', 50)

Пример 1. Закодированные байты атаки Trojan Source "ранний возврат" в Python.

#!/usr/bin/env python3
bank = { 'alice': 100 }

def subtract_funds(account: str, amount: int):
    ''' Subtract funds from bank account then return; '''
    bank[account] -= amount
    return

subtract_funds('alice', 50)

Пример 2. Отображаемый код Trojan Source атаки "ранний возврат" в Python.

В примерах 1 и 2 соответственно показаны закодированные байты и отображаемый код атаки с ранним возвратом в Python3. Просматривая отображаемый текст исходного кода в примере 2, можно было бы ожидать, что значение bank['alice'] будет равно 50 после выполнения программы. Однако значение bank['alice'] остается 100 после выполнения программы. Это связано с тем, что слово return в документационной строке фактически выполняется из-за переопределения Bidi. В результате чего функция возвращается преждевременно, а код, который вычитает значение из банковского счета пользователя, не запускается вообще.

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

Б. Закомментирование

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

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

#include <stdio.h>
#include <stdbool.h>

int main()
{
  bool isAdmin = false;
  /*RLO } LRIif (isAdmin)PDI LRI begin admins only */
    printf("You are an admin.\n");
  /* end admin only RLO { LRI*/
  return 0;
}

Пример 3. Закодированные байты Trojan Source атаки "закомментирование" в C.

#include <stdio.h>
#include <stdbool.h>

int main()
{
  bool isAdmin = false;
  /* begin admins only */ if (isAdmin) {
    printf("You are an admin.\n");
  /* end admin only */ }
  return 0;
}

Пример 4. Отображаемый текст Trojan Source атаки "закомментирование" в C.

В примерах 3 и 4 соответственно показаны закодированные байты и отображаемый текст закомментированной атаки с комментариями в C. Отображаемый текст создает впечатление, что текст не должен отображаться, поскольку пользователь не является администратором. Однако при выполнении программа выводит "You are an admin". Условной конструкции на самом деле не существует; в логической кодировке текст полностью находится в комментарии. В данном примере функция Юникода, в которой знаки препинания, учитывающие направленность, такие как {, отображаются в обратном порядке – справа налево.

В. Растянутые строки

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

#!/usr/bin/env node

var accessLevel = "user";
if (accessLevel != "userRLO LRI// Check if adminPDI LRI") {
  console.log("You are an admin.");
}

Пример 5. Закодированные байты Trojan Source атаки с растянутой строкой в JavaScript.

#!/usr/bin/env node

var accessLevel = "user";
if (accessLevel != "user ") { // Check if admin
  console.log("You are an admin.");
}

Пример 6. Отображаемый текст Trojan Source атаки с растянутой строкой в JavaScript.

В примерах 5 и 6 соответственно показаны закодированные байты и отображаемый текст атаки с растянутой строкой в JavaScript. Кажется, что уровень доступа пользователя – "user", и ничего не должно записываться в консоль, но фактически, код выводит "You are an admin". Это связано с тем, что явный комментарий, следующий за проверкой, на самом деле не является комментарием, а включен в строковый литерал для проверки строк.

Как правило, метод растянутых строк позволяет злоумышленнику спровоцировать сбой проверки строк.

Однако существуют и другие, предположительно, более простые способы, с помощью которых злоумышленник может незаметно вызвать сбой проверки строк. Например, злоумышленник может вставить невидимые символы, то есть символы Юникода, которые отображают отсутствие глифа, например, пробел нулевой ширины (ZWSP – Zero Width Space; символ Юникода U+200B) в строковые литералы, используемые при проверке. Несмотря на то, что эти невидимые символы не изменяют способ отображения строкового литерала, они приведут к сбою проверки строк. Другой вариант – использовать символы, которые выглядят одинаково, известные как омоглифы, например, кириллическая буква "x", которая обычно отображается идентично латинской "x", используемой в английском языке, но занимает другую кодовую позицию. В зависимости от контекста использование других приемов кодировки символов может быть более актуальным, чем атака с растянутой строкой с использованием символов Bidi.

V. Смежные исследования

А. Безопасность URL-адресов

Обманчиво закодированные URL-адреса уже давно являются инструментом выбора спамеров [21], причем одним из самых ранних задокументированных примеров является случай с paypaI.com. Эта кампания в июле 2000 года пыталась обманом заставить пользователей раскрыть пароли от paypal.com, зарегистрировав домен со строчной буквой l, замененной визуально похожей прописной буквой I [22].

Эти атаки на домены становятся еще более опасными с появлением Юникода, который содержит гораздо больший набор визуально похожих символов или омоглифов, чем ASCII. Фактически, консорциум Юникод предоставляет длинный отчёт о безопасности, в котором рассматриваются проблемы, связанные с доменом [23]. Омоглифы в URL-адресах были тщательно изучены в литературе [24]—[27].

Punycode – стандарт для преобразования URL-адресов Юникода в ASCII, был создан для минимизации атак с использованием омоглифов в URL [28]. Этот стандарт сопоставляет хорошо известные омоглифы с одними и теми же последовательностями Punycode и предотвращает регистрацию многих визуально идентичных URL-адресов.

Б. Вредоносный NLP

Переопределения Bidi и омоглифы использовались для создания вредоносных элементов в среде машинного обучения NLP [19]. Эти символы также, как и невидимые символы, такие как пробелы нулевой ширины и управляющие символы удаления, используются для создания строк, которые визуально идентичны некоторой целевой строке, но представлены разными кодировками Юникода. Оптимальные кодировки определяются с использованием безградиентных методов оптимизации, которые могут использоваться для управления производительностью моделей как целенаправленно, так и нецеленаправленно.

В. Визуально обманчивое вредоносное ПО

Символы Bidi исторически использовались для изменения внешнего вида расширений файлов [18]. Этот метод помогает распространять вредоносное ПО через электронную почту. Пользователь запускает исполняемый файл, считая, что он безопасен.

Переопределения направленности аналогичным образом использовались по крайней мере в одном семействе вредоносных программ для маскировки имен вредоносных системных служб [29].

Также существуют атаки, в которых злоумышленник использует омоглифы для создания файловых имен, визуально похожих на ключевые системные файлы. Затем он заменяет ссылки на системные файлы вредоносными [30].

VI. Оценочные испытания

А. Экспериментальная установка

Чтобы подтвердить существование атак, описанных в этой работе, мы реализовали данные концепции на простых программах на C, C++, C#, JavaScript, Java, Rust, Go и Python. В качестве доказательства концепции мы приведем примеры программ, исходный код которых при отображении содержит логику, при которой ничего выводиться не будет. Однако скомпилированные версии программ выводят текст "You are an admin." из-за применения Trojan Source атак, использующих символы переопределения Bidi.

Чтобы атака такого типа работала, используемые компиляторы или интерпретаторы должны принимать на вход некоторую форму Юникода, например, UTF-8. Мы полагаем, что это сработает в подавляющем большинстве современных языков. Также необходимо, чтобы язык синтаксически поддерживал современный интернационализированный текст в строковых литералах или комментариях.

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

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

Следующие разделы данной работы описывают и доказывают существование концепции Trojan Source атаки для определенных языков программирования.

Б. С

Помимо поддержки строковых литералов, C поддерживает однострочные и многострочные комментарии [31]. Однострочные комментарии начинаются с // последовательности и заканчиваются символом новой строки. Многострочные комментарии начинаются с /∗ последовательности и заканчиваются ∗/ последовательностью. Удобно, что многострочные комментарии могут начинаться и заканчиваться на одной строке, несмотря на название. Строковые литералы содержатся в двойных кавычках, например " · ". Строки можно сравнивать с помощью функции strcmp, которая возвращает ложное значение, если строки равны и истинное значение, если строки неравны.

Как обсуждалось ранее, в примерах 3 и 4 показана атака "закомментирование" в C. Мы также приводим пример атаки с растянутой строкой в C в приложении Д в примерах 24 и 25.

Язык С хорошо подходит для закомментированных методов эксплойта и методов с растянутыми строками, но только частично подходит для ранних возвратов. Причиной этому служит многострочный разделитель комментариев – */. Он переупорядочивается с использованием переопределения символов справа налево и превращается в /*. Это визуально намекает, что что-то не так. Данную проблему можно устранить, написав обратные разделители комментариев, такие как /*/. Однако это менее изящный способ, так как остаются другие визуальные подсказки, такие как точка с запятой, в конце строки. Пример рабочей, но менее изящной атаки с ранним возвратом в C можно видеть на примерах 26 и 27 приложения Д. Код выглядит так, будто бы должно печататься "Hello World.", а на самом деле не печатается ничего.

Мы доказали, что эти атаки успешно осуществимы как на GNU gcc v7.5.0 (на Ubuntu), так и на Apple clang v12.0.5 (на macOS).

В. С++

Поскольку C++ является языком, производным от C, неудивительно, что для C++ актуальны те же методы атак [32]. Аналогичные программы, подтверждающие концепцию Trojan Source атак в C++, приведены в приложении А в примерах 8—11.

Мы убедились, что эти атаки успешно осуществимы как на GNU g++ v7.5.0 (на Ubuntu), так и на Apple clang++ v12.0.5 (на macOS).

Г. C#

C# – объектно-ориентированный язык, созданный компанией Microsoft. Как правило, язык работает под .NET, кроссплатформенной управляемой среде выполнения, и часто используется различными компаниями [33]. C#, так же как C и C++, подвержен атаке. Представляем примеры атак, доказывающие существование Trojan Source атак в C#, в примерах 12—15 приложения Б.

Мы выяснили, что обе атаки успешно эксплуатируются в .NET 5.0 с использованием dotnet-script интерпретатора на macOS.

Д. JavaScript

JavaScript, также известный как ECMAScript, является интерпретируемым языком, который находит широкое применение в браузерах, предоставляя сценарии для придания интерактивности веб-страницам. Также все чаще используется для реализации веб-приложений и API на стороне сервера [34]. JavaScript так же, как C, C++ и C#, подвержен атаке. Представляем примеры, доказывающие возможность применения Trojan Source атак в JavaScript в приложении Ж в примерах 32 и 33, а также в ранее рассмотренных примерах 5 и 6.

Мы убедились, что эти атаки применимы в Node.js v16.4.1 (macOS), которая представляет собой локальную среду выполнения JavaScript, созданную на основе V8, движка JavaScript, созданного Chrome.

Е. Java

Java – универсальный, компилируемый в байт-код язык, поддерживаемый Oracle [35]. Он так же, как C, C++, C# и JavaScript, подвержен атаке. Примеры атак, доказывающих существование Trojan Source атак в Java, можно найти в приложении В в примерах 16—19.

Мы доказали, что эти атаки осуществимы в OpenJDK 16.0.1 на macOS.

Ж. Rust

Rust – высокопроизводительный язык, все чаще используемый в системном программировании [36]. Он подвержен тем же атакам, что C, C++, C#, JavaScript и Java. Примеры атак, доказывающих существование Trojan Source атак в Rust, расположены в приложении Г в примерах 20—23.

Мы доказали, что эти атаки осуществимы в Rust v1.53.0 (на macOS). Однако обратите внимание, что одно из двух доказательств (в примерах 22 и 23) выдает предупреждение о неиспользуемой переменной при компиляции. Однако злоумышленник может обойти это предупреждение, просто используя эту переменную в любом месте программы.

З. Go

Go – это многоцелевой язык с открытым исходным кодом, созданный Google [37]. Он так же, как C, C++, C#, JavaScript, Java и Rust, подвержен атаке. Представляем примеры атак, доказывающих существование Trojan Source атак в Go в приложении Е в примерах 28 и 29.

Мы доказали, что эти атаки осуществимы в Go v1.16.6 на macOS. Отметим, что неиспользуемые переменные приводят к ошибкам компиляции в официальном компиляторе Go. Именно поэтому наше доказательство Trojan Source атаки "закомментирование" отличается от нашего стандартного подхода, демонстрирующего, что ни одна переменная не останется неиспользуемой.

И. Python

Python – скриптовый язык общего назначения, широко используемый в науке о данных и многих других сферах [38]. Python поддерживает многострочные комментарии в виде строк документов, открываемых и закрываемых с помощью ''' или """. Мы уже демонстрировали элегантную атаку раннего выхода в примерах 1 и 2.

Дополнительное доказательство закомментированной атаки в Python 3 в закодированном виде можно посмотреть в приложении З в примерах 34 и 35.

Мы доказали, что эти атаки могут использоваться в Python 3.9.5, скомпилированного с помощью clang 12.0.0 (на macOS), а также в Python 3.7.10, скомпилированного с использованием GNU gcc (на Ubuntu).

К. Обозреватели кода

Нас также интересовало то, как эти атаки визуализировались в редакторах и репозиториях кода, используемых в современных окружениях разработки, поскольку многие инструменты имеют разные реализации Юникода. Поэтому мы протестировали последние версии редакторов кода Visual Studio, Atom, Sublime Text, Notepad++, vim и emacs. Мы также протестировали интерфейсы веб-репозиториев GitHub и Bitbucket. Каждый тест был повторен на трех компьютерах на Windows 10, macOS Big Sur и Ubuntu 20.04. Результаты можно увидеть в приложении в таблице II, где  представляет код, который на момент написания работы отображался так же, как приведенные в данной статье примеры визуализаций. Также описаны любые отклонения от этого отображения.

VII. Обсуждение результатов

А. Этика

Мы придерживались определенных этических принципов на протяжении всего исследования. Мы не тестировали Trojan Source атаки на чужих кодовых базах. Более того, мы раскрыли информацию об уязвимостях компаниям и организациям, владеющим продуктами, в которых мы обнаружили уязвимости. Мы предложили 99-дневный период эмбарго после нашего первого раскрытия, чтобы дать возможность исправить продукты с дефектами, которые мы обсудим далее.

Б. Осуществимость атак

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

Известно много способов внедрения бэкдоров в критически важные кодовые базы. Одним из примеров является попытка внедрения бэкдора для повышения привилегий до root пользователя в ядро Unix, осуществленной так же неуловимо, как изменение токена "==" на "=" токен [39]. Эта уязвимость была обнаружена опытными разработчиками. Методы, описанные в данной работе, позволяют подобной атаке в будущем стать невидимой.

Недавние исследования в области безопасности и удобства написания кода показали, что значительная часть разработчиков "с удовольствием" копирует и вставляет небезопасный исходный код из неофициальных онлайн-источников, таких как Stack Overflow [20], [40]. Поскольку переопределения Bidi сохраняются благодаря типичному копированию и вставке, фрагменты вредоносного кода с невидимыми уязвимостями можно размещать на веб-сайтах в надежде, что они окажутся в производственном коде. Рынок таких уязвимостей динамичен, а затраты на основных платформах в настоящее время оцениваются в семизначные суммы [41].

Как показывают наши исследования, на момент написания статьи C, C++, C#, JavaScript, Java, Rust, Go и Python уязвимы для Trojan Source атак. В более широком смысле этот класс атак с высокой вероятностью может быть применим к любому языку с компиляторами, которые работают с Юникодом. Это представляет серьезную угрозу для организаций, безопасность которых зависит от целостности цепочек поставок ПО.

В. Подсветка синтаксиса

Многие разработчики используют текстовые редакторы, которые в дополнение к основным функциям редактирования текста обеспечивают подсветку синтаксиса. Более того, многие репозитории кода, такие как GitHub, обеспечивают подсветку синтаксиса через веб-браузер. Часто комментарии отображаются цветом, отличающимся от кода. Многие доказательства концепции Trojan Source атак, представленные в этой работе, вводят разработчиков в заблуждение, заставляя их думать, что комментарии являются кодом или наоборот.

Мы надеялись, что подсветка переопределений Bidi будет заметно отличаться от обычной подсветки синтаксиса, но исследование прошло неоднозначно. Определенные атаки приводили к появлению странной подсветки синтаксиса в некоторых редакторах. Этого может быть достаточно, чтобы предупредить разработчиков о наличии проблемы с кодировкой. Однако у каждого редактора была специфичная подсветка синтаксиса. Во время других атак аномальной подсветки при тех же настройках не наблюдалось.

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

Переопределения Bidi обычно приводят к тому, что курсор "прыгает" по строке при использовании клавиш со стрелками для перехода по токенам или для посимвольного выделения строки текста. Этот артефакт – эффект влияния логического упорядочения токенов, присутствующий на многих операционных системах и реализациях Юникода. Некоторым опытным разработчикам может быть достаточно такого поведения даже при отсутствии видимых изменений в тексте. Однако мы считаем, что для этого разработчикам потребуется просматривать код гораздо внимательнее, чем обычно.

Г. Атаки с применением невидимых символов

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

Мы предположили, что невидимые символы в именах функций могут определять другую функцию, отличную от функции, определяемой исключительно видимыми символами. Это может позволить злоумышленнику задать свою версию стандартной функции, такую как printf в C, которую можно запустить, вызвав функцию с невидимым символом в имени функции. Злоумышленник может незаметно добавить такую функцию в кодовую базу, задав ее в пакетах с открытым исходным кодом, который импортируется в глобальное пространство имен программы.

Однако мы обнаружили, что все компиляторы, рассмотренные в данной статье, выдавали ошибки компиляции при использовании описанного выше метода, за исключением одного компилятора – Apple clang v12.0.5, который выдавал предупреждение вместо ошибки.

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

Д. Атаки с использованием омоглифов

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

Нам успешно удалось реализовать доказательства концепции атаки с использованием омоглифов на языках, обсуждаемых в данной статье: C, C++, C#, JavaScript, Java, Rust, Go и Python – все они оказались уязвимыми к данной атаке. В нашем исследовании мы определили две функции, которые, казалось бы, назывались sayHello, однако в первой использовалась латинская буква H, а во второй – H на кириллице.

#include <iostream>

void sayHello()
{
  std::cout << "Hello, World!\n";
}

void sayНello()
{
  std::cout << "Goodbye, World!\n";
}

int main()
{
  sayНello();
  return 0;
}

Пример 7. Атака с использованием омоглифа в функции в C++.

В примере 7 реализована атака с использованием омоглифов в C++. Эта программа выводит текст "Goodbye, World!" при компиляции с использованием clang++. Хоть этот пример и кажется безвредным, атака с использованием омоглифов может принести значительный ущерб при применении к обычным функциям, например, через импортированную библиотеку. Например, предположим, что функция с именем hashPassword была заменена аналогичной функцией, которая возвращает то же значение, что и исходная функция, но перед этим "сливает" предварительно хэшированные пароли по сети.

Все компиляторы и интерпретаторы, рассмотренные в этой статье, выводили текст "Goodbye, World!", подтверждая концепцию атаки. Было всего три исключения. GNU gcc от и его аналог на C++, g++, оба выдавали ошибки обработки токенов. Особого внимания заслуживает компилятор Rust, который выдал предупреждение "mixed_script_confusables" при создании бинарного файла атаки с использованием омоглифа. В тексте предупреждения сообщалось, что имя функции с Н на кириллице содержало "mixed script confusables". Предлагалось перепроверить и убедиться, что использование функции необходимо. Это тщательно продуманная защита от атак с использованием омоглифов. Следовательно, можно сделать вывод о том, что как минимум одна группа компиляторов заметила рассматриваемую атаку.

Эта защита, вместе с защитой от атак с использованием невидимых символов, может послужить прецедентом. Справедливо ожидать, что в будущем компиляторы также будут иметь средства защиты от Trojan Source атак.

Е. Средства защиты

Самый простой способ защиты – запрет на использование символов, управляющих направленностью текста как в спецификациях языка, так и в компиляторах, реализующих эти языки.

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

Такую простую защиту можно усилить, добавив небольшое число изменений. Например, ввести запрет на использование всех символов, управляющих направленностью текста. В таком случае запрет будет распространяться и на тех, кто использует допустимые варианты использования переопределений Bidi в комментариях. Поэтому наиболее приемлемым вариантом защиты может быть запрет на использование незавершенных (unterminated) символов переопределения Bidi в строковых литералах и комментариях. Гарантия того, что каждое переопределение завершается, то есть, например, что каждый LRI имеет соответствующий PDI, делает невозможным искажение исходного кода за пределами строковых литералов и комментариев.

Защита от Trojan Source атак должна предоставляться по умолчанию во всех компиляторах, поддерживающих Юникод. Отключение защиты должно быть разрешено только при указании специально предназначенного флага подавления.

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

Конвейеры сборки, которые используются производителями ПО для создания и подписи производственного кода, могут проверять наличие переопределений Bidi перед началом каждой сборки и прерывать сборку при обнаружении таких символов в исходном коде. Альтернативно, конвейеры сборки могут проверять наличие более специфичных наборов незавершенных переопределений Bidi. Такая тактика мгновенно обеспечивает надежную защиту для разработчиков ПО.

Репозитории кода и текстовые редакторы также могут помочь предотвратить Trojan Source атаки, сделав их видимыми для ревьюверов. Например, репозитории кода, такие как веб-интерфейсы для просмотра закоммиченного кода, могут выделять переопределения Bidi и строки кода, содержащие их, чтобы программист сразу замечал такие атаки.

Редакторы кода могут работать аналогично. На самом деле, некоторые уже делают так; vim, например, по умолчанию показывает переопределения Bidi в виде числовых кодовых позиций, не давая отработать алгоритму Bidi. Однако многие редакторы кода не придерживаются такого поведения, включая большинство GUI-редакторов, таких как Microsoft VS Code и Apple Xcode (на момент написания работы).

Ж. Согласование раскрытия информации

Мы связались с девятнадцатью независимыми компаниями и организациями с целью согласования условий по раскрытию информации для создания средств защиты для затронутых компиляторов, интерпретаторов, редакторов кода и репозиториев кода. Мы установили 99-дневный запрет на раскрытие информации, в течение которого получатели информации могли бы воспользоваться средствами защиты до того, как мы опубликуем результаты нашего исследования атаки. Компании отвечали по-разному. Кто-то обещал исправить ошибки при помощи патчей и программ Bug Bounty. Другие сразу же отказывались и ссылались на защиту прав.

Изначально мы отобрали несколько компаний-получателей раскрытой информации, определив мейнтейнеров продукта, которых, как показали наши эксперименты, затронула атака Trojan Source. Мы также включили компании, которые, по нашим данным, поддерживали собственные внутренние компиляторы и инструменты сборки. Первая часть раскрытой информации была отправлена 25 июля 2021 года.

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

Из девятнадцати поставщиков ПО, с которыми мы сотрудничали, семь использовали сторонние платформы для получения информации об уязвимостях, шесть пользовались специальными веб-порталами, четыре получали информацию по электронной почте, зашифрованной PGP, а два – по электронной почте, без PGP. Все они подтвердили получение информации. В итоге девять из них обязались выпустить патчи.

Одиннадцать получателей предлагали программы Bug Bounty, предполагающие выплаты за раскрытие уязвимостей. Из них пять выплатили вознаграждение, средние выплаты составили 2246,40 долларов, в диапазоне до 4475 долларов.

9 сентября 2021 года мы отправили отчёт об уязвимости в CERT/CC, Координационный центр CERT, при поддержке CISA [42]. Наш отчёт приняли в тот же день для осуществления скоординированной помощи в раскрытии информации. Благодаря этому все пострадавшие поставщики получили доступ к VINCE, инструменту, обеспечивающему общую платформу связи между поставщиками, внедряющими средства защиты. Тринадцать наших получателей, включая CERT/CC, выбрали инструмент VINCE для обмена данными. CERT/CC также сообщил эту информацию трем дополнительным поставщикам помимо девятнадцати, с которыми мы уже связывались.

18 октября 2021 года две Trojan Source атаки вошли в CVE [43]: CVE-2021-42574 для отслеживания атаки Bidi и CVE-2021-42694 для отслеживания атаки с использованием омоглифа. Эти CVE были приняты организацией MITRE в соответствии со спецификацией Юникода.

В тот же день мы отправили PGP-зашифрованную информацию о раскрытии в список рассылки distros [44], в котором на момент написания статьи содержатся представители групп безопасности 21 операционной системы. Этот список координирует применение патчей для всех мейнтейнеров ОС, но максимальный период эмбарго составляет 14 дней.

Закономерности, отмеченные на протяжении всего процесса скоординированного раскрытия информации:

1) Новые модели уязвимостей: раскрытие информации об уязвимостях, несоответствующей общеизвестным шаблонам уязвимостей (например, CWEs [45]), скорее всего, будет отсеиваться получателями информации. Стоит отметить, что некоторые компании не признавали угрозу, если речь шла о неизвестной угрозе. Они признавали только легко подтверждаемые угрозы, такие как SQL инъекции. Это особенно относится к тем поставщикам ПО, которые передают процесс присуждения вознаграждений за ошибки сторонним службам. Мы обнаружили, что чаще всего нам удавалось возобновить процесс раскрытия в случае получения доступа к личным данным фирмы-получателя или в случае отказа на предоставление данных, при обращении в службы предоставления уязвимостей с просьбой связаться с постоянной службой безопасности.

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

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

4) Общение: платформа VINCE от CERT/CC предоставляет полезный, нейтральный и беспристрастный инструмент для межорганизационного обсуждения во время скоординированного раскрытия информации. Инструмент позволяет пострадавшим поставщикам размещать сообщения на скрытой доске обсуждений и значительно облегчает общение со всеми пострадавшими сторонами в одном месте. Команда CERT/CC также поможет скоординировать общение с пострадавшими поставщиками, на которых распространяется эмбарго. Этот полезный способ позволяет приложить больше усилий для раскрытия информации на безвозмездной основе. Как и CVE, CERT/CC повышает достоверность раскрытия информации.

5) Поддержка проектов с открытым исходным кодом: раскрытие информации командам, обеспечивающим безопасность ОС с открытым исходным кодом, помогает в создании патчей во всей экосистеме. Это также полезно для участников сообществ проектов с открытым исходным кодом, которые в противном случае не могут предлагать методы раскрытия, подпадающие под действие эмбарго. В частности, операционные системы Linux при поддержке коммерческих организаций получают финансирование и выплаты, которые гарантируют, что ошибки в инструментах с открытым исходным кодом будут исправлены до публичного раскрытия информации. Обычно в таких компаниях работают (или тесно сотрудничают с ними) мейнтейнеры проектов с открытым исходным кодом. Поэтому можно отнести их к мейнтейнерам безопасности.

З. Проверка экосистемы

В данном исследовании мы пытались выяснить, сможем ли мы найти какие-либо примеры Trojan Source атак, чтобы придать их огласке. По этой причине мы попытались проанализировать как можно больше экосистем с открытым исходным кодом на наличие данной атаки.

Мы создали регулярное выражение, которое определяло незавершенные последовательности переопределений Bidi в комментариях и строках. GitHub предоставил нам результаты выполнения этого шаблона для всех общедоступных коммитов, введенных в GitHub с января по середину октября 2021 года. Результаты содержали 7444 коммита (исключая языки разметки), которые затрагивали 2096 уникальных файлов, все еще присутствующих в общедоступных хранилищах по состоянию на октябрь 2021 года.

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

Тем не менее, мы обнаружили использования некоторых методов, аналогичных атакам с использованием Trojan Source атак. Был зафиксирован случай, когда инструмент статического анализа кода для смарт контрактов Slither [46] анализировал символы переопределения справа налево. Случай с данным инструментом доказывает необходимость проверки: он использует символ RLO для изменения порядка отображения двух односимвольных переменных, передаваемых в качестве аргументов. Также был зафиксирован случай использования символов RLI и LRI, применяемых для скрытия вызова system("cat /etc/passwd"); в скрипте Ruby. Однако используемый метод оставил визуальные артефакты и, по-видимому, является проверкой функциональности UTF8, а не полезной нагрузкой эксплойта. Мы также обнаружили несколько случаев обфускации кода на JavaScript, в которых использовались символы Bidi. Это необязательно было сделано злонамеренно, однако является нетипичным использованием переопределений направленности. Наконец, мы нашли несколько реализаций эксплуатации уязвимости для переопределения направленности в расширениях имен файлов, как упоминалось ранее [18].

Параллельно участники проекта Rust сканировали все исторические материалы, представленные в crates.io, менеджере пакетов Rust, и не обнаружили никаких доказательств эксплуатации в экосистеме Rust.

VIII. Заключение

В данной работе мы представили новый тип атаки, который позволяет вставлять невидимые уязвимости в исходный код. Рассматриваемые Trojan Source атаки используют управляющие символы Юникода для изменения порядка отображения блоков символов, что позволяет комментариям и строкам выглядеть как код и наоборот. Это позволяет злоумышленнику создавать код, который для компиляторов и ревьюверов кода выглядит по-разному. Мы представляем доказательства концепции для C, C++, C#, JavaScript, Java, Rust, Go и Python и утверждаем, что эта атака вполне может внедриться в любой язык программирования, который поддерживает интернационализированный текст в комментариях и строковых литералах, а также может появиться и в других стандартах кодирования.

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

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

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

С научной точки зрения, это исследование также вносит вклад в растущий объем работы по обеспечению удобства использования для разработчиков. Недостаточно, чтобы компилятор был проверен; он также должен быть безопасным для использования. Компиляторы, которые тривиально подвержены атакам злоумышленников, нельзя назвать безопасными.

Благодарности

Мы хотели бы поблагодарить GitHub за помощь в исследовании экосистемы с открытым исходным кодом, Пьетро Альбини и Мару Бос из проекта Rust за анализ crates.io и CERT/CC за помощь в скоординированном раскрытии.

Ссылки

[1] K. Thompson, "Reflections on trusting trust," Commun. ACM, vol. 27, no. 8, pp. 761–763, 1984. [Online]. Доступно здесь: https://doi.org/10.1145/358198.358210

[2] S. Peisert, B. Schneier, H. Okhravi, F. Massacci, T. Benzel, C. Landwehr, M. Mannan, J. Mirkovic, A. Prakash, and J. Michael, "Perspectives on the solarwinds incident," IEEE Security & Privacy, vol. 19, no. 02, pp. 7–13, mar 2021.

[3] The Unicode Consortium, "Unicode Bidirectional Algorithm," The Unicode Consortium, Tech. Rep. Unicode Technical Report #9, Feb. 2020. [Online]. Доступно здесь: https://www.unicode.org/reports/tr9/tr9-42.html

[4] J. Painter and J. McCarthy, "Correctness of a compiler for arithmetic expressions," in Proceedings of Symposia in Applied Mathematics, vol. 19. American Mathematical Society, 1967, pp. 33–41. [Online]. Доступно здесь: http://jmc.stanford.edu/articles/mcpain/mcpain.pdf

[5] M. A. Dave, "Compiler verification: a bibliography," ACM SIGSOFT Software Engineering Notes, vol. 28, no. 6, pp. 2–2, 2003.

[6] D. Patterson and A. Ahmed, "The next 700 compiler correctness theorems (functional pearl)," Proceedings of the ACM on Programming Languages, vol. 3, no. ICFP, pp. 1–29, 2019.

[7] V. D'Silva, M. Payer, and D. Song, "The correctness-security gap in compiler optimization," in 2015 IEEE Security and Privacy Workshops, 2015, pp. 73–87.

[8] L. Simon, D. Chisnall, and R. Anderson, "What you get is what you C: Controlling side effects in mainstream C compilers," in 2018 IEEE European Symposium on Security and Privacy (EuroS&P), Apr. 2018, pp. 1–15.

[9] The Unicode Consortium, "The Unicode Standard, Version 13.0," Mar. 2020. [Online]. Доступно здесь: https://www.unicode.org/versions/ Unicode13.0.0

[10] C. J. Alberts, A. J. Dorofee, R. Creel, R. J. Ellison, and C. Woody, "A systemic approach for assessing software supply-chain risk," in 2011 44th Hawaii International Conference on System Sciences, 2011, pp. 1–8.

[11] A. Nappa, R. Johnson, L. Bilge, J. Caballero, and T. Dumitras, "The attack of the clones: A study of the impact of shared code on vulnerability patching," in 2015 IEEE Symposium on Security and Privacy, 2015, pp. 692–708.

[12] J. Biden, "Executive Order on Improving the Nation's Cybersecurity," May 2021, Executive Order 14028. [Online]. Доступно здесь: https://www.whitehouse.gov/briefing-room/presidential-actions/ 2021/05/12/executive-order-on-improving-the-nations-cybersecurity

[13] R. J. Ellison and C. Woody, "Supply-chain risk management: Incorporating security into software development," in 2010 43rd Hawaii International Conference on System Sciences, 2010, pp. 1–10.

[14] E. Levy, "Poisoning the software supply chain," IEEE Security Privacy, vol. 1, no. 3, pp. 70–73, 2003.

[15] B. A. Sabbagh and S. Kowalski, "A socio-technical framework for threat modeling a software supply chain," IEEE Security Privacy, vol. 13, no. 4, pp. 30–39, 2015.

[16] M. Ohm, H. Plate, A. Sykosch, and M. Meier, "Backstabber's knife collection: A review of open source software supply chain attacks," in Detection of Intrusions and Malware, and Vulnerability Assessment, C. Maurice, L. Bilge, G. Stringhini, and N. Neves, Eds. Cham: Springer International Publishing, 2020, pp. 23–43.

[17] OWASP, "A9:2017 Using Components with Known Vulnerabilities," 2017. [Online]. Доступно здесь: https://owasp.org/www-project-top-ten/2017/ A9_2017-Using Components with Known Vulnerabilities.html

[18] Brian Krebs, "'Right-to-Left Override' Aids Email Attacks," Sep. 2011. [Online]. Доступно здесь: https://krebsonsecurity.com/2011/09/right-to-left-override-aids-email-attacks/

[19] N. Boucher, I. Shumailov, R. Anderson, and N. Papernot, "Bad Characters: Imperceptible NLP Attacks," 2021.

[20] Y. Acar, M. Backes, S. Fahl, D. Kim, M. L. Mazurek, and C. Stransky, "You get where you're looking for: The impact of information sources on code security," in 2016 IEEE Symposium on Security and Privacy (SP), 2016, pp. 289–305.

[21] G. Simpson, T. Moore, and R. Clayton, "Ten years of attacks on companies using visual impersonation of domain names," in APWG Symposium on Electronic Crime Research (eCrime). IEEE, 2020.

[22] B. Sullivan, "PayPal alert! Beware the 'Paypai' scam," Jul. 2000. [Online]. Доступно здесь: https://www.zdnet.com/article/paypal-alert-beware-the-paypai-scam-5000109103/

[23] The Unicode Consortium, "Unicode Security Considerations," The Unicode Consortium, Tech. Rep. Unicode Technical Report #36, Sep. 2014. [Online]. Доступно здесь: https://www.unicode.org/reports/tr36/tr36-15.html

[24] E. Gabrilovich and A. Gontmakher, "The homograph attack," Commun. ACM, vol. 45, no. 2, p. 128, Feb. 2002. [Online]. Доступно здесь: https://doi.org/10.1145/503124.503156

[25] T. Holgers, D. E. Watson, and S. D. Gribble, "Cutting through the confusion: A measurement study of homograph attacks," in Proceedings of the Annual Conference on USENIX '06 Annual Technical Conference, ser. ATEC '06. USA: USENIX Association, 2006, p. 24.

[26] MITRE, "CAPEC-632: Homograph Attack via Homoglyphs (Version 3.4)," MITRE, Common Attack Pattern Enumeration and Classification 632, Nov. 2015. [Online]. Доступно здесь: https://capec.mitre.org/data/ definitions/632.html

[27] H. Suzuki, D. Chiba, Y. Yoneya, T. Mori, and S. Goto, "Shamfinder: An automated framework for detecting idn homographs," in Proceedings of the Internet Measurement Conference, ser. IMC '19. New York, NY, USA: Association for Computing Machinery, 2019, p. 449–462. [Online]. Доступно здесь: https://doi.org/10.1145/3355369.3355587

[28] A. M. Costello, "RFC 3492 Punycode: A Bootstring encoding of Unicode for Internationalized Domain Names in Applications (IDNA)," Mar. 2003. [Online]. Доступно здесь: https://tools.ietf.org/html/rfc3492

[29] Microsoft, "Win32/Sirefef," Sep. 2017. [Online]. Доступно здесь: https://www.microsoft.com/en-us/wdsi/threats/malware-encyclopedia-description?Name=Win32/Sirefef

[30] J. Lell, "[Hacking-Contest] Invisible configuration file backdooring with Unicode homoglyphs," May 2014. [Online]. Доступно здесь: https://www.jakoblell.com/blog/2014/05/07/hacking-contest-invisible-configuration-file-backdooring-with-unicode-homoglyphs/

[31] ISO, ISO/IEC 9899:2018 Information technologyProgramming languages — C, 4th ed. Geneva, Switzerland: International Organization for Standardization, Jun. 2018. [Online]. Доступно здесь: https://www.iso.org/ standard/74528.html

[32] ISO, ISO/IEC 14882:2020 Information technologyProgramming languagesC++, 6th ed. Geneva, Switzerland: International Organization for Standardization, Dec. 2020. [Online]. Доступно здесь: https://www.iso.org/standard/79358.html

[33] ISO, ISO/IEC 23270:2018 Information technologyProgramming languagesC#, 3rd ed. Geneva, Switzerland: International Organization for Standardization, Dec. 2018. [Online]. Доступно здесь: https://www.iso.org/standard/75178.html

[34] Ecma, ECMA-262, 12th ed. Geneva, Switzerland: Ecma International, Jun. 2021. [Online]. Доступно здесь: https://www.ecma-international.org/ publications-and-standards/standards/ecma-262

[35] J. Gosling, B. Joy, G. Steele, G. Bracha, A. Buckley, D. Smith, and G. Bierman, The Java® Language Specification, 16th ed. Java Community Press, Feb. 2021. [Online]. Доступно здесь: https: //docs.oracle.com/javase/specs/jls/se16/jls16.pdf

[36] The Rust Project Developers, The Rust Reference. The Rust Foundation, 2018. [Online]. Доступно здесь: https://doc.rust-lang.org/reference

[37] The Go Project Developers, The Go Programming Language Specification. Google, Feb. 2021. [Online]. Доступно здесь: https://golang.org/ref/spec

[38] The Python Project Developers, The Python Language Reference, 3rd ed. The Python Software Foundation, 2018. [Online]. Доступно здесь: https://docs.python.org/3/reference

[39] J. Corbet, "An attempt to backdoor the kernel," Linux Weekly News, Nov. 2003. [Online]. Доступно здесь: https://lwn.net/Articles/57135

[40] F. Fischer, K. Böttinger, H. Xiao, C. Stransky, Y. Acar, M. Backes, and S. Fahl, "Stack Overflow considered harmful? The impact of copy&paste on android application security," in 2017 IEEE Symposium on Security and Privacy (SP), 2017, pp. 121–136.

[41] N. Perlroth, This Is How They Tell Me the World Ends: The Cyberweapons Arms Race. Bloomsbury, 2021.

[42] Carnegie Mellon University Software Engineering Institute, "CERT Coordination Center." [Online]. Доступно здесь: https://www.kb.cert.org

[43] MITRE, "About the CVE Program," Oct. 2021. [Online]. Доступно здесь: https://www.cve.org/About/Overview

[44] Openwall Project, "Operating system distribution security contact lists," Sep 2021. [Online]. Доступно здесь: https://oss-security.openwall.org/wiki/ mailing-lists/distros

[45] MITRE, "CWE Overview," Oct. 2021. [Online]. Доступно здесь: https://cwe.mitre.org/about/index.html

[46] J. Feist, "Slither – a Solidity static analysis framework," Oct. 2018. [Online]. Доступно здесь: https://blog.trailofbits.com/2018/10/19/slither-a-solidity-static-analysis-framework/

Приложение

А. Доказательство существования Trojan Source атаки в C++

#include <iostream>
#include <string>

int main() {
  std::string access_level = "user";
  if (access_level.compare("userRLO LRI// Check if adminPDI LRI")) {
    std::cout << "You are an admin.\n";
  }
  return 0;
}

Пример 8. Закодированные байты Trojan Source атаки с растянутой строкой в C++.

#include <iostream>
#include <string>

int main() {
  std::string access_level = "user";
  if (access_level.compare("user")) { // Check if admin
    std::cout << "You are an admin.\n";
  }
  return 0;
}

Пример 9. Отображаемый текст закомментированной Trojan Source атаки в С++.

#include <iostream>

int main() {
  bool isAdmin = false;
  /*RLO } LRIif (isAdmin)PDI LRI begin admins only */
    std::cout << "You are an admin.\n";
  /* end admin only RLO { LRI*/
  return 0;
}

Пример 10. Закодированные байты закомментированной Trojan Source атаки в С++.

#include <iostream>

int main() {
  bool isAdmin = false;
  /* begin admins only */ if (isAdmin) {
    std::cout << "You are an admin.\n";
  /* end admins only */ }
  return 0;
}

Пример 11. Отображаемый текст закомментированной Trojan Source атаки в C++.

Б. Доказательство существования Trojan Source атаки в C#

#!/usr/bin/env dotnet-script

string access_level = "user";
if (access_level != "userRLO LRI// Check if adminPDI LRI") {
  Console.WriteLine("You are an admin.");
}

Пример 12. Закодированные байты Trojan Source атаки с растянутой строкой в C#.

#!/usr/bin/env dotnet-script

string access_level = "user";
if (access_level != "user") { // Check if admin
  Console.WriteLine("You are an admin.");
}

Пример 13. Отображаемый текст Trojan Source атаки с растянутой строкой в С#.

#!/usr/bin/env dotnet-script

bool isAdmin = false;
/*RLO } LRIif (isAdmin)PDI LRI begin admins only */
  Console.WriteLine("You are an admin");
/* end admin only RLO { LRI*/

Пример 14. Закодированные байты закомментированной Trojan Source атаки в С#.

#!/usr/bin/env dotnet-script

bool isAdmin = false;
/* begin admins only */ if (isAdmin) {
  Console.WriteLine("You are an admin");
/* end admins only */ }

Пример 15. Отображаемый текст закомментированной Trojan Source атаки в C#.

В. Доказательство существования Trojan Source атаки в Java

public class TrojanSource {
  public static void main(String[] args) {
    String accessLevel = "user";
    if (accessLevel != "userRLO LRI// Check if adminPDI LRI") {
      System.out.println("You are an admin.");
    /* end admin only RLO { LRI*/
  }
}

Пример 16. Закодированные байты Trojan Source атаки с растянутой строкой в Java.

public class TrojanSource {
  public static void main(String[] args) {
    String accessLevel = "user";
    if (accessLevel != "user") { // Check if admin
      System.out.println("You are an admin.");
    }
  }
}

Пример 17. Отображаемый текст Trojan Source атаки с растянутой строкой в Java.

public class TrojanSource {
  public static void main(String[] args) {
    boolean isAdmin = false;
    /*RLO } LRIif (isAdmin)PDI LRI begin admin only */
      System.out.println("You are an admin.");
    /* end admin only RLO { LRI*/
  }
}

Пример 18. Закодированные байты закомментированной Trojan Source атаки в Java.

public class TrojanSource {
  public static void main(String[] args) {
    boolean isAdmin = false;
    /* begin admin only */ if (isAdmin) {
      System.out.println("You are an admin.");
    /* end admin only */ }
  }
}

Пример 19. Отображаемый текст закомментированной Trojan Source атаки в Java.

Г. Доказательство существования Trojan Source атаки в Rust

fn main() {
  let access_level = "user";
  if (access_level != "userRLO LRI// Check if adminPDI LRI") {
    println!("You are an admin.");
  }
}

Пример 20. Закодированные байты Trojan Source атаки с растянутой строкой в Rust.

fn main() {
  let access_level = "user";
  if access_level != "user" { // Check if admin
    println!("You are an admin");
  }
}

Пример 21. Отображаемый текст Trojan Source атаки с растянутой строкой в Rust.

fn main() {
  let is_admin = false;
  /*RLO } LRIif is_adminPDI LRI begin admin only */
    println!("You are an admin.");
  /* end admin only RLO { LRI */
}

Пример 22. Закодированные байты закомментированной Trojan Source атаки в Rust.

fn main() {
  let is_admin = false;
  /* begin admins only */ if is_admin {
    println!("You are an admin.");
  /* end admins only */ }
}

Пример 23. Отображаемый текст закомментированной Trojan Source атаки в Rust.

Д. Доказательство существования Trojan Source атаки в C

#include <stdio.h>
#include <string.h>

int main() {
  const char *access_level = "user";
  if (strcmp(access_level, "userRLO LRI// Check if adminPDI LRI")) {
    printf("You are an admin.\n);
  }
  return 0;
}

Пример 24. Закодированные байты Trojan Source атаки с растянутой строкой в C.

#include <stdio.h>
#include <string.h>

int main() {
  const char *access_level = "user";
  if (strcmp(access_level, "user")) { // Check if admin
    printf("You are an admin.\n);
  }
  return 0;
}

Пример 25. Отображаемый текст Trojan Source атаки с растянутой строкой в С.

#include <stdio.h>

int main() {
  /* Say hello; newline RLI /*/ return 0 ;
  printf("Hello world.\n");
  return 0;
}

Пример 26. Ранний возврат закодированных байтов Trojan Source атаки в С.

#include <stdio.h>

int main() {
  /* Say hello; newline; return 0 /*/
  printf("Hello world.\n");
  return 0;
}

Пример 27. Ранний возврат отображаемого текста Trojan Source атаки в С.

Е. Доказательство существования Trojan Source атаки в Go

package main

import "fmt"

func main {
  var accessLevel = "user"
  if accessLevel != "userRLO LRI// Check if adminPDI LRI" {
    fmt.Println("You are an admin.")
  }
}

Пример 28. Закодированные байты Trojan Source атаки с растянутой строкой в Go.

package main

import "fmt"

func main() {
  var accessLevel = "user"
  if accessLevel != "user" { // Check if admin
    fmt.Println("You are an admin.")
  }
}

Пример 29. Отображаемый текст Trojan Source атаки с растянутой строкой в Go.

package main

import "fmt"

func main() {
  var isAdmin = false
  var is SuperAdmin = false
  isAdmin = isAdmin || isSuperAdmin
  /*RLO } LRIif (isAdmin)PDI LRI begin admins only */
    fmt.Println("You are an admin.")
  /* end admin only RLO { LRI*/
}

Пример 30. Закодированные байты закомментированной Trojan Source атаки в Go.

package main

import "fmt"

func main(){
  var isAdmin = false
  var is SuperAdmin = false
  isAdmin = isAdmin || isSuperAdmin
  /* begin admins only */ if (isAdmin) {
    fmt.Println("You are an admin.")
  /* end admin only */ }
}

Пример 31. Отображаемый текст закомментированной Trojan Source атаки в Go.

Ж. Доказательство существования Trojan Source атаки в JavaScript

#!/usr/bin/env node

var isAdmin = false;
/*RLO } LRIif (isAdmin)PDI LRI begin admins only */
  console.log("You are an admin.");
/* end admin only RLO { LRI*/

Пример 32. Закодированные байты закомментированной Trojan Source атаки в JS.

#!/usr/bin/env node

var isAdmin = false;
/* begin admins only */ if (isAdmin) {
  console.log("You are an admin.");
/* end admin only */ }

Пример 33. Отображаемый текст закомментированной Trojan Source атаки в JS.

З. Доказательство существования Trojan Source атаки в Python

#!/usr/bin/env python3

access_level = "user"
if access_level != 'noneRLOLRI': # Check if admin PDILRI'
    print("You are an admin.\n")

Пример 34. Закодированные байты закомментированной Trojan Source атаки в Python.

#!/usr/bin/env python3

access_level = "user"
if access_level != 'none': # Check if admin
    print("You are an admin.")

Пример 35. Отображаемый текст закомментированной Trojan Source атаки в Python.

И. Визуальное представление для ревьювера кода

Таблица II. Подверженность атаке редакторов и веб-репозиториев кода по результатам данной работы.