Carvis and the story of reverse engineering Uber


So the story starts out simple. Four engineers have an idea for an app. One of those game changing, “how has nobody thought of this!”, kind of ideas (or at least that's what we all like the think they are at first). The idea was to create a service that would aggregate the current price and time estimates from both Uber and Lyft and order you whichever was cheapest or fastest, depending on what you had requested. Simple enough right?

Because this blog is suppose to be about reverse engineering the functionality behind Uber's app (mainly the use of their private API end points) I will make this quick. From the start we realized very quickly we weren't going to get very far with the Developer Plans provided by both Uber and Lyft. They were super restrictive and to actually do any real ordering on behalf of another user, you had to have the app fully approved. Good luck trying to get two companies in an all out war to agree to an app that would potentially pick their direct competitor. So this obviously lead us very quickly to the idea of reverse engineering the functionality we wanted.

I was very excited, to say the least. I had had some experience with reverse engineering mobile apps and knew it was going to be fun ride. I had decompiled Android apps and sifted through the mostly gibberish output, like a game of “Where’s Waldo” looking for little clues. I also had plenty of experience with man in the middle attacks, which was of course our first plan of actions as the private endpoints were the top priority.

This is the part of the story I stop and say that all of this was done in an educational environment and never once did we EVER plan on taking this public or monetizing the app. We had no malicious intent. This was all about the challenge of wresting with two of the most professional and well put together apps from two of the most powerful companies in the world. We went in and came out with the utmost respect for both Uber and Lyft and I if you read on you'll understand why.

So with the task at hand I decided to take inventory of the tools needed for such an endeavor. Any good hacker knows the first step is always reconnaissance. First things first I went and gathered all the information I could about security features at both Uber and Lyft. Needless to say there isn't much out there. Now it was time to gather the tools. When it comes to man in the middle attacks, most people think of Burp Suite or Wireshark. Both are fine products and would have worked perfectly for what I needed but I wanted to try something different. Charles Proxy. Charles Proxy works very much the same as Burp Suite or Wireshark albeit with less bells and whistles. It's a project that seems to be the product of a single man and is a great utility for your tool belt IMHO.

Next up is a nifty little tool called Cycript. Its an Objective-C and JavaScript hybrid that allows you to do a lot of cool things. I was interested in it solely for its ability to be injected into a running application where it would then give me a live Javascript runtime environment. An environment fully capable of executing code, tracking down function names, changing variables etc. Here is a demo of Cycript in action. Last but not least was the trusty APKtool. Anyone who has done anything with hacking and Androids knows this tool. The short and sweet is you stick an app in and it spits it out decompiled.

After hooking up Charles Proxy and getting it all configured with my phone came the moment of truth. Charles Proxy comes with its own SSL certificate, meaning any traffic served via HTTPS would comes out decrypted. Or so I thought. When we plugged everything in and fired up the apps the Lyft traffic was coming across clear as day. We quickly recorded the endpoints we needed along with the request and response bodies and went to fire up Uber expecting the exact same easy results. Wrong.

When we passed the Uber traffic through charles proxy all we got was red flags. It wouldn't decrypt. (Depicted in the image below). Of course, Uber was utilizing a technique known as SSL pinning. It's something you can do in applications when you know all the traffic being served to and from the app is always going to coming from yourself. The TL/DR is that you ‘pin’ a copy of your SSL cert somewhere in the application and use it to validate against any other cert being used to decrypt your traffic. Because the SSL cert being used by Charles obviously didn't match the copy that was being used in the Uber app nothing was decrypting. In fact the app wouldn't even send the packets!

charles fail

Obviously this was a setback but luckily I had plans in place to try and circumvent things of this nature if they came up. This is where Cycript comes into play. The plan was to inject Cycript into the running Uber app, find the method running the SSL cert validation, and turn it off. Sounds easy enough right? Wrong. All of the resources online, which weren't many, described the places where iPhones store the third party applications no longer existed on my new iPhone 6S plus running iOS 9.3.3. Being determined, I brute forced it with man power. Searching down every single file path starting from the root directory. Finally after about 3 hours I found something I thought looked fruitful. Of course by fruitful I mean a directory filled with more directories labeled with titles like this C2DBE391-8EBD-46...AD4-933D3DF489F

Due to the absurdity of the files names, and for the sake of my sanity, I decided to move from the terminal to a handy app called iFile. I found it on Cydia and boy was it a life saver. It’s a file and folder visualizer for you iPhone. Now that I knew where to go poking around, I could use iFile to dig into these mystery directories and see if I could find something of worth. Of course with names like C2DBE391-8EBD-46...AD4-933D3DF489F you had to find another way to identify which app was what. As I started digging I noticed a folder named cached_assets. Bang. Found it. This was where each app was caching photos. All I had to do was dig through them one by one until I found Uber. One after another, for another hour plus, until FINALLY I found it.

Ok now for the fun part. I needed to load Cycript onto my phone, SSH into it, pull up the Uber app and inject it. AGAIN, simple enough right? ….I think you know the answer by now. It wouldn't load. But why? Was I not doing it right? To be fair this was my first time using Cycript. So I decided to point it at another app and bam, it loads and works perfectly. Another app? Bam works just fine. I point it at Lyft...and B..bust. Same repose as Uber. Hmmm? A quick google search revealed (albeit via translated Chinese blog, Thanks Google translate) that this person believes Uber was made aware of Cycript and spun up a patch to block it. Foiled again.

Still I am undeterred, after all, this is hacking right? But I hadn't planned for this. So, I do what all good hackers, developers, managers and anyone who is stuck in this day in age does. I googled it. Sure enough on the first page was something, even by name, that looked promising. SSL Kill 2. I won't get heavy into the details of how SSLKill Switch works (follow the link for that) but the TL/DR is that when you load it on your phone you now have a neat little switch in your Utilities tab that allows you to turn off ALL SSL certificate validation. Neat huh? Now that being said this is obviously a slightly risky tool in that anyone can read ALL of your phone traffic. So obviously, don't use this on public Wifi.

After the fairly quick and painless process of loading SSL Kill on to my phone I was up and running. Finally, I had cracked it. Charles was streaming all of my Uber traffic, unencrypted. I followed by doing a full sign-in, order, sign-out process, 3 times for good measure. This was exactly what I had been looking for. Uber's private API endpoints along with the request and response bodies. The plan from here was simple. Figure out what fields needed to be dynamically generated for each individual user and which fields could be spoofed and hard coded. By now you should know what happens whenever I assumed something would be simple.