Издательский дом ООО "Гейм Лэнд"СПЕЦВЫПУСК ЖУРНАЛА ХАКЕР #45, АВГУСТ 2004 г.

Лопнуть как мыльный пузырь

Vint (vint@glstar.ru)

Спецвыпуск Xakep, номер #045, стр. 045-004-1


Переполнение буфера: основные идеи и принципы

Уже много лет компьютерный мир борется с одной из самых сильных атак, вызывающих повышение привилегий как локального, так и удаленного пользователя, – buffer overflow.

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

Так, например, регистр EAX применяется для пользовательских данных, регистр ECX используется как счетчик в циклах и повторяющихся операциях и т.д. Основные регистры и их назначения были унаследованы от 286-го процессора, но была расширена разрядность каждого регистра (до 32 бит) и в название добавили букву "E" (extended, в переводе с англ. – расширенный). Кроме базовых, доступных пользователю регистров, существуют так называемые системные регистры. Запись в них напрямую запрещена, и они используются для контроля за выполнением программы. Примерами таких регистров могут служить EBP и ESP, использующиеся в операциях со стеком, EIP, представляющий собой указатель на инструкцию, которую процессор будет выполнять следующей. И еще один регистр, о котором тебе желательно знать, – регистр флагов EFLAGS, это, по сути, 32 бита, которые используются как переключатели-флаги при работе процессора.

Так дела обстоят с регистрами в процессоре, но для понимания термина "buffer overflow" нужно ознакомиться с понятием стека.

Стек и его строение

Стек представляет собой непрерывную область памяти, адресация на которую происходит с помощью регистров ESP (указатель стека) и SS (указатель на сегмент стека). Именно в стеке хранится тот загадочный буфер, переполнение которого так пугает всех разработчиков ПО. Расположение буфера внутри стека таит в себе огромную опасность: компилятор помещает буфер переменной в стек, а до этого, чуть раньше, записывается адрес возврата из процедуры. Стек, таким образом, имеет следующую структуру:

Но такой расклад несет только полбеды: стек работает с данными по принципу "первым пришел, последним ушел" (FILO). То есть это обычная пирамида: чтобы добраться до самого низа, нужно разобрать все строение – чтобы взять данные, положенные в стек первыми, нужно вынуть всю информацию из стека. На языке команд процессора эти операции носят названия PUSH («запихать») и POP («достать»). Именно с помощью таких операндов происходит вся основная работа с содержимым стека.

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

Представим себе простую программу C, в которой используется переменная, объявленная как char buff[10];, то есть переменная buff имеет размер строго в 10 байт, что явно определено при ее объявлении. Больше 10 байт данная переменная принять не сможет, хотя меньше – пожалуйста. И программа, имеющая так объявленную переменную, будет работать долго и стабильно до тех пор, пока в переменную buff будут помещаться строки длиной не более 10 символов. Но это в идеале и при условии, что программа работает в правильных руках ;-). А что произойдет, если попытаться присвоить переменной buff строку длиной более 10 символов? А произойдет самое интересное! Представим, что мы откомпилировали программу, содержащую такую переменную, и начали исследовать ее в отладчике. Самое главное, на что следует обратить внимание, – это на стек и на его строение в уязвимой программе. На скрине ты можешь увидеть схему, составленную на основе анализа информации отладчика.

Содержание  Вперед на стр. 045-004-2