Challenge: API Key Security

  • Hey all,

    Courtesy of malinga91 's post regarding non-repudiation of scores, 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.

  • Try Construct 3

    Develop games in your browser. Powerful, performant & highly capable.

    Try Now Construct 3 users don't see these ads
  • 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:

    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!

  • 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:

    (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
            }
        }
    })();
    [/code:cfu0wb6o]
    
    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:cfu0wb6o]c.prototype.jm = function(c, b, a, g)[/code:cfu0wb6o]
    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.
Jump to:
Active Users
There are 1 visitors browsing this topic (0 users and 1 guests)