В начале октября появилась новость о том, что найдена критическая уязвимость в библиотеке libutils. Библиотека libutils входит в состав Android OS, в ней содержится реализация различных примитивов, которыми могут пользоваться другие приложения. Минимальные последствия, к которым могла бы привести эксплуатация этой уязвимости, — падение ПО, использующего библиотеку stagefright и обрабатывающего MP3/MP4-файлы.
Хотя эксплойты к обнаруженным уязвимостям в «дикой природе» появляются не сразу, мы считаем необходимым даже в отсутствие информации об обнаружении эксплойтов быть готовыми к детектированию. Чтобы создать защиту, необходимо иметь в руках рабочий эксплойт, поэтому, как правило, для для реализации детектирования используются PoC-файлы.
В данном случае создание детектирующей логики для возможных эксплойтов к уязвимости осложнял тот факт, что в открытом доступе PoC-файлов не было. В связи с этим мы решили сами произвести исследование и сгенерировать PoC-файл.
Рассказывая о нашей работе, мы опустим некоторые технические подробности, во избежание использования этой информации злоумышленниками.
Для начала мы посмотрели, какие изменения вносились в исходный код libutils для того, чтобы исправить анонсированную уязвимость. Как выяснилось, одним из последних изменений был следующий код:
Проверка входного параметра в функции allocFromUTF8 класса String8
По коду видно, что если параметр len = SIZE_MAX, то при выделении памяти произойдет integer overflow.
Мы предположили, что для того, чтобы добиться некорректной работы ПО, обрабатывающего MP3-файлы, надо сделать следующее: передать в функцию allocFromUTF8 вторым параметром число, равное значению SIZE_MAX. Данная функция вызывается из нескольких мест класса String8. Если проанализировать реализацию объекта String8, то можно понять, что интересующая нас функция вызывается в следующих местах:
- в конструкторе класса String8 (возможны две реализации);
- в методе setTo (возможны две реализации).
Еще стоит отметить тот факт, что для одной из реализаций конструктора и для одной из реализаций метода setTo на вход передается параметр, который потом передается allocFromUTF8. Из этого делаем еще один вывод: нас интересует код, который создает объект String8 и явно в конструкторе класса передает размер строки или вызов метода setTo (с указанием размера).
Исходя из информации, которую мы знаем, уязвимоcть эксплуатируется при обработке MP3-файлов, следовательно, надо изучить использование класса String8 в коде, который отвечает за обработку MP3-файлов. Данный код легко найти в следующей ветке — \media\libstagefright\MP3Extractor.cpp.
Использование класса String8 в коде MP3Extractor.cpp
Одно из первых использований данного класса можно заметить при обработке тега COMM MP3-файла (данный тег хранит информацию о комментариях к MP3-файлу):
Считывание комментариев из MP3-файла с использованием уязвимого класса String8
Как видно по коду для зачитывания строк используется еще один класс ID3 (нас интересует метод getString), который отвечает за обработку ID3 данных.
Перед тем, как перейти к рассмотрению кода данного компонента, рассмотрим структуру тега COMM (обратимся к официальной документации — http://id3.org/d3v2.3.0).
Пример тега COMM из обычного MP3-файла
В соответствие с документацией имеем:
COMM – Frame ID
00 00 00 04 – size
00 00 – flags
00 – text encoding
00 00 00 – Language
00 – null terminated short description
74 65 73 74 (test) – actual text
Рассмотрим код обработки парсера ID3:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
void ID3::Iterator::getString(String8 *id, String8 *comment) const { getstring(id, false); // обработка короткого описания if (comment != NULL) { getstring(comment, true); } } void ID3::Iterator::getstring(String8 *id, bool otherdata) const { id->setTo(""); const uint8_t *frameData = mFrameData; if (frameData == NULL) { return; } uint8_t encoding = *frameData; if (mParent.mVersion == ID3_V1 || mParent.mVersion == ID3_V1_1) { ..... } size_t n = mFrameSize - getHeaderLength() - 1; // ошибка, возможно переполнение !!! if (otherdata) { // skip past the encoding, language, and the 0 separator frameData += 4; int32_t i = n - 4; while(--i >= 0 && *++frameData != 0) ; int skipped = (frameData - mFrameData); if (skipped >= (int)n) { return; } n -= skipped; } if (encoding == 0x00) { // ISO 8859-1 convertISO8859ToString8(frameData + 1, n, id); } else if (encoding == 0x03) { // UTF-8 id->setTo((const char *)(frameData + 1), n); } else if (encoding == 0x02) |
Как видно по коду, при определенных условиях мы можем вызвать метод setTo класса String8, который в свою очередь позовет allocFromUTF8 с заранее высчитанным n.
Осталось понять, можем ли мы влиять на n. И сделать так, чтобы в результате вычислений в n записалось -1 (0xFFFFFFFF).
Размер заголовка зависит от версии ID3 формата.
Осталось разобраться с mFrameSize. Код, который производит подсчет данного параметра, достаточно большой. Опытным путем было установлено, что значение переменной mFrameSize при обработке файла также зависит от тега COMM и верси обрабатываемого файла.
Из этого следует, что мы можем влиять на две переменные из выражения:
size_t n = mFrameSize — getHeaderLength() – 1
Изменяя данные в теге COMM, мы влияем на mFrameSize. Используя простую математику, мы можем добиться выполнения следующего выражения:
mFrameSize — getHeaderLength() – 1 = -1
В результате выполнения в переменную n запишется следующее значение -1 (0xFFFFFFFF).
Теперь нам осталось только передать это значение в функцию setTo. По коду видно, что данный метод позовется при определенных значениях поля encoding в заголовке тега COMM.
Вызов метода setTo с передачей размера данных
При соблюдении этих условий, мы получим MP3-файл с некорректным тегом COMM, в результате обработки которого мы сможем наблюдать падение стандартного бразуера и музыкального проигрывателя:
Стэк трейс падения при обработке MP3-файла с некорректным тегом COMM
Таким образом, мы получили PoC эксплойта к уязвимости.
Продукты «Лаборатории Касперского» детектируют данный эксплойт как HEUR:Exploit.AndroidOS.Stagefright.b
По следам stagefright 2