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

МАССИВное переполнение

Мысла Владислав

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


aka DigitalScream (digitalscream@real.xakep.ru)

А ты знаешь, что такое Array Overflow?

Оставались считанные секунды… "Не может быть, что нельзя угадать 10-значный код для входа, если системой пользуется миллион клиентов", – думал он… Код за кодом – закрыто! Но вдруг блок ввода заморгал…

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

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

Листинг

Пример 1. Протоколирование ошибок

...

Door::Door() {

lIndex = 0;

TypedUID = new UserID[0xFF]; }

...

void Door::LogBadInput( UserID uidInput ) {

TypedUID[ lIndex ] = uidInput;

lIndex ++; }

...

Door::~Door() {

for( long lOffset = 0;lOffset > lIndex; ++lOffset )

WriteToLog(

FormatLogMessage( "UserID", TypedUID[lOffset], sizeof(UserID) );

delete [] TypedUID; }

...

Какую ошибку хранит в себе этот код?

Чтение данных из памяти

При работе с массивами данных программист использует массивы. Если необходимо хранить в массиве объекты или структуры, то он может с легкостью создавать массивы, в качестве элементов которых будут нужные ему данные. Но, если нужно создать массив строк, то в качестве элементов необходимо брать указатели на строки, а не их самих. Это определено тем, что элементом массива может быть любой тип данных, с предопределенным объемом. А строка может состоять из 1, 2, 6, 165 байтов, поэтому в массив записывается именно указатель на строку (он занимает 4 байта), который никак не зависит от ее длины.

Следовательно, массив – это последовательность данных фиксированной длины. Теперь посмотрим на пример 2.

Листинг

Пример 2. Переполнение массивов

...

UserIDList = new byte[0x07];

Password = new char[0x07];

...

byte Door::GetUserIDByIndex(long lIndex)

{

return UserIDList[lIndex - 1 ]; }

...

Что здесь неверно? На первый взгляд, все в порядке: функции GetUserIDByIndex передают индекс пользователя, а она возвращает его идентификатор. Для хранения идентификаторов используется массив UserIDList из 0x07 элементов (нумерация начинается с нуля). Но что будет, если мы передадим GetUserIDByIndex число, большее 0x07, то есть что произойдет при попытке доступа к элементу с индексом больше размерности массива?

Взгляни на рисунок. Это участок памяти, в которой хранится список идентификаторов пользователей, и объявленная сразу же за ним переменная, в которой хранится пароль админа. Если ты попытаешься передать функции GetUserIDByIndex в качестве аргумента единицу, то она вернет идентификатор первого пользователя, двойку – второго и т.д. Но, если передать ей число 8, функция все равно вернет результат. Откуда взялся 8-й элемент, если размер списка – 7? Все очень просто: индекс элемента – это неявный указатель на местоположение элемента в памяти. Неявный потому, что адрес вычисляется как адрес_массива+индекс_элемента*размер_элемента. В нашем примере размер элементов – 1 байт, значит индекс и будет смещением относительно начала массива. Но! В памяти сразу за массивом находится пароль админа, а так как идентификаторов всего 7, то 8-й индекс – это первый символ пароля. Таким образом, если существует функция, предназначенная для аутентификации пользователя, которой передают индекс пользователя и пароль, причем она имеет вид, показанный в примере 3 (в случае неправильного пароля выводит сообщение об ошибке, содержащее идентификатор пользователя), то, передав ей индекс 8, ты получишь сообщение вида "Bad password for user 97".

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