LORCON helped disprove that idea with Wifi. The USRP+OpenBTS combo is doing the same for GSM based handsets.
The crinkly bits is that to find bugs with OpenBTS, you have to trick a cellphone into connecting your hostile base-station rather than a commercial cellphone tower. This is why I found Charlie Miller's and Collin Mulliner's research interesting: they claim they discovered a way to inject SMS locally for testing that wouldn’t be seen by your provider, making fuzz testing easier. I have seen local SMS injection exploits before but never for the iPhone, so I thought i’d spend a day poking around and see what I could come up with. The rest of the blog post is an accounting of how I spent the time searching for this vuln, how I duplicated a vuln that fits their description, and what to do next.
The first thing I needed was an iPhone. I have one I use everyday, but I'm afraid of bricking it. Instead I dug up an old first-gen iPhone. I assumed that executing the fuzzing code mentioned in the abstract would require jailbreaking the phone since it seems impossible to accomplish that task within the iPhone SDK. I was delightfully surprised to find that using redsn0w made jailbreaking the 3.0 firmware a snap. I installed some basic apps I thought I would need, including the iPhone toolchain (you can compile code directly on the iPhone), ruby, OpenSSH, and the mobile terminal. After looking through the Cydia repository I saw there were some apps that allowed for the sending and receiving of SMS messages. These seemed to be a great place to start. The first example I found is called "aSMS", which has a Google Code project: http://code.google.com/p/iphone-sms/
The aSMS app is a bit odd, the front-end is in the browser, and the backend is a built-in webserver on the iPhone. I spent a few minutes going through the source and found it used what appears to be a baseband debug trick to send messages. The word "baseband" is one way of referring to the separate CPU and operating-system that runs the cellular radio. "Baseband hacking" is were do things like unlocking a mobile phone so it can run on any carrier, and enable features a carrier doesn't want you to use (like tethering or MMS). More specifically the trick uses the device /dev/tty.debug. Googling for "tty.debug" and iPhone led me to another Google Code site and a tool called sendmodem: http://code.google.com/p/iphone-elite/downloads/list
Sendmodem came with a makefile, a .c source file, and a compiled binary. As a side note: this is the best possible situation for a person like myself. If I can’t find the answers I want in the source I can reverse the binary to look at additional items that get added at build time. If that doesn’t yield the answers I am looking for, I can compile my own version and debug it. Something I found funny was the note of the sendmodem wiki that states this code come from the aSMS app I started out with. The wiki also sent me here (http://www.developershome.com/sms/howToSendSMSFromPC.asp) which provided information on how to send a SMS using AT commands and a cellular modem. And finally the wiki provided me a list of undocumented AT commands (http://code.google.com/p/iphone-elite/wiki/UndocumentedATcommands)
With all this information, I started poking around my iPhone. The first thing I wanted to do is see if all the information I had been reading about was still around in the newest v3.0 OS my test phone is running. Nothing would be worse than spending hours on an assumption only to find that the feature you need was removed a few revisions ago. The first thing I tried was using the tty.debug trick to send a text message. I wrote a small ruby script for that:
It worked. I then tried to send a test message to my own number. I figured this could be the simplest way to achieve the functionality for fuzzing. However, this didn't work so well. Every time I sent a text message to the same number it originated from the baseband would disconnect and no longer receive text messages until the device get a reboot.
After a little over two hours into this exercise, I had a lot of information but a lot more epic fail. I'm the king of Thomas Edison's quote of "I have not failed, I've just found 10,000 ways that won't work". Feeling the path I was on was fruitless, I tried another direction: I looked through the filesystem for anything called "sms". Although I got a lot of hits, but the most interesting thing is "sms.db" in "/private/var/mobile/Library/SMS". Using the "file" command I discovered the database is a SQLite3 database. Since that is a fairly well documented database, and there are tons of tools to view the contents, I copied it off the phone and to my MacBook. The used "SQLite Manager", a Firefox plugin which can be found here: https://addons.mozilla.org/en-US/firefox/addon/5817
The sms.db overview and structure as seen in SQLite Manager.
The result of the SQL query "SELECT * FROM message"
After examining the different tables and data it seems that this is where SMS messages are stored to be later retrieved for viewing and such. Using this as a ending point I can work my way backwards to where the messages come from.
Next I enabled syslog debugging, so I can information while sending a SMS message to the device, this should help identify processes that are involved in receiving and processing messages.
The last message in the log reads "CommCenter[30]: removing received message 2147483653".
CommCenter is involved in receiving and processing SMS messages to some degree. Searching the disk for CommCenter gives a lot of results but one catches my eye: /private/var/CommCenter/spool. The word "spool" looks similar to the Unix "mailspool", and is likely the place to store files that are being sent or received by the device. The spool directory has two subdirectories MobileOrginated and MobileTerminated. Both directories were empty, but if the Unix style spool architecture is being used, temp files will be created as messages are sent and received and removed when no longer needed. I wrote a quick and dirty Ruby script that will monitor the directory and copy any files it finds, even if they live for only a second.
Simple Ruby script to check and see if Directory is empty, if not copy the contents to /tmp/
I then run the script and send a SMS to the target phone. I get the expected output that a file has been created and moved to /tmp. The file is named r.sms.2147483652
The contents of the /private/var/CommCenter/spool/MobileTerminated direcotry.
Examining the contents its pretty easy to see that this is the incoming message I sent from another from. I ha the phone number that the message was sent from, the message, and some unprintable characters. I then copied this file off the iphone to my macbook and used hexdump to view the message to see what the unprintable characters are.
The text message in hexdump.
Doing the same for the MobileOriginated directory got a file called p.sms.58. The structure seems almost the same with the destination phone number and the message surrounded by a few unprintable characters. The message I sent was "What up Homey!"
The p.sms.58 message in hexdump.
I now know an intermediary point in the SMS delivery process. I attempted to create my own file in the MobileTerminated directory to see if it would be delivered as an SMS message, but no luck. Something is copying the files there then notifying CommCenter there are messages to be processed. The next step was to analyze the CommCenter binary and find any clues on how it operates and where the signal to process messages comes from.
I created a tar file of the iPhone filesystem with the command "tar czvf /tmp/fs.tgz /" and let it run. Although this is not the most efficient way to do this (a copy of the tar file is going to end up in the tar file) is it pretty fast. I then used WinSCP to copy the file down to a VMWare Fusion image of Windows XP running on my Macbook. My Windows image has most of my reverse engineering tools, including IDA Pro and HexWorksop. It also has the Windows version of Ruby installed because Ruby is pretty useful for reverse engineering binaries. The fs.tgz file is unzipped with WinRAR and the search for CommCenter begins. CommCenter is located in /System/Library/PrivateFrameworks/CoreTelephony.framework/Support
I loaded the file, selected the CPU (ARM), and configured my analyze options. Although IDA Pro is the best tool for this type of work it sometimes doesn't get everything, so I had to go through the disassembled code and fix a few things. The problems were pretty forward and easily fixed. An example problem was this:
This will become more readable like this:
After a few minutes of analysis it seems clear that the baseband module receives the message, and then CommCenter reads it using an AT command. At this point we can break testing into two different parts: CommCenter and MobileSMS.
CommCenter Testing: Fuzz From SMS.db to MobileSMS UI
MobileSMS is the application that handles reading the files from the database and displaying them. Testing MobileSMS is as simple as writing malformed messages to sms.db and then running the SMS application. Well, it would be simple if it wasn't for the database triggers. Trying just to insert a message ends with an error. The is answer is to delete the triggers then recreate them. Here is how to do it in SQL. This statment can be entered into the SQLite Manager:
drop trigger insert_unread_message;
drop trigger mark_message_unread;
drop trigger mark_message_read;
drop trigger delete_message;
CREATE TRIGGER insert_unread_message AFTER INSERT ON message WHEN NOT new.flags = 2 BEGIN UPDATE msg_group SET unread_count = (SELECT unread_count FROM msg_group WHERE ROWID = new.group_id) + 1 WHERE ROWID = new.group_id; END;
CREATE TRIGGER mark_message_unread AFTER UPDATE ON message WHEN old.flags = 2 AND NOT new.flags = 2 BEGIN UPDATE msg_group SET unread_count = (SELECT unread_count FROM msg_group WHERE ROWID = new.group_id) + 1 WHERE ROWID = new.group_id; END;
CREATE TRIGGER mark_message_read AFTER UPDATE ON message WHEN NOT old.flags = 2 AND new.flags = 2 BEGIN UPDATE msg_group SET unread_count = (SELECT unread_count FROM msg_group WHERE ROWID = new.group_id) - 1 WHERE ROWID = new.group_id; END;
CREATE TRIGGER delete_message AFTER DELETE ON message WHEN NOT old.flags = 2 BEGIN UPDATE msg_group SET unread_count = (SELECT unread_count FROM msg_group WHERE ROWID = old.group_id) - 1 WHERE ROWID = old.group_id; END;
Once that is done a simple insert statement should work:
INSERT INTO "main"."message" ("ROWID","address","date","text","flags","replace","svc_center","group_id","association_id","height","UIFlags","version","subject","country","headers","recipients","read") VALUES ('4','111111111111111111111111111111111111111111111111111111111111111','999999999999999','aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa','38','0',NULL,'3','0','0','9882987','0','hjdfbvljhvbzjldhvbjlvbjdbvjhdbvjhdvhjdg','aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa','aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa','aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa','0')
You can figure out the columns and what value they are expecting from looking at the table description. To understand what they mean I found a URL in the SendSMS code that explains SMS fields and formatting: http://www.dreamfabric.com/sms/. This information is useful if you want to make a fuzzer more accurate and directed. One problem is that the information does not exactly match up with he database format so it requires some experimentation to find out what each field means. The results follow:
Field Name | Input expected | Description |
ROWID | INT | The datbase row, used as a key |
address | Text | The phone number in the From field |
date | INT | The time the message was recieved in Unix time |
text | Text | The message body |
flags | INT | Show if a message was sent or received. 2 is for sent and 3 is for received |
replace | INT | Shows is a message is replacing a current message, like is a newer version is received. |
svc_center | Text | |
group_id | INT | Used by the SMS UI to group messages. If this is set to a id that is not also in the msg_group table, the message will not be displayed. |
association_id | INT | |
height | INT | |
UIFlags | INT | If sent to 1 the UI will act as if a URL is present. |
version | INT | |
subject | Text | |
country | Text | Country Code. The United States is set to us |
headers | Blob | |
recipients | Blob | |
read | INT | Shows if message has been read or not. 0 means it is unread, 1 means it has been read |
This information was collected by a trial an error process of sending and receiving messages then duplicating them with different options. If that didn't work I took the SMS message apart and traced it through CommCenter to its insertion in the database. If the field is still blank I can't find anywhere it is actually used and may be used for MMS which I do not have enabled because I am on ATT. Below are some pics of my trial and error process.
The SQLite view of the test messages.
What the testing actually looks like on the iPhone. You might notice the red exclmation points next to the final two messages. This means the SMS was unable to send and it may happen after mucking arounf witht he database enough. Luckily you can just delete the database, kill -HUP the Springboard process, then restart the MobileSMS application. This will recreate a virgin database for you. The failed messages look like this in syslog:
Jul 23 18:24:49 daveTestIphone2g MobileSMS[85]: no URLs for message: Incoming test
Jul 23 18:25:07 daveTestIphone2g com.apple.SpringBoard[26]: told to send sms 18
Jul 23 18:25:07 daveTestIphone2g CommCenter[30]: queuing sms message with id 18
Jul 23 18:25:16 daveTestIphone2g com.apple.SpringBoard[26]: internalID: [18]
Jul 23 18:25:16 daveTestIphone2g com.apple.SpringBoard[26]: notifying clients of event: 3 (recordID: 18)
Jul 23 18:25:16 daveTestIphone2g SpringBoard[26]: send error: < 0x2483d0="">
Jul 23 18:25:16 daveTestIphone2g MobileSMS[85]: send error: < 0x191ad0="">
Jul 23 18:25:16 daveTestIphone2g MobileSMS[85]: _SMSMessageSendError
As far as a fuzzer actually goes using Ruby and the SQLite gem means that a fuzzer could be whipped up in a few minutes. Knowing the field input types and the rules around what gets processed vs what doesn't helps speed this process along. A simple script will just open the database, insert several rows, close the database the kill -HUP the SpringBoard process. The HUP seems to help the UI close then reopen the database and process your new messages.
Things to try involve long strings, unusually high numbers, and setting values that aren't normally set. Below is an example of a crash received after processing a database full of fuzzed messages. The offsets have been removed so we are not accused of releasing 0-day, how ever if you follow the above steps it is pretty easy to duplicate.
CommCenter: Fuzz From Baseband to SMS.db
Back to testing the code that recieves the message and puts it in SMS.db. Looking back at the sendsms tool it seems the best way is through the use of "/dev/tty.debug". Since the sendsms command to my own address doesn't work lets look at other ways to do it. http://rednaxela.net/pdu.php helps you by allowing you to put put in a SMS PDU and it will tell you what it means or you can create your own. A sample SMS PDU is needed. Using minicom on the iphone I connect to the baseband and issue a few commands that give me a binary SMS PDU.
I need my SMSC number and since it is not stored in the SMS.db file typing a debug code on the keypad will show you. I type *#5005*7672# Here is what it looks like:
Next I went back to the Rednaxela site and build a test mesage:
Taking a tour of CommCenter in IDA I trace some code from the function that begins processing spooled messages in MobileTerminated, the the trigger for it.
The function that process SMS messages:
And the command that beings the processing is +CMT:
Starting Minicom I sent the message and play around with various AT commands.
It seems that sending a message with AT+CMGS=length of message produces the desired result. Creation of a test message is easy and it reads "Testing Local Testing". After hitting CTRL-Z after pasting in the PDU I got this on the phone:
Referring back to the website earlier that describes a SMS PDU and all the different options allows for quick fuzzer creation. You can write a small Ruby script to take your fuzzzer output and write it to /dev/tty.debug and monitor the results. This path finds as many bug as the other and they should be tracked down and verified.
In Closing
This paper shows the extent that researchers can follow breadcrumbs to reproduce a work. That's the risk of partial disclosure: a simple description, such as "SMS crash on an iPhone" can give researchers enough hints to reproduce the work.
I haven't figured how how to successfully exploit the crashes I've found. The SMS network only allow 160 characters through their networks. It might take a multistage process (send many SMS messages with shellcode, then a final message that overflows a buffer to run it). Or, it might be something simple to overwrite a few bytes to unlock a phone.
4 comments:
Or it might have been the usual publicity stunt
I don't follow?
@Thierry Zoller
If you're implying that Charlie Miller's claim of successfully fuzzing the iPhone via SMS is a publicity stunt, you obviously don't know what you're talking about. Charlie Miller is a respected security researcher who does not indulge in theatrics. There is also a document called the NDA that Apple makes us sign, which prohibits us from talking about our findings. Were you even at Syscan where Miller revealed his SMS fuzzing?
Great write up Dave! With Screenshots too! Herb would be proud... Go ahead and take that 2-hour lunch. You deserve it!
Post a Comment