iOS Security Uncategorized

Reverse Engineering the DirecTV App’s DVR Authentication

Disclaimer: The work below was done nearly a year ago. I have no way to test to see if the information is still accurate. I’m guessing it is, but if you’re able to check and see, feel free to let me know if you still get the same results I did.

A while back a friend came to me and they were interested in writing their own mobile app to control their DirecTV DVR. DirecTV already had its own mobile app, but this person wanted to be able to customize some of the behavior. The existing app gave us a good starting point to try and see how to communicate with the DVR.

The first place to start was to inspect the local network traffic between the mobile app and DVR. Any proxying tool should work; I went with Charles Proxy. When doing this, we observed an interesting pattern with the traffic. Every time the app sent a request to the server, the server would respond 401 Unauthorized. The app would then send a second request, identical to the first, but this time with an Authorization header. The server would accept this second request and respond. This wouldn’t just happen once at the beginning of a session. Every single request would get a 401 the first time, then would be resent with Authorization headers.

Inspecting the server’s 401 response, it contained a WWW-Authenticate header which included four keys: realm, Qop, Nonce, and Opaque. A quick online search of these keys revealed that the server seemed to be issuing a digest authentication challenge.

A digest authentication challenge is part of digest access authentication; an authentication method that can be used with web servers. The way digest authentication works is that the client and server each know a pre-shared secret (a password). When the server responds to a client with a digest authentication challenge, it’s telling the client how to authenticate itself. The client will generate two strings:

string1 = md5(username:realm:password)
string2 = md5(method:digestURI)

These two strings are then used by the client to generate the authentication response:


If we wanted to talk to this DVR server, we needed to figure out how to authenticate ourselves. In order to authenticate our response we needed a username, realm, password, method, digestURI, nonce, nonceCount, cnonce, and qop.

As mentioned above, the server’s challenge response gave us the realm, qop, and nonce. From the client’s plaintext HTTP response we were also able to obtain the username (c0pi10t), method (GET), and digestURI (path in the requested URL).

This left us still needing the password, nonceCount and cnonce. The cnonce is an arbitrary value chosen by the client (us!) and the nonceCount can just always be 00000001. So really all that was left was the password. The password is the very thing that makes digest authentication secure. The client and server ship with the shared password known to both of them, so they never have to transmit it over the wire.

In order to obtain the password, one option was to try brute force. Digest authentication is used with SIP, for which a couple of brute forcing tools already exist. However, if the password being used is sufficiently complex, brute force is impractical. I took an existing tool and tweaked it a bit to at least start a brute force script while working on some other ideas.

While that ran, I attempted to inspect the application binary itself. Sometimes developers do silly things and leave files around with interesting information, store secret values in insecure places, or don’t bother to obfuscate strings in their binary. Knowing the username gave me a known value to search for. Unfortunately, cursory searches didn’t reveal any clues inside the binary and couldn’t even find a match for our username, so they seemed to at least be doing something to obfuscate the strings in the application binary.

I also decided to start skimming through the digest access authentication RFC (RFC 2069). Looking through the table of contents, one section immediately jumped out: Security Considerations. This section covered some of the benefits that digest access authentication has over basic auth, as well as possible attacks.

Section 3.3 – Man in the Middle – A simple but effective attack would be to replace the Digest challenge with a Basic challenge to spoof the client into revealing their password.

The RFC also goes on to explain how this could be combatted. The developers are likely to have coded the client to only respond to digest authentication challenges, and ignore challenges asking for basic auth. The client knows that the server will be using digest authentication and there’s no reason it should accept basic auth as a challenge, especially when an RFC that’s over 20 years old clearly outlines this attack. But it never hurts to try.

Once again using Charles Proxy, when the client tried an unauthenticated request and the server responds with a digest challenge, we modified the response to have a “WWW-Authenticate: Basic” header, indicating to the client that it should authenticate itself with Basic auth (base64 encoded username and password).

When we tried this, much to our surprise, the client actually responded to our spoofed request with Basic Auth. It’s a base64, colon-delimited string, which decoded gives us: c0pi10t:8th5Bre$Wrus. We already had the username (the first part), and now we also had the password.

At this point the door is pretty wide open for writing our own client that can talk to the DirecTV DVR. We have all the pieces we need to authenticate all of our requests to the DVR so that it believes it’s talking to the official DirecTV mobile app. And not just this specific DVR, but any DirecTV DVR that’s capable of working with the mobile app. Based on my (very novice) understanding of digest access authentication, the password must be the same for any DVRs that want to work with the mobile app.


These Aren’t Your Dad’s UDIDs

Throw everything you thought you knew about Apple UDIDs out the window. Historically, Apple’s UDIDs have always been a hexadecimal string exactly 40 characters long. Last week, Apple released new hardware. Most notably the iPhone XS, iPhone XS Max, and iPhone XR. On those new devices, they seem to have changed the format of UDIDs. They are now a string of 8 characters, then a hyphen followed by 16 more characters. XXXXXXXX-XXXXXXXXXXXXXXXX

I have no idea why they’d make such a change. It will likely have little to no impact on developers. We use an internal tool that checked the length of a string to determine if the UDID was from an iOS device or an Android device. It made the (now incorrect) assumption that an iOS UDID would be always be exactly 40 characters long. I imagine people may have other pieces of software out there that they’ve written with this same assumption for one reason or another, who will also run into problems. But beyond that, this change is mostly just an oddity for us to “huh” at.


iOS 8 Location Services PSA

When Apple announced their changes to Location Services in iOS 8 at WWDC this year, a couple of things jumped out as being potentially problematic for developers (as well as users). I wrote about the changes in-depth on iMore back in June, but now that iOS 8 is out, and the changes are causing some confusion for people, I think it’s time to revisit them and discuss possible problems.

There are now two types of permission you can ask for with Location Services: Always and When In Use. I won’t go over which each one means here, but refer to the screenshot from WWDC below for a quick guide on which type your app will need. Any apps built for iOS 7 will default to asking for Always permission when run on iOS 8. This means your users will see a new, scary alert telling them that your app is monitoring their location even when they’re not using it. It also means even after a user has said yes, iOS will ask them again after a few days if they want to continue to allow your app to access their location even when not in use. For some apps this is fine. For others, users will panic and disable the permission. For this reason it’s important to push an update to properly handle Location Service permission on iOS 8. It’s also a healthy reminder to ensure your app will fail gracefully when users deny your app access to their location.

iOS 8 Location Services Matrix

The other important thing for developers to understand is the new requirements for prompting users for their location. If you don’t do this correctly, users will not get prompted to allow Location Services. I’ve seen and heard of a number of developers spinning their wheels when they run into this. It’s insidious because your user will not see any indication that something didn’t work, and Xcode doesn’t do a great job of telling developers when they’ve screwed this up. You are now required to define Location Services description strings in your Info.plist. If you request permission for Location Services and don’t have this set up properly, iOS won’t even bother prompting users.

Maps asking for permission in iOS 8

The two keys to concern yourself with are NSLocationAlwaysUsageDescription and NSLocationWhenInUseUsageDescription. The values of these keys are the text that iOS will present to users in the Location Services alert when it prompts a user to give your app access. This is where you need to succinctly explain to your users why you need to know their location. If you only define one of these, but your app asks for the other type of permission, iOS won’t prompt the user.

Xcode Info.plist

You’ll also need to update your code to call requestAlwaysAuthorization or requestWhenInUseAuthorization when you want to ask the user for permission before attempting to call startUpdatingLocation.

Also be mindful of any third-party SDKs you’re using that might request Location Services before you do. Your app gets one chance to ask a user for permission. If your app only needs WhenInUse permission, but a third-party library is still rocking some iOS 7 code and it asks before you do, your app will request Always permission and if the user says no, you’re hosed.

I’ve also encountered third-party libraries that rely on which description string you’ve defined to infer which permission it should ask for. The problem with this is if you define both strings, there’s no way to know which string the library will key off of. In this case, you would only want to define the string of the permission type you intend to use. This should be fine, but if your app, or one of its libraries, ever asks for the permission that you haven’t defined a string for, we’re back to iOS never prompting the user. If you’re seeing your app fail to request permission, in all likelihood, you’re attempting to request a Location Services permission that you don’t have a description string for.

One last note: it appears if you compile with Xcode 5, you can still get away with not defining description strings, but obviously you don’t want to lean on that. I only mention it here because it’s still possible to ship an app for iOS 8 that will work fine without usage description strings, but this will likely come back to bite you later. Even if your code works now, make sure you’re doing things right so you and your users aren’t surprised later when your stuff breaks.

Finally, if you have trouble with apps from other developers not asking for Location Services when they need it because they’ve encountered one of the problems above, you can go to Settings > Privacy > Location Services and give the app permission there. The app will show up in the list, and when you tap on it you’ll see “Never” and either “Always” or “While Using the App” as your two choices. Enabling Location Services here will give the app the same permission as if it had correctly prompted you in the first place.

For more information, or clarification on anything I got wrong, see Apple’s CLLocationManager documentation.


Software development, QA, and the reality of bugs

There was a lot of chatter this week after Apple pushed out iOS 8.0.1 with bugs that left some iPhone 6 and 6 Plus users without cellular service or Touch ID. If Apple has ever published an iOS update with such significant bugs, I can’t remember it. In the wake of the release, some publications thought the best thing to do would be to write defamatory articles pinning the failure to a single person: Apple’s QA manager who oversees iOS testing. As a QA lead and somebody who has worked in software for a number of years, this was cringeworthy to read. It’s not only a shitty thing for a news site to do, but also demonstrates that they lack any sort of understanding of software development. In response, I decided to write “Why bad bugs hit good people”. Head over to iMore for my full not-quite-rant.

iOS Security Uncategorized

Escrow Keybags and the iOS 6 lock screen bug

There was a lot of fuss last week about an alleged iPhone lock screen bug that allowed an attacker to bypass the lock screen and access your iPhone’s filesystem. I wrote an article on iMore explaining that this wasn’t the case and the confusion seemed to be the result of a misunderstanding on how iPhone passcodes work.

As mentioned in the article, when an unlocked iPhone is plugged in to a computer with iTunes running (or a locked phone that you then enter the passcode on), iTunes has a mechanism that allows it to access the device’s filesystem in the future without requiring the user to enter their passcode. I ambiguously referred to this “mechanism” because I had absolutely no clue how it worked. All I knew is that the first time you plug a locked device in to a computer, iTunes will give you an error message. However, once you enter the device’s passcode, iTunes will never prompt you again to enter it on that computer. Whether the phone is locked or not, iTunes (and any other app that can talk to your iPhone) will gladly read files from the device and write new data to it.

Twitter user Hubert was kind enough to send me more info about this process. He sent me an article that looks at how iOS 5 backups work. The article briefly explains the use of Escrow Keybags by iTunes (technically I think usbmux is what uses them) to talk to locked devices after they have been unlocked once. The gist seems to be that there are certificates and keys which are exchanged via iTunes when an iPhone is plugged in and unlocked, which are then stored in an Escrow Keybag on the host computer. The Escrow Keybag for every device you’ve ever plugged in to your machine can be found in /private/var/db/lockdown and is named with the UDID for the device that it is for. When a device is plugged in to your computer in the future, this Escrow Keybag is used to unlock the device without the need for the user to enter the passcode.

The confusion on the alleged lock screen bypass bug came from this. The bug discovered simply made the screen go black with a status bar still visible. If you plugged this device in to a computer that it had previously been plugged in to while unlocked, the computer used the Escrow Keybag in order to read the file system without needing the passcode. This would have happened with or without causing the black screen glitch. If the person who found the glitch had plugged their iPhone into a computer that it had never been connected to, they would have gotten an error from iTunes saying that the device was locked and they would need to enter their passcode.

The original video demonstrating the bug can be found here. A video of the same bug being reproduced, but with the phone plugged in to a computer it has not been unlocked with before can be seen here. My sincerest apologies that I don’t have any block rocking beats accompanying my video.


Kaleidoscope 2

The folks over at Black Pixel have just released Kaleidoscope 2.

Use Kaleidoscope to spot the differences in text, images, and folders. Review and merge changes in seconds with the world’s most advanced file comparison application.

I regularly use Kaleidoscope in my workflow for diffing images, text and files. If you do QA, development or design, you need this app. Go check it out.

Full Disclosure: I have a large bias toward the people at Black Pixel because they’re awesome.


Nobody panic… Dropbox is down

If you’re having problems syncing with Dropbox right now, you’re not alone. Dropbox’s services seem to be experiencing trouble uploading any new files (though viewing existing seems to be working for now). Dropbox’s status page doesn’t give much info, but does report they’re having some technical issues and believe services will be back in the next hour. You can watch for updates on the @DropboxOps Twitter account.

Update 8:10AM MST 1/11/13: @DropboxOps just announced all issues have been fixed and all services should now be working.

Mac OS X Server Uncategorized

Mac Mini Servers: A Cautionary Tale

This post will deviate from the type of content I generally post here. It isn’t really related to QA and instead deals with a recent problem we encountered when upgrading our build servers. I’m posting about it here in hopes that others may save themselves time and trouble from the lesson that we learned. If you’re interested, read on, if not, hopefully I get some more QA posts up over the holidays. If you just want to know what the problem was and how we fixed it, you can scroll right to the bottom.

We recently decided to give our build servers an upgrade. Our existing setup was a Mac mini running Jenkins and 4 Xserves that served as slaves. While the Xserves were great as servers, their product line has been discontinued, they’re noisey, they’re large, and they require more power than Mac minis. And even though redundant networking and power supplies are nice, they’re overkill for our needs. So when we went to update the servers we decided go to with Mac mini servers.

Setting up the first slave took a little bit of time and patience. We decided to start clean and set these up from scratch rather than migrate the existing servers. We had a general idea of what was needed and figured out what pieces we were missing through trial-and-error. Taking notes on steps as we went through the first one allowed for an expedited process on the subsequent servers. When we finally got the first one setup we launched it as a slave on Jenkins, assigned a project to build on it, kicked it off and, with some tweaking, got the build to succeed.

With my set of steps to follow, I quickly ran through the second server and had it up and going in an hour or so. Feeling fairly confident in the process at this point, I decided to try a third server near the end of the day, but this one was a special case. This one was going to replace the server that also ran our VPN. Initially to try and avoid having to re-create all the VPN users and have everybody set up a new password, I set up this Mac mini as a clone of the existing server it would be replacing.  During the cloning process though I began to give things more thought. After talking with my boss we decided that VPN would actually be moved off of the build servers to a more dedicated box. Once the cloning finished, I booted the mini into the recovery menu and did a re-install.

An hour or so later the re-install was complete, but it hadn’t gone quite as I had hoped.  There still seemed to be certain user data and settings that had persisted through the re-install, but it was good enough for now. I ran through my list of steps, skipping a few for software that had persisted through the re-install, and got the server added to Jenkins. Feeling satisfied with my work for the day, I decided to hold off until the next day to do the remaining servers.

The next morning we noticed something strange; Jenkins was showing that two of the new servers had response times of a few seconds while the old servers were showing response times of a few milliseconds. Jenkins was also reporting a clock difference on the two new servers of being a few seconds ahead while the old ones all showed in sync. We also noticed a few builds had failed overnight, but all at different points in the build process. Some of them were even failing in the middle of uploading builds to an external server.

Our first guess was a networking problem, but why? When we created the new build servers we gave them the same names as their predecessors that were to be retired. We also assigned them the same IP addresses, but due to some technical hurdles, the static IPs weren’t being handled by the router. Instead we were telling the machines to use DHCP, but were manually assigning them the IPs we wanted them to have. We wondered if these factors had caused some weird networking problems when trying to reach these new machines that had taken over old hostnames and IPs. While trying to diagnose the issue I  noticed that if I pinged one of the new servers continuously, intermittently several packets would timeout.

Oddly, the third server wasn’t exhibiting any of the symptoms that the other two servers were. I had more or less followed the same steps, but one notable difference is this server had gotten a new hostname and IP since the existing Xserve needed to stay up for now to run our VPN. We were already thinking it seemed like some kind of network issue, and now we had one server that didn’t take over an existing IP and hostname and it wasn’t having any of the same problems.

Now that we had figured out the issue, it was just a matter of moving the other two servers we had already setup to new hostnames and IPs. The change only took a couple minutes and we were back in business. Jenkins was showing the response times as low and the clocks in sync. Everything looked great… until a little while later when I got an email notifying me that a build had failed. I looked and sure enough, it was one of our new Mac minis and like the other failures, it happened in the middle of the build. Looking at the Jenkins interface, the slave had now been marked as down as its response times had gotten so bad. I looked to the third server that had shown the initial success and it was still fine. Once again, I was perplexed.

I tried searching online for any clues on what to do about a drifting clock in Jenkins with no luck. I went to an OS X Server room on IRC and asked for help, but was directed to try the Jenkins room. I went to the Jenkins room for help and nobody had any suggestions. I spent the night mulling things over and was running out of ideas.

The next morning I talked with Casey, one of our developers who had been helping me try to troubleshoot the issue the last couple of days. Casey had an idea; what about the energy saving preferences? I quickly took a look at the two problematic servers and noticed they were set to put the computer to sleep after 30 minutes of inactivity. I checked the third server and this option was disabled. I quickly disabled the option on the other two servers and wished for the best. Initially things looked better, but I had already been fooled several times with other solutions that initially had good results.

We let the servers go for a few hours and this time the good results actually stuck. That was it; Casey figured it out. In retrospect it all makes sense. Well… almost all of it. The build processes didn’t count as activity because they were running in the background; the OS was relying on mouse/keyboard input to tell it the computer was in use. The reason the third server didn’t have any issues is because one of the settings that persisted across the reinstallation was the Xserve’s Energy Saver preferences. The Xserve’s seemed to have shipped with a much saner default of “Never” for the computer sleep option. I still don’t quite understand the symptoms of showing the clock as drifting ahead or why the response time appeared slow rather than just non-existant. Regardless, this was a much harder lesson to learn than it probably should have been. It should have occurred to us much sooner to check the Energy Saver preferences, but I do hope that Apple will change the default on the Mac mini servers to have these preferences disabled and save others the trouble and confusion. I don’t imagine that many people out there running servers want them going to sleep every 30 minutes.

tl;dr: Mac mini servers running as build servers had symptoms of slow response times with Jenkins, Jenkins reporting their system clocks as ahead, and builds failing at random points, but not all the time. This was fixed by changing the Mac mini server Energy Saver preferences from the default of putting it to sleep after 30 minutes of inactivity, to never putting it to sleep.


Apple to Fix IAP Vulnerability in iOS 6

Apple recently announced that the IAP vulnerability discovered earlier this week will be fixed in iOS 6. They have also released documentation for developers outlining best practices that should be taken to ensure they are not affected by this attack moving forward. One interesting bit is that Apple actually instructs developers to make use of private APIs to secure their apps.

Thanks to Matthew Panzarino for the heads up.



Getting Started With Mobile Testing

Over at Ministry of Testing they have put together a nice mind map that serves as a great starting point of things to consider for testing (as well as development and design) when you’re starting a mobile project. I’m hoping at some point somebody will make platform specific ones for Android and iOS. If not… maybe I will. Anyway, head on over to check it out: Getting Started With Mobile Testing – A MindMap