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

Integer Overflow

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

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


Этот пример очень прост для понимания, но в реальной жизни все обстоит гораздо сложнее. Чаще всего уязвимости такого класса преследуют одну цель – нарушить работу приложения в местах записи или чтения из памяти. Это делается, чтобы получить возможность внедрения shell-кода и/или перезаписи указателей на функции, данные и т.д. И, чтобы как-нибудь повлиять на процессы записи в память, иногда можно прибегнуть к целочисленным переполнениям. Основная идея атаки остается прежней, просто делаются попытки повлиять на переменные, хранящие размер буфера, его длину и все, что с этим связано. Хотя арифметические переполнения и могут привести к переполнениям памяти, на практике чаще можно встретить уязвимости, несущие несколько другой характер.

Проблема потери значимых разрядов

Взгляни на пример 2 (в нем функция получает имя пользователя и возвращает его полное имя, включающее также название домена) и попытайся понять, что в нем не так.

Пример 2. Псевдоисходный код функции

const int MAX_SIZE = 0xFF;

const int ZERO_AT_EOS = 0x01;

.....

int Protocol::GetMaxStrSize( int intMaximalSize,

int intReservedSize ) {

return intMaximalSize - ( intReservedSize + ZERO_AT_EOS ); }

char* Protocol::GenerateFullName( char* szUserName ,

int intUserNameSize ) {

unsigned short shUserNameSize = intUserNameSize;

char* szDomainName = GetDomainName();

if( shUserNameSize < [1]

GetMaxStrSize( MAX_SIZE, strlen(szDomainName ) ) ) {

WriteToLog( “UserName is too long” ); }

else { char* szResult = new char[MAX_SIZE];

memcpy( szResult, szUserName, intUserNameSize ); [2]

memcpy( szResult + intUserNameSize,

szDomainName, strlen( szDomainName ) );

szResult[ intUserNameSize +

strlen( szDomainName ) ] = ‘\x00’; } }

На первый взгляд, проблем в функции нет, но это только кажется. Дело в том, что функция GenerateFullName получает два параметра: имя пользователя (указатель на строку) и длину этого же имени (32-битное число). При входе в функцию происходит проверка, поместится ли полное имя пользователя в строку-результат (см. [1]). И если полное имя превосходит по размерам некий максимальный размер MAX_SIZE, то копирование не производится. Всегда ли это так? Сравнение длины производится относительно переменной shUserNameSize, а не intUserNameSize, причем они должны быть равны друг другу. На самом деле, последнее утверждение не всегда справедливо. И это связано с разрядностью разных типов данных, что приводит к обрезанию значений переменных.

Назад на стр. 045-026-2  Содержание  Вперед на стр. 045-026-4