Техника сетевых атак
Шрифт:
· char user[10];
· char pass[10];
· printf("Login:"); gets( amp;user[0]);
· printf("Passw:"); gets( amp;pass[0]);
· if (!strcmp( amp;pass[0],"guest"))
· return 1;
· return 0;
·}
·
· main
· {
· printf("Buffer Overflows Demo\n");
· if (auth)
· printf("Password ok\n");
· else
· printf("Invalid password\n");
·}
На первый взгляд, программа как будто бы должна работать нормально. Но функция gets, читающая строку с клавиатуры, не имеет никаких представлений о размере выделенного под нее буфера, и принимает данные до тех пор, пока не встретит символ возврата каретки. Если пользователь введет в качестве своего имени строку, превышающую десять символов [307], ее «хвост» затрет адрес возврата функции и дальнейшее выполнение программы окажется невозможным.
Например, если запустить этот пример под управлением Windows 2000, и в качестве имени пользователя ввести строку “1234567890qwerty” операционная система выдаст следующее сообщение, предлагая либо завершить работу приложения, либо запустить отладчик (если он установлен) для выяснения причин сбоя: «Исключение unknown software exception (0xc000001) в приложении по адресу 0x0012ffc0».
Рисунок 72 Реакция системы на переполнение буфера
Допустим, в программе присутствует некая функция (условно названная “root”), которая выполняет действия, необходимые злоумышленнику. Может ли он специально подобранной строкой изменить адрес возврата таким образом, чтобы вместо сообщения о неправильно набранном пароле, управление передавалось на эту функцию?
Для ответа на такой вопрос необходимо знать по какому адресу расположена интересующая злоумышленника функция, и какой именно байт из введенной строки затирает адрес возврата. Выяснить это можно с помощью дизассемблирования кода программы.
Дизассемблирование - процесс сложный и требующий от исследователя хороших знаний ассемблера, архитектуры операционной системы и техники компиляции кода. Без этого разобраться с алгоритмом работы программы практически невозможно. К сожалению, практически не существует литературы, посвященной дизассемблированию, поэтому, в большинстве случаев приходится осваивать эту тему самостоятельно [308].
Все, сказанное ниже, рассчитано на читателя средней квалификации, как минимум знающего назначение наиболее употребляемых команд микропроцессора Intel 80x86. В качестве дизассемблера выбрана IDA PRO четвертой версии [309], однако, можно воспользоваться и другими инструментами, такими как SOURCER, W32Dasm или на худой конец DumpBin, который поставляется с любым Windows-компилятором.
Результат дизассемблирования buff.demo.exe показан ниже (на диске, прилагаемом к книге, он расположен в файле “/LOG/buff.demo.lst”). Исследователь должен изучить «устройство» функции Auth, (как ее найти во много килобайтовом листинге - тема отдельного разговора). Для облегчения понимания, листинг снабжен подробными комментариями.
·.text:00401000; Segment type: Pure code
·.text:00401000 _text segment para public 'CODE' use32
·.text:00401000 assume cs:_text
·.text:00401000;org 401000h
·.text:00401000 assume es:nothing, ss:nothing, ds:_data, fs:nothing, gs:nothing
·.text:00401000 Root proc near
·.text:00401000; Функции root расположена по адресу 0x401000
·.text:00401000 push ebp
·.text:00401000 ;… назначение процедуры root значение не имеет
·.text:00401000 ;… для ее вызова достаточно знать по какому адресу она расположена в памяти
·.text:00401000;… а расположена она по адресу 0x401000
·.text:00401001 mov ebp, esp
·.text:00401003 push offset aHelloRoot; "Hello, Root!\n"
·.text:00401008 call _printf
·.text:0040100D add esp, 4
·.text:00401010 pop ebp
·.text:00401011 retn
·.text:00401011 Root endp
·.text:00401012
·.text:00401012; --------------- S U B R O U T I N E ---------------------------------------
·.text:00401012
·.text:00401012; Attributes: bp-based frame
·.text:00401012
·.text:00401012 auth proc near; CODE XREF: main+10p
·.text:00401012
·.text:00401012 var_18 = byte ptr -18h
·.text:00401012 var_C = byte ptr -0Ch
·.text:00401012; Так IDA обозначает локальные переменные, а цифры указывают относительное
·.text:00401012; расположение от конца кадра стека.
·.text:00401012; В Момент вызова функции указатель стека указывает на адрес возврата
·.text:00401012 push ebp
·.text:00401012; В стек заносится регистр ebp, значение указателя стека уменьшается на 4
·.text:00401013 mov ebp, esp
·.text:00401013; Открывается кадр стека:
·.text:00401013; В регистр ebp заносится значение регистра указателя стека esp.
·.text:00401013; Регистр ebp будет использоваться для адресации локальных переменных относительно конца кадра стека
·.text:00401015 sub esp, 18h
·.text:00401015; Резервируется 0x18 (24 в десятичной нотации) байт под локальные переменные