Инструментальные средства обеспечения безопасности

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

Разбить на страницы
Показывать лекцию целиком

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

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

Flawfinder

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

Реализация

Преимущества программы заключаются в ее каталоге проблемных функций. Она обеспечивает несколько режимов работы, но, скорее всего, вам понадобятся лишь некоторые из них. Полный список функций представлен в таблице 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.

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

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-трафик, или серверы, разблокированные без оповещения группы безопасности.
  • Даже если у вас есть доступ к исходящему коду, вам, может быть, и не удастся идентифицировать прорехи в системе безопасности. Однако вы можете применить методы из каждой из трех предыдущих концепций, чтобы заблокировать или, по крайней мере, уменьшить уязвимость безопасности в программном обеспечении вашей сети.

    RATS

    Программа RATS (Rough Auditing Tool for Security) от Secure Software Solutions может помочь программистам сгладить шероховатости их приложений, написанных на C, C++, Perl, PHP или Python. В отличие от Flawfinder, RATS написана на C и содержит внешнюю коллекцию правил, описанных в XML, которые применяются для каждого языка.

    Реализация

    Программа легко компилируется большинством Unix-систем, нужно лишь убедиться, что у вас установлен Expat XML Parser (http://sourceforge.net/projects/expat/). Если Expat установлен, компиляция проходит по команде ./configure и make.

    Для программы доступно несколько параметров:

    RATS v1.3 - Rough Auditing Tool for Security
    Copyright 2001 by Secure Software Solutions
    http://www.securesw.com
    usage: rats [-adhilrwx] name1 name2 ... namen
        -a <fun>        report any occurrence of function "fun" in the source file(s)
        -d <filename>   specify an alternate vulnerability database.
        -h              display usage information (what you"re reading)
        -i              report functions that accept external input
        -l <language>   force the specified language to be used
        -r              include references that are not function calls
        -w <1,2,3>      set warning level (default 2)
        -x              do not load default databases

    RATS анализирует файлы, написанные на C, но может переключаться на основе анализа расширения файлов:

  • Perl. pl, .pm
  • PHP. php
  • Python. py, .PY
  • Для явного указания языка C, Perl, Python или PHP используйте параметр -l. Проверка C и C++ основана на тех же самых правилах.

    Программы на Perl, Python и PHP проверяются без реальной проверки особенностей конкретного языка. Perl проверяется с точки зрения системных функций (функций, эквивалентных C) вместо проверки синтаксиса и управления переменными. У вас по-прежнему может остаться очень небезопасное Web-приложение, созданное на Perl (Python или PHP); RATS обеспечивает только базовые проверки. В соответствии с ограничениями RATS, типы данных Perl не проходят проверку на строгое соответствие типам и тестирование на выход за границы. Как и C-программы, Perl- скрипты не проверяются на опасность переполнения буфера.

    Совет. Perl поддерживает параметр T для "испорченных" переменных. Perl никогда не передаст такие переменные в системные функции (такие, как exeс). Это улучшает большинство входных проверок для управляющих метасимволов, которые требуется проводить в безопасных программах.

    Параметры -a и -d всегда в состоянии расширить возможности RATS. Используйте параметр -a для поиска конкретных функций по аналогии с утилитой grep. Параметр -d применяется чаще, но вам потребуется владеть XML-синтаксисом. Ниже приводится пример структуры описания теста для RATS, проверяющего функцию tmpfile.

    <Vulnerability>
        <Name>tmpfile</Name>
        <Info>
        <Description>Many calls for generating temporary 
    file names are insecure, susceptible to race conditions). 
    Use a securely generated file name, for example, by 
    pulling 64 bits of randomness from /dev/random, 
    base 64 encoding it and using
    that as a file suffix.</Description>
        <Severity>Medium</Severity>
        </Info>
    </Vulnerability>

    Тег name содержит имя функции. Можно задать несколько значений рейтинга (высокий, средний и низкий). Обратите внимание на то, что просто поиск функции может дать много ошибочных положительных ответов; более гибким было бы программировать дополнения к средствам аудита.

    Пример из жизни. mtr 0.46

    MTR - это средство General Public License (GPL), которое объединяет в себе функциональность трассировки и Ping. Damian Gryski установил условие переполнения буфера способом, которым MTR справляется с переменной окружения MTR_OPTIONS (Bugtraq ID 4217). Переменные окружения имеют давнюю историю в качестве векторов атаки на переполнение буфера. Таким образом, не удивительно, что RATS проверяет функции, которые используют переменные окружения.

    $ rats mtr.c
    mtr.c:72: High: getopt_long
    Truncate all input strings to a reasonable length before passing them to this function
    mtr.c:139: High: fixed size local buffer
    Extra care should be taken to ensure that character arrays that are allocated on the stack 
    are used safely. They are prime targets for buffer overflow attacks.
    mtr.c:180: High: getenv
    Environment variables are highly untrustable input. They may be of any length, and contain 
    any data. Do not make any assumptions regarding content or length.
    If at all possible avoid using them, and if it is necessary, sanitize them and truncate 
    them to a reasonable length.
    mtr.c:185: High: printf
    mtr.c:190: High: printf
    Check to be sure that the non-constant format string passed as argument 1 to
    this function call does not come from an untrusted source that could have added
    formatting characters that the code is not prepared to handle.
    mtr.c:236: High: gethostbyname
    DNS results can easily be forged by an attacker (or arbitrarily set to large
    values, etc.), and should not be trusted.

    Вот строка кода, которая генерировала вывод в RATS (такая же строка могла быть обнаружена с командой grep getenv mtr.c):

    parse_mtr_options (getenv ("MTR_OPTIONS"));

    RATS идентифицировал потенциальную уязвимость. Дело контролера трассировать уязвимость в функции parse_mtr_options и определять, обоснован ли вывод. Przemyslaw Frasunek сумел найти иллюстрацию того, как функция parse_mtr_options нерационально оперировала с переменной MTR_OPTIONS. Вот часть уязвимого кода:

    while (p) {
        argv[argc++] = p;
        p = strtok (NULL, " \t");
    }

    Переменная p является указателем ячейки памяти, которая может содержать не только значение переменной окружения MTR_OPTIONS, но также данные, которые могут быть помещены в память и использоваться для исполнения произвольных команд. Функция strtok C оперирует со строками, хранимыми в памяти, разыскивая конфигурации, специфицированные в ее втором аргументе ( " \t" или комбинацию пробела и символа табуляции в этом примере). Когда strtok получает значение NULL для первого аргумента, она оперирует на текущем указателе, в данном случае p. Однако взломщик может создать зловредные MTR_OPTIONS, которые запишут shellcode поверх указателя - другими словами, выполнят произвольную команду.

    Авторская вставка реализует проверку длины переменной p и сообщает о посторонних данных:

    while (p  (argc < (sizeof(argv)/sizeof(argv[0])))) {
        argv[argc++] = p;
        p = strtok (NULL, " \t");
    }
    if (p) {
        fprintf (stderr, "Warning: extra arguments ignored: %s", p);
    }

    Контроль от RATS и следование рекомендации "Не делайте никаких предположений относительно содержимого или длины" отразили атаку.

    Пример из жизни. Канарейка во мгле

    В начале этой лекции мы высказывали пожелание, чтобы появился компилятор, который будет создавать "неразрушаемые" приложения. Stackguard с http://immunix.org - это коллекция вставок в программы для компилятора GCC. Эти вставки превращают GCC в активного защитника любого кода C или C++, которые он компилирует. Базовая концепция заключается в том, что вызовы функций, потенциально уязвимых к переполнению буфера, имеют случайные значения, присоединяемые к области памяти.

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

    Мы будем лишь поддакивать великолепной документации Stackguard, чтобы описать защиту от переполнения буфера в соответствующих деталях. Посетите сайт http://immunix.org для получения дополнительной информации.

    Помните, что установки Stackguard и "nonexecutable stack" не являются панацеей от переполнения буфера. Есть документированные способы перехитрить методы защиты, подобные Stackguard. Защитите свою сеть, свою главную машину, а затем приложение - лишняя осторожность никогда не помешает.

    Страницы:

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

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

    Flawfinder

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

    Реализация

    Преимущества программы заключаются в ее каталоге проблемных функций. Она обеспечивает несколько режимов работы, но, скорее всего, вам понадобятся лишь некоторые из них. Полный список функций представлен в таблице 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.

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

    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-трафик, или серверы, разблокированные без оповещения группы безопасности.
  • Даже если у вас есть доступ к исходящему коду, вам, может быть, и не удастся идентифицировать прорехи в системе безопасности. Однако вы можете применить методы из каждой из трех предыдущих концепций, чтобы заблокировать или, по крайней мере, уменьшить уязвимость безопасности в программном обеспечении вашей сети.

    RATS

    Программа RATS (Rough Auditing Tool for Security) от Secure Software Solutions может помочь программистам сгладить шероховатости их приложений, написанных на C, C++, Perl, PHP или Python. В отличие от Flawfinder, RATS написана на C и содержит внешнюю коллекцию правил, описанных в XML, которые применяются для каждого языка.

    Реализация

    Программа легко компилируется большинством Unix-систем, нужно лишь убедиться, что у вас установлен Expat XML Parser (http://sourceforge.net/projects/expat/). Если Expat установлен, компиляция проходит по команде ./configure и make.

    Для программы доступно несколько параметров:

    RATS v1.3 - Rough Auditing Tool for Security
    Copyright 2001 by Secure Software Solutions
    http://www.securesw.com
    usage: rats [-adhilrwx] name1 name2 ... namen
        -a <fun>        report any occurrence of function "fun" in the source file(s)
        -d <filename>   specify an alternate vulnerability database.
        -h              display usage information (what you"re reading)
        -i              report functions that accept external input
        -l <language>   force the specified language to be used
        -r              include references that are not function calls
        -w <1,2,3>      set warning level (default 2)
        -x              do not load default databases

    RATS анализирует файлы, написанные на C, но может переключаться на основе анализа расширения файлов:

  • Perl. pl, .pm
  • PHP. php
  • Python. py, .PY
  • Для явного указания языка C, Perl, Python или PHP используйте параметр -l. Проверка C и C++ основана на тех же самых правилах.

    Программы на Perl, Python и PHP проверяются без реальной проверки особенностей конкретного языка. Perl проверяется с точки зрения системных функций (функций, эквивалентных C) вместо проверки синтаксиса и управления переменными. У вас по-прежнему может остаться очень небезопасное Web-приложение, созданное на Perl (Python или PHP); RATS обеспечивает только базовые проверки. В соответствии с ограничениями RATS, типы данных Perl не проходят проверку на строгое соответствие типам и тестирование на выход за границы. Как и C-программы, Perl- скрипты не проверяются на опасность переполнения буфера.

    Совет. Perl поддерживает параметр T для "испорченных" переменных. Perl никогда не передаст такие переменные в системные функции (такие, как exeс). Это улучшает большинство входных проверок для управляющих метасимволов, которые требуется проводить в безопасных программах.

    Параметры -a и -d всегда в состоянии расширить возможности RATS. Используйте параметр -a для поиска конкретных функций по аналогии с утилитой grep. Параметр -d применяется чаще, но вам потребуется владеть XML-синтаксисом. Ниже приводится пример структуры описания теста для RATS, проверяющего функцию tmpfile.

    <Vulnerability>
        <Name>tmpfile</Name>
        <Info>
        <Description>Many calls for generating temporary 
    file names are insecure, susceptible to race conditions). 
    Use a securely generated file name, for example, by 
    pulling 64 bits of randomness from /dev/random, 
    base 64 encoding it and using
    that as a file suffix.</Description>
        <Severity>Medium</Severity>
        </Info>
    </Vulnerability>

    Тег name содержит имя функции. Можно задать несколько значений рейтинга (высокий, средний и низкий). Обратите внимание на то, что просто поиск функции может дать много ошибочных положительных ответов; более гибким было бы программировать дополнения к средствам аудита.

    Пример из жизни. mtr 0.46

    MTR - это средство General Public License (GPL), которое объединяет в себе функциональность трассировки и Ping. Damian Gryski установил условие переполнения буфера способом, которым MTR справляется с переменной окружения MTR_OPTIONS (Bugtraq ID 4217). Переменные окружения имеют давнюю историю в качестве векторов атаки на переполнение буфера. Таким образом, не удивительно, что RATS проверяет функции, которые используют переменные окружения.

    $ rats mtr.c
    mtr.c:72: High: getopt_long
    Truncate all input strings to a reasonable length before passing them to this function
    mtr.c:139: High: fixed size local buffer
    Extra care should be taken to ensure that character arrays that are allocated on the stack 
    are used safely. They are prime targets for buffer overflow attacks.
    mtr.c:180: High: getenv
    Environment variables are highly untrustable input. They may be of any length, and contain 
    any data. Do not make any assumptions regarding content or length.
    If at all possible avoid using them, and if it is necessary, sanitize them and truncate 
    them to a reasonable length.
    mtr.c:185: High: printf
    mtr.c:190: High: printf
    Check to be sure that the non-constant format string passed as argument 1 to
    this function call does not come from an untrusted source that could have added
    formatting characters that the code is not prepared to handle.
    mtr.c:236: High: gethostbyname
    DNS results can easily be forged by an attacker (or arbitrarily set to large
    values, etc.), and should not be trusted.

    Вот строка кода, которая генерировала вывод в RATS (такая же строка могла быть обнаружена с командой grep getenv mtr.c):

    parse_mtr_options (getenv ("MTR_OPTIONS"));

    RATS идентифицировал потенциальную уязвимость. Дело контролера трассировать уязвимость в функции parse_mtr_options и определять, обоснован ли вывод. Przemyslaw Frasunek сумел найти иллюстрацию того, как функция parse_mtr_options нерационально оперировала с переменной MTR_OPTIONS. Вот часть уязвимого кода:

    while (p) {
        argv[argc++] = p;
        p = strtok (NULL, " \t");
    }

    Переменная p является указателем ячейки памяти, которая может содержать не только значение переменной окружения MTR_OPTIONS, но также данные, которые могут быть помещены в память и использоваться для исполнения произвольных команд. Функция strtok C оперирует со строками, хранимыми в памяти, разыскивая конфигурации, специфицированные в ее втором аргументе ( " \t" или комбинацию пробела и символа табуляции в этом примере). Когда strtok получает значение NULL для первого аргумента, она оперирует на текущем указателе, в данном случае p. Однако взломщик может создать зловредные MTR_OPTIONS, которые запишут shellcode поверх указателя - другими словами, выполнят произвольную команду.

    Авторская вставка реализует проверку длины переменной p и сообщает о посторонних данных:

    while (p  (argc < (sizeof(argv)/sizeof(argv[0])))) {
        argv[argc++] = p;
        p = strtok (NULL, " \t");
    }
    if (p) {
        fprintf (stderr, "Warning: extra arguments ignored: %s", p);
    }

    Контроль от RATS и следование рекомендации "Не делайте никаких предположений относительно содержимого или длины" отразили атаку.

    Пример из жизни. Канарейка во мгле

    В начале этой лекции мы высказывали пожелание, чтобы появился компилятор, который будет создавать "неразрушаемые" приложения. Stackguard с http://immunix.org - это коллекция вставок в программы для компилятора GCC. Эти вставки превращают GCC в активного защитника любого кода C или C++, которые он компилирует. Базовая концепция заключается в том, что вызовы функций, потенциально уязвимых к переполнению буфера, имеют случайные значения, присоединяемые к области памяти.

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

    Мы будем лишь поддакивать великолепной документации Stackguard, чтобы описать защиту от переполнения буфера в соответствующих деталях. Посетите сайт http://immunix.org для получения дополнительной информации.

    Помните, что установки Stackguard и "nonexecutable stack" не являются панацеей от переполнения буфера. Есть документированные способы перехитрить методы защиты, подобные Stackguard. Защитите свою сеть, свою главную машину, а затем приложение - лишняя осторожность никогда не помешает.

    Вернуться к учебному плану