ANTICHAT.XYZ    VIDEO.ANTICHAT.XYZ    НОВЫЕ СООБЩЕНИЯ    ФОРУМ  
Баннер 1   Баннер 2
Antichat снова доступен.
Форум Antichat (Античат) возвращается и снова открыт для пользователей. Здесь обсуждаются безопасность, программирование, технологии и многое другое. Сообщество снова собирается вместе.
Новый адрес: forum.antichat.xyz
Вернуться   Форум АНТИЧАТ > ИНФО > Статьи
   
 
 
Опции темы Поиск в этой теме Опции просмотра

  #2  
Старый 16.01.2008, 18:06
Аватар для flintstone
flintstone
Новичок
Регистрация: 16.12.2007
Сообщений: 6
Провел на форуме:
40653

Репутация: 6
По умолчанию

10 Целочисленное переполнение
Похоже, термин «целочисленные переполнения» вошел в моду. Сейчас им часто обозначают широкий круг уязвимостей, многие из которых не имеют отношения к «настоящему» целочисленному переполнению. Первое четкое определение целочисленного переполнения было дано в докладе «Профессиональный анализ исходного кода» на конференции BlackHat USA в 2002 г., хотя эта проблема и ранее была известна и описана специалистами в области безопасности.
Целочисленное переполнение происходит тогда, когда целое число превышает свое максимальное допустимое значение или надает ниже минимума. Максимальное и минимальное значения целого числа зависят от его типа и размера. 16-разрядное целое со знаком имеет максимальное значение 32 767 (0x7fff) и минимальное значение -32 768 (-0x8000). 32-разрядное целое без знака имеет максимальное значение 4 294 967 295 (Oxffffffff) и минимальное значение 0. Если 16-разрядное целое со знаком, равное 32 767, будет увеличено на 1, оно в результате целочисленного переполнения становится равным -32 768.
Целочисленное переполнение может пригодиться для обхода проверки размеров или для выделения буферов, размер которых заведомо недостаточен для хранения копируемых в них данных. К категории целочисленного переполнения в общем случае относятся переполнение сложения/вычитания и переполнение умножения.
Переполнение сложения/вычитания возникает при сложении или вычитании двух величин, в результате которого результат выходит за верхнюю или нижнюю границу целого типа. Например, следующий код создает потенциальную опасность целочисленного переполнения:
Цитата:
char *buf;
int allocation_size = attacker_defined_size + 16;
buf = malloc(allocation_size);
memcpy (buf, input, attacker_defined_size);
Если значение переменной attacker_defined_size лежит и диапазоне от -16 до-1, сложение вызовет целочисленное переполнение, и буфер, выделенный в результате вызова malloc(), окажется слишком мал для данных, копируемых вызовом memcpy(). Подобный код очень часто встречается в приложениях, распространяемых с открытыми исходными текстами. Эксплуатация подобных уязвимо-стей сопряжена с некоторыми трудностями, н все же такие дефекты сушествуют.
Переполнение вычитания обычно возникает тогда, когда в программе предполагается некоторый минимальный размер вводимой пользователем величины. Так, в следующем фрагменте существует угроза целочисленного переполнения:
Цитата:
#define HEADER_SIZE 16
char data[1024],*dest;
int n;
n = read(sock,data,sizeof(data));
dest = mailoc(n);
memcpy(dest,data+HEADER_SIZE,n - HEADER_SIZE);
В этом примере целочисленное переполнение произойдет, если размер прочитанных из сети данных меньше предполагаемого минимального размера (HEADER_SIZE).
Переполнение умножения возникает при умножении двух величин, результат которого превышает максимальное допустимое значение целого типа. Уязвимости этого типа были обнаружены в OpenSSH и библиотеке Sun RPC и 2002 г. Следующий фрагмент OpenSSH (до версии 3.4) является типичным примером переполнения умножения.
Цитата:
nresp = packet_get_int();
if (nresp > 0) (
response = xmalloc(nresp * sizeof(char*));
for (i = 0; i < nresp; i++)
response[i] = packet_get_string(NULL);
}
Здесь nresp представляет собой целое число, полученное непосредственно из SSH-пакета. Оно умножается па размер указателя на символ (в данном случае — 4), и программа выделяет приемный буфер соответствующего размера. Если значение nresp превышает 0x3fffffff, результат умножения выходит за верхнюю границу беззнакового целого, и происходит переполнение.
Появляется возможность выделить очень маленький блок памяти и скопировать в него большое количество указателей на символы. Интересно заметить, что эта уязвимость могла реально эксплуатироваться в OpenBSD как раз из-за более защищенной реализации кучи, когда управляющие структуры не хранятся в самой куче. В реализациях кучи со встроенными управляющими структурами запись указателей ведет к сбоям при последующих попытках выделения памяти (как в packet_get_string).
Целые числа меньшей разрядности в большей степени подвержены угрозе переполнения; для 16-разрядных целых типов целочисленное переполнение может быть вызвано стандартными функциями вроде strlen(). В частности, этот тип целочисленного переполнения имел место в функции RtlDosPathNameToNtPathName_U, ставшей причиной уязвимости IIS WebDAV, описанной в Microsoft Security Bulletin MS03-007.
Дефекты целочисленного переполнения по-прежнему актуальны и часто встречаются на
практике. Хотя многие программисты знают о потенциальных дефектах строковых операций, они хуже представляют себе риски, возникающие при манипуляциях целыми числами. Вероятно, эти уязвимости будут встречаться в программах еще много лет.

11 Преобразование целых чисел с разной разрядностью
Преобразования между целыми числами разного размера порой приводят к интересным и неожиданным результатам. Такие преобразования могут быть небезопасными, если программист не продумает их последствия; встретив соответствующие команды в исходном коде, следует тщательно изучить их. Преобразования могут привести к усечению данных, смене знака или его распространению внутри числа. Иногда при этом возникают дефекты, которые можно реально эксплуатировать.
Преобразование большего целого типа к меньшему (скажем, 32-разрядного к 16-разрядному, или 16-разрядного к 8-разрядному) может привести к усечению или смене знака. Скажем, если знаковое 32-разрядное целое с отрицательным значением -65 535 преобразуется в 16-разрядное целое, то результат окажется равным +1 из-за усечения старших 16 бит.
Преобразования меньших целых типов к большим могут приводить к распространению знака в зависимости от исходного и приемного типов. Скажем, при преобразовании знакового 16-разрядного целого со значением -1 к 32-разядно-му целому без знака будет получен результат 4 Гбайт минус 1.
В табл. 11.1 описаны последствия разных преобразований целочисленных типов. Представленная информация проверена для последних версий gcc.

Таблица 11.1. Преобразования целочисленных типов

Цитата:
Исходный тип/размер Исходное значение Итоговый тил/раэмер Итоговое значение
16-разрядный со знаком -1 (Oxffff) 32-разрядный без знака 4294967295 (Oxffffffff)
16-разрядный со знаком -1 (Oxffff) 32-разрядный со знаком -1 (Oxffffffff)
16-разрядный без знака 65535 (Oxffff) 32-разрядный без знака 65535 (Oxfffl)
16-разрядный без знака 65535 {Oxffff) 32-разрядный со знаком 65535 (Oxffff)
32-разрядный со знаком -1 (Oxffffffff) 16-разрядный без знака 65535 (Oxffff)
32-разрядный со знаком -1 (Oxffffffff) 16-разрядный со знаком -1 (Oxffff)
32-разрядный без знака 32768(0x8000) 16-разрядный без знака 32768 (0x0000)
32-разрядный без знака 32768(0x8000) 16-разрядный со знаком -32768 (0x8000)
32-разрядный со знаком -40960 (Oxffff6000) 16-раэрядный со знаком 24576 (0x6000)
Будем надеяться, что таблица поможет разобраться в тонкостях преобразований целых разных типов. Хорошим примером служит уязвимость, недавно обнаруженная в функции prescan программы Scndmail. Знаковый символ (8 разрядов) читался из входного буфера и преобразовывался в 32-разрядное число со знаком. Происходило расширение знака до 32-разрядиого значения -1, которое интерпретировалось как признак специальной ситуации NOCHAR. В результате в процедуре проверки ошибок происходил сбои, и появлялась возможность удаленного использования переполнения буфера.
Откровенно говоря, преобразования целых чисел разного размера довольно сложны. Если недостаточно глубоко продумать суть таких преобразований, они часто становятся источником ошибок. Кстати говоря, в современных приложениях не так уж много реальных причин для использования целых чисел разного размера; но если такие причины все же существуют, внимательно проанализируйте код преобразования.

12 Повторное освобождение памяти
Хотя ошибка повторного освобождения одного и того же блока памяти на первый взгляд кажется вполне безопасной, она может принести к порче содержимого памяти и выполнению произвольного кода. Некоторые реализации кучи полностью или хорошо защищены от таких дефектов, поэтому их практическое применение возможно не на всех платформах.
Как правило, программисты не делают подобных ошибок и не пытаются освобождать локальную переменную дважды (хотя мы сталкивались с такими примерами). Уязвимости повторного освобождения чаще всего встречаются тогда, когда буферы в куче хранятся в указателях с глобальной видимостью. Многие приложения при освобождении глобального указателя присваивают ему значение NULL, чтобы предотвратить его повторное использование. Если приложение не делает чего-нибудь в этом роде, желательно начать поиски мест, в которых фрагмент памяти может освобождаться дважды. Такие уязвимости также встречаются в коде на C++ при освобождении экземпляра класса, некоторые члены которого уже были освобождены.
Недавно в zlib была обнаружена уязвимость, в которой некоторая ошибка в процессе разархивации приводила к двукратному освобождению глобальной переменной. Кроме того, недавняя уязвимость CVS-сервера также была результатом повторного освобождения.

13 Неосвобождение выделенной памяти
Хоть применение функции освобождающей выделенный блок памяти, например free(), не
является действительно необходимым, поскольку любая распределённая память автоматически освобождается по завершении программы, следует всё-таки уделить внимание и тут. В более сложных программах возможность, связанная с освобождением повторным использованием памяти, может иметь значение.
Объём статистической памяти фиксируется во время компиляции; этот объём не изменяется при выполнении программы. В процессе выполнения программы объём памяти, выделяемый для автоматических переменных, изменяется в автоматическом режиме. Однако объём памяти, используемый для распределённой памяти, только возрастает, если не воспользоваться функцией free(). Например, предположим, что функция создаёт временную копию массива, как показано в следующем коде:
Цитата:

int main( )
{
Double glad[2000];
int i;

for(i=0;i<1000;i++)
gobble(glad, 2000);

}
void gobble(double ar[],int n)
{
Double * temp = (double *) malloc(n * sizeof(double));

/* free(temp); //забыли об использовании free() */
}
Сначала вызывается функция gobble(), создаётся указатель temp для распределения 16 000 байтов памяти (при условии, что тип double хранит 8 байтов), затем вызывается функция malloc().
Предположим, что функция free() не используется. Если функция завершается, указатель temp, являясь автоматической переменной, исчезает. Причём указанные 16 000 байтов памяти продолжают существовать. К этому объёму нельзя получить доступ, поскольку адрес отсутствует.
Его нельзя использовать повторно, так как не вызывается функция free().
Вторично вызывается gobble(), создаётся снова указатель temp, функция malloc() опять применяется для распределения 16 000 байтов памяти. Первый блок из 16 000 байтов больше не является доступным, поэтому функция malloc() должна обнаруживать второй блок из 16 000 байтов. Если функция завершается, этот блок памяти также становится недоступным и не используется повторно.
Однако цикл выполняется 1000 раз, поэтому ко времени завершения цикла из пула памяти удаляется 16 000 000 памяти байтов. Фактически, программа может превысить лимит выделяемой памяти. Проблема такого рода называется утечкой памяти. Чтобы предотвратить её появление, необходимо в конце выполняемой программы вызвать функцию free().

14 Использование памяти вне области видимости
Некоторые фрагменты памяти в приложении имеют область видимости, а также срок жизни, в течение которого они являются действительными. Любое использование этих фрагментов до того, как они станут действительными, или после того, как они станут недействительными, рискованно. Потенциальный результат — порча памяти, приводящая к выполнению произвольного кода.

15 Использование неинициализированных переменных
Хотя случаи использования неинициализированных переменных в программах попадаются относительно редко, иногда это все же случается, и тогда в приложениях могут возникнуть реально эксплуатируемые дефекты. Статическая память (в частности, секции .data и .bss) инициализируется нулями при запуске программы. Для переменных в стеке и куче гарантия такой инициализации отсутствует, поэтому для устойчивой работы программы они должны специально инициализироваться перед первой операцией чтения.
Содержимое неинициализированной переменной по своей сути является неопределенным. Тем не менее, можно точно предсказать, какие данные будут содержаться в неинициализированной области памяти. Например, неинициализированная стековая переменная будет содержать данные, оставшиеся от предыдущих вызовов функций. В ней могут оказаться данные аргументов, сохраненные регистры или локальные переменные от предыдущих вызовов, в зависимости от ее местонахождения в стеке. Если благодаря везению нападающему удастся взять под контроль нужную область памяти, часто открывается возможность эксплуатации таких уязвимостей.
Уязвимости неинициализированных переменных встречаются редко, потому что обычно ведут к немедленному аварийному завершению программы. Как правило, их можно встретить в редко выполняемом коде, скажем, в блоках, управление которым передается в результате маловероятных ошибок. Многие компьютеры пытаются выявить случаи обращения к неинициализированным переменным. В Microsoft Visual C++ предусмотрена логика выявления подобных состояний; то же делает и gcc, но ни один компилятор не справляется с этой работой идеально. Следовательно, ответственность возлагается в первую очередь на разработчика, которому не следует допускать таких ошибок.
В следующем гипотетическом примере продемонстрирован упрошенный случай использования неициализированной переменной:
Цитата:
int vuln_fn(char *data, int some_int) {
char *test;
if(data) {
test = malloc(strlen(data) + 1);
strcpy(test,data);
some_function(test);
}
if(some_int < 0) {
free(test);
return -1;
}
free(test);
return 0;
}
Если аргумент data содержит null, то указатель test оказывается неинициализированным. Он продолжает оставаться в атом состоянии до конца функции, когда для него вызывается функция free. Обратите внимание: ни gcc, ни Visual C++ во время компиляции не предупреждают программиста об этой ошибке.
Хотя такой тип уязвимостей неплохо поддается автоматическому обнаружению, дефекты использования неинициализированных переменных все еше встречаются в приложениях (например, дефект, обнаруженный Стефаном Эссером в PHP в 2002 г.). Несмотря на относительную редкость, эти дефекты бывают довольно неочевидными, и могут оставаться незамеченными в течение многих лет.

16 Использование памяти после освобождения
Буферы в куче остаются действительными, начиная с момента выделения и до момента освобождения памяти вызовом free или realloc с нулевым размером. Любые попытки записи в буфер и куче после его освобождения приводят к порче содержимого памяти п открывают возможность выполнения постороннего кода.
Уязвимости использования памяти после освобождения чаще всего встречаются тогда, когда программа освобождает один из нескольких существующих указателей на буфер. Подобные уязвимости вызывают непредвиденное повреждение кучи и обычно ликвидируются в процессе разработки. Как правило, они попадают л окончательную версию приложения лишь в редко выполняемых блоках кода. Примером может послужить уязвимость в функции psprintf программы Apache 2. опубликованная в мае 2003 года — программа случайно освобождала активный блок памяти, а затем передавала его функции выделения памяти Apache, которая являлась аналогом malloc.

17 Проблемы многопоточности и реентерабельности
Приложения с открытыми исходными текстами в большинстве не являются многопоточными. С другой стороны, в немногочисленных многопоточных приложениях не всегда реализованы необходимые меры предосторожности. Любой многопоточный код, в котором разные программные потоки без блокировки работают с одними и теми же глобальными переменными, создает потенциальную угрозу для безопасности. Обычно такие дефекты обнаруживаются лишь после того, как приложение начинает эксплуатироваться под интенсивной нагрузкой, а иногда вообще остаются незамеченными или относятся к категории перемежающихся сбоев, которые не удается подтвердить.
Как указал Михал Залевски (Michal Zalewski) в статье «Problems with Msktemp()» (август 2002), передача сигналов в Unix может привести к остановке программы, при которой глобальные переменные оказываются в неожиданном состоянии. Если в обработчиках сигналов используются библиотечные функции, небезопасные в плане реентерабельности, это может привести к порче памяти.
Хотя у многих функций существуют версии, безопасные в отношении и программных потоков, и реентерабельности, используются они не всегда. При поиске таких уязвимостей необходимо представлять себе, что происходит при доступе со стороны нескольких потоков. Очень важно понимать, как работают базовые библиотечные функции, потому что проблемы могут крыться именно в них. Если помнить об этом, выявление дефектов многопоточности окажется не такой уж и сложной задачей.

Приложение
Хочу представить список наиболее опасных функций для C/C++ взятых из исходников программы Flawfinder, мною переведённых и дополненных. Список можно скачать тут.
 
Ответить с цитированием
 



Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
[Обзор уязвимостей vBulletin] bandera Форумы 74 07.06.2010 16:19
[Обзор уязвимостей в форумных движках] Grey Форумы 48 28.12.2009 20:03
Классификация Хацкеров A-Spt_N(o) Болталка 31 13.01.2008 16:12



Здесь присутствуют: 1 (пользователей: 0 , гостей: 1)
 


Быстрый переход




ANTICHAT.XYZ