
Синтетический вариант переполнения буфера стека CWE-120 на основе CVE-2020-8597 (pppd EAP) в качестве цели статического анализа CodeQL
Преднамеренно уязвимая программа на C (~130 строк), используемая как цель для статического анализа CodeQL. Она воспроизводит класс ошибки CVE-2020-8597 — стековое переполнение буфера rhostname в EAP pppd (CWE-120) — в программе, которая не разделяет ни имён функций pppd, ни глубины вызовов, ни структуры диспетчеризации.
Цель — проверка общности: запрос CodeQL, написанный для обнаружения ошибки pppd, должен срабатывать и на этой программе без каких-либо правок. Если это происходит, запрос выражает класс ошибки, а не форму исходного кода.
Эта программа намеренно небезопасна и существует только для анализа. Не разворачивайте её. Ошибка, которую она воспроизводит, публична (CVE-2020-8597, раскрыта в 2020).
Длина, управляемая атакующим, копируется в буфер фиксированного размера без проверки, соотносящей эту длину с размером буфера.
Проверка границ присутствует — просто она проверяет не то: заявленная длина сверяется с полученным кадром (что предотвращает переполнение при чтении), но никогда с sizeof(dest) (что как раз предотвращает переполнение при записи). Это та же корневая причина, что и мёртвая проверка vallen >= len + sizeof(rhostname) в pppd.
Обе сохраняют единственное свойство, которое делает это анализом потока данных, а не поиском по grep: косвенный вызов через таблицу указателей на функции между источником и приёмником.
handle_hello() — уязвима. memcpy(name, payload + 2, vlen) с vlen, ограниченным кадром, а не sizeof(name).handle_echo() — безопасна. Та же форма источника и приёмника, но vlen также ограничен sizeof(buf). Это отрицательный контроль: запрос должен срабатывать на handle_hello и оставаться молчаливым здесь.Одна UDP-датаграмма = один кадр:
[ type : 1 ] [ length : 2, big-endian ] [ value : length bytes ]
type 0x01 → hello, 0x02 → echo. Hello-кадр с заявленной длиной от 65 до ~2045 переполняет name[64].
make # gcc -Wall -Wextra -O0 -g -o tlv_server tlv_server.c
Linux/POSIX (BSD-сокеты). Собирается чисто, без предупреждений.
CodeQL отслеживает реальную компиляцию, поэтому выполняйте чистую сборку:
make clean
codeql database create db --language=cpp --command="make"
# or, without the clean step:
codeql database create db --language=cpp --command="make -B"
Затем выполните запрос Part 3 для базы db; он должен сообщить о memcpy в handle_hello и не сообщать о memcpy в handle_echo.
| pppd / CVE-2020-8597 | этот проект |
|---|
| Источник | read() на PPP-дескрипторе | recvfrom() на UDP-сокете |
| Диспетчеризация | глобальный struct protent *protocols[], линейный поиск по номеру протокола | локальный для файла const struct frame_op ops[], линейный поиск по 1-байтовому тегу |
| Глубина до приёмника | get_input → (*input) → eap_input → eap_request | dispatch_frame → (*handle) → handle_hello |
| Приёмник | char rhostname[256] | char name[64] |
| Неверная проверка | vallen ограничено длиной пакета len | vlen ограничено длиной кадра plen |