Опубликован: 20.02.2007 | Доступ: свободный | Студентов: 3507 / 793 | Оценка: 4.42 / 4.03 | Длительность: 40:03:00
Лекция 11:

Простые средства аудита исходных кодов

< Лекция 10 || Лекция 11: 12 || Лекция 12 >
Аннотация: Лекция посвящена борьбе с главным источником уязвимости - переполнением буфера - уже на стадии написания исходного кода

Быстрое исследование безопасности Web-сайтов и почтовых рассылок, в которых перечисляются уязвимые места программного обеспечения, обнаруживает заметную тенденцию: переполнение буфера - главный источник уязвимости независимо от производителя, аппаратного обеспечения и операционной системы. Было бы прекрасно иметь средство, которое обеспечивало безопасность кода, как только он написан. Некоторые причины переполнения буфера кроются в особенностях языков C и C++, которые поддерживают строки в памяти и указатели; эти возможности оцениваются, как потенциально небезопасные для программирования.

У Active Server Pages (ASP), Perl, Python и PHP есть свои особенности с точки зрения безопасности - мир взлома Web-приложений, базирующихся на этих языках, жив и здоров. Но хорошо написанный на любом языке код повышает безопасность. Проект OpenBSD живет по правилам, вытекающим из непрерывного исправления ошибок. Если кто-то обнаружил, что программа некорректно использует функцию snprintf, метка с подозрением в ошибке присваивается всем другим программам, которые используют функцию snprintf. Не каждая ошибка влечет за собой возникновение уязвимости с точки зрения удаленного взлома, но стабильность, сопровождение и активная защита - все это составные части великолепного приложения.

Flawfinder

Программа Flawfinder, написанная Дэйвом Веллером, собрала наиболее распространенные ошибки программирования на C и C++ и поместила их в утилиту, которая может проверить представленный ей программный код. Эта утилита не разбирает C-синтакс или тонкости программирования; однако она хорошо служит для проверки приложений с точки зрения здравого смысла. Программа написана на языке Python, и в ней менее 1000 строк, которые представляют собой великолепный материал для настройки.

Реализация

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

Таблица 11.1. Опции командной строки Flawfinder
Опция Описание
--context, -c Показывает строку, которая содержит потенциальную недоработку; сходна с использованием grep для поиска каждой функции или демонстрации результатов каждого совпадения.
--columns Показывает номер столбца потенциальной недоработки. Например, уязвимый strcpy может начинаться с 16-го символа строки.
--minlevel=X, -m X Задает минимальный уровень риска, для которого сообщается совпадение. Значение X может быть от 0 (нет риска) до 5 (максимальный риск). По умолчанию - 1.
--neverignore, -n Не поддерживает директиву ignore в исходном файле.
--immediate, -i Показывает потенциальные совпадения по мере их нахождения.
--inputs Показывает только функции, которые получают внешний ввод (задает переменные из данных, полученных извне программы). Устанавливает минимальный уровень равный 0.
--quiet Не показывает информацию о совпадении во время сканирования.
--loadhitlist=F Загружает совпадения из файла F вместо анализа исходных программ.
--savehitlist=F Сохраняет совпадения в файле F.
--diffhitlist=F Не показывает совпадения, содержащиеся в файле F. Полезна для сравнения пересмотров.

Самый быстрый способ запустить программу - это определить для нее директорию или список файлов для проверки:

$ flawfinder src/

По умолчанию программа проверяет только C-файлы. Эти файлы она определяет по их расширению: c, h, ec, ecp, pgc, C, cpp, cxx, cc, pcc, hpp или H. Программа не разбирает язык C. Flawfinder различает некоторые потенциально опасные функции, которые используют переменные вместо констант, приводя к высокому риску ошибок.

Если у одного из ваших файлов нет указанного расширения, вы можете задать имя файла в командной строке:

$ flawfinder ftpcmd.y

Вывод будет представлен в виде:

filename:line_number:column_number [risk_level] 
  (type) function_name:message

Параметр column_number может быть опущен в случае, если не включен параметр -columns. Используйте параметр -m, чтобы определить уровень риска, выше которого следует выдавать сообщение. Flawfinder помещает каждое предупреждение в зависимости от категорий: переполнение буфера, ситуация гонок, неадекватный случайный номер и ошибочный временный файл.

Используйте параметр -savehitlist для вывода информации в файл. Это упрощает анализ выходной информации, особенно для больших проектов. Параметр -difflist полезен, когда обрабатывается большой проект. Flawfinder игнорирует предупреждения, которые уже есть в файле, заданном с помощью параметра ( -difflist <filename> ). Следовательно, вы можете сохранять файлы с предупреждениями на разных стадиях разработки, чтобы отслеживать новые функции.

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

/* Flawfinder: ignore */
/* RATS: ignore */
/* ITS4: ignore */

Вы также можете вставить эти строки с комментариями в стиле C++ ( // ). Когда Flawfinder встречает одну из этих директив в исходном коде, то не выдает сообщение об ошибке в следующей строке - независимо от того, насколько небезопасен код.

Как видите, Flawfinder хорошо согласуется с другими директивами аудита.

Пример из жизни. Wu-ftpd 2.6.0

Сервер FTP Вашингтонского университета сильно пострадал во время своей эволюции от версии 2.4 к версии 2.6. Один из уязвимых моментов, которые привлекли внимание Bugtraq на tf8@zolo.freelsd.net (Bugtrq ID 1387), принадлежит к классу уязвимостей на базе строк формата. Поиск ошибок ( Flawfinder ) содержит каталог неверно использованных функций и сообщений о каждом, кто их нашел.

$ flawfinder ftpd.c
Flawfinder version 0.21, (C) 2001 David A. Wheeler.
Number of dangerous functions in C ruleset: 55
Examining ftpd.c
ftpd.c:5593 [5] (race) chown: this accepts filename arguments; if an attacker
can move those files, a race condition results. . Use fchown( ) instead.
ftpd.c:412 [4] (format) vsnprintf: if format strings can be influenced by an
attacker, they can be exploited. Use a constant for the format specification.
ftpd.c:416 [4] (format) snprintf: if format strings can be influenced by an
attacker, they can be exploited. Use a constant for the format specification.
ftpd.c:684 [4] (buffer) strcpy: does not check for buffer overflows. Consider
using strncpy or strlcpy.
ftpd.c:3158 [4] (buffer) sprintf: does not check for buffer overflows. Use
snprintf or vsnprintf.
ftpd.c:5890 [4] (buffer) sprintf: does not check for buffer overflows. Use
snprintf or vsnprintf.
ftpd.c:6160 [4] (format) syslog: if syslog"s format strings can be influenced
by an attacker, they can be exploited. Use a constant format string for syslog.
ftpd.c:6618 [4] (format) vsnprintf: if format strings can be influenced by an
attacker, they can be exploited. Use a constant for the format specification.
11.1.

Четыре строки жирным шрифтом соответствуют этим строкам в исходном коде.

sprintf(proctitle, "%s: %s", remotehost, pw-pw_name);
...
sprintf(proctitle, "%s: connected", remotehost);

Фактическая эксплуатация повлияла на эти строки немедленно за функциями sprintf. Это демонстрирует, как поиск ошибок ( Flawfinder ) может указать правильное направление для нахождения ошибок в программах. Например, часть вставки в программу для исправления ошибок строки формата выглядит так ("-" в начале строки означает удалить строку; "+" означает добавить ее).

remotehost[sizeof(remotehost) - 1] = '\0';
 sprintf(proctitle, "%s: connected", remotehost);
-setproctitle(proctitle);
+setproctitle("%s", proctitle);

Пример из жизни. Что пропускают автоматизированные средства аудита

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

Несовпадение целых чисел. В приложениях C и C++ программисты хранят числовые переменные в разнообразных форматах: 16-ти разрядных, 32-х разрядных, со знаком (могут иметь отрицательные значения) или без знака (только положительные значения). OpenSSH был уязвим к компенсационному взлому CRC-32, обнаруженному Michal Zalewski (Bugtraq ID 2347), который эксплуатировал проблему с хранением двух несовпадающих числовых переменных. Эта уязвимость требовала изменения только лишь одной строки - поменять переменную n с 16-разрядного на 32-х разрядное значение.

- static word 16        n = HASH_MINSIZE / HASH_ENTRYSIZE;
+ static word 32        n = HASH_MINSIZE / HASH_ENTRYSIZE;

Это значение использовалось позже в цикле FOR, который оперировал с 32-х разрядным значением, 1.

u_int32_t   l;
for (l = n; l < HASH_FACTOR(len / SSH_BLOCKSIZE); l = l << 2);
if (h == NULL)
{
    debug("Installing crc compensation attack detector.");
    n = l;
    h = (u_int16_t *) xmalloc(n * HASH_ENTRYSIZE);
}

Изначально значение n было 4096. Цикл FOR будет умножать один на два ( l = l << 2 ) до тех пор, пока он не пересечет определенную границу. Возможно, что 1 достигнет значения 65536, однако максимальное значение для 16-ти разрядного числа - только 65535. Следовательно, n будет обнулено. Это не влияло на Secure Shell (SSH) до тех пор, пока цикл FOR не использовал это значение для последующей функции.

register u_int32_t  i;
for (i = HASH(c) & (n - 1); h[i] != HASH_UNUSED;

Если n равно нулю, то n-1 равно -1, но для положительного 32-разрядного целого числа -1 выглядит как 0xFFFFFFFF в шестнадцатеричной системе счисления (оно не может быть отрицательным). Другими словами, HASH(c) & (n-1) становится HASH(c), т.е. значением, которым может манипулировать взломщик.

Булевы тесты. Логические тесты и неявные пограничные значения также приводят к ошибкам - но ошибки не могут быть найдены автоматически. Например, уязвимость OpenSSH Channel Code Off-By-One (Bugtraq ID 4241), обнаруженная Joost Pol, происходит из-за неявной ошибки в проверке числовой границы. Взгляните на уязвимый код (первая строка) и его исправленный вид (вторая строка):

- if (id < 0 || id > channels_alloc) {
+ if (id < 0 || id >= channels_alloc) {

Целиком оператор IF выглядит следующим образом:

if (id < 0 || id = channels_alloc) {
            log("channel_lookup: %d: bad id", id);
            return NULL;
    }
    c = channels[id];

Уязвимый оператор IF не исполняется, если значение id равняется предельному значению channels_alloc. Это вызывает проблемы в следующей команде, когда программа пытается вызвать массив channels [id].

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

  • Уровень правки. Эксплуатация только началась, поэтому пользователям пришлось ждать, чтобы Microsoft внесла правки. Конечно, эта уязвимость существовала еще в течение 6 месяцев, указывая на другие проблемы управления конфигурацией или недостатки в подготовке пользователя.
  • Безопасность главной машины. Если пользователи не использовали фильтры ISAPI - в частности .ida - уязвимость не ощущалась. Если это приложение отсылалось в менее привилегированное состояние (например, пользователи добавляли фильтры ISAPI по мере необходимости), пользователи не сталкивались с этой проблемой сразу. Скольким пользователям понадобилось расширение .printer (в котором также было переполнение буфера)?
  • Безопасность сети затронула многие серверы организаций, которые принадлежали к тестовым сетям, внутренним сетям. Они ошибочно разрешали входящий Web-трафик, или серверы, разблокированные без оповещения группы безопасности.

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

< Лекция 10 || Лекция 11: 12 || Лекция 12 >