«Техника сетевых атак»
Вид материала | Краткое содержание |
- Комплексный подход к обнаружению сетевых атак, 70.8kb.
- Предотвращение атак: революция или эволюция?, 98.18kb.
- Темы курсовых работ по дисциплине «Безопасность и управление доступом в ис» Модель, 15.44kb.
- Технические особенности сетевых сообществ и их педагогические следствия, 124.33kb.
- А. А. Грабко научный руководитель В. С. Горбатов, к т. н., доцент Московский инженерно-физический, 29.26kb.
- Это комплекс взаимосвязанных и согласованно функционирующих программных и аппаратных, 277.11kb.
- Лекция: Определение локальных сетей и их топология Вэтой лекции говорится о базовой, 250.42kb.
- Лекция: Определение локальных сетей и их топология Вэтой лекции говорится о базовой, 318.77kb.
- Тутер Нина Валерьевна Клинико-психофизиологический анализ панических атак при различных, 560.1kb.
- Использование современных компьютерно-сетевых технологий в информационном обеспечении, 158.29kb.
Устройство конвейера и перенаправление ввода-вывода (глава для начинающих)
- В этой главе:
- Концепция ввода-вывода
- Перенаправление ввода-вывода
- Использование перенаправления ввода-вывода для атак
- Устройство конвейера
- Поддержка конвейера MS-DOS
- Использование конвейера для атак
Понимание концепции ввода-вывода в UNIX требуется как для самих атак, так и для успешной защиты от них.
Любую программу можно рассматривать как «черный ящик» с входом и выходом. Это справедливо для большинства консольных приложений MS-DOS и Windows 9x/Windows NT, но графические приложения помимо результатов своей работы выводят множество вспомогательной информации, и в определенных ситуациях оказываются бесполезными. Например, все серверные приложения должны по возможности минимизировать обмен с пользователем, посылая и принимая только полезную информацию, а обличить ее в красивый интерфейс можно и на клиентской стороне.
Разработчики UNIX значительно облегчили написание модульных программ, унифицировав систему ввода-вывода. С любым устройством стало возможно обращаться точно так, как с файлом, в частности читать и писать в него. Поэтому, результат работы одной программы может быть использован другой или выведен на экран.
Например, программу копирования файлов “copy” (MS-DOS) ничуть ни хуже использовать для создания новых файлов и вывода их содержимого на экран. Для этого достаточно вспомнить, что с клавиатурой и терминалом (объединенных в MS-DOS в устройстве под именем ‘con’) можно обращаться точь-в-точь как с обычным файлом. Доказывает это утверждение следующий эксперимент:
- copy con myfile
- Hello, World!
- Z
- 1 файлов скопировано
- copy myfile con
- Hello, World!
- 1 файлов скопировано
Начинающим, вероятно, следует пояснить – символ
В качестве другого примера используем коротенькую демонстрационную программу, написанную на языке Си – “/SRC/io.c”, исходный текст которой приведен ниже (для наглядности никакие проверки не выполняются).
- #include
- int main(int argc, char *argv[])
- {
- char buf[100],out[7],tmp,p=0;
- FILE *f;
- f=fopen(argv[1],"r");
- fgets(&buf[0],100,f);
- fclose(f);
- f=fopen(argv[2],"w");
- while(buf[p])
- {
- sprintf(&out[0],"0x%X\n",buf[p++]);
- fputs(&out[0],f);
- }
- return 0;
- }
- int main(int argc, char *argv[])
Она читает одну строку из файла, переданного в качестве первого аргумента командной строки, и записывает во второй ASCII коды символов в шестнадцатеричном представлении. Например, так:
- io.exe con con
- Hello, Sailor!
- 0x48
- 0x65
- 0x6C
- 0x6C
- 0x6F
- 0x2C
- 0x20
- 0x53
- 0x61
- 0x69
- 0x6C
- 0x6F
- 0x72
- 0x21
- 0xA
Характерная особенность этой (да и многих других) программ – использование клавиатуры и терминала для приема и отображения информации. Но постоянно указывать ‘con con’ слишком утомительно, гораздо лучше заранее назначить устройства ввода-вывода по умолчанию.
В стандартной библиотеке языка Си для этой цели используются файловые манипуляторы stdin и stdout, связанные со стандартными устройствами ввода и вывода соответственно. Модернизированный вариант программы может выглядеть так (на диске он находится под именем “/SRC/iostd.c”):
- #include
- int main(int argc, char *argv[])
- {
- char buf[100],out[7],tmp,p=0;
- fgets(&buf[0],100,stdin);
- while(buf[p])
- {
- sprintf(&out[0],"0x%X\n",buf[p++]);
- fputs(&out[0],stdout);
- }
- return 0;
- }
- int main(int argc, char *argv[])
Но как в этом случае заставить программу выводить результаты работы в файл, а не терминал? Стандартный ввод-вывод выгодно отличается возможностью направить его куда угодно, указав в командной строке специальный символ “>” для вывода и “<” для ввода.
Например, используем ранее созданный файл myfile и выведем результат работы iostd в файл “out.txt”. Это можно сделать следующим образом:
- iostd.exe
out.txt
- copy out.txt con
- 0x48
- 0x65
- 0x6C
- 0x6C
- 0x6F
- 0x2C
- 0x20
- 0x53
- 0x61
- 0x69
- 0x6C
- 0x6F
- 0x72
- 0x21
- 0xA
- 1 файлов скопировано
Да, это работает! Но как? Командный интерпретатор при запуске анализирует командную строку и если обнаруживает символы перенаправления ввода-вывода, открывает соответствующие файлы и связывает с ними потоки ввода-вывода.
Перенаправление ввода-вывода полезная штука, но зачастую слишком уж уязвимая для атак. Рассмотрим следующий код (расположенный на диске под именем “/SRC/iohack.c”), часто встречающийся в UNIX-приложениях.
- #include
- main()
- {
- FILE *f;
- char buf[100],c=2;
- printf("$");
- fgets(&buf[0],100,stdin);
- if (buf[0]!='<')
- printf("%s\n",&buf[0]);
- else
- {
- while(buf[c]!=0xA) c++;
- buf[c]=0;
- if (f=fopen(&buf[1],"r"))
- while((c=fgetc(f))!=EOF)
- printf("%c",c);
- }
- }
- main()
Программа запрашивает у пользователя строку и выводит ее на экран. Но, если указать знак перенаправления потока ввода, на экран выдастся содержимое запрашиваемого файла! Например:
- iohack.exe
- $Hello!
- Hello!
- iohack.exe
- $
- Hello, Sailor!
- Hello, Sailor!
С одной стороны, поддержка приложением перенаправления ввода-вывода очень удобна и облегчает работу с компьютером (в самом деле, зачем вводить текст вручную, если его можно взять из файла?), но… часто приводит к последствиям никак не запланированными разработчиком94.
Приведенный выше пример, запущенный в среде UNIX, встретив конструкцию “и только к тем, файлам, которые пользователь мог бы просмотреть и без помощи этой программы, скажем, командой “cat”.
Но все безоблачно лишь на первый взгляд. В UNIX есть два типа процессов – наследующие права пользователя и исполняющиеся от имени системы. Примером последних может служить почтовый демон SendMail, обычно исполняющийся с наивысшими привилегиями. Ранние версии поддерживали перенаправление ввода-вывода и позволяли приаттачить к письму любой файл, даже недоступный простому пользователю.
Разумеется, SendMail не одинок, и подобные ошибки допущены во множестве приложений, щедро разбросанных по серверам. Часто для их поиска даже не требуется кропотливого изучения исходных текстов программы – достаточно во всех вводимых строках (или полях заголовка) подставить символ “<” и посмотреть на реакцию программы – нет-нет, да повезет!
Однако возможности перенаправления ввода-вывода очень ограничены. Рассмотрим это на следующем примере, – пусть требуется получить отсортированный в обратном порядке список файлов текущей директории. Команда ‘ls’ не предоставляет такого сервиса, поэтому придется воспользоваться утилитой ‘sort’ Сперва сохраним результат работы команды ‘ls’ (или dir в MS-DOS) в файле temp. Затем используем его в качестве входного потока данных утилиты ‘sort’, запушенной с ключом ‘-r’ для задания обратного порядка сортировки.
- $ ls >temp
- $ sort -r
- temp
- sioux.pl
- passwd
- iohack.o
- iohack.c
- index_hack.php
- demos.txt
- bomb.pl
- attack2.php
- temp
Да, это работает, но требует создания лишнего файла на диске и в целом недостаточно удобно. А нельзя ли связать стандартный вывод одной программы со стандартным вводом другой? Можно! И такая конструкция называется конвейер или труба (от английского pipe).
Для создания конвейера необходимо использовать символ “|”, разделяющий запускаемые программы следующим образом: “программа 1 параметры | программа 2 параметры | программа 3 параметры”. Разумеется, стандартный ввод первой программы в цепочке и стандартный вывод последней могут быть перенаправлены с помощью символов “<” и “>” соответственно (результат попытки перенаправления остальных непредсказуем, и обычно разрывает цепочку конвейера).
Интереснее проследить, как происходит взаимодействие процессов и передача данных по конвейеру. Конвейер – сути дела тот же файл, но скрытый от пользователя, построенный по принципу FIFO (от английского First Input First Output – первый пришел – первым и уйдешь). Так, запись в конвейер, из которого никто не читает, рано или поздно вызывает его переполнение (а размер буфера обычно порядка четырех килобайт) и приводит к блокировке процесса-писателя, до тех пор, пока хотя бы один байт из конвейера не будет прочитан. Точно так, попытка чтения из пустого конвейера приводит к остановке процесса-читателя до тех пор, пока в нем не окажется хотя бы один байт.
Рисунок 012.txt Схематическое изображения конвейера
Но в любом случае, при обработке конвейера программы поочередно запускаются слева направо, и приведенный выше пример, переписанный с учетом конвейера, может выглядеть так:
- $ ls | sort -r
- sioux.pl
- passwd
- iohack.o
- iohack.c
- index_hack.php
- demos.txt
- bomb.pl
- attack2.php
Не правда ли намного проще и элегантнее? Между прочим, конвейеры поддерживаются не исключительно одной UNIX, – не хуже с ними справляется и старушка MS-DOS. Доказывает это эксперимент, приведенный ниже:
- dir /b | sort /r
- sioux.pl
- passwd
- iohack.o
- iohack.c
- index_hack.php
- demos.txt
- bomb.pl
- attack2.php
Кажется, в этом нет ничего удивительного, но зададим простой вопрос, – как однозадачная операционная система MS-DOS может одновременно запустить два процесса? Оказывается, в ней реализован несколько другой механизм поддержки конвейера, – сначала запускается первая слева программа, записывает все результаты своей работы в некоторый промежуточный буфер, затем запускается вторая программа и получает из буфера входные данные. Это уже не труба получается, а настоящий бассейн!
С первого взгляда в таком подходе ничего дурного нет, но некоторые примеры могут работать некорректно или и вовсе не работать. К таким относится, например, UNIX-утилита “yes”, посылающая на стандартный вывод бесконечный поток символов ‘y’. За кажущейся бесполезностью она часто требуется для пакетного выполнения программ, периодически отвлекающих пользователя запросами на подтверждение выполнения какой-нибудь операции.
В UNIX конвейер полностью заполняется символами ‘y’, и выполнение утилиты “yes” приостанавливается, до тех пор, пока другой процесс не возьмет из конвейера один или несколько символов. Но в MS-DOS два процесса не могут исполняться параллельно, и пока процесс “yes” не закончит выполнение, никакое другое приложение не сможет получить управление, а поскольку выполнение ”yes” не завершиться никогда (программа-то умышленно зациклена) система скинет ласты и впадет в дурной цикл.
Рисунок 013.txt Сравнение конвейеров в UNIX и MS-DOS. В MS-DOS конвейер больше похож на «бассейн», чем на «трубопровод»
Поддержка конвейеров – штука замечательная, но только не с точки зрения безопасности. Следующий код доказывает это утверждение (на диске он находится под именем “/SRC/pipe.hack.pl”).
- open(FH,<>);
- if (FH)
- {
- while(
)
- {
- print;
- }
- }
- {
На первый взгляд, программа предназначена для вывода содержимого файла на экран, но ниже показано, что произойдет, если воспользоваться символом конвейера:
- ls|
- sioux.pl
- passwd
- iohack.o
- iohack.c
- index_hack.php
- demos.txt
- bomb.pl
Опаньки! Да ведь функция open языка Perl негласно поддерживает конвейер! Вместо открытия файла происходит его запуск! Вряд ли стоит объяснять, какие последствия вытекают из этого! Так, одна из версий SendMail позволяла в качестве обратного адреса отправителя письма подставить строчку “|/usr/bin/sh” и оболочка действительно запускалась, предоставив атакующему привилегированный доступ в систему (от имени демона).
Огромное количество защит оказалось взломано именно «благодаря» поддержке конвейера, позволяющего выполнять на удаленной машине любой код95. Аналогично перенаправлению ввода-вывода, конвейерные дырки могут быть обнаружены не только тщательным изучением исходных тестов приложений, но и простой подстановкой знака “|” во все доступные строки ввода и поля заголовков. Не так уж и редко это срабатывает.
Важно отметить, подобной «вкусности» подвержена не только операционная система UNIX, но и множество других, в частности Windows 9x/Windows NT. Убедиться в этом поможет приведенный выше код “pipe.hack.pl”. Достаточно запустить его на платформе Windows и ввести следующую команду:
- dir |
- Том в устройстве F не имеет метки
- Серийный номер тома: 2F42-0AE8
- Содержимое папки F:\TPNA\src
- . <ПАПКА> 28.06.00 23:14 .
- .. <ПАПКА> 28.06.00 23:14 ..
- IO C 294 06.07.00 10:29 io.c
- IO OBJ 775 06.07.00 10:18 io.obj
- IO EXE 32 768 06.07.00 10:18 io.exe
- IOSTD C 228 06.07.00 10:30 iostd.c
- IOSTD OBJ 627 06.07.00 10:26 iostd.obj
- IOSTD EXE 32 768 06.07.00 10:26 iostd.exe
- MYFILE 16 06.07.00 10:53 myfile
- OUT TXT 89 06.07.00 10:53 out.txt
- IOHACK C 295 06.07.00 15:18 iohack.c
- IOHACK OBJ 827 06.07.00 14:58 iohack.obj
- IOHACK EXE 32 768 06.07.00 14:58 iohack.exe
- PIPEHA~1 PL 65 06.07.00 22:29 pipe.hack.pl
- 12 файлов 101 520 байт
- 2 папок 1 710 641 152 байт свободно
Методы противодействия и защиты от подобных ошибок будут описаны в главе «Атака на WEB-сервер», а ниже будет объяснено почему символ конвейера появляется то слева от команды (как в примере с “|/usr/bin/sh”), то справа (“dir |”). Вообще-то «классический» конвейер состоит минимум из двух программ, и вывод первой из них попадает на ввод второй. То есть конструкцию “program 1 | program 2” можно изобразить как “stdin program 1 program 2 stdout”. А в случае, когда используется всего лишь одна программа, вывод программы, стоящей до символа конвейера, перенаправляется в открываемый функцией “open” манипулятор, а вывод программы, стоящей за символом конвейера, никуда не перенаправляется и идет прямиком на терминал.
Сказанное позволяет продемонстрировать приведенный ниже код (на диске, прилагаемом к книге, он находится в файле “/SRC/pipe.test.pl”):
- open(FH,<>);
- if (FH)
- {
- while($x=
)
- {
- print “Этот текст прочитан из файла:$x”;
- }
- }
Строка «Этот текст прочитан из файла», предваряющая переменную $x, позволит отличить символы, получаемые чтением из файла, от текста непосредственно выводимого программой на экран.
- echo Hello, Sailor |
- Этот текст прочитан из файла:Hello, Sailor
- |echo Hello, Sailor!
- Hello, Sailor!
В первом случае, когда команда “echo” стоит до символа конвейера, результат ее работы направляется в открываемый функцией open манипулятор, откуда он может читается оператором “<>” и выводится на экран вызовом “printf”.
В другом случае, когда команда “echo” стоит после символа конвейера, результат ее работы направляется в стандартное устройство вывода, и минует оператор “print”.
И так-то славно дело пошло! Я сижу на мачте верхом, кручу бочку одной рукой, другой снимаю с конвейера готовую продукцию, передаю Фуксу, тот Лому, а Лом считает, записывает и выпускает на берег. Часа за три весь остров заселили.
Александр Некрасов
Приключения капитана Врунгеля