Challenge: API Key Security

Discuss game development design and post your game ideas

Post » Tue Jan 03, 2017 5:40 pm

Hey all,

Courtesy of @malinga91 's post regarding non-repudiation of scores, @BackendFreak and I have decided to test this out.

Toby created a basic AJAX client that will make requests back to his server and it is secured using only API keys.

We are opening this up to anyone who wants to try and crack Toby's security on this test project.

The compiled, hosted, minified project is here:
http://tobyr.wtfgamesgroup.com/securitychallenge/

It is the HTML5 export from Construct2, and just makes a secured request back to one of Toby's API servers.
The goal is to submit false data to his server and have the server accept it.

Anyone is welcome to try, please post any successes you have in this thread.
Last edited by gumshoe2029 on Tue Jan 03, 2017 10:23 pm, edited 2 times in total.
https://www.ravenheart.ca/home
Company name changed to avoid Facebook-type shenanigans

"Someone once told me I bite off more than I can chew...

I told them I would rather choke on greatness than nibble on mediocrity."
B
22
S
6
G
1
Posts: 1,414
Reputation: 4,822

Post » Tue Jan 03, 2017 9:55 pm

Just for the record: The request sends an email, password and API key token (like a user registration call). Once you manage to make a fake call (try to register another user) you shall see a server response like this one:

Image

Please post a call URL and the screen shot once you manage to break the security.

This is basically a test of C2 (JS) trick-security without using SSL.

Good Luck! ;)
ImageImage
B
27
S
16
G
68
Posts: 935
Reputation: 38,579

Post » Fri Jan 06, 2017 6:38 pm

I am going to throw out there what I did so far, since I just got swamped with work for my primary project.

capx to test:
https://drive.google.com/open?id=0B-xiq ... 0VPbHA5WHM

the unminified c2runtime.js is here:
https://drive.google.com/open?id=0B-xiq ... DFXekRQYjg

The key encode function is here:
Code: Select all
(function() {
    function c() {}
    var l = null,
        g = cc.prototype;
    g.Ya = function(c) {
        this.aa = c;
        this.b = c.b
    };
    g.Ya.prototype.Z = function() {};
    g.Ga = function(c) {
        this.type = c;
        this.b = c.b
    };
    var n = g.Ga.prototype;
    n.Z = function() {};
    n.Zd = function() {};
    g.e = new function() {};
    g.Qa = new function() {};
    c.prototype.jm = function(c, b, a, g) {
        b = t.encode(b);
        for (var l = 0, l = 0; l < a; l++) b = b.charAt(Math.floor(Math.random() * (b.length - 1))) + b;
        for (l = 0; l < g; l++) b += b.charAt(Math.floor(Math.random() * (b.length - 1)));
        c.ca(b)
    };
    c.prototype.fm = function(c, b) {
        null === l &&
            (l = new SHA1);
        c.ca(l.hash(b))
    };
    g.ga = new c;
    var t = {
        vc: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
        encode: function(c) {
            var b = "",
                a, g, l, d, e, k, f = 0;
            for (c = t.lm(c); f < c.length;) a = c.charCodeAt(f++), g = c.charCodeAt(f++), l = c.charCodeAt(f++), d = a >> 2, a = (a & 3) << 4 | g >> 4, e = (g & 15) << 2 | l >> 6, k = l & 63, isNaN(g) ? e = k = 64 : isNaN(l) && (k = 64), b = b + this.vc.charAt(d) + this.vc.charAt(a) + this.vc.charAt(e) + this.vc.charAt(k);
            return b
        },
        decode: function(c) {
            var b = "",
                a, g, l, d, e, k = 0;
            for (c = c.replace(/[^A-Za-z0-9\+\/\=]/g, ""); k <
                c.length;) a = this.vc.indexOf(c.charAt(k++)), g = this.vc.indexOf(c.charAt(k++)), d = this.vc.indexOf(c.charAt(k++)), e = this.vc.indexOf(c.charAt(k++)), a = a << 2 | g >> 4, g = (g & 15) << 4 | d >> 2, l = (d & 3) << 6 | e, b += String.fromCharCode(a), 64 != d && (b += String.fromCharCode(g)), 64 != e && (b += String.fromCharCode(l));
            return b = t.km(b)
        },
        lm: function(c) {
            c = c.replace(/\r\n/g, "\n");
            for (var b = "", a = 0; a < c.length; a++) {
                var g = c.charCodeAt(a);
                128 > g ? b += String.fromCharCode(g) : (127 < g && 2048 > g ? b += String.fromCharCode(g >> 6 | 192) : (b += String.fromCharCode(g >>
                    12 | 224), b += String.fromCharCode(g >> 6 & 63 | 128)), b += String.fromCharCode(g & 63 | 128))
            }
            return b
        },
        km: function(c) {
            for (var b = "", a = 0, g = 0, l = 0, d = 0; a < c.length;) g = c.charCodeAt(a), 128 > g ? (b += String.fromCharCode(g), a++) : 191 < g && 224 > g ? (l = c.charCodeAt(a + 1), b += String.fromCharCode((g & 31) << 6 | l & 63), a += 2) : (l = c.charCodeAt(a + 1), d = c.charCodeAt(a + 2), b += String.fromCharCode((g & 15) << 12 | (l & 63) << 6 | d & 63), a += 3);
            return b
        }
    }
})();


The basic encoding scheme seems to be looping through the initial input length and choosing pseudorandom characters from this set:
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=

I pulled the encode function straight out of the JS, but the length varies and I am not versed enough in JS (or really I don't have the right tools in hand) to trace the input variables back to the HTML.

The key function to trace back out is:
Code: Select all
c.prototype.jm = function(c, b, a, g)

and tracing c, b, a and g back out to the HTML inputs, from there you can just fabricate inputs and retrace the steps back down to the encode function.
https://www.ravenheart.ca/home
Company name changed to avoid Facebook-type shenanigans

"Someone once told me I bite off more than I can chew...

I told them I would rather choke on greatness than nibble on mediocrity."
B
22
S
6
G
1
Posts: 1,414
Reputation: 4,822


Return to Game Development, Design & Ideas

Who is online

Users browsing this forum: No registered users and 2 guests