![](https://habrastorage.org/getpro/habr/post_images/f91/7eb/788/f917eb7887efdf4ac6c14802f1a9e8ad.png)
Поиск бага
Для поиска будем использовать вот такую песочницу:
- 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 пикселей) картинку из концентрических квадратов:
![](https://habrastorage.org/getpro/habr/post_images/efe/f91/78b/efef9178bc59bf4f3f471db79d67be51.png)
“Портить” этот файл конечно лучше специально написанным для этой цели 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:
![](https://habrastorage.org/getpro/habr/post_images/f31/020/a13/f31020a13c900df11b703775d4c5d0d6.png)
Присмотримся к функции
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 также подтвержден Yekver, см. список ниже)
Другие девайсы/версии iOS не пробовал, поскольку у меня их нету. В комментариях и личных сообщениях также пишут что баг воспроизводится на
- iPod Touch 4g с iOS 6.1.5 (10b400) — спасибо Templier за тест
- Apple iPad mini с iOS 7.0.6 — спасибо Павлу Ахрамееву за тест
- iPad mini Retina с iOS 7.0.4 — спасибо Templier за тест
- iPad mini Retina c iOS 7.0.6 — спасибо maxru за тест
- iPad Air с iOS 7.0.4 — спасибо ryad0m за тест
- iPad Air с iOS 7.0.6 — спасибо ryad0m за тест
- iPhone 5 с iOS 6.1 — спасибо silvansky за тест
- iPhone 5 с iOS 7.0.6 — спасибо Yekver за тест
- iPhone 5s с последней iOS 7.1 — спасибо Anakros за тест
Если кто желает попробовать повалить Safari на своем iOS девайсе, вот ссылка:
codedigging.com/test.xbm
Выводы
Баг конечно критический, но с точки зрения безопасности не особо страшный для пользователей. Максимум что случится — это приложение, использующее WebKit не сможет прожевать
.xbm
картинку и вылетит. Неприятно но не смертельно.В Apple я сообщил, надеюсь что в следующем обновлении iOS все исправят.
Happy debugging!
Update Jun 20, 2014: Сегодня пришло письмо от Apple sec team. Пишут что ошибка рассмотрена, ей будет присвоен CVE и она будет исправлена в ближайшем апдейте iOS. «Не пройдет и пол года...» (с)
Update Jun 30, 2014
APPLE-SA-2014-06-30-3 iOS 7.1.2 iOS 7.1.2 is now available and addresses the following: ... CoreGraphics Available for: iPhone 4 and later, iPod touch (5th generation) and later, iPad 2 and later Impact: Viewing a maliciously crafted XBM file may lead to an unexpected application termination or arbitrary code execution Description: An unbounded stack allocation issue existed in the handling of XBM files. This issue was addressed through improved bounds checking. CVE-ID CVE-2014-1354 : Dima Kovalenko of codedigging.com
Хм, ну ок, исправили в iOS 7.1.2. Молодцы, чо :)