Модераторы: Daevaorn
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Безопасность, безопасность! А вы её тестируете? Потенциальная уязвимость TOR 
:(
    Опции темы
Thunderbolt
Дата 7.11.2012, 10:57 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


DevRel
*


Профиль
Группа: Участник
Сообщений: 122
Регистрация: 7.11.2007
Где: Тула

Репутация: 10
Всего: 16



В коде программ нет мест, где нельзя допустить ошибку. Ошибка может быть в самом простом месте. Если алгоритмы, механизмы обмена данными и интерфейсы люди привыкли тестировать, то с безопасностью всё обстоит гораздо хуже. Часто она реализуется по остаточному принципу. Программист думает, вот сейчас пару строк напишу, и всё будет хорошо. И даже тестировать не надо. Код слишком прост, чтобы допустить в нем ошибку! А вот и нет. Раз занимаетесь безопасностью и пишите какой-то код для этого, то тестируйте его не менее тщательно!

Где важна безопасность? Во многих приложениях. Но не будем рассуждать абстрактно. Давайте возьмём, например, исходные коды приложения Tor. Это свободное программное обеспечение для реализации второго поколения так называемой "луковой маршрутизации". Это система, позволяющая устанавливать анонимное сетевое соединение, защищённое от прослушивания. Подробнее что это такое и для чего используется, можно узнать, открыв статью в Википедии.

Казалось бы, в таком приложении надо быть максимально внимательным к безопасности данных. И не просто внимательным! Скажем так, такое приложение стоит разрабатывать, только находясь в состоянии паранойи и мании преследования.

И действительно, в программе TOR много чего делается, чтобы скрыть и защитить информацию. Но когда изучаешь код, то становится грустно. Из-за банальных ошибок и опечаток многие механизмы защиты просто непросто бездействуют.

Одним из механизмов страховки является обнуление буферов, которые больше не используются. В таких буферах могут содержаться пароли, ip-адреса и другие пользовательские данные. Если эти данные не уничтожать, то они в виде мусора потом могут быть отправлены в интернет. И это не сказки, а реалии жизни. О том, как такое может произойти, можно почитать в статье "Перезаписывать память - зачем?".

Разработчики TOR знают об этой опасности. И пытаются затирать содержимое буферов, используя функцию memset(). Это Epic Fail. Компилятор вправе убрать из кода вызов функции memset(), если заполняемый ею буфер более нигде не используется.

Рассмотрим фрагмент кода, взятый из TOR:

Код
int
crypto_pk_private_sign_digest(....)
{
  char digest[DIGEST_LEN];
  ....
  memset(digest, 0, sizeof(digest));
  return r;
}


Смотрите. На стеке создается буфер 'digest'. Далее он используется. Не важно, как. Главное что в конце мы хотим его обнулить. Для этого написан вызов функции memset(). Однако, после этого буфер 'digest' более в функции никак не используется. При оптимизации компилятор заметит это и удалит вызов функции. Это не изменит логику работы программы. Но это сделает её опасной с точки зрения приватности данных.

Те, кого интересуют подробности, могут заглянуть сюда. Здесь приведён ассемблерный листинг, где показано как исчезает вызов функции memset(). В качестве компилятора используется Visual C++ 2010 и ключ "/O2".

Чтобы гарантированно затереть память следует использовать такие функции, как RtlSecureZeroMemory(). Они специально созданы для таких случаев и не могут быть удалены компилятором.

Вы скажете, что из мухи я делаю слона. И что никакие важные данные никуда не попадут. Возможно. Но кто даст гарантию? Раз разработчики сделали обнуление массива, то значит, они чего-то опасались. И делали они это не в одном, и не в двух местах. Много где. Жаль только, что усилия часто потрачены впустую. Чтобы не быть голословным, приведу список мест с ошибками.

Список файлов и строк, где анализатор PVS-Studio выдал предупреждение "V597 The compiler could delete the 'memset' function call, which is used to flush '...' buffer. The RtlSecureZeroMemory() function should be used to erase the private data":
  • crypto.c 1015
  • crypto.c 1605
  • crypto.c 2233
  • crypto.c 2323
  • tortls.c 2453
  • connection_or.c 1798
  • connection_or.c 2128
  • onion.c 295
  • onion.c 384
  • onion.c 429
  • rendclient.c 320
  • rendclient.c 321
  • rendclient.c 699
  • rendclient.c 942
  • rendclient.c 1284
  • rendclient.c 1285
  • rendservice.c 705
  • rendservice.c 900
  • rendservice.c 903
  • rendservice.c 904
  • rendservice.c 905
  • rendservice.c 906
  • rendservice.c 1409
  • rendservice.c 1410
  • rendservice.c 1411
  • rendservice.c 1412
  • rendservice.c 1413
  • rendservice.c 1414
  • rendservice.c 1415
  • rendservice.c 2078
  • rendservice.c 2079
  • rendservice.c 2080
  • rendservice.c 2516
  • rendservice.c 2517
  • rendservice.c 2518
  • rendservice.c 2668
  • rendservice.c 2669
  • rendservice.c 2670
  • tor-gencert.c 108

Я сознательно привел такой длинный список. Я хочу, чтобы вы осознали всю глубину проблемы отсутствия проверок кода, который должен позаботиться о безопасности. Кажется, ну как можно ошибиться, используя memset()!? Оказывается, ещё как можно.

И это проблема не только TOR. Это вообще проблема многих приложений и библиотек. Далеко ходить не надо. Какие библиотеки использует TOR? Например, он использует OpenSSL. Это криптографический пакет с открытым исходным кодом для работы с SSL/TLS. Давайте посмотрим, как очищают память разработчики OpenSSL.

Разработчики OpenSSL знают, что нельзя использовать memset() для обнуления буферов памяти. Поэтому, они написали свою функцию. Вот она:

Код
unsigned char cleanse_ctr = 0;
void OPENSSL_cleanse(void *ptr, size_t len)
{
  unsigned char *p = ptr;
  size_t loop = len, ctr = cleanse_ctr;
  while(loop--)
  {
    *(p++) = (unsigned char)ctr;
    ctr += (17 + ((size_t)p & 0xF));
  }
  p=memchr(ptr, (unsigned char)ctr, len);
  if(p)
    ctr += (63 + (size_t)p);
  cleanse_ctr = (unsigned char)ctr;
}


Отличный параноидальный код. К нему претензий нет. Он затрет память. Причем не просто нулями, а случайными числами.

Вот только из-за ошибок, эта функция не спасает, и приватные данные останутся на своём месте. Посмотрим вот на этот код:

Код
void usage(void)
{
  static unsigned char *buf=NULL,*obuf=NULL;
  ....
  OPENSSL_cleanse(buf,sizeof(buf));
  OPENSSL_cleanse(obuf,sizeof(obuf));
  ....  
}


Столько усилий было потрачено для написания функции OPENSSL_cleanse(). И всё мимо.

Присмотритесь. Не замечаете ничего плохого?

Выражения sizeof(buf) и sizeof(obuf) вычисляют размер указателя, а вовсе не размер буфера. В результате, в 32-битной программе в буфере будут затерты только первые 4 байта. Все остальные приватные данные стерты не будут.

Можно найти в OpenSSL и другие идентичные ошибки (см. V597):
  • ec_mult.c 173
  • ec_mult.c 176

Выводы:
  1. Если безопасность данных является важной составляющей в вашем программном обеспечении, то вы должны реализовывать соответствующие тесты. Например, создавая юнит-тесты для функции, вы должны дополнительно проверить, что в стеке не осталась важных данных. Для этого можно вызвать функцию, вначале которой создать массив вида "char buf[10000]" и поискать в нём строки, которые могли остаться в стеке.
  2. Тестируйте не только DEBUG версию, но и RELEASE. Рассмотренные в статье ошибка c memset() в DEBUG версии не проявят себя.
  3. Используйте статические анализаторы кода. Они могут сказать очень много интересного про ошибки и небезопасные места в коде.
  4. Критические с точки зрения приложения лучше делать открытыми. Сейчас я случайно забрел в открытый проект TOR и нашёл ошибки. Эту информацию можно использовать, чтобы сделать продукт лучше. А могу ли я проверить закрытый код? Нет. А значит, подобные ошибки могут оставаться незамеченными разработчиками годами.
  5. Сколько бы у программиста не было опыта и стажа, он не застрахован он простых и глупых ошибок. Помните, что утверждение "профессиональные программисты не допускают простых ошибок и опечаток" это миф. Это не так. Лучше быть самокритичным. Уже одно осознание, что можно допустить ошибку, помогает избежать многих из них. Зная это, вы не поленитесь написать дополнительный тест, запустить анализатор кода и просто перечитать написанный код.




--------------------
Карпов Андрей, DevRel в PVS-Studio.
PM MAIL WWW   Вверх
volatile
Дата 7.11.2012, 18:13 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 2107
Регистрация: 7.1.2011

Репутация: 37
Всего: 85



Thunderbolt, спасибо что продолжаете нас баловать интересными статьями.
Ща посмотрел на RtlSecureZeroMemory
Единственно что она делает такого, чего не делает memset, это приводит указатель к (volatile*)
Так что тем кто не в венде, (да и тем кто в ней) вероятно в таких случаях достаточно объявить (привести) указатель к (volatile type*)
Ибо зачем юзать какой-то blackbox. т.е непонятную "волшебную" функцию, да еще с таки длинным и некрасивым назаванием ? =)
А здесь, стандартные сишные средства, переносимость, кроссплатформенность, и т.д. и т.п.

Вот код мелкомягких:
Код

FORCEINLINE
PVOID
RtlSecureZeroMemory(
    __in_bcount(cnt) PVOID ptr,
    __in SIZE_T cnt
    )
{
    volatile char *vptr = (volatile char *)ptr;

#if defined(_M_AMD64)

        __stosb((PBYTE )((DWORD64)vptr), 0, cnt);

#else

    while (cnt) {
        *vptr = 0;
        vptr++;
        cnt--;
    }

#endif

    return ptr;
}

#endif

PM MAIL   Вверх
Thunderbolt
Дата 7.11.2012, 19:35 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


DevRel
*


Профиль
Группа: Участник
Сообщений: 122
Регистрация: 7.11.2007
Где: Тула

Репутация: 10
Всего: 16



Цитата(volatile @ 7.11.2012,  18:13)
Thunderbolt, спасибо что продолжаете нас баловать интересными статьями.
Ща посмотрел на RtlSecureZeroMemory
Единственно что она делает такого, чего не делает memset, это приводит указатель к (volatile*)
Так что тем кто не в венде, (да и тем кто в ней) вероятно в таких случаях достаточно объявить (привести) указатель к (volatile type*)
Ибо зачем юзать какой-то blackbox. т.е непонятную "волшебную" функцию, да еще с таки длинным и некрасивым назаванием ? =)
А здесь, стандартные сишные средства, переносимость, кроссплатформенность, и т.д. и т.п.

Вот код мелкомягких:
Код

FORCEINLINE
PVOID
RtlSecureZeroMemory(
    __in_bcount(cnt) PVOID ptr,
    __in SIZE_T cnt
    )
{
    volatile char *vptr = (volatile char *)ptr;

#if defined(_M_AMD64)

        __stosb((PBYTE )((DWORD64)vptr), 0, cnt);

#else

    while (cnt) {
        *vptr = 0;
        vptr++;
        cnt--;
    }

#endif

    return ptr;
}

#endif

Я думаю лучше использовать RtlSecureZeroMemory() или иную функцию, которую предоставляют библиотеки для данного компилятора. Они созданы именно для этого. Создав самодельное, легко что-то не учесть. Кстати, вот ещё на эту тему и способах исправления кода: https://www.securecoding.cert.org/confluenc...64F87ACF13E0DE5

--------------------
Карпов Андрей, DevRel в PVS-Studio.
PM MAIL WWW   Вверх
Alca
Дата 8.11.2012, 15:15 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 3993
Регистрация: 14.6.2006

Репутация: 7
Всего: 50



Цитата

Посмотрим вот на этот код:

  static unsigned char *buf=NULL,*obuf=NULL;
  ....
  OPENSSL_cleanse(buf,sizeof(buf));
  OPENSSL_cleanse(obuf,sizeof(obuf));


развеселил  smile 
ты бы еще туда strlen втюхал!!


Это сообщение отредактировал(а) Alca - 8.11.2012, 15:19


--------------------
PM WWW ICQ Skype Jabber   Вверх
bsa
Дата 8.11.2012, 15:37 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Модератор
Сообщений: 9185
Регистрация: 6.4.2006
Где: Москва, Россия

Репутация: 63
Всего: 196



Alca, ты к кому обращаешься? К разработчикам OpenSSL или к Thunderbolt. Если второе, то твой пост имеет смысл: "не читал, но осуждаю".
PM   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Правила форума "С++:Общие вопросы"
Earnest Daevaorn

Добро пожаловать!

  • Черновик стандарта C++ (за октябрь 2005) можно скачать с этого сайта. Прямая ссылка на файл черновика(4.4мб).
  • Черновик стандарта C (за сентябрь 2005) можно скачать с этого сайта. Прямая ссылка на файл черновика (3.4мб).
  • Прежде чем задать вопрос, прочтите это и/или это!
  • Здесь хранится весь мировой запас ссылок на документы, связанные с C++ :)
  • Не брезгуйте пользоваться тегами [code=cpp][/code].
  • Пожалуйста, не просите написать за вас программы в этом разделе - для этого существует "Центр Помощи".
  • C++ FAQ

Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Earnest Daevaorn

 
1 Пользователей читают эту тему (1 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | C/C++: Общие вопросы | Следующая тема »


 




[ Время генерации скрипта: 0.0798 ]   [ Использовано запросов: 23 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.