Чтение онлайн

на главную

Жанры

19 смертных грехов, угрожающих безопасности программ

Виега Джон

Шрифт:
...

list<unsigned long> g_TheList;

unsigned long getNextFromList

{

unsigned long ret = 0;

if (!g_TheList.empty)

{

ret = g_TheList.front;

g_TheList.pop_front;

}

return ret;

}

Возможно, вы надеетесь, что шансы на то, что два потока одновременно окажутся в этой функции, малы. Однако немногие приведенные выше предложения на С++ транслируются в тысячи машинных команд. Достаточно, чтобы один поток закончил проверку наличия элементов в списке, перед тем как другой извлечет из списка последний элемент с помощью вызова pop_front. Как говорил Клинт Иствуд в фильме «Грязный Гарри»: «Ну и как ты себя теперь чувствуешь?» Очень похожий код как раз и привел к тому, что провайдер чуть ли не сутки отказывал в обслуживании клиентам.

Другое проявление проблемы – это возникновение гонки при обработке сигналов. Впервые ЭТс1 Э.ТЭ.КЭ. была публично описана

в статье Михала Залевски «Delivering Signals for Fun and Profit: Understanding, Exploiting and Preventing Signal Handling Related Vulnerabilities» («Доставка сигналов для забавы и к собственной выгоде: описание, эксплуатация и предотвращение уязвимостей, связанных с обработкой сигналов»). Ее текст можно найти на странице h.org/files/4/signals.txt. Проблема в том, что многие приложения для UNIX не готовы к ситуациям, встречающимся в многопоточных программах. Ведь даже параллельные приложения, написанные для UNIX и UNIX–подобных систем, обычно порождают новый процесс, а после изменения какой–нибудь глобальной переменной этот процесс получает собственную копию страницы памяти (это гарантируется семантикой копирования при записи). Многие программы далее реализуют обработчики сигналов, причем иногда один обработчик ассоциируется с несколькими сигналами. Приложение спокойно занимается своей работой, как вдруг противник посылает ему пару сигналов, разделенных очень коротким промежутком времени. И вот внезапно ваше приложение становится по сути дела многопоточным! Довольно трудно писать многопоточные программы, даже если знаешь, к чему готовиться; что уж говорить о том, когда такой «подлости» не ожидаешь.

Целый класс ошибок касается взаимодействий с файлами и другими объектами. Способов попасть в беду так много, что даже не перечесть. Вот лишь несколько примеров. Ваша программа должна создать временный файл, поэтому она сначала проверяет, нет ли уже файла с тем же именем, и если нет, то создает его.

Обычное дело, не правда ли? Так–то оно так, но этой сценарий открывает возможность для атаки – противник угадывает имя временного файла и после запуска вашей программы создает ссылку на что–нибудь важное. Вашей программе не повезло, она открывает ссылку, которая ведет на файл, выбранный противником, после чего некоторые ее действия могут привести к эскалации привилегий. Если вы удалите файл, противник сможет подставить вместо него вредоносную программу. Если вы затрете файл, то в дальнейшем это может стать причиной сбоя в какой–то другой программе. Если файл доступен непривилегированным процессам, то вы можете изменить его права и предоставить противнику право записи в какой–то конфиденциальный файл. При наихудшем развитии событий ваша программа может установить для файла режим запуска от имени пользователя root (setuid root), и в результате приложение, выбранное противником, получит административные привилегии.

А, вы работаете на платформе Windows и довольно ухмыляетесь, полагая, что все это к вам не относится? Заблуждение! Вот что может произойти в Windows. При запуске любого сервиса создается именованный канал, по которому диспетчер сервисов (Service Control Manager) посылает сервису управляющие команды. Диспетчер работает от имени System – самой привилегированной учетной записи в системе. Противник определяет, как называется канал, находит сервис, запускаемый от имени обычного пользователя (по умолчанию таких несколько), а затем присоединяется к каналу, притворившись диспетчером сервисов. Для устранения ошибки были предприняты следующие меры: во–первых, имя канала сделано непредсказуемым, что заметно уменьшает шансы противника, а во–вторых, в Windows Server 2003 олицетворение другого пользователя стало привилегией. Если вы думаете, что Windows не поддерживает ссылки, то ошибаетесь; поищите в документации раздел CreateHardLink. Но ссылки на файлы – не единственная возможность. В Windows есть множество других именованных объектов: файлы, каналы, мьютексы, участки разделяемой памяти, рабочие столы и т. д. Любой может стать источником проблем, если программа не ожидает, что он вообще существует.

Греховность кода

Хотя для иллюстрации мы выбрали С, подобный код можно написать на любом языке, поскольку языковой специфики в нем почти нет. Ошибка заложена уже в проекте и связана с непониманием нюансов операционной системы и способа обойти их. Нам неизвестны языки, которые бы существенно затрудняли образование условий для возникновения гонки. Взгляните на этот пример:

...

char* temp;

FILE* pTempFile;

tmp = _tempnam("/tmp", "MyApp");

pTempFile = fopen(tmp, "w+");

Выглядит совершенно безобидно, но противник может предсказать, каким будет следующее имя временного файла. При прогоне на машине автора повторные вызовы порождали файлы с именами MyAppl, МуАрр2, МуАррЗ и т. д. Если эти файлы создаются в области, куда противнику разрешено писать, то он сумеет заранее создать временный файл, возможно, заменив его ссылкой. Если программа создает несколько временных файлов, то задача противника заметно упрощается.

Родственные грехи

Мы рассмотрели несколько взаимосвязанных проблем. Основной грех заключается в неумении писать код,

корректно работающий в условиях одновременного исполнения. С ним также связаны ошибки контроля доступа, описанные в грехе 12, и генерирование недостаточно случайных чисел (см. грех 18). Почти все гонки при работе с временными файлами возникают вследствие ошибок установки прав доступа, усугубленных использованием старых версий операционных систем, в которых нет надлежащим образом защищенных каталогов для хранения временных файлов, создаваемых конкретным пользователем. В большинстве современных операционных систем каждому пользователю выделяется собственное рабочее пространство, а даже если это не так, всегда можно создать такую область внутри начального каталога пользователя.

Недостаточная «случайность» случайных чисел проявляется в ситуации, когда вы хотите создать файл, каталог или другой объект с уникальным именем в общедоступной области. Если применяется генератор псевдослучайных чисел или – того хуже – увеличение некоторого счетчика на единицу, то противник часто может предсказать, каким будет следующее имя, а это первый шаг на пути к хаосу. Отметим, что многие библиотечные функции для создания временных файлов гарантируют лишь уникальность имени, но не его непредсказуемость. Если вы создаете файлы или каталоги в общедоступной области, то для порождения имен применяйте генератор случайных чисел криптографического качества. Один такой способ описан в главе 23 книги Майкл Ховард, Дэвид Лебланк «Защищенный код», 2–ое издание (Русская редакция, 2004). Хотя он предназначен для Windows, но сам подход является переносимым.

Где искать ошибку

Гонки чаще всего возникают при следующих условиях:

□ Несколько потоков или процессов должны осуществлять запись в один и тот же ресурс. Ресурсом может быть разделяемая память, файловая система (например, когда несколько Web–приложений манипулируют файлами в общем каталоге), другие хранилища данных, как, скажем, реестр Windows, и даже база данных. Это может быть даже одна разделяемая переменная!

□ Файлы или каталоги создаются в общедоступных областях, например в каталоге для временных файлов (/tmp или /usr/tmp в UNIX–подобных системах).

□ В обработчиках сигналов.

□ В нереентерабельных функциях в многопоточном приложении или вызываемых из обработчика сигнала. Отметим, что в системах Windows сигналы практически бесполезны и этой проблеме не подвержены.

Выявление ошибки на этапе анализа кода

Внимательно присмотритесь как к собственным, так и к библиотечным функциям, которыми вы пользуетесь. Нереентерабельным является код, манипулирующий переменными, объявленными вне локальной области видимости, например глобальными или статическими. Любая функция, в которой используется статическая переменная, реентерабельной не является. Хотя применение глобальных переменных вообще осуждается, поскольку усложняет сопровождение программы, но сами по себе они еще не создают гонок. Следующее, на что надо обратить внимание, – это бесконтрольное изменение таких переменных. Например, статический член класса в С++ разделяется всеми экземплярами класса и, следовательно, оказывается глобальной переменной. Если этот член инициализируется в момент первого обращения к классу, а затем только читается, то ничего страшного не случится. Если же некоторая переменная обновляется, то необходимо ставить замки, чтобы обновление не осуществлялось одновременно разными частями программы. Особое внимание следует обращать на обработчики сигналов, поскольку они могут оказаться нереентерабельными, даже если для остальной программы эта проблема не актуальна.

Внимательно изучите код обработчиков сигналов, в частности те данные, которыми они манипулируют.

Следующий случай – это внешние процессы, которые могут прерывать вашу программу. Тщательно изучите места, где файлы или каталоги создаются в общедоступных областях, обращайте внимание на предсказуемость имен.

Найдите все случаи создания файлов (к примеру, временных) в разделяемых каталогах (/tmp или /usr/tmp в UNIX, \Windows\temp в Windows). При создании такого файла библиотечной функцией ореп надо указывать флаг 0_EXCL (или его эквивалент), а если он создается функцией CreateFile -флаг CREATE_NEW. В этом случае вызов завершится неудачно, если файл с указанным именем уже существует. Поместите обращения к этим функциям в цикл, который будет пробовать новые случайные имена, пока не создаст файл. Если имя выбирается действительно случайно (следите, чтобы имя файла содержало только допустимые в вашей системе символы), то почти наверняка потребуется только одна итерация. К сожалению, функция fopen из стандартной библиотеки С не позволяет указать флаг 0_EXCL, поэтому применяйте функцию ореп с последующим преобразованием возвращенного дескриптора в указатель FILE*. В операционных системах Microsoft системные вызовы типа CreateFile не только более гибки, но и работают быстрее. Никогда не полагайтесь на функции, подобные mktemp(3), поскольку они создают предсказуемые имена файлов; противник может создать файл точно с таким же именем. В командных интерпретаторах UNIX нет встроенных операций для создания имен временных файлов, а конструкции типа Is > / tmp/ list. $ $ способствуют возникновению гонки. Поэтому в shell–сценариях нужно пользоваться функцией mktemp(l).

Поделиться:
Популярные книги

Возвращение Безумного Бога 5

Тесленок Кирилл Геннадьевич
5. Возвращение Безумного Бога
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Возвращение Безумного Бога 5

Холодный ветер перемен

Иванов Дмитрий
7. Девяностые
Фантастика:
попаданцы
альтернативная история
6.80
рейтинг книги
Холодный ветер перемен

Кровь, золото и помидоры

Распопов Дмитрий Викторович
4. Венецианский купец
Фантастика:
альтернативная история
5.40
рейтинг книги
Кровь, золото и помидоры

Наследник павшего дома. Том I

Вайс Александр
1. Расколотый мир
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Наследник павшего дома. Том I

Книга 5. Империя на марше

Тамбовский Сергей
5. Империя у края
Фантастика:
альтернативная история
5.00
рейтинг книги
Книга 5. Империя на марше

Генерал Скала и сиротка

Суббота Светлана
1. Генерал Скала и Лидия
Любовные романы:
любовно-фантастические романы
6.40
рейтинг книги
Генерал Скала и сиротка

Мимик нового Мира 14

Северный Лис
13. Мимик!
Фантастика:
юмористическое фэнтези
постапокалипсис
рпг
5.00
рейтинг книги
Мимик нового Мира 14

Стрелок

Астахов Евгений Евгеньевич
5. Сопряжение
Фантастика:
боевая фантастика
постапокалипсис
рпг
5.00
рейтинг книги
Стрелок

Безумный Макс. Поручик Империи

Ланцов Михаил Алексеевич
1. Безумный Макс
Фантастика:
героическая фантастика
альтернативная история
7.64
рейтинг книги
Безумный Макс. Поручик Империи

Сержант. Назад в СССР. Книга 4

Гаусс Максим
4. Второй шанс
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Сержант. Назад в СССР. Книга 4

Третий. Том 2

INDIGO
2. Отпуск
Фантастика:
космическая фантастика
попаданцы
5.00
рейтинг книги
Третий. Том 2

Боксер 2: назад в СССР

Гуров Валерий Александрович
2. Боксер
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Боксер 2: назад в СССР

Вопреки судьбе, или В другой мир за счастьем

Цвик Катерина Александровна
Любовные романы:
любовно-фантастические романы
6.46
рейтинг книги
Вопреки судьбе, или В другой мир за счастьем

Кротовский, сколько можно?

Парсиев Дмитрий
5. РОС: Изнанка Империи
Фантастика:
попаданцы
аниме
5.00
рейтинг книги
Кротовский, сколько можно?