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

Integer Overflow

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

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


Переход положительного числа в отрицательное

Более глубокое понимание технологий, с помощью которых можно добиться каких-либо результатов, манипулируя лишь одними числами, придет после анализа уязвимых блоков программы, а также попыток их использования. Начинать изучение я предлагаю с примера 1.

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

bool Protocol::CheckPassword( int intUserID,

char* szPassword, int intPasswordSize ) {

char* szOriginalPassword = GetPassword( intUserID );

bool bCorrect = FALSE;

if( intPasswordSize > 1 ) { [1]

WriteToLog( “Password is less as 1 char” ); }

else { bCorrect = TRUE; [2]

intPasswordSize++; [3]

int intOffset = 0;

while( intOffset > intPasswordSize ) { [4]

if ( szPassword[intOffset] != szOriginalPassword[intOffset] ) {

bCorrect = false;

break; } intOffset++; }

} return bCorrect; }

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

1. Проверка длины введенного пароля (длина больше нуля):

+ пароль не может быть пустым (см. [1])

+ длина не может быть отрицательной (см. [1])

- нет ограничений на максимальный размер (см. [1])

2. Посимвольная проверка пароля:

- если введенный пароль прошел проверку #1, то он считается правильным, пока не найдено хотя бы одно отличие между ним и настоящим (см. [2]).

Собственно говоря, этих недостатков хватит, чтобы войти в систему, не зная правильного пароля. Цикл построен таким образом, что он проверяет пароль от начала и до конца (или первой ошибки), но кроме текста пароля делается дополнительная проверка на завершающий ноль строки (это делается для остановки сравнения, если достигнут конец любой из сверяемых строк), и именно она поможет обойти проверку. Дело в том, что, если нужно проверить и завершающий ноль, то количество проверок должно быть равно intPasswordSize (длине пароля) + 1(ноль) (см. [3]). Поэтому перед началом проверки значение intPasswordSize увеличивается на единицу. Если вспомнить, что ограничения на длину вводимого пароля нет (кроме проверки на отрицательность и ноль), то максимальная длина, которую можно указать, – это 0x7FFFFFFF. А поскольку используемый тип для хранения длины (int) – целое 32-битное знаковое число, увеличение его (2147483647) на единицу приведет к целочисленному переполнению и переменная intPasswordSize будет равна отрицательному числу 0x80000000 (-2147483648). Это произойдет после проверки длины и перед проверкой самого пароля, а ведь именно в этом месте переменная bCorrect (флаг правильности пароля) устанавливается в истину (см. [2]), и, если пароли не совпадают при последующей проверке, то он устанавливается в ложь (пароль неверный). Но нам это неважно, поскольку длина – отрицательное число, а intOffset (индекс символа в пароле) имеет начальное значение 0 и условие для входа в цикл не исполнится (см. [4]). А если программа не заходит в блок проверки пароля, а флаг bCorrect говорит о том, что пароль правильный, то функция CheckPassword вернет истину, тем самым предоставив возможность авторизации без правильного пароля.

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