██▀▀▀▀▀▀ ██ ██▀▀▀▀▀█ ██▀▀▀▀▀▀ ██▀▀▀▀▀▀█ ██▀▀█ ██ ██▀▀▀▀▀▀ ██▀▀▀▀▀▀ ██▀▀▀▀▀▀ ██ ▄▄▄▄ ██ ██▄▄▄▄▄█▄ ██▄▄▄▄▄▄ ██ ██ ██ ██ ██ ██▄▄▄▄▄▄ ██▄▄▄▄▄▄ ██ ██ █ ██ ██ █ █ ██ ▀█ ██ ██ ▀█ █ ██ ██ ▀▀▀▀▀▀▀▀ ▀▀ ▀▀▀▀▀▀▀▀▀ ▀▀▀▀▀▀▀▀ ▀▀▀▀▀▀▀▀▀ ▀▀ ▀▀▀▀▀ ▀▀▀▀▀▀▀▀ ▀▀▀▀▀▀▀▀ ▀▀▀▀▀▀▀▀ ▌▌▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ ▄▄▄▄▄▄▄▄▄ ▄▀ ▀▄ SNAPCHAT SECURITY ADVISORY █ █ █ █ FOUND: 07/27/2013 █ █ RELEASED: 08/27/2013 ▄▀▀▀▄▀ ▀▄▀▀▀▄ ▀▄ ▄▀ ▀▄ ▄▀ CONTACT US: █ █ security@gibsonsec.org, @gibsonsec ▄▀ ▀▄ ▄▀ ▀▄ (Feel free to contact us, Snapchat) ▀▄ r2▄▀ ▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀ ▌▌▀▀ 0x00 - TOC ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ 0x00 - TOC 0x01 - Intro 0x02 - Protocol 0x03 - Encryption 0x04 - Advertising 0x05 - Conclusion ▌▌▀▀ 0x01 - Intro ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ Recently, Gibson Security (or GibsonSec, as we prefer) have been testing android applications for vulnerabilities. One overlooked part of an applications security is often the API it uses, and we see these vulnerabilities again and again with android applications. Whether it be an OAuth key we can take and use in our own malicious application, or issues regarding form validation, there is usually some exploit we can take advantage of for our own benefit. In the last fortnight, we have been working hard reversing Snapchat, and just recently finished our protocol documentation for their API. Using our Snapchat API implementation, someone could save media sent to them, DoS Snapchat users, and as we recently found, build a database of Snapchat usernames and phone numbers, connecting names to aliases easily, and with further work connecting social media accounts to entries. We also found that if someone was able to gain access to Snapchat's servers, they could easily view, modify or replace snaps sent. With a couple of lines of python, someone could view all your unread messages, and depending on the situation, modify and even replace the images completely. Of the vulnerabilities found in Snapchat, these are the ones we feel are the most danger to the image of Snapchat as a secure, fast and easy app for people to use. We attempted to apply for the "Software Developer" position at Snapchat saying we would gladly help improve the security and performance of the application, but failed to get a response. The current "security" was put in place somewhat haphazardly and only after the lack of secure encryption and data transmission was brought up publicly. We don't believe that it is up to scratch for a service such as Snapchat which is based on the idea of ephemeral media, and hope that Snapchat significantly improve their security. We hope you enjoy the release, GibsonSec ▌▌▀▀ 0x02 - Protocol ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ Snapchat use a fairly simple (yet strangely implemented) protocol on top of HTTP. We won't reveal everything about the protocol, only what is needed for these problems, but the rest is easily figured out. API endpoint: https://feelinsonice.appspot.com (Yes, we're serious..) ─── Creating request tokens ───────────────────────────────────────── def create_token(token, timestamp): secret = "iEk21fuwZApXlz93750dmW22pw389dPwOk" pattern = ("00011101111011100011110101011110" "11010001001110011000110001000110") h1 = sha256(secret + token) h2 = sha256(timestamp + secret) return [h1[i] if c == "0" else h2[i] for c in pattern] The basic idea behind this is that you take the hash of your auth token (which you get from the server on login) and the timestamp of your request, then combine them together using lots of voodoo and witchcraft (read: some simple string iteration) and get your request token back out. This token is used in every request sent to the api, under the name req_token. In two cases (logging in and requesting a password reset), you wouldn't have an authentication token, so a static auth token is used here instead: m198sOkJEn37DjqZ32lpRu76xmw288xSQ9 NB! The timestamp can any integer as long as it is the same in your request (in the timestamp field) and your call to create_token(). ─── Logging in to Snapchat ────────────────────────────────────────── POST /ph/login HTTP/1.1 Host: feelinsonice.appspot.com Content-Length: 134 Content-Type: application/x-www-form-urlencoded username=&password=password×tamp=1376814395296&req_to ken=9309c75723b1c4186e82d4edf4a16f14cbe24d8d19f3da84d9b4e214d3c5110b Here, req_token comes from a call to create_token() with the params: >> create_token("m198sOkJEn37DjqZ32lpRu76xmw288xSQ9", 1376814395296) "9309c75723b1c4186e82d4edf4a16f14cbe24d8d19f3da84d9b4e214d3c5110b" where "m198..." is the static token used to login and 1376... is the current unix timestamp (sent as timestamp and used in create_token()) This will get a JSON reply with all sorts of fields in it; we're only interested in the auth_token field. It should look like this: f51e1fe0-40aa-4c57-ac87-594f101722f8 Store that for later, we'll need it again for our next API call. ─── Finding your "friends" ───────────────────────────────────────── POST /ph/find_friends HTTP/1.1 Host: feelinsonice.appspot.com Content-Length: 170 Content-Type: application/x-www-form-urlencoded username=×tamp=1376814395296&req_token=e4b9c7b723b7c4 1c6282d4ad84816f1acbd296ad8ff3d684d363e2493390310e&countryCode=US&nu mbers=%7B%22311-555-4202%22%3A%20%22Kate%20Libby%22%7D This is a bit of a bulky request, so lets explain the various fields: · req_token is the result of a call to create_token() with the auth token we stored logging in (you *did* store that, right?). · countryCode is your standard two letter country ISO code. · numbers is an encoded (and escaped) JSON dictionary of numbers (key) relating to address book names (value). It should look some thing like this: · pre-HTTP-quote: {"311-555-4202": "Kate Libby"} · post-HTTP-quote: %7B%22311-555-4202%22%3A%20%22Kate%20Libby%22%7D The reply (if an account was found with a given number) will look something like this: {logged: true, results: [{ type: 0, name: "acidburn", display: "Kate Libby" }]} In the reply, there's one field we're interested in: results. It's an array of objects with three fields: type, name and display. · type is either 0 (a public account) or 1 (a private account). · name is the person's Snapchat account name. · display is the display name you gave to Snapchat in your request. In bulk, these replies aren't interesting. The fun comes in when you send a single phone number at a time. Doing this, you can make a 1:1 link between a person's phone number and their Snapchat account. Handy feature? Yes. Easily exploitable? Definitely. Thanks to a lack of limiting, the upper bound limit to the amount of numbers you can send in a request is incredibly high - we were able to get replies for upwards of 75,000 phone numbers at once, getting a reply with thousands of active Snapchat accounts. The idea that simply linking your phone number to Snapchat can give away your account to anyone interested with minimal effort is quite scary. Using easily found ranges of mobile numbers, one could potentially get the username and phone number of everyone using Snapchat (who attached a phone number) in the U.S. in a small timeframe. ─── Denial of Service against Snapchat users ──────────────────────── Due to the lack of limiting (which we mentioned above), it is easy to send several hundred friend requests or snaps directed at one person simultaneously, potentially overloading the app. Attempting to load approximately 100 snaps at once causes the app to lock up for a period (and in most instances, crash). ▌▌▀▀ 0x03 - Encryption ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ You might have heard, or you might know by now, that snaps sent and received are "encrypted". That may be the case, however: · Snaps are encrypted using AES in ECB mode. This is possibly one of the least effective modes of encryption; Identical blocks of plaintext are encrypted into identical blocks of ciphertext. Granted, this is only an issue if you use the same encryption key, which leads us to our next point. · Snaps are encrypted using symmetric-key encryption! The key is the same in both the Android and iOS app, and it's just sitting around in the app waiting for someone to find it. What is this magic encryption key used by any and all Snapchat app? M02cnQ51Ji97vwT4 You can find this (in the Android app) in a constant string located in com.snapchat.android.util.AESEncrypt; no digging required, it is quite literally sitting around waiting to be found by anyone. On a more positive note (perhaps), in the 3.0.4 (18/08/2013) build of the Android app, there is - oddly enough - a second key! 1234567891123456 Again, this is just sitting around in AESEncrypt waiting to be found. These two static keys used for encryption lead us on to our big topic under encryption. ─── Proof Snapchat can intercept your media ───────────────────────── Snapchat might not suggest you send state secrets (read: pictures of your privates) via Snapchat, but people seem to do it anyway. We see time and time again apps and workarounds that allow users to save any and all snaps that are sent to them (a recent one being called "Snap Save" for iOS), yet people only think about the user end of the application, often forgetting the servers in between. Thanks to the symmetric encryption used by Snapchat for every snap ever sent, if malicious users gained access to Snapchat's servers, they could potentially: · View all currently unread snaps (without anyone knowing). · Modify or replace currently unread snaps (without anyone knowing). · See old snaps already sent - Snapchat claim to remove snaps once read, but the only people who can be sure of that are Snapchat themselves; as users, we're just taking their word for it. ▌▌▀▀ 0x04 - Advertising ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ Whilst we were browsing the latest changes to the Android client, we came across a new addition which caught our eyes. A new class was added under the name BroadcastSnap, along with a new field called events which was added to the sync and logout tasks. After some resilient digging, we found out a few things about this new mysterious type of snap: · It stores some text and a URL. · They can only be dismissed by opening them. · Double clicking on one will open the URL in your default browser. · Information about the click event is sent to Snapchat in the new events field (url, id, and sender name). · It shows up as an unopened cyan box in the snap feed. (see: gibsonsec.org/snapchat/broadcastsnap.png) · Unlike the rest of the application which uses POST requests, they send a single GET request instead. Just to be rebellious.. · There's no way to generate them as a client - they will have to be created by the server, presumably. Making an educated guess from the information stored in the snap and the name it's given, we can come to two obvious conclusions: · They'll be used by Team Snapchat to send out relevant information and perhaps links to updates of the ToS or some such. · They're going to be used for the in-app advertising which Evan Spiegel has been suggesting they'll implement for a while. They're fairly obtrusive, which will definitely hassle users: · They float to the top of your feed, just begging for attention. · You can only dismiss them by opening them - this takes you out of the app, which might take a while for users with slower devices. · The attached text (which is displayed next to the timestamp) will plague your feed for as long as it takes to be pushed down and out. ▌▌▀▀ 0x05 - Conclusion ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ As can be seen above, Snapchat is far from perfect, but we hope that we can contribute to reinforcing the need for better applications of cryptography in Snapchat and applications like it. Snapchat are in a world where some (if not most) of their users are placing their trust in the security behind the app; they can't fall short on securing their application. Being a relatively new company, Snapchat, like any startup, are going to be working under shorter deadlines, which - although it may benefit the business in the short-term - leaves vulnerabilities in the final product to be found (if ever) and exploited by a malicious third party. We can offer the following advice to anyone developing an application, which can be applied from the projects of a bedroom programmer to a Fortune 500 company: · Implement internal code guidelines; nicely structured code is easier to read, and also leads to less hacky and spaghetti-like code, which can make vulnerabilities harder to find. · Audit your code often, using external services as you would with your finances. · Always plan for the worst case scenario. · Pay attention to security-focused mailing lists, and learn from the vulnerable code of other developers.