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

Integer Overflow

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

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


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

.....

char* Protocol::GenerateFullName( char* szUserName ,

int intUserNameSize ) {

char* szDomainName = GetDomainName();

if( intUserNameSize < [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’; } }

Принцип проверки прост – длина источника меньше размера приемника. Если она превышает допустимый размер, то функция прекращает свою работу. Но проблема заключается в том, что при сравнении чисел учитывается их знак. А это значит, что если передать intUserNameSize с отрицательным значением, то проверка будет проигнорирована, так как отрицательное число будет всегда больше положительного результата, возвращаемого функцией GetMaxStrSize. Самое интересное, что по идее функция memcpy должна проигнорировать отрицательный объем для копирования (см. [2]). А на самом деле все происходит иначе – она обрабатывает аргумент длины как беззнаковую переменную. Таким образом, ей передается очень большое число (больше, чем 0x7FFFFFFF), и она должна попытаться скопировать строку этой длины в буфер результата. Но это приведет к тому, что приложение аварийно закончит свою работу. Эта техника хороша для обхода разного рода условий, но для эксплуатации в чистом виде она не может быть использована. Более интересно, если memcpy передаются переменные типа byte(char) или даже word(short), потому как их максимальное значение не столь велико, а это дает возможность эксплуатировать функции, которые принимают строки, без явного указания их длины в аргументах.

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

.....

If ( ( intUserNameSize + intDomainNameSize ) <

GetMaxStrSize( MAX_SIZE, 0 ) ) {

WriteToLog( “UserName is too long” ); }

else {

..... }

Абстрактность знака

Также не стоит забывать о том, что проверка, реализованная в примере 5, тоже является некорректной, поскольку при сложении двух положительных чисел результат может оказаться отрицательным. Посмотри на таблицу, чтобы убедиться, что результат арифметической операции не всегда такой, каким он должен получиться на самом деле.

Числа – это загадка, тем более когда речь идет о компьютерных технологиях. Ты можешь написать грамотное приложение, но если неправильно выбраны числовые типы, то это может привести к непредсказуемым результатам! Представь себе, условия не работают корректно, ограничения на размер буфера сняты! Это достаточное условие для хакера, и, поверь, если он знает о числах столько же, сколько и ты сейчас, то он с легкостью взломает твою программу! Необходимо запомнить эти тонкости, и тогда ты сможешь без особого труда находить ошибки такого класса.

Абстрактность знака

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

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