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

    В этой статье мы ищем (и, что характерно, находим!) критический баг в 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. Молодцы, чо :)

    Similar posts

    Ads
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More

    Comments 30

      +1
      А для JB такие баги нельзя использовать?
        +1
        К сожалению отрицательные значения ширины отфильтровываются логикой webkit и не доходят до проблемной функции, поэтому манипулируя шириной картинки мы можем двигать стек только в сторону младших адресов. Что делает arbitrary code execution мягко говоря затруднительным. А раз нету arbitrary code execution, то и jailbreak строить вроде как не на чем.
          +1
          А нельзя подобрать достаточно большое число для ширины, чтобы после умножения и переполнения получилось отрицательное?
            +1
            Проблема в том, что

            — ширина в файле указывается в десятичной системе
            — какую бы ширину мы не указали, хоть 1234567890 — все равно «сыграют» только первые шесть цифр, т.е. в проблемную функцию будет передано 123456 в качестве ширины
            — более того, логика webkit отбрасывает значения меньше 86000 и больше 600000

            С такими раскладами даже помноженная на 6 ширина не вылезет в отрицательное значение, увы.
        +1
        А сам получившийся файл не выложите?
        Вообще, способ поиска уязвимости действительно старый, но рабочий.
          +1
          Попробуйте открыть в Mobile Safari ссылку codedigging.com/test.xbm
          Safari по идее должен упасть
            +1
            Я уже допёр, что xbm — текстовый формат и сделал себе такой файлик =)
            Спасибо!
              +1
              Я там давал ссылки в статье на описание формата. Это древний текстовый формат придуманный суровыми линуксоидами для курсоров в X10 :) Придумывалось явно что бы было похоже на C/C++.
                +2
                Я по коду в статье сначала и подумал, что это часть сишного кода для генерации файла. Но потом таки открыл описание формата. )
                  +1
                  Ну а как у вас оно? Сафари рушит? Потому что я попробовал только на своем подопытном айфоне, да на телефоне жены :) И когда постил эти статью — стремался, если честно: а вдруг ни у кого ничего не падает, один я идиот на двух телефонах попробовал и кричу тут про «критический баг» :)
                    +1
                    Ещё как падает =)
                    Кстати, iOS Simulator не падает.
                      +1
                      Ну и отлично, а то я уже начал волноваться :)
                      Ссылку на файл добавил в текст статьи.
                      По поводу iOS Simulator — там скорее всего совершенно другой машинный код в и состояние стека этой функции, так что может и не падать конечно.
                        +1
                        Пресловутое видео, а так же пресловутый арабский текст так же не роняли симулятор. Так что не удивительно. =)
                        +1
                        Простите, а на каком девайсе/версии iOS падает? Хочу добавить эти данные в список в статье.
                          +1
                          iPhone 5 iOS 6.1
              • UFO just landed and posted this here
                  0
                  Могу попробовать пропатчить соответствующую библиотеку, впилить туда проверку параметров — прямо в бинарник, машинным кодом. Я когда-то много лет назад делал такой фокус для WinRar (http://www.xakep.ru/magazine/xs/045/068/4.asp), для либы под iOS такое тоже можно сделать. Только ведь это счастье можно будет применить только на jailbreakнутых девайсах, которых от общего числа очень небольшой процент согласитесь ) Т.е. в глобальном плане самодельный патч все равно мало что изменит. Так что остается ждать обновления от Apple )
                  +1
                  iPad mini Retina — падает :)
                    0
                    Спасибо за тест, добавлю в список в статье :)
                    0
                    iPhone 5 (iOS 7.0.6) — Safari упал
                      0
                      Спасибо, добавил в список
                  +1
                  Какая это по счёту уязвимость на этой неделе?
                    +3
                    Да это и не уязвимость в общем. Так, баловство :)
                    +1
                    Ipad air. iOS 7.0.4 и 7.0.6. Баг присутствует.
                      0
                      Спасибо, добавил в список в статье
                      +1
                      iPad mini Retina iOS 7.0.6 — падает
                        0
                        Спасибо, добавил в список в стаье )
                        +1
                        Думаю, все девайсы сейчас подвержены этой ошибке. Если интересно, на 5s с последней 7.1 тоже падает.
                          0
                          Думаю вы правы, тем более что сам метод поиска багов, озвученный в начале статьи, предполагает нахождение какого-нибудь древнего бага, который тянется ещё былинных времен :) Спасибо за тест, добавлю ваш девайс/ОС в список.
                            +1
                            Спасибо за тест

                            А вам за статью!

                        Only users with full accounts can post comments. Log in, please.