Читаем Техника сетевых атак полностью

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

· 015f:0040125d e80afeffff call 0040106c = BUFF.DEMO.EXE:.text+0x6c

· BUFF.DEMO.EXE:.text+0x262:

· *015f:00401262 83c40c add esp,+0c

С помощью IDA легко установить, что процедура, располагающая по адресу 0x40106C, представляет собой main():

·.text:0040106C main proc near; CODE XREF: start+AFp

·.text:0040106C push ebp

·.text:0040106D mov ebp, esp

Но переполнение буфера произошло в процедуре auth, ссылок на адрес которой (0х401000) в протоколе, выданном Доктором Ватсоном вообще нет! Это происходит потому что, адрес возврата из процедуры auth был затерт введенной строкой и Доктор Ватсон не смог определить откуда произошел вызов. Исключение вызвала не сама функция main, а одна из вызываемых ею процедур. Установить же истинного «виновника» исключения теперь практически невозможно.

Единственной зацепкой, за которую можно ухватиться оказываются параметры переданные функции (если они не были затерты [334]). По роду и значению параметров можно хотя бы приблизительно определить какая функция была вызвана. По крайней мере, это позволит сузить круг поиска.

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

· #include «stdio.h»
· #include «string.h»· #include «windows.h»·· int file(char *buff)· {· char *p;· int a=0;· char proto[10];· p=strchr( amp;buff[0],':');· if (p)· {· for (;a!=(p- amp;buff[0]);a++)· proto[a]=buff[a];
·· proto[a]=0;·· if (strcmp( amp;proto[0],"file"))· return 0;· else· WinExec(p+3,SW_SHOW);··}· else· WinExec( amp;buff[0],SW_SHOW);· return 1;
··}··· main(int argc,char **argv)· {· if (argc»1) file( amp;argv[1][0]);·}

 

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

Если в имени файла присутствует символ “:”, то программа полагает, что имя записано в формате “протокол://путь к файлу/имя файла”, и пытается выяснить какой именно протокол был указан. При этом она копирует название протокола в буфер фиксированного размера, полагая, что при нормальном ходе вещей, его хватит для вмещения имени любого протокола. Но если ввести строку наподобие “ZZZZZZZZZZZZZZZZZZZZZZ:”, то произойдет переполнение буфера со всеми вытекающими отсюда последствиями (см. рисунок 084).


Рисунок 084


Приведенный пример относится к самым простым. Но существуют более коварные ошибки, проявляющиеся лишь при стечении определенных обстоятельств и обнаружить их можно только случайно или тщательным изучением исходных кодов (а в отсутствии исходных кодов - дизассемблированием или отладкой).

В первую очередь необходимо отобразить внимание на буфера фиксированного размера, расположенные в стеке. Блоки памяти, выделяемые вызовом alloc, находятся в куче (heap) и их переполнение (даже если и имеет место) не приводит к модификации адреса возврата, сохраненного в стеке.

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

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


Как устроен генератор паролей?


O В этой главе:

O Алгоритмы перебора пароля

O Достоинства и недостатки словарного перебора

O Оценка стойкости пароля

Перейти на страницу:

Похожие книги

Programming with POSIX® Threads
Programming with POSIX® Threads

With this practical book, you will attain a solid understanding of threads and will discover how to put this powerful mode of programming to work in real-world applications. The primary advantage of threaded programming is that it enables your applications to accomplish more than one task at the same time by using the number-crunching power of multiprocessor parallelism and by automatically exploiting I/O concurrency in your code, even on a single processor machine. The result: applications that are faster, more responsive to users, and often easier to maintain. Threaded programming is particularly well suited to network programming where it helps alleviate the bottleneck of slow network I/O. This book offers an in-depth description of the IEEE operating system interface standard, POSIX (Portable Operating System Interface) threads, commonly called Pthreads. Written for experienced C programmers, but assuming no previous knowledge of threads, the book explains basic concepts such as asynchronous programming, the lifecycle of a thread, and synchronization. You then move to more advanced topics such as attributes objects, thread-specific data, and realtime scheduling. An entire chapter is devoted to "real code," with a look at barriers, read/write locks, the work queue manager, and how to utilize existing libraries. In addition, the book tackles one of the thorniest problems faced by thread programmers-debugging-with valuable suggestions on how to avoid code errors and performance problems from the outset. Numerous annotated examples are used to illustrate real-world concepts. A Pthreads mini-reference and a look at future standardization are also included.

David Butenhof

Программирование, программы, базы данных
Java 7
Java 7

Рассмотрено все необходимое для разработки, компиляции, отладки и запуска приложений Java. Изложены практические приемы использования как традиционных, так и новейших конструкций объектно-ориентированного языка Java, графической библиотеки классов Swing, расширенной библиотеки Java 2D, работа со звуком, печать, способы русификации программ. Приведено полное описание нововведений Java SE 7: двоичная запись чисел, строковые варианты разветвлений, "ромбовидный оператор", NIO2, новые средства многопоточности и др. Дано подробное изложение последней версии сервлетов, технологии JSP и библиотек тегов JSTL. Около двухсот законченных программ иллюстрируют рассмотренные приемы программирования. Приведена подробная справочная информация о классах и методах Core Java API.

Ильдар Шаукатович Хабибуллин

Программирование, программы, базы данных