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, причем они должны быть равны друг другу. На самом деле, последнее утверждение не всегда справедливо. И это связано с разрядностью разных типов данных, что приводит к обрезанию значений переменных. |