МАССИВное переполнение Мысла Владислав Спецвыпуск 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". |