Chapter 8. The Ringtone Massacre

Note

Saturday, March 21, 2009

Dear Diary,

Last week a good friend of mine loaned me his jailbroken,[82] first-generation iPhone. I was very excited. Ever since Apple announced the iPhone, I had wanted to see if I could find a bug in the device, but until last week I had never had access to one.

I finally had an iPhone to play with, and I wanted to search for bugs. But where to start? The first thing I did was make a list of installed applications and libraries that seemed most likely to have bugs. The MobileSafari browser, the MobileMail app, and the audio libraries were at the top of the list. I decided that the audio libraries were the most promising targets since these libraries do a lot of parsing and are heavily used on the phone, so I tried my luck on them.

I performed the following steps when searching the iPhone audio libraries for a bug:

The iPhone, with its iPod-based roots, is a powerful audio-capable device. Three frameworks available on the phone provide different levels of sound functionality: the Core Audio,[84] Celestial, and Audio Toolbox[85] frameworks. In addition, the iPhone runs an audio daemon called mediaserverd, which aggregates the sound output of all applications and governs events such as volume and ringer-switch changes.

The iPhone’s audio system with all its different frameworks seemed a bit complicated, so I decided to start by building a simple fuzzer to search for obvious bugs. The fuzzer that I built does the following:

I created the following simple, mutation-based file fuzzer to prepare the test cases on a Linux host:

The fuzzer from Example 8-1 takes four arguments: the size of the sample target file, the file offset to manipulate, a 1-byte value that gets written to the given file offset, and the name of the target file. After writing the fuzzer, I compiled it:

linux$ gcc -o fuzz fuzz.c

I then began fuzzing files of the Advanced Audio Coding[86] (AAC) format, which is the default audio format used on the iPhone. I chose the standard iPhone ringtone, named Alarm.m4r, as a sample target file:

linux$ cp Alarm.m4r testcase.m4r

I typed the following line into the terminal to get the size of the test-case file:

linux$ du -b testcase.m4r
415959  testcase.m4r

The command-line options below instruct the fuzzer to replace the byte at file offset 4 with 0xff (decimal 255):

linux$ ./fuzz 415959 4 255 testcase.m4r
[+] file offset: 0x00000004 (value: 0x000000ff)

I then verified the result with the help of xxd:

linux$ xxd Alarm.m4r | head −1
0000000: 0000 0020 6674 7970 4d34 4120 0000 0000  ... ftypM4A ....

linux$ xxd testcase.m4r | head −1
0000000: 0000 0020 ff74 7970 4d34 4120 0000 0000  ... .typM4A ....

The output shows that file offset 4 (file offsets are counted starting with 0) was replaced with the expected value (0xff). Next, I created a bash script to automate the file mutation:

This script, which is just a wrapper for the fuzzer illustrated in Example 8-1, automatically creates four test cases of the target file Alarm.m4r (see line 20). Starting at file offset 0 (see line 7), the first 4 bytes of the target file (see line 10) are each replaced with a 0xff (see line 13). When executed, the script produced the following output:

linux$ ./go.sh
[+] file offset: 0x00000000 (value: 0x000000ff)
[+] file offset: 0x00000001 (value: 0x000000ff)
[+] file offset: 0x00000002 (value: 0x000000ff)
[+] file offset: 0x00000003 (value: 0x000000ff)

I then verified the created test cases:

linux$ xxd file0.m4a | head −1
0000000: ff00 0020 6674 7970 4d34 4120 0000 0000  ... ftypM4A ....

linux$ xxd file1.m4a | head −1
0000000: 00ff 0020 6674 7970 4d34 4120 0000 0000  ... ftypM4A ....

linux$ xxd file2.m4a | head −1
0000000: 0000 ff20 6674 7970 4d34 4120 0000 0000  ... ftypM4A ....

linux$ xxd file3.m4a | head −1
0000000: 0000 00ff 6674 7970 4d34 4120 0000 0000  ....ftypM4A ....

As the output shows, the fuzzer worked as expected and modified the appropriate byte in each test-case file. One important fact I haven’t mentioned yet is that the script in Example 8-2 changes the file extension of the alarm ringtone from .m4r to .m4a (see line 20). This is necessary because MobileSafari doesn’t support the .m4r file extension used by iPhone ringtones.

I copied the modified and unmodified alarm ringtone files into the web root directory of the Apache webserver that I had installed on the Linux host. I changed the file extension of the alarm ringtone from .m4r to .m4a and pointed MobileSafari to the URL of the unmodified ringtone.

As illustrated in Figure 8-1, the unmodified target file Alarm.m4a successfully played on the phone in MobileSafari. I then pointed the browser to the URL of the first modified test-case file, named file0.m4a.

Figure 8-2 shows that MobileSafari opens the modified file but isn’t able to parse it correctly.

So what had I achieved so far? I was able to prepare audio-file test cases via mutation, launch MobileSafari, and instruct it to load the test cases. At this point, I wanted to find a way to automatically open the test-case files in MobileSafari one by one while monitoring mediaserverd for faults. I created this small Bash script to do the job on the phone:

The Bash script illustrated in Example 8-3 works this way:

After I implemented this little script, I created 1,000 mutations of the Alarm.m4r ringtone starting at file offset 0, copied them to the web root directory of the web server, and started the audiofuzzer.sh script on the iPhone. From time to time the phone crashed due to memory leaks. Every time that happened, I had to reboot the phone, extract the filename of the last processed test case from the access logfile of the web server, adjust line 18 of Example 8-3, and continue fuzzing. Fuzzing the iPhone can be such a pain . . . but it was worth it! In addition to the memory leaks that froze the phone, I also found a bunch of crashes due to memory corruption.