Suture-это статический инструмент Tope-To и High Order Taint Analysess (для C), основанный на LLVM IR и построен на докторе Checker (большое спасибо его создателям!), Два основных момента::
Мы называем его шов в надежде, что он может быть хирургически точным, в то же время способная сшивать несколько функций Syscalls/входа вместе, чтобы построить потоки данных высокого порядка. Для получения более подробной информации, пожалуйста, обратитесь к нашей статье: Статически обнаружение уязвимостей в стиле высокого порядка в ядрах OS в ACM CCS'21 .
Сначала клонировать репо:
~$ git clone https://github.com/seclab-ucr/SUTURE.git suture
Затем настройте среду LLVM для шва:
~$ cd suture
~/suture$ python setup_suture.py -o ../suture_deps
Опции (для setup_suture.py ):
-b Определяет название ветви LLVM для установки для швов, по умолчанию по умолчанию «release_90» (то есть LLVM 9.0) в этом зеркальном репо LLVM.-o указывает папку для размещения всех вещей, необходимых для швов (например, LLVM), используйте любую папку, которую вы предпочитаете. В зависимости от вашего оборудования, настройка LLVM может занять довольно много времени. После того, как он закончится, файл SRCIPT с именем env.sh будет сгенерирован в папке Suture Root, он содержит команды для установки переменных среды, используемых Suture.
ВАЖНО : Обязательно активируйте эту env.sh каждый раз, прежде чем создавать/использовать шов (вы также можете добавить его содержащие команды в .BASHRC для автоматической активации при входе в систему оболочки)!
~/suture$ source env.sh
Далее мы собираемся построить шов:
~/suture$ cd llvm_analysis
~/suture/llvm_analysis$ ./build.sh
При успешной сборке шов готов к использованию.
Шар может использоваться для обнаружения уязвимостей с высоким порядком, в этом разделе, в этом разделе мы проходим этот процесс с примером (например, мотивирующий пример, как показано в разделе 2.1 в нашей статье).
Для обнаружения уязвимостей Suture требуют два типа ввода: (1) программный модуль, составленный в биткод LLVM (например, файл .bc ) и (2) файл конфигурации для модуля, который проявляет свои функции входа и контролируемые пользователи аргументы.
Давайте сначала подготовим бит -код LLVM для нашего мотивирующего примера:
~/suture$ cd benchmark
~/suture/benchmark$ ./gen.sh motivating_example
Примечание . Для удобства мы предоставляем gen.sh для компиляции .c к .bc и .ll (Bitcode Human LLVM), с уровнем оптимизации -O1 .
Теперь мы должны иметь мотивирующий_example.bc под той же тесной папкой, это модуль программы ввода для шва.
Затем он приходит к файлу конфигурации, мы уже подготовили один для мотивирующего примера:
~/suture/benchmark$ cat conf_motivating_example
entry0 MY_IOCTL 1
entry1 NULL_ARG
entry2 NULL_ARG
Объяснение : Каждая строка в файле конфигурации описывает информацию одной функции входа (например, функция верхнего уровня без вызывающих абонентов и обычно служит внешним интерфейсом) в программном модуле, он содержит до 3 токенов, разделенных пространством:
entry_x MY_IOCTL 0_2 .Таким образом, как показано выше, conf_motivating_example указывает, что существует три функции записи в мотивации_example.c : entry0 (параметр 1 контролируется пользователем), intry1 и intry2 (нет контролируемых пользователя параметров). Это соответствует коду мотивирующего примера.
После того, как программа BitCode и файл конфигурации входа будут готовы, мы можем запустить Suture, чтобы обнаружить уязвимость Daint:
~/suture$ ./run_nohup.sh benchmark/motivating_example.bc benchmark/conf_motivating_example
Объяснение : run_nohup.sh - это простой сценарий, вызывающий скомпилированный анализ LLVM проходов шва:
~/suture$ ./run_nohup.sh [path/to/program.bc] [path/to/entry_func_config]
После начала, в зависимости от фактического оборудования и сложности целевой программы, необходимое время для того, как шар для завершения анализа и обнаружения уязвимости сильно варьируется. Однако такая простая программа, как наш мотивирующий пример, обычно заканчивается мгновенно.
Решите, заканчивается ли анализ : во время выполнения Suture продолжает войти в файл в том же каталоге файла конфигурации записи , предположим, что путь файла конфигурации IS /path/to/conf_program , файл журнала будет / path/to/conf_program.log. Мы можем решить, заканчивается ли анализ, контролируя журнал:
~/suture$ grep "Bug Detection Phase finished" benchmark/conf_motivating_example.log
Вышеупомянутый файл журнала также является выходом Suture, Suture внедрит свои обнаруженные потенциальные уязвимости в файл журнала, которые можно извлечь и организовать в окончательный отчет о предупреждении после завершения анализа:
~/suture$ ./ext_warns.sh benchmark/conf_motivating_example.log
Объяснение : ext_warns.sh извлекут все предупреждения (в JSON), встроенные в заданный файл журнала, реорганизовать и красивые напечатать их в окончательные отчеты о предупреждении. Предупреждающие отчеты будут помещены в папку под тем же путем файла журнала, предположим, что файл журнала является /path/to/ conf_program.log, папка отчета о предупреждении будет /path/to/warns-conf_program-yyyy-mm-dd .
~/suture$ ls benchmark/warns-conf_motivating_example-2021-11-10/
all int_overflow taint_data_use taint_loopbound taint_ptr_def
В папке есть 5 отчетов о предупреждении, все содержит все типы предупреждений, сгруппированных вместе в соответствии с их взаимосвязью потока данных, в то время как другие отчеты содержат только определенные типы предупреждений (например, переполнение целого числа, испорченное указатель и т. Д.), Более подробная информация о том, как мы, предупреждения, можно найти в нашей статье (раздел 4). Обычно мы используем только унифицированный отчет все .
~/suture$ cat -n benchmark/warns-conf_motivating_example-2021-11-10/all
1 =========================GROUP 0 (2 - 2)=========================
2 #####Warned Insts#####
3 (u'motivating_example.c', 30, [u'IntegerOverflowDetector'])
4 ######################
5
6 ++++++++++++++++WARN 0 (2 - 2)++++++++++++++++
7 IntegerOverflowDetector says: motivating_example.c@30 (bar : %add = add i8 %0, -16, !dbg !31)
8 ********Trace 0(2)********
9 #####CTX##### entry0
10 entry0 (motivating_example.c@18)
11 #####INSTS#####
12 >>>>>>>>>>>>>>>>>>tag: 0x55b206570420 tf: 0x55b20695b1b0 (2)>>>>>>>>>>>>>>>>>>
13 motivating_example.c@18 ( %cond = icmp eq i32 %cmd, 0, !dbg !31)
14 motivating_example.c@21 ( store i8 %user_input, i8* getelementptr inbounds (%struct.data, %struct.data* @d, i64 0, i32 1, i64 0), align 4, !dbg !32, !tbaa !34)
15 #####CTX##### entry1 -> bar
16 entry1 (motivating_example.c@35)
17 ----> (motivating_example.c@35 : %call = tail call i32 @bar(i8* bitcast (%struct.data* @d to i8*)), !dbg !27)
18 bar (motivating_example.c@30)
19 #####INSTS#####
20 >>>>>>>>>>>>>>>>>>tag: 0x55b2065e7050 tf: 0x55b2068174f0 (1)>>>>>>>>>>>>>>>>>>
21 motivating_example.c@30 ( %0 = load i8, i8* %add.ptr, align 1, !dbg !31, !tbaa !32)
22 motivating_example.c@30 ( %add = add i8 %0, -16, !dbg !31)
23
Объяснение : На высоком уровне отчет о предупреждении содержит некоторые группы предупреждений, каждая группа содержит несколько предупреждений , и каждое предупреждение содержит несколько поток , которые происходят от пользовательского ввода и погружаются до того же конфиденциального оператора программы. Другими словами, одно предупреждение поднимается для определенного заявления программы и определенного типа (например, целочисленного переполнения), в то время как группа содержит несколько тесно связанных предупреждений с точки зрения потока данных (см. Раздел 4 в нашей статье), поэтому группа может иметь несколько предупрежденных заявлений программы и включать различные типы предупреждения.
Возьмите приведенный выше отчет о предупреждении в качестве примера:
===GROUP No. (min_order - max_order)=== , где min_order является минимальным порядком (например, просто порядок - это время вызовов функции входа, необходимых в этой группе, см. Раздел 3.2 в нашей статье для более формального определения.) Трексов Taint в этой группе и max_order the Max.WARN No. локально для своей группы.***Trace No. (order)*** , Taint Trace всегда имеет уникальный порядок , который можно рассчитать (например, не диапазон).Отчет о предупреждении определяет действительную уязвимость целочисленного переполнения в мотивирующем примере, избегая при этом потенциальных ложных тревог, которые может генерировать менее точный инструмент статического анализа, в разделе 2.1 можно найти детали.
Этот репо также содержит некоторые другие инструменты/сценарии, которые вы можете найти полезными.
~/suture$ python llvm_analysis/AnalysisHelpers/EntryPointIdentifier/entry_point_identifier.py /path/to/prog_module.bc
~/suture$ cat /path/to/prog_module.bc.all_entries
Объяснение : Этот скрипт может идентифицировать некоторые общие функции ввода в модуле драйвера Linux Device (например, ioctl () , read () , write () и т. Д.), что помогает построению файла конфигурации ввода в качестве ввода в шов. Эта утилита в основном реализована оригинальным Dr. Checker на основе некоторых знаний домена ядра (например, конкретные структуры данных, такие как File_operations , используемые для определения точек ввода драйверов), мы внесли некоторые улучшения (например, сделайте эвристику более надежной).
~/suture$ python flt_warns.py /path/to/warn_report [Regex] > /path/to/filtered_warn_report
Объяснение : Исходя из нашего опыта, многие ложные тревоги в отчете о предупреждении часто имеют одинаковую проблематичную подтойкую трассу (см. Раздел 6.3 в нашей статье). До тех пор, пока рецензент по предупреждению осматривает одну ложную тревогу и распознает FP-индуцирующий суб-трэнд, естественно, она может попытаться автоматически исключить все другие подобные ложные тревоги, содержащие одну и ту же подключение, снижая воспроизводимую рецензенту скорость ложной тревоги. Для этой цели мы предоставляем этот простой FLT_WARNS.PY , который принимает первоначальный отчет о предупреждении и регулярное выражение Python в качестве входных данных, затем он будет соответствовать RE против каждой Taint Trace в отчете, как только соответствует, Taint Trace будет рассматриваться как ложная тревога. Сценарий генерирует новый отчет о предупреждении отфильтрованного предупреждения, за исключением всех подходящих следах.
Возможно, вы заинтересованы в использовании швов в качестве статического анализа общего назначения (например, получить набор переменной с точками/делом в определенном месте программы), это наверняка выполнимо, но в отличие от обнаружения уязвимости, вам необходимо затихивать руки и погрузиться в код швов, чтобы ознакомиться со структурами основных данных, и некоторые важные функции. Я надеюсь, что следующие советы могут помочь:
LLVM развивается очень быстро, без гарантии обратной совместимости. Так что очень вероятно, что вы столкнетесь с некоторыми ошибками компиляции при попытке создать швар с более новой версией LLVM (например,> 9.0). Но, к счастью, такие ошибки компиляции обычно легко разрешить (например, часто нам просто нужно заменить устаревшие API LLVM на более новые). Таким образом, в основном, чтобы адаптировать шар к более новой версии LLVM, сначала нам нужно настроить более новый LLVM в 0x0 (например, необходимо изменить Setup_suture.py , чтобы клонировать репо LLVM из активного источника и указать более новое имя ветви), а затем попытаться построить Suture против IT, решав разборчивые комбинированные имена.