PetrSec 🐞 Notes
60 subscribers
11 photos
1 file
15 links
Канал разработчка на тему Application Security - фаззинг, статический анализ, уязвимости, реверс 🤙

По всем вопросам пиши @petrsec
Download Telegram
__attribute__ в языках С и С++

Не
давно изучил интересную возможность языка С - атрибуты функций и переменных. К примеру, с помощью атрибутов можно написать код, который выполнится до и после функции main (Но который в main не вызывается явным образом 😳):

#include <stdio.h>

__attribute__ ((constructor)) void before() {
printf("%s\n", "before");
}

__attribute__ ((destructor)) void after() {
printf("%s\n", "after");
}

int main()
{
printf("%s\n", "inside main");
return 0;
}

После запуска этого кода stdout будет таким:
before
inside main
after


__attribute__ были введены в GCC начиная с версии 2.0 , выпущенной в 1992 году как часть расширений, позже вошли в стандарт. Они предоставляют разработчикам гибкость при работе с компилятором, особенно в контексте низкоуровневого программирования, оптимизации и взаимодействия с аппаратным обеспечением.
Атрибуты позволяют:

- Указывать особенности функций, например, что функция является "чистой" (pure) или "константной" (const), чтобы компилятор мог применять оптимизации.
- Контролировать выравнивание данных в памяти int x __attribute__ ((aligned (16))) = 0;
- Управлять порядком инициализации глобальных переменных.
- Запрещать вызов attributeнкций __attribute__((deprecated))
- Объявляattributeволы
__attribute__((weak)), которые могут быть переопределены сильными.

// Эта функция никогда не возвращает управление
void __attribute__((noreturn)) exit_function() {
while (1);
}


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

С момента введения атрибутов в стандарте языка C++, появился новый синтаксис, атрибуты стали записываться через [[attribute]]. Поэтому сейчас существует разница в синтаксисе:
// В стиле GCC
void __attribute__((noreturn)) terminate();

// В стиле C++
[[noreturn]] void terminate();

#cpp
👍2
Undefined Behavior Sanitizer (UBSan) ☣️

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

Что мы знаем об UBSan? Он детектирует неопределенное поведение - отсутствие ограничений на поведение программы со стороны стандарта языка С/C++. Включается опцией -fsanitize=undefined и находит целый класс ошибок. Сам санитайзер разработан Google в процессе работы над проектом LLVM/Clang в 2012 году, а сейчас поддерживается сообществом LLVM и GCC.

Напишем простой пример программы с целочисленным переполнением:
int main() {
int k = 0x7fffffff;
k += argc;
return 0;
}


Соберём ее c UBSan и запустим:
clang++ -fsanitize=undefined -o out test.cc && ./out
test.cc:3:5: runtime error: signed integer overflow: 2147483647 + 1 cannot be represented in type 'int'
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior test.cc:3:5

echo $?
0

Программа не падает, сообщение о сработке санитайзера идет на stderr, а программа завершается с кодом 0. Так ведёт себя UBSan по дефолту, если не задать дополнительных опций.

Разбор опций

-fsanitize=undefined - добавляет UBSan-инструментацию, по дефолту выводит подробное сообщение о срабатывании в stderr и пытается продолжить выполнение. Если далее ошибок не случилось, возвращает в итоге 0. За вывод подробного отчета об ошибке отвечает runtime библиотека libubsan (в инструкциях будут __ubsan_handle_*). При этом, сама UBSan-инструментация (проверки на UB) при этом не трубуют внешних либ.

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

-fsanitize-recover=undefined - при обнаружения UB программа выводит сообщение в stderr, но не завершается аварийно (это дефолтное поведение при сборке с флагом -fsanitize=undefined).

-fno-sanitize-recover=undefined - при обнаружения UB программа выводит сообщение в stderr и завершает выполнение с кодом 1.

-fsanitize-trap=undefined - при обнаружения UB программа завершается с SIGILL (код 132) и выводит сообщение:
Illegal instruction (core dumped) ./a.out

Это так называемый режим ловушки (trap-mode), при котором компилятор для отображения подробностей ошибки не добавляет в код тяжелую инструментацию из libubsan, а вставляет только инструкции детектирования UB, и при их срабатывании ставит недопустимую аппарутную инструкцию (для x86/x86_64 это ud2 - Undefined Instruction), на ней бинарь обычно и падает с SIGILL или SIGTRAP. В этом случае значительно уменьшается размер бинаря, так как libubsan не линкуется.

-fsanitize-minimal-runtime - компромиссный режим вывода информации об ошибке, только тип ошибки и адрес инструкции:
./ubsan_min
ubsan: add-overflow by 0x0000000000400f6c


Для сравнения вес бинарей при сборке с разными опциями:
409K - undefined
20K - undefined-minimal-runtime
13K - undefined-trap


Что же происходит при сборке под фаззинг AFL++, если мы собираем с UBSan?

⚡️Когда мы ставим переменную AFL_USE_UBSAN=1 под капотом ставятся флаги компиляции:
-fsanitize=undefined (активирует санитайзер UBSan);
-fsanitize-trap=undefined (заставляет программу генерировать SIGILL при срабатывании UBSan);
Этот режим позволяет с максимальной скоростью фаззить и находить краши, вызваные UB и не тратить ресурсы на подробное отображение причины и типа краша.

💡А если ставим переменные AFL_USE_UBSAN=1 и AFL_UBSAN_VERBOSE=1, то под капотом ставятся флаги:
-fsanitize=undefined;
-fno-sanitize-recover=undefined (подробный вывод в stderr и завершение с кодом 1);
Этот флаг AFL++ разумно использогвать после фаззинга при разборе результатов, потому что он сильно садит скорость фаззинга и добавляет веса бинарям за счет тяжелой инструментации из libubsan.

Надеюсь получилось внести бОльшую ясность в эту тему, в первую очередь для себя)

#fuzz #fuzzing #testing #DAST #dast #afl #vulnerability #cpp
🔥2👍1