Разберите типичный баг переполнения буфера на C: void copy(char *dst, const char *src) { strcpy(dst, src); } — какие техники предотвращения, детектирования и восстановления существуют (ASLR, canaries, bounds checking, sanitizers)?

11 Янв в 06:34
31 +1
0
Ответы
1
Кратко о баге
- Ваша функция использует `strcpy(dst, src);`, которая копирует bytes до нуль-терминатора. Переполнение происходит, если длина источника не меньше длины назначения: ∣src∣≥∣dst∣|src| \ge |dst|srcdst. Тогда записываются данные за границей буфера (стек/куча), можно перезаписать сохранённый фрейм/адрес возврата и захватить управление.
Типичный стек (упрощённо)
- [локальные переменные (буфер)] → [canary?] → [saved EBP] → [return address]
Переполнение буфера может перезаписать canary, EBP и/или return address.
Как предотвращать, детектировать и восстанавливаться — по категориям
1) Превентивные техники (уменьшают шанс эксплойта)
- Языковые средства: использовать безопасные высокоуровневые структуры/языки (Rust, Java, std::string вместо C-строк).
- Явные проверки длины: проверять длину до копирования, например:
- требование: копировать не более размера dst: проверить ∣src∣<N |src| < N src<N где NNN — ёмкость dst.
- Безопасные API: `strlcpy`, `snprintf`, `memcpy` с явным размером и проверкой; избегать `strcpy/gets/scanf("%s")`.
- Компиляторные защиты:
- Stack canaries: включаются флагом типа `-fstack-protector-strong`/`-fstack-protector-all`. Вставляет случайную метку (canary) между буферами и контрольными данными; при изменении — программа вызывает abort.
- DEP/NX (Data Execution Prevention): делает стек неисполняемым, предотвращая инжекцию и выполнение shellcode.
- PIE/ASLR-совместимые сборки: компилировать в PIE для лучшей рандомизации адресного пространства.
- RELRO (partial/full) и семантика линковщика для защиты GOT.
- Системные настройки:
- ASLR (Address Space Layout Randomization) — рандомизирует расположение процесса в памяти (бинарников, библиотек, кучи, стека), усложняет нахождение gadget’ов.
2) Детектирование (найти баги во время разработки/тестирования или в рантайме)
- Сканеры и статический анализ: Coverity, clang-analyzer, cppcheck — находят ненадёжные вызовы, потенциальные переполнения.
- Фаззинг: AFL, libFuzzer — генерируют входы, выявляют переполнения/утечки.
- Динамические санитайзеры:
- AddressSanitizer (`-fsanitize=address`) — хорош для обнаружения out-of-bounds на стеке/куче, use-after-free; использует shadow memory, даёт подробный стек трассировки.
- MemorySanitizer (`-fsanitize=memory`) — выявляет использование неинициализированной памяти.
- UndefinedBehaviorSanitizer (`-fsanitize=undefined`) — ловит UB.
- Valgrind — медленный, но полезный рантайм-детектор памяти.
- Рантайм canary/stack protector: при обнаружении нарушения canary обычно вызывает `__stack_chk_fail()` → аварийное завершение, что детектирует попытку переполнения в проде.
- Мониторинг/IDS: инструментальные средства для обнаружения аномалий в поведении процесса (анализ падений, ASLR-сломы/информационные утечки).
3) Восстановление и ограничение ущерба (если нарушение произошло)
- Fail-safe: при срабатывании защиты (canary, ASAN) — корректная реакция: аварийное завершение процесса и перезапуск сервиса под контролем supervisor (systemd, supervisord). Это предотвращает продолжение компрометации.
- Least privilege / sandboxing: запускать процесс с ограниченными правами, в контейнере или с seccomp/namespace, чтобы даже при захвате ущерб был ограничен.
- Чёткая политика логирования и автоматического рестарта: собрать дамп/лог, рестартнуть сервис, уведомить опер-центр.
- Крономеханизмы/контроль целостности: исполняемые файлы, конфиги — проверять подпись/хеши.
- Recovery на уровне приложения: если возможно, применять транзакционную модель, откат незавершённых действий при аварии.
4) Ограничения/побочные эффекты и обходы
- Canaries: эффективны против перезаписи return address, но могут быть обойдены, если есть информация о значении canary (информационная утечка) или если атакующий меняет данные до проверки, не нарушая canary (например, переполнение на heap/коррупция указателей).
- DEP/NX: блокирует код-инъекцию, но не ROP (return-oriented programming), который использует существующий исполняемый код.
- ASLR: может быть обойден при наличии информационных утечек или при частичной рандомизации; слабая энтропия упрощает подбор.
- Sanitizers: полезны в тестах, в проде обычно отключены из-за накладных расходов; ASan добавляет потребление памяти и замедляет.
Практические рекомендации
- В продакшне: собирайте с `-fstack-protector-strong`, `-D_FORTIFY_SOURCE=2`, включите NX, ASLR, PIE/RELRO; минимизируйте права процесса; используйте sandboxing/containers; автоматический рестарт и мониторинг.
- В разработке/CI: статический анализ + фуззинг + адресный санитайзер (`-fsanitize=address -fsanitize=undefined`) + покрытие тестами.
- В коде: явно проверяйте размеры, используйте безопасные API/контейнеры, и при сомнении — не доверяйте входным данным.
Короткий пример безопасной замены
- Вместо `strcpy(dst, src);`:
- проверять длины: `if (strlen(src) >= dst_size) /* error */; else strcpy(dst, src);`
- или использовать `strlcpy(dst, src, dst_size);` (где доступно) или `snprintf(dst, dst_size, "%s", src);`.
Вывод
- Нет единственной защиты — нужна защита по всему стеку: безопасный код + компиляторные защиты + рантайм-изоляция + тестирование (sanitizers, fuzz).
11 Янв в 06:44
Не можешь разобраться в этой теме?
Обратись за помощью к экспертам
Гарантированные бесплатные доработки в течение 1 года
Быстрое выполнение от 2 часов
Проверка работы на плагиат
Поможем написать учебную работу
Прямой эфир