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

Заметки по безопасному программированию на С
  #1  
Старый 10.09.2007, 18:24
Ni0x
Постоянный
Регистрация: 27.08.2006
Сообщений: 367
Провел на форуме:
2009677

Репутация: 472
Wink Заметки по безопасному программированию на С

Это не статья в полном смысле этого слова, скорее это заметки по безопасному программированию на языке C. Моя цель - это рассмотрение наиболее популярных ошибок, которые можно встретить в приложениях на данном языке. Сразу скажу, что я не ставил перед собой задачу описать эксплутацию этих уязвимостей, так как это совсем другая тема. Прочитав этот материал, вы сможете анализировать свои программы на предмет описанных мной багов. Позже, вы научитесь самостоятельно определять уязвимые места в своем коде, даже больше того, у вас появится возможность дальше развиваться в направлении анализа кода. При написании я рассчитывал на средний уровень читающего, я старался писать доступным языком, но совсем банальные вещи, которые по моему мнению должен знать читатель, я не определял.

Содержание:

1) Переполнение буфера
2) Форматные строки
3) Уязвимость единичного смещения
и некорректное завершение строк

продолжение следует...

1) Переполнение буфера
Начнем с самого основного, уязвимости, связанные с переполнением буфера. Буфер может переполняться в самых разных местах памяти, включая стек и кучу.
Переполнение буфера обычно происходит, когда программа пытается записать данные за пределы конца буфера. Множество стандартных функций из crt не имеют никакого представления о размерах приемных буферов (strcpy, strcat, gets и тд). Несмотря на то, что переполнения можно избежать обычной проверкой перед вызовом функции, такие ошибки всеравно допускаются, хотя в чистом виде их можно встретить либо в совсем старых исходниках, либо в работах начинающих программистов. Отдельно стоит упомянуть и про то, что стандартные строковые функции имеют аналоги, которые позволяют ограничить размер записываемых данных. Таким образом, вместо strcpy, strcmp, и sprintf используются strncpy, strncmp, snprint, соответственно. Простой пример уязвимой программы:
Код:
 #include <stdio.h>
 #include <string.h>
 
 int main(int argc, char *argv[])
 {
   char buffer[16];
   if (argc < 2)
   {
   printf("Usage: %s arg", argv[0]);
   return 1;
   }
   else
   strcpy(buffer, argv[1]);
   return 0;
 }
Переполнение происходит из-за того, что функция strcpy не имеет понятия о размере буфера, таким образом, если argv[1] превысит размер buffer, то данные выйдут за границу приемного буфера и появиться возможность эксплутации программы. Теперь рассмотрим безопасный вариант:
Код:
 #include <stdio.h>
 #include <string.h>
 #define SIZE 10
 
 int main(int argc, char *argv[])
 {
   char buffer[SIZE];
   if (argc < 2)
   {
   printf("Usage: %s arg", argv[0]);
   return 1;
   }
   else
   {
   strncpy(buffer, argv[1], SIZE - 1);
   buffer[SIZE - 1] = '\0';
   }
   return 0;
 }
Здесь используется безопасный аналог функции strcpy, который четко задает размер копируемых данных и строка в конце завершается нулем, подробнее про нуль-сивол читайте ниже.
Тема довольно широко описана в большом количестве статей, поэтому я не стал уделять много внимания описанию данного вида уязвимостей. Стоит лишь упомянуть и про то, что производители операционных систем не стоят на месте и пытаюстя внедрить новые технологии по борьбе с данным классом уязвимостей. В windows доступны некоторые программные решения, которые предотвращают выполнение кода за пределами переполненного буфера, если такое переполнение было осуществлено. Среди этих решений - DEP в Windows XP SP2 и выше, который кстати существует и на аппаратном уровне. ОС такого вида называют системами с неисполняемым стеком. Думаю ни для кого не секрет, что все что создал человек можно взломать, DEP и прочие защиты не исключение. Советую ознакомиться с некоторыми работами по теме переполнения:
http://forum.antichat.ru/thread26791.html
http://shellcode.ru/index.php?name=News&file=article&sid=11

2) Форматные строки
Не смотря на то, что уязвимости форматных строк устаревают, я всеравно посчитал нужным рассказать об этом, так как и сейчас можно встретить уязвимые приложения, которые встречаются в основном в UNIX. Эта уязвимость базируется на том, что атакующий может контролировать форматную строку. Под форматной строкой понимается форматная строка которая передается функциям, получающим аргументы в стиле printf. Стоит атакующему получить контроль над форматной строкой и он получит возможность передать функцие спецификаторы, приводящие к самым разным результатам. Рассмотрим небольшой пример:
Код:
#include <stdio.h>

int main(int argc, char *argv[])
{
 char buff[16], buff_[16];
 printf("What is your name? ");
 gets(buff);
 sprintf(buff_ , "Hello, %s", buff);
 printf(buff_);
 return 0;
}
Теперь рассмотрим спецификаторы. Начнем с краткой информации по всем типам, которую я позаимствовал с википедии.
Цитата:
d, i — десятичное знаковое число, размер по-умолчанию, sizeof( int ). По-умолчанию записывается с правым выравниванием, знак пишется только для отрицательных чисел;
o — восьмеричное беззнаковое число, размер по-умолчанию sizeof( int );
u — десятичное беззнаковое число, размер по-умолчанию sizeof( int );
x и X — шестнадцатеричное число, x использует маленькие буквы (abcdef), X большие (ABCDEF), размер по-умолчанию sizeof( int );
f и F — числа с плавающей запятой. По-умолчанию выводятся с точностью 6, если число по модулю меньше единицы, перед десятичной точкой пишется 0. Величины ±∞ представляются в форме [-]inf или [-]infinity, Величина Nan представляется как [-]nan или [-]nan(любой текст далее). Использование F выводит указанные величины заглавными буквами (-INF, NAN). Аргумент по-умолчанию имеет размер double.
e и E — числа с плавающей запятой в экспоненциальной форме записи (вида 1.1e+44); e выводит символ «e» в нижнем регистре, E — в верхнем (3.14E+0);
g и G — число с плавающей запятой; форма представления зависит от значения величины (f или e);
a и A — число с плавающей запятой в шестнадцатеричном виде;
c — вывод символа с кодом, соответствующим переданному аргументу; переданное число приводится к типу usnigned char (или wint t, если был указан модификатор длины l);
s — вывод строки с нулевым завершающим байтом; если модификатор длины - l, выводится строка wchar_t*;
p — вывод указателя, внешний вид может существенно различаться в зависимости от внутреннего представления в компиляторе и платформе (например, 16 битная платформа MS-DOS использует форму записи вида FFEC:1003, 32-битная платформа с плоской адресацией использует адрес вида 00FA0030);
n — запись по указателю, переданному в качестве аргумента количество символов, записанных на момент появления командной последовательности, содержащей n;
% — символ для вывода знака процента (%), используется для возможности вывода символов процента в строке printf, всегда используется в виде %%.
Наиболее интересны для нас лишь несколько спецификаторов. Спецификатор %s базируется как указатель на строку, тоесть интерпретатор форматного вывода извлекает из стека парный ему аргумент, считая что это указатель на строку, понятно, что там может лежать вовсе не нужная нам строка. Отдельно упомяну нуль в конце, так как язык С использует ASCIIZ строки, то по стандарту строки должны закнчиваться нулем в конце. Вывод "мусорной" строки может привести к самым разным последствиям, в том числе к выводу произвольного дампа памяти и разрушению стека. Такой тип уязвимости называют атакой на строку форматирования. Откомпилируйте пример и введите заместо верного ответа спецификатор %s, обратите внимание на результат. Продолжим рассматривать спецификаторы, спецификатор %x выводит парное двойное слово из стека в шестнадцатиричном формате, это может пригодиться, если вам нужно получить адреса определенных данных. Также нужно сказать про спецификатор %n, цель которого записать в парный указатель количество выведенных байтов на данный момент. Перезаписываться будет не сам указатель, а область памяти, на которую он указывает. Это открывает огромные возможности нападающему. Ошибки форматной строки достаточно легко обнуружать в ходе анализа исходного кода, поэтому данный тип уязвимостей с каждым днем теряет свою актуальность.

3) Уязвимость единичного смещения
Уязвимость единичного смещения очень актуальна на данный момент, смсысл данной уязвимости состоит в том, что небольшое число байт записывается за пределы выделенной памяти. Чаще всего, именно один байт, в результате некорректного завершения строки нулем. Посмотрим на вызов функции strncat:
Код:
strncat(buf, tmp, sizeof(buf) - strlen(buf));
Ошибка единичного смещения в данном примере возникает из-за того, что strncat завершает выходную строку нулем, тоесть если третий аргумент данной функции не будет равен объему оставшегося места в выходном буфере с вычетом одного байта, то \0 запишется за пределами нашего буффера. Значит безопасный вызов функции будет таким:
Код:
 strncat(buf, tmp, sizeof(buf) - strlen(buf) - 1);
Не стоит проверять на уязвимость только функции для работы со строками, обращайте внимание на все функции работы с памятью. Хотя эксплутировать данную уязвимость очень сложно, вероятность есть всегда. Далее рассмотрим ошибки некорректного заверешния строк в C, как известно ASCIIZ строки завершаются нулем, что не очень практично. Однако, от этого никуда не деться, поэтому разберемся с этим поподробнее. Допустим строка не заканчивается нулем, что тогда? Тогда все дальнейшее содержимое памяти будет рассматриваться как строка, до первого нуль-символа естественно. Последствия могут быть самыми разными, от включения в строку "мусора" до аварийного заверешния программы. Основная проблема кроется в тех самых функциях рантайма для работы со строками. Скажем strncpy не завершит строку нулем, если место в приемном буфере кончилось, поэтому нужно явно дописывать нуль-символ в конец строки. Нельзя недооценивать данную уязвимость, так как простор для действий атакующего ограничивается лишь рядом обстоятельств в виде рядом лежащих данных. Также обращаю ваше внимание на пропуск завершающего нуль-символа, скажем, если после пропуска завершителя произойдет запись данных - это может привести к фатальным последствиям, вплоть до выполнения произвольного кода.

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

Последний раз редактировалось Ni0x; 11.09.2007 в 17:09..
 
Ответить с цитированием
 



Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
Чемпионат мира по программированию: российские гении заняли не самые лучшие места ~!DoK_tOR!~ Мировые новости 6 25.03.2007 14:57
В Токио пройдет Чемпионат мира по программированию Zek Мировые новости 4 09.03.2007 18:28
Русский стал чемпионом мира по программированию -=ka$at1k=- Мировые новости 7 06.07.2006 17:48
Предлагаем услуги по программированию mysterious-killer О Работе 0 17.05.2006 13:39
Mail.Ru поддерживает российскую команду на чемпионате мира по программированию novichok Мировые новости 1 05.04.2006 00:12



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


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




ANTICHAT.XYZ