Pscan

http://www.striker.ottawa.on.ca/~aland/pscan/

Pscan (Problem Scanner) — это программа, которая позволяет сканировать код Си-программ на наличие общих ошибок в вызове и объявлении функций, которые часто приводят к такой распространенной ошибке как переполнению буфера. Сканер работает по следующему принципу: он ищет одну из «проблемных» функций (на основе своего списка) и применяет следующее правило. ЕСЛИ последний параметр функции — строка какого-либо формата, И формат строки НЕ постоянен, ТОГДА сравнивает и делает вывод об уязвимости. После того, как потенциальная уязвимость найдена, сканер выдает ее на консоль с предложением о коррекции данного куска кода.

Pscan является консольной утилитой, работает в Unix-системах, поставляется в tgz-архиве и собирается в бинарный файл стандартной командой make. На выходе, при передаче исполняемому файлу в качестве параметра си-файла, как и все подобные утилиты, сканер выводит номер строки, описание уязвимости и название функции, в которой найдена проблема. Программе можно передавать как отдельный файл, так и задавать сканирование по маске.

Запуск программы с ключом -v (verbose) даст более детальное описание уязвимостей. Ключ -w позволяет выводить предупреждения, когда строки непостоянного формата
(non-constant strings) используются как передаваемый в функцию параметр.

Рассмотрим применение программы, передавая ей для анализа файл test1.c 


[root@localhost pscan-1.2]# ./pscan test1.c
test1.c:38 SECURITY: fprintf call should have «%s» as argument 1
test1.c:66 SECURITY: sprintf call should have «%s» as argument 1
test1.c:77 SECURITY: sprintf call should have «%s» as argument 1
test1.c:95 SECURITY: sprintf call should have «%s» as argument 1
test1.c:118 SECURITY: printf call should have «%s» as argument 0
test1.c:131 SECURITY: syslog call should have «%s» as argument 1
[root@localhost pscan-1.2]#

Как видим, отчет нам выдали в довольно лаконичной форме. Видим, что соответствующим функциям не хватает аргумента «%t» где t — тип передаваемых данных, т.е. это можно интерпретировать как неявно заданный формат строки. Затем, по указанному выше алгоритму, программа сравнивает, так ли на самом деле (т.е. присутствует «%t» или нет), и, в случае неудачи, возвращает 0, иначе — 1.
Таким образом, в программе test1.c найдена одна ошибка на переполнение буфера (строка 118).

Запуская программу с ключом -vv, получаем несколько более «разговорчивый» отчет:


[root@localhost pscan-1.2]# ./pscan -vv /root/scanz/test2.c
Scanning /root/scanz/test2.c …
/root/scanz/test2.c:6 FUNC printf Last argument is variable or reference: BAD
/root/scanz/test2.c:17 FUNC sprintf Last argument is constant string: OK
/root/scanz/test2.c:18 FUNC sprintf Last argument is constant string: OK
/root/scanz/test2.c:19 FUNC sprintf format string with 1 parameters: OK
/root/scanz/test2.c:20 FUNC sprintf format string with 1 parameters: OK
/root/scanz/test2.c:21 FUNC sprintf format string with 1 parameters: OK
/root/scanz/test2.c:22 FUNC printf format string with 1 parameters: OK
/root/scanz/test2.c:23 FUNC scanf format string with 1 parameters: OK
/root/scanz/test2.c:24 FUNC scanf format string with 1 parameters: OK
/root/scanz/test2.c:25 FUNC scanf format string with 1 parameters: OK
/root/scanz/test2.c:26 FUNC scanf format string with 1 parameters: OK
/root/scanz/test2.c:29 FUNC printf Last argument is variable or reference: BAD
/root/scanz/test2.c:35 FUNC syslog format string with 2 parameters: OK
/root/scanz/test2.c:36 FUNC syslog Last argument is constant string: OK
/root/scanz/test2.c:38 FUNC syslog Last argument is constant string: OK
Total problems identified: 2
[root@localhost pscan-1.2]#

Суть та же самая, найдено две ошибки, в тех местах, где, как видим по отчету, аргумент функции printf варьируется (не постоянен) или зависит от чего-либо (потенциально не постоянен).

RATS


http://www.securesw.com/projects.html

RATS («Грубая утилита для аудита безопасности», Rough Auditing Tool for Security) — утилита для сканирования исходных кодов программ на С/С++, Perl, PHP и Python и обнаружения уязвимостей, связанных с ошибками в программировании, таких как переполнения буфера и TOCTOU (Time Of Check, Time Of Use). Как подразумевает название программы, она предназначена только для грубого анализа исходника, и может обнаружить не все ошибки, или же наоборот, принять за ошибку корректный участок кода (интересно, что это — признание автора в несовершенстве алгоритма или скромность, граничащая с осторожностью?). То есть «ручную» проверку кода этот сканер не заменит, но серьезно поможет в сем трудном деле.

Как и подавляющее большинство *nix’овых консольных утилит, RATS поставляется в tar-файле, сжатом gzip’ом, и устанавливается, как и большинство собранных autoconf’ом программ, с помощью процедуры ./configure, make, make install. По умолчанию процедура установки происходит в /usr/local/bin.

Использование утилиты:

rats [-d <filename>] [-h] [-r] [-w <level>] [-x] [file1 file2 … filen]

Где основные опции:

-d <filename>
Путь к альтернативной базе уязвимостей (если дефолтовая не устраивает)

-h
Список всех команд

-l <lang>
Явное указание языка исходника, даже не взирая на расширение файла, для более точного аудита
данного языка (позволяется выбирать «c», «perl», «php» и «python»).

-r
Указание своих, нестандартных вызовов функций, для отражения их в отчете.

-w <level>
Установка уровня опасности уязвимостей. Уровень 1 включает только проверку на высокую степень уязвимости (несерьезные, мелкие потенциальные уязвимости игнорируются). Уровень 2 включает в себя проверку на среднюю степень уязвимости (это уровень по умолчанию). Уровень 3 включает также проверку на незначительные, мелкие возможные уязвимости.

-x
Указание не грузить стандартные базы уязвимостей (/usr/local/lib) по умолчанию.

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

Запускаем утилиту, передавая ей файл test2.c


[root@localhost flawfinder-0.21]# rats -d /root/scanz/rats-1.3/vuln_db.c /root/scanz/Flaw-finder/flawfinder-0.21/test2.c
not well-formed (invalid token) at line 1
/root/scanz/Flaw-finder/flawfinder-0.21/test2.c:13: High: strcpy
/root/scanz/Flaw-finder/flawfinder-0.21/test2.c:14: High: strcpy
Check to be sure that argument 2 passed to this function call will not more
data than can be handled, resulting in a buffer overflow.

/root/scanz/Flaw-finder/flawfinder-0.21/test2.c:17: High: sprintf
/root/scanz/Flaw-finder/flawfinder-0.21/test2.c:18: High: sprintf
/root/scanz/Flaw-finder/flawfinder-0.21/test2.c:19: High: sprintf
Check to be sure that the format string passed as argument 2 to this function
call doest not come from an untrusted source that could have added formatting
characters that the code is not prepared to handle. Additionally, the format
string could contain `%s’ without precision that could result in a buffer
overflow.

/root/scanz/Flaw-finder/flawfinder-0.21/test2.c:18: High: sprintf
/root/scanz/Flaw-finder/flawfinder-0.21/test2.c:19: High: sprintf
Check to be sure that the non-constant format string passed as argument 2 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.

/root/scanz/Flaw-finder/flawfinder-0.21/test2.c:20: 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.

/root/scanz/Flaw-finder/flawfinder-0.21/test2.c:21: High: scanf
/root/scanz/Flaw-finder/flawfinder-0.21/test2.c:22: High: scanf
/root/scanz/Flaw-finder/flawfinder-0.21/test2.c:23: High: scanf
/root/scanz/Flaw-finder/flawfinder-0.21/test2.c:24: High: scanf
Check to be sure that the format string passed as argument 2 to this function
call doest not come from an untrusted source that could have added formatting
characters that the code is not prepared to handle. Additionally, the format
string could contain `%s’ without precision that could result in a buffer
overflow.

/root/scanz/Flaw-finder/flawfinder-0.21/test2.c:25: High: gets
/root/scanz/Flaw-finder/flawfinder-0.21/test2.c:28: High: gets
/root/scanz/Flaw-finder/flawfinder-0.21/test2.c:29: High: gets
Gets is unsafe!! No bounds checking is performed, buffer is easily overflowable by user. Use fgets(buf, size, stdin) instead.

/root/scanz/Flaw-finder/flawfinder-0.21/test2.c:32: High: syslog
/root/scanz/Flaw-finder/flawfinder-0.21/test2.c:33: High: syslog
/root/scanz/Flaw-finder/flawfinder-0.21/test2.c:35: High: syslog
Truncate all input strings to a reasonable length before
passing them to this function

В первой группе предупреждений контролируется отсутствие ограничение на длину передаваемого функции strcpy параметра, что может вызвать переполнение буфера. Во втором — та же история с sprintf, нам
предлагают  убедиться, что вызов функции не идет из опасного источника и не произойдет т.н. «ошибка форматирования строки». При вызове функции gets программа предупреждает, что в данном контексте использование ее не рекомендуется, ибо здесь не используется хоть какой-нибудь проверки и «буфер легко переполняем пользователем». Вместо нее настойчиво требуют использовать более безопасную fgets.
Наконец, в вызове функции syslog, нам рекомендуют урезать все входные параметры (строки) до приемлемой длины (ограничить длину, другим словом), перед передачей их этой функции.

Quaudit

Qaudit.pl — это не совсем программа, а простой кроссплатформенный перл-скрипт для анализа исходных текстов си программ на уязвимости. Для его использования на машине должен быть установлен перл. Главный минус скрипта — отсутствие базы уязвимостей.

Рассмотрим анализ программы test1.c


[root@localhost bin]#perl /root/scanz/qaudit.pl
qaudit.pl::q(uick)audit: version[02]. by: vade79[v9@fakehalo.org].
———————————————————————————
qaudit> help
interface commands: file audit exit.
qaudit> file /root/scanz/test1.c
source file set to: /root/scanz/test1.c.
qaudit> audit
executing audit on: /root/scanz/test1.c
———————————————————————————
test1.c:*NOTICE:START: entering format bug check mode.
test1.c:FMT_CHK:38:WARNING: fprintf(stderr, variable); /* problematic */
test1.c:FMT_CHK:44:WARNING: fprintf(stderr, format, variable1, variable2);
test1.c:FMT_CHK:113:WARNING: printf((variable ? fmt1 : fmt2), string3); /* OK */
test1.c:FMT_CHK:118:WARNING: printf((variable ? string1 : string2)); /* problematic */
test1.c:*NOTICE:STOP: exiting format bug check mode.
test1.c:*NOTICE:START: entering bounds check mode.
test1.c:*NOTICE:STOP: exiting bounds check mode.
test1.c:*NOTICE:START: entering exec check mode.
test1.c:*NOTICE:STOP: exiting exec check mode.
test1.c:*NOTICE:START: entering environmental variable check mode.
test1.c:*NOTICE:STOP: exiting environmental variable check mode.
test1.c:*NOTICE:START: entering miscellaneous check mode.
test1.c:MSC_CHK:54:WARNING: sprintf(buffer, «string»); /* OK */
test1.c:MSC_CHK:61:WARNING: sprintf(buffer, «%s»); /* OK */
test1.c:MSC_CHK:66:WARNING: sprintf(buffer, variable); /* problematic */
test1.c:MSC_CHK:71:WARNING: sprintf(buffer, «%s», variable); /* OK */
test1.c:MSC_CHK:77:WARNING: sprintf(buffer, «%d», sprintf(buffer1, variable)); /* problematic! */
test1.c:MSC_CHK:95:WARNING: sprintf(buffer,
test1.c:MSC_CHK:103:WARNING: sprintf(buffer, «%s %s %s», one, two, three); /* OK */
test1.c:MSC_CHK:120:WARNING: // sprintf(buffer, variable); C++ comments get ignored, for good or for bad.
test1.c:MSC_CHK:122:WARNING: /* sprintf(buffer, variable); these comments get ignored, too */
test1.c:MSC_CHK:129:WARNING: sprintf(s, «PASV port %i assigned to %s», i, remoteident);
test1.c:*NOTICE:STOP: exiting miscellaneous check mode.
———————————————————————————
qaudit> exit
exiting qaudit
[root@localhost scanz]# 

Здесь мы сталкиваемся с преимуществом того, что программа реализована как скрипт. Она передает на
выход сразу целую строку, благодаря чему мы видим и комментарии в коде. Очевидна строгая проверка — программа выдает предупреждения даже там, где в исходнике комментарий «OK», но в то же время пропускает закомментированные функции. Минус — ни о какой базе уязвимостей не может быть и речи.

The конец

Итак, к озакномлению были представлены четыре популярные проги плюс один скрипт :). Я намеренно не стал анализировать качество работы каждой утилиты: исходники-тесты у тебя есть, репорты тоже есть, бери, сравнивай, анализируй и думай 🙂 В принципе, написать самому подобную прогу — не проблема, главное выработать эффективный алгоритм. Ну а потом — никто не мешает тебе запихнуть все это дело в ГУИ. И еще. Надеюсь, ты понимаешь, что далеко не каждая найденный сканнером баг — это баг (а не фича :), т.к. все они подходят к сканирования формально, в силу как раз используемых алгоритмов, и не являются панацеей, однако если твоя прога состоит из нескольких сот строк, и будет использоваться в потенциально небезопасном месте (aka в сети, например, на серваке, да еще при этом обрабатывать какие-либо запросы извне), то проверить ее на подобные баги — не излишество, а суровая необходимость.

Оставить мнение

Check Also

Цифровой паноптикон. Настоящее и будущее тотальной слежки за пользователями

Даже если ты тщательно заботишься о защите своих данных, это не даст тебе желаемой приватн…