Заметки по безопасному программированию на С

Discussion in 'С/С++, C#, Rust, Swift, Go, Java, Perl, Ruby' started by Ni0x, 10 Sep 2007.

  1. Ni0x

    Ni0x Elder - Старейшина

    Joined:
    27 Aug 2006
    Messages:
    338
    Likes Received:
    157
    Reputations:
    37
    Это не статья в полном смысле этого слова, скорее это заметки по безопасному программированию на языке C. Моя цель - это рассмотрение наиболее популярных ошибок, которые можно встретить в приложениях на данном языке. Сразу скажу, что я не ставил перед собой задачу описать эксплутацию этих уязвимостей, так как это совсем другая тема. Прочитав этот материал, вы сможете анализировать свои программы на предмет описанных мной багов. Позже, вы научитесь самостоятельно определять уязвимые места в своем коде, даже больше того, у вас появится возможность дальше развиваться в направлении анализа кода. При написании я рассчитывал на средний уровень читающего, я старался писать доступным языком, но совсем банальные вещи, которые по моему мнению должен знать читатель, я не определял.

    Содержание:

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

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

    1) Переполнение буфера
    Начнем с самого основного, уязвимости, связанные с переполнением буфера. Буфер может переполняться в самых разных местах памяти, включая стек и кучу.
    Переполнение буфера обычно происходит, когда программа пытается записать данные за пределы конца буфера. Множество стандартных функций из crt не имеют никакого представления о размерах приемных буферов (strcpy, strcat, gets и тд). Несмотря на то, что переполнения можно избежать обычной проверкой перед вызовом функции, такие ошибки всеравно допускаются, хотя в чистом виде их можно встретить либо в совсем старых исходниках, либо в работах начинающих программистов. Отдельно стоит упомянуть и про то, что стандартные строковые функции имеют аналоги, которые позволяют ограничить размер записываемых данных. Таким образом, вместо strcpy, strcmp, и sprintf используются strncpy, strncmp, snprint, соответственно. Простой пример уязвимой программы:
    Code:
     #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, то данные выйдут за границу приемного буфера и появиться возможность эксплутации программы. Теперь рассмотрим безопасный вариант:
    Code:
     #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. Стоит атакующему получить контроль над форматной строкой и он получит возможность передать функцие спецификаторы, приводящие к самым разным результатам. Рассмотрим небольшой пример:
    Code:
    #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;
    } 
    
    Теперь рассмотрим спецификаторы. Начнем с краткой информации по всем типам, которую я позаимствовал с википедии.
    Наиболее интересны для нас лишь несколько спецификаторов. Спецификатор %s базируется как указатель на строку, тоесть интерпретатор форматного вывода извлекает из стека парный ему аргумент, считая что это указатель на строку, понятно, что там может лежать вовсе не нужная нам строка. Отдельно упомяну нуль в конце, так как язык С использует ASCIIZ строки, то по стандарту строки должны закнчиваться нулем в конце. Вывод "мусорной" строки может привести к самым разным последствиям, в том числе к выводу произвольного дампа памяти и разрушению стека. Такой тип уязвимости называют атакой на строку форматирования. Откомпилируйте пример и введите заместо верного ответа спецификатор %s, обратите внимание на результат. Продолжим рассматривать спецификаторы, спецификатор %x выводит парное двойное слово из стека в шестнадцатиричном формате, это может пригодиться, если вам нужно получить адреса определенных данных. Также нужно сказать про спецификатор %n, цель которого записать в парный указатель количество выведенных байтов на данный момент. Перезаписываться будет не сам указатель, а область памяти, на которую он указывает. Это открывает огромные возможности нападающему. Ошибки форматной строки достаточно легко обнуружать в ходе анализа исходного кода, поэтому данный тип уязвимостей с каждым днем теряет свою актуальность.

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

    Первые 3 пункта я написал, жду конструктивной критики и исправления логических и грамматических ошибок, так как писал не совсем в адекватном состоянии. Так как всю тему целиком охватить достаточно сложно, то я буду пополнять заметки со временем. Постараюсь ответить на любые нормальные вопросы по теме.
     
    #1 Ni0x, 10 Sep 2007
    Last edited: 11 Sep 2007
    6 people like this.
  2. inv

    inv Banned

    Joined:
    3 Aug 2007
    Messages:
    261
    Likes Received:
    143
    Reputations:
    -58
    боян но про new\delete\malloc\realloc\free\ хотелось бы услышать....ну или ссылку на статью где изложены все подробности...
     
  3. GlOFF

    GlOFF Elder - Старейшина

    Joined:
    8 May 2006
    Messages:
    689
    Likes Received:
    484
    Reputations:
    4
    Думаю это теоретическая статья. Было бы очень круто увидеть приведенные примеры программ и так сказать експлойты юзающие данные баги в защите. Это было бы очень наглядным к сказанному.
    Ni0x Спс за статью!
     
  4. Ni0x

    Ni0x Elder - Старейшина

    Joined:
    27 Aug 2006
    Messages:
    338
    Likes Received:
    157
    Reputations:
    37
    inv, ну я же говорю, не надо писать мне боян и прочие выражения такого рода. Что именно ты хочешь услышать? Есть множество уязвимостей, основанных на функциях, связанных с выделением/освобождением памяти.
    GlOFF, этот материал изначально позиционировался как теоритический, я специально не стал приводить примеры эксплутации данных уязвимостей, так как по каждому пункут есть свои нюансы и свои техники, рассматривать каждый аспект по отдельности с эксплутацией подобно написанию книги. У меня в планах есть написание статьи, где будет и практика и теория, но ее реализация будет после того, как я закончу с этими заметками.
     
    #4 Ni0x, 10 Sep 2007
    Last edited: 10 Sep 2007
  5. GlOFF

    GlOFF Elder - Старейшина

    Joined:
    8 May 2006
    Messages:
    689
    Likes Received:
    484
    Reputations:
    4
    Ni0x Очень жаль, ну ладно. Все же респект за статью!
     
  6. ZaCo

    ZaCo Banned

    Joined:
    20 Jun 2005
    Messages:
    737
    Likes Received:
    336
    Reputations:
    215
    да все подобные статьи не актуальны и по сути уже не нужны. интересно было бы почитать про анализ существующего кода на ошибки, возможные в будущем ошибки, расчет архитектуры программы... естественно я не про анализ 5-строчечного кода говорю.
    зы хорошую статью греат писал насчет вылова ошибок доступа к участкам памяти
     
  7. Ni0x

    Ni0x Elder - Старейшина

    Joined:
    27 Aug 2006
    Messages:
    338
    Likes Received:
    157
    Reputations:
    37
    ZaCo, зря ты так, на данный момент есть необкатанные типы уязвимостей, про которые ты наврятле найдешь нормальную информацию. А про анализ - это да, это я согласен. Думаю, решим.
     
  8. _Great_

    _Great_ Elder - Старейшина

    Joined:
    27 Dec 2005
    Messages:
    2,032
    Likes Received:
    1,119
    Reputations:
    1,139
    Ой бред какойто. Аргумент извлекается в любом случае и НЕ БЫТЬ его там не может - это стек, там всегда есть данные (ну почти.. границу стека в данном случае не рассматриванием). Просто я к тому, что ничего перебирать он не будет - это бред.
    А уж будет ли извлеченный дворд указателем - проблемы программиста
    Приведенный пример неуязвим же на этот тип атаки. Буфер не передается как форматная строка в данном случае. Стоит переписать так:
    Code:
    #include <stdio.h>
    
    int main(int argc, char *argv[])
    {
     char buff[16];
     printf("What is your name? ");
     gets(buff);
     printf("Hello, ");
     printf(buff);
     return 0;
    } 
    Тогда действительно будут проблемы при вводе форматных символов.
    Я видел книжку с отличным описанием данного типа атак. К сожалению, не помню ни названия, ни автора - читал у Cr4sh'а дома когда бухали по пьяни))
    Чтото про эксплоиты
     
    #8 _Great_, 11 Sep 2007
    Last edited: 11 Sep 2007
    1 person likes this.
  9. TaNkist

    TaNkist Elder - Старейшина

    Joined:
    6 Apr 2006
    Messages:
    147
    Likes Received:
    47
    Reputations:
    19
    IMHO, вместо замены strcpy на strncpy, лучше определять длину строки и если длина превышает размер буфера просто вывести ошибку.
     
    1 person likes this.
  10. Ni0x

    Ni0x Elder - Старейшина

    Joined:
    27 Aug 2006
    Messages:
    338
    Likes Received:
    157
    Reputations:
    37
    TaNkist, про strncpy хотябы знать надо.
    Заметки дополню и исправлю некоторые ошибки.
     
  11. ProTeuS

    ProTeuS --

    Joined:
    26 Nov 2004
    Messages:
    1,239
    Likes Received:
    542
    Reputations:
    445
    >>Тогда действительно будут проблемы при вводе форматных символов.
    >>Я видел книжку с отличным описанием данного типа атак. К сожалению, не помню >>ни названия, ни автора - читал у Cr4sh'а дома когда бухали по пьяни))
    >>Чтото про эксплоиты

    меньше надо пить)
    Эрикссон. "Исскуство экплойта" или "The Art Of Exploitation"
     
    2 people like this.
  12. _Great_

    _Great_ Elder - Старейшина

    Joined:
    27 Dec 2005
    Messages:
    2,032
    Likes Received:
    1,119
    Reputations:
    1,139
    Ты тоже ее тогда по пьяни читал, да? (
     
    1 person likes this.
  13. KEZ

    KEZ Ненасытный школьник

    Joined:
    18 May 2005
    Messages:
    1,604
    Likes Received:
    754
    Reputations:
    397
    Прочитал статью - написал такую же. Твою статью прочитал Вася - написал такую же. Маша прочитала статью Васи - написала подобную. Так развивается кодинг на античате.
     
  14. hackconnect

    hackconnect Elder - Старейшина

    Joined:
    8 Mar 2007
    Messages:
    49
    Likes Received:
    5
    Reputations:
    0
    Немножко дополнительной информации о практических примерах:
    - Классная, но довольно старая для применения сейчас, статья xCrZx "Эксплойтинг Win32".
    - Доки на milw0rm.com/papers
    - Еще я как то переводил для первого номера HC.Ezine один из паперов про эксплуатирование формат стринг баги.

    >>"Исскуство экплойта" или "The Art Of Exploitation"
    <<"Искусство эксплуатирования"
    учим русский и английский.
     
    #14 hackconnect, 15 Sep 2007
    Last edited: 15 Sep 2007
  15. nc.STRIEM

    nc.STRIEM Members of Antichat

    Joined:
    5 Apr 2006
    Messages:
    1,036
    Likes Received:
    347
    Reputations:
    292
    http://oz.by/books/more.phtml?id=1017728&partner=myminsk
     
  16. _Great_

    _Great_ Elder - Старейшина

    Joined:
    27 Dec 2005
    Messages:
    2,032
    Likes Received:
    1,119
    Reputations:
    1,139
    Английский стоит учить переводчикам книги, ибо называется она именно так. В принципе, дословный перевод никто не просит - переводят ведь смысл, а не слова, не так ли?
     
  17. hackconnect

    hackconnect Elder - Старейшина

    Joined:
    8 Mar 2007
    Messages:
    49
    Likes Received:
    5
    Reputations:
    0
    Да по-моему (по тому как я написал) - даже звучит лучше, и вообще, толковее.

    зы
    Искусство сплоита))) мухахаха долбить 1 приложение механически - искусство?)) написать чтоли издателям дибилам?