
In this article we are looking for (and, characteristically, we find!) A critical bug in CoreGraphics in iOS. I’ll say right away that this bug certainly doesn’t pull into full-fledged vulnerability - its operation does not lead, for example, to arbitrary code execution. However, this bug allows you to crash applications that use WebKit: Mobile Safari, Google Chrome for iOS, all mail clients, etc., which can also be useful for a hacker in some situations. So, let's start the search.
Find a bug
For the search we will use this sandbox:
- iPhone 4
- iOS 7.0.4 with evasi0n jailbreak
- a bunch of LLDB + debugserver as a debugger
We will search for a bug in WebKit or in the system libraries that WebKit uses. Without further ado, let's follow a well-known path:
')
- Find some ancient multimedia format that is currently in very little use, but still supported by WebKit.
- Write a fuzzer for this format, run on some application that uses WebKit (for example, Mobile Safari) and leave to drink tea.
- ...
- Profit?! .. If not, go back to step 1.
Let's start with the first item. Having rummaged in Wikipedia and having looked at what formats of images supports WebKit, we will pay attention to
XBM . This is a
text format for black and white images, as old as a mammoth shit, but WebKit still supports it. Since XBM has not been used by anyone in the web for many years, the WebKit developers most likely have long since scored for testing and licking the corresponding code in the engine. So you can search for in this code some old-stop error.
Well, with the format decided. We turn to the second paragraph of our plan to search for a bug. Read the
description of the XBM format , then find some
.xbm
file on the network and try to “spoil” it so that it would cause an error in WebKit or in some system library that WebKit uses. After a brief search, I got this file:
#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};
If we open this file in Mobile Safari, we will see a small (16 by 16 pixels) image from concentric squares:

To “spoil” this file is of course better than a fuzzer specially written for this purpose, but fuzzer is too lazy to write, so we will first try to “spoil” the file in the old-fashioned way, with our hands. Let's play with
test_width
and
test_height
- all of a sudden, when rendering a picture, WebKit doesn’t check these values and do we have something to fill up somewhere? Attempts to assign zero or negative values of
test_width
and
test_height
unfortunately do not lead to anything. However, very soon we find out that for large values,
test_width
Mobile Safari crashes! For example, when trying to open such a file
#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 just closes without any messages. Google Chrome for iOS behaves the same way. Considering that both browsers use WebKit, it looks like we found a bug either in WebKit itself or in some system library that WebKit uses to render images.
Bug analysis
So where exactly does our bug live and how does it work? Why applications fall? Open our “spoiled” file in Mobile Safari under the debugger and see the backtrace:

Let's take a closer look at the
argb32_image_mark
function in
CoreGraphics
more closely, because judging by the backtrace, it is the one that causes the
memset
that is dropping the application. Run Mobile Safari again under the debugger and see what happens in the
argb32_image_mark
function if the browser
.xbm
an
.xbm
file with an image width of
123456
. And the following happens (please take into account that the code that is not related to the bug is omitted, and the addresses differ from those in the backtrace screenshot because of ASLR):
CoreGraphics`argb32_image_mark (at 0x2f96a970):
...
0x2f96a97a: mov r6, sp; r6 = sp
0x2f96a97c: mov r5, r0; r5 = first argument argb32_image_mark
...
0x2f96a9a0: ldr r0, [r5, # 4]; r0 = [first argument + 4] = image width
...
0x2f96a9b0: str r0, [r6, # 100]; save the width of the image to a local variable
...
0x2f96a9d2: ldr r3, [r1, # 12]; r3 = [second argument + 12]
...
0x2f96a9ea: ldr r1, [r6, # 100]; get the width of the image from a local variable in 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
After following these instructions, the new
sp
value is set to
sp = sp - (([second argument + 12] + 6) * image width + 99) & 0xfffffff8
However, whatever image I opened,
[ + 12]
was always zero. Given this fact, we can assume that
sp = sp - (6 * image width + 99) & 0xfffffff8
The function
argb32_image_mark
poorly controls the parameter
and if the width is too large,
sp
“leaves” far beyond the bounds of the selected stack. Then the
memset
call immediately follows and an attempt to fill the memory with zeros far behind the stack leads to the crash of the application:
0x2f96aa10: mov r0, r11; the new sp is the address for memset
0x2f96aa12: movs r1, # 0; memory reset starting from this address
0x2f96aa14: blx 0x2fa339cc; memset call
...
Actually this is a critical bug in CoreGraphics which was discussed in the title of the article.
Where does it work?
I have a bug playing on Mobile Safari and Google Chrome for iOS on
- iPhone 4 with iOS 7.0.4
- iPhone 5 with iOS 7.0.6 (the bug on this device and the iOS version is also confirmed by Yekver , see the list below)
I did not try other devices / iOS versions, since I don’t have them. In the comments and personal messages they also write that the bug is reproduced on
- iPod Touch 4g with iOS 6.1.5 (10b400) - thanks to Templier for the test
- Apple iPad mini with iOS 7.0.6 - thanks to Pavel Akhrameev for the test
- iPad mini Retina with iOS 7.0.4 - thanks to Templier for the test
- iPad mini Retina c iOS 7.0.6 - thanks to maxru for the test
- iPad Air with iOS 7.0.4 - thanks to ryad0m for the test
- iPad Air with iOS 7.0.6 - thanks to ryad0m for the test
- iPhone 5 with iOS 6.1 - thanks to silvansky for the test
- iPhone 5 with iOS 7.0.6 - thanks to Yekver for the test
- iPhone 5s with latest iOS 7.1 - thanks to Anakros for the test
If anyone wants to try to crash Safari on his iOS device, here is the link:
codedigging.com/test.xbmfindings
The bug is of course critical, but from a security point of view, not particularly scary for users. The most that happens is an application that uses WebKit will not be able to chew
.xbm
image and
.xbm
. Unpleasant but not fatal.
I told Apple, I hope that everything will be fixed in the next iOS update.
Happy debugging!
Update Jun 20, 2014: Today I received an email from Apple sec team. They write that the error has been reviewed, CVE will be assigned to it and it will be fixed in the nearest iOS update. "Half a year will not pass ..." (c)
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 randomly crafted XBM file may lead to an
unexpected application termination or arbitrary code execution
Description: An unbounded stack allocation issue
handling of XBM files. This issue was addressed through improved
bounds checking.
CVE-ID
CVE-2014-1354: Dima Kovalenko of codedigging.com
Hmm, well, ok, fixed in iOS 7.1.2. Well done, cho :)