Pull to refresh

Критический баг в CoreGraphics в iOS

Reading time 5 min
Views 10K
В этой статье мы ищем (и, что характерно, находим!) критический баг в CoreGraphics в iOS. Сразу скажу, что на полноценную уязвимость этот баг конечно не тянет — его эксплуатация не приводит, например, к arbitrary code execution. Однако этот баг позволяет аварийно завершать приложения которые используют WebKit: Mobile Safari, Google Chrome для iOS, всяческие почтовые клиенты и т.п., что тоже может быть полезно для хакера в некоторых ситуациях. Итак, приступим к поискам.

Поиск бага


Для поиска будем использовать вот такую песочницу:

  • iPhone 4
  • iOS 7.0.4 с evasi0n jailbreak
  • связка LLDB + debugserver в качестве отладчика

Баг будем искать в WebKit ну или в системных библиотеках которые WebKit использует. Не мудрствуя лукаво, пойдем по хорошо известному пути:

  1. Найти какой-нибудь древний мультимедийный формат, который в настоящее время уже мало используется, но все еще поддерживается WebKit.
  2. Написать fuzzer для этого формата, запустить на каком-то приложении которое использует WebKit (например на Mobile Safari) и уйти пить чай.
  3. 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 также подтвержден 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. Молодцы, чо :)
Tags:
Hubs:
+20
Comments 30
Comments Comments 30

Articles