В этой статье мы ищем (и, что характерно, находим!) критический баг в CoreGraphics в iOS. Сразу скажу, что на полноценную уязвимость этот баг конечно не тянет — его эксплуатация не приводит, например, к arbitrary code execution. Однако этот баг позволяет аварийно завершать приложения которые используют WebKit: Mobile Safari, Google Chrome для iOS, всяческие почтовые клиенты и т.п., что тоже может быть полезно для хакера в некоторых ситуациях. Итак, приступим к поискам.
Поиск бага
Для поиска будем использовать вот такую песочницу:
- iPhone 4
- iOS 7.0.4 с evasi0n jailbreak
- связка LLDB + debugserver в качестве отладчика
Баг будем искать в WebKit ну или в системных библиотеках которые WebKit использует. Не мудрствуя лукаво, пойдем по хорошо известному пути:
- Найти какой-нибудь древний мультимедийный формат, который в настоящее время уже мало используется, но все еще поддерживается WebKit.
- Написать fuzzer для этого формата, запустить на каком-то приложении которое использует WebKit (например на Mobile Safari) и уйти пить чай.
- …
- Profit?!.. Если нет — вернуться к пункту 1.
Начнем с первого пункта. Порывшись в Wikipedia и посмотрев какие форматы изображений поддерживает WebKit, обратим внимание на XBM. Это текстовый формат для черно-белых изображений, древний как говно мамонта, однако WebKit его до сих пор поддерживает. Поскольку XBM уже много лет никем не используется в web, разработчики WebKit скорее всего давно забили на тестирование и “вылизывание” соответствующего кода в движке. А значит можно поискать в этом коде какую-то старую-престарую ошибку.
Хорошо, с форматом определились. Перейдем ко второму пункту нашего плана по поиску бага. Почитаем описание формата XBM, потом найдем в сети какой-то .xbm
файл и попытаемся его “испортить” так что бы он вызывал ошибку в WebKit или в какой-нибудь системной библиотеке которую WebKit использует. После недолгих поисков мне попался вот такой файл:
#define test_width 16 #define test_height 16 static unsigned char test_bits[] = { 0xff, 0xff, 0x01, 0x80, 0xfd, 0xbf, 0x05, 0xa0, 0xf5, 0xaf, 0x15, 0xa8, 0xd5, 0xab, 0x55, 0xaa, 0x55, 0xaa, 0xd5, 0xab, 0x15, 0xa8, 0xf5, 0xaf, 0x05, 0xa0, 0xfd, 0xbf, 0x01, 0x80, 0xff, 0xff };
Если мы откроем этот файл в Mobile Safari, то увидим небольшую (16 на 16 пикселей) картинку из концентрических квадратов:
“Портить” этот файл конечно лучше специально написанным для этой цели fuzzer’ом, но fuzzer писать лень, так что мы для начала попробуем “испортить” файл по-старинке, руками. Поиграемся с test_width
и test_height
— вдруг WebKit при рендере картинки не проверяет эти значения и у нас получится что-то где-то переполнить? Попытки присвоить нулевые или отрицательные значения test_width
и test_height
к сожалению ни к чему не приводят. Однако очень скоро мы выясняем что при больших значениях test_width
Mobile Safari завершается аварийно! Например при попытке открыть вот такой файл
#define test_width 123456 #define test_height 16 static unsigned char test_bits[] = { 0xff, 0xff, 0x01, 0x80, 0xfd, 0xbf, 0x05, 0xa0, 0xf5, 0xaf, 0x15, 0xa8, 0xd5, 0xab, 0x55, 0xaa, 0x55, 0xaa, 0xd5, 0xab, 0x15, 0xa8, 0xf5, 0xaf, 0x05, 0xa0, 0xfd, 0xbf, 0x01, 0x80, 0xff, 0xff };
Mobile Safari просто закрывается безо всяких сообщений. Точно также себя ведет и Google Chrome для iOS. Учитывая что оба браузера используют WebKit, похоже что мы нашли баг либо в самом WebKit либо в какой-то системной библиотеке которую WebKit использует для отрисовки изображений.
Анализ бага
Так где же именно живет наш баг и как он устроен? Почему падают приложения? Откроем наш “испорченный” файл в Mobile Safari под отладчиком и посмотрим backtrace:
Присмотримся к функции argb32_image_mark
в CoreGraphics
повнимательнее, ведь судя по backtrace именно она вызывает memset
который валит приложение. Снова запустим Mobile Safari под отладчиком и проследим что происходит в функции argb32_image_mark
если загрузить браузером .xbm
файл с шириной изображения 123456
. А происходит следующее (прошу принять во внимание что код, не имеющий отношения к багу, пропущен, а адреса отличаются от тех что на скриншоте backtrace из-за ASLR):
CoreGraphics`argb32_image_mark (at 0x2f96a970): ... 0x2f96a97a: mov r6, sp ; r6 = sp 0x2f96a97c: mov r5, r0 ; r5 = первый аргумент argb32_image_mark ... 0x2f96a9a0: ldr r0, [r5, #4] ; r0 = [первый аргумент + 4] = ширина изображения ... 0x2f96a9b0: str r0, [r6, #100] ; сохраняем ширину изображения в локальную переменную ... 0x2f96a9d2: ldr r3, [r1, #12] ; r3 = [второй аргумент + 12] ... 0x2f96a9ea: ldr r1, [r6, #100] ; достаем ширину изображения из локальной переменной в r1 ... 0x2f96a9f6: adds r0, r3, #6 ; r0 = r3 + 6 0x2f96a9f8: muls r0, r1, r0 ; r0 = r1*r0 0x2f96a9fa: add.w r2, r0, #96 ; r2 = r0 + 96 ... 0x2f96aa04: adds r0, r2, #3 ; r0 = r2 + 3 0x2f96aa06: bic r0, r0, #3 ; r0 = r0 & 0xfffffff8 0x2f96aa0a: sub.w r11, sp, r0 ; r11 = sp - r0 0x2f96aa0e: mov sp, r11 ; sp = r11
После выполнения этих инструкций, новое значение sp
устанавливается в
sp = sp - (([второй аргумент + 12] + 6) * ширина изображения + 99) & 0xfffffff8
Однако какие-бы изображения я не открывал, [второй аргумент + 12]
был всегда нулевой. Учитывая этот факт, можем считать что
sp = sp - (6 * ширина изображения + 99) & 0xfffffff8
Функция argb32_image_mark
плохо контролирует параметр ширина изображения
и если ширина оказывается слишком большой, sp
“уезжает” далеко за границы выделенного стека. Затем немедленно следует вызов memset
и попытка забить нулями память далеко за стеком приводит к краху приложения:
0x2f96aa10: mov r0, r11 ; новое значение sp - это адрес для memset 0x2f96aa12: movs r1, #0 ; обнуление памяти начиная с этого адреса 0x2f96aa14: blx 0x2fa339cc ; вызов memset ...
Собственно это и есть критический баг в CoreGraphics о котором шла речь в заголовке статьи.
Где это работает?
У меня баг воспроизводится на Mobile Safari и Google Chrome для iOS на
- iPhone 4 с iOS 7.0.4
- iPhone 5 с iOS 7.0.6
Другие девайсы/версии iOS не пробовал, поскольку у меня их нету. Если кто желает попробовать повалить Safari на своем iOS девайсе, вот ссылка:
Выводы
Баг конечно критический, но с точки зрения безопасности не особо страшный для пользователей. Максимум что случится — это приложение, использующее WebKit не сможет прожевать .xbm
картинку и вылетит. Неприятно но не смертельно.
В Apple я сообщил, надеюсь что в следующем обновлении iOS все исправят.
Happy debugging!
Автор: dimakovalenko