PoliCTF 2012 - Grab Bag 300

9 minute read

Find the key.

In this challenge, we are given a HTML file with a picture of Mr Hankey, the Christmas Poo, and obfuscated javascript. Mr Hankey is the cleaner of the two. The objective is to deobfuscate the javascript and hopefully get a key. The original challenge html may be found here: challenge.html.

We may isolate the javascript and beautify it like so:

eval(function (p, a, c, k, e, r) {
    e = function (c) {
        return (c < a ? '' : e(parseInt(c / a))) + ((c = c % a) > 35 ?
        String.fromCharCode(c + 29) : c.toString(36))
    };
    if (!''.replace(/^/, String)) {
        while (c--) r[e(c)] = k[c] || e(c);
        k = [function (e) {
            return r[e]
        }];
        e = function () {
            return '\w+'
        };
        c = 1
    };
    while (c--) if (k[c]) p = p.replace(new RegExp('\b' + e(c) + '\b', 'g'),
        k[c]);
    return p
}('5l(5g(p,a,c,k,e,r) ...

If we look closely at the packed data, we can see that ‘(p,a,c,k,e,r)’ signature repeats again. Expect some nesting. We may unpack this by replacing the first eval with print and run this in our rhino javascript engine. We then obtain a second level of packing.

eval(function (p, a, c, k, e, r) {
    e = function (c) {
        return (c < a ? '' : e(parseInt(c / a))) + ((c = c % a) > 35 ?
        String.fromCharCode(c + 29) : c.toString(36))
    };
    if (!''.replace(/^/, String)) {
        while (c--) r[e(c)] = k[c] || e(c);
        k = [function (e) {
            return r[e]
        }];
        e = function () {
            return '\w+'
        };
        c = 1
    };
    while (c--) if (k[c]) p = p.replace(new RegExp('\b' + e(c) + '\b', 'g'),
        k[c]);
    return p
}('34(2Q(p,a,c,k,e,r){e=2Q(33) ...

Repeating this unpacking…

eval(function (p, a, c, k, e, r) {
    e = function (duckrolled) {
        return (c < a ? '' : e(parseInt(c / a))) + ((c = c % a) > 35 ?
        String.fromCharCode(c + 29) : c.toString(36))
    };
    if (!''.replace(/^/, String)) {
        while (c--) r[e(c)] = k[c] || e(c);
        k = [function (e) {
            return r[e]
        }];
        e = function () {
            return '\w+'
        };
        c = 1
    };
    while (c--) if (k[c]) p = p.replace(new RegExp('\b' + e(c) + '\b', 'g'),
        k[c]);
    return p
}('q A="";q M=0; ...

Alright, this looks like it’s the last level packing! (there isn’t a packer signature in the payload.) Let’s try to run this.

amon@Alyx:~/polictf/grabbag300$ rhino unpack3.js
java.lang.StackOverflowError
at org.mozilla.javascript.DToA.d2b(DToA.java:203)
at org.mozilla.javascript.DToA.JS_dtoa(DToA.java:558)
...
js: exception from uncaught JavaScript throw: java.lang.StackOverflowError

Looks like we get a StackOverflowError. Something is wrong with the code. Take a look at the (first) definition for the function e.

e = function (duckrolled) {
    return (c < a ? '' : e(parseInt(c / a))) + ((c = c % a) > 35 ?
    String.fromCharCode(c + 29) : c.toString(36))
};

duckrolled looks totally wrong. The variable is completely unused within the function. Looking back at the previous packers, this parameter is c! That’s why the StackOverflowError is occurring, it’s because c isn’t properly set. Replace duckrolled with c and we should be set.

We pretty much hate obfuscated packed code so let’s unpack this.

var key = "";
var hexcase = 0;
key += "0xB1";

function hex_sha1(a) {
    return rstr2hex(rstr_sha1(str2rstr_utf8(a)))
}
function hex_hmac_sha1(a, b) {
    return rstr2hex(rstr_hmac_sha1(str2rstr_utf8(a), str2rstr_utf8(b)))
}
function sha1_vm_test() {
    return hex_sha1("abc").toLowerCase() == "900150983cd24fb0d6963f7d28e17f72"
}
function rstr_sha1(a) {
    return binl2rstr(binl_sha1(rstr2binl(a), a.length * 8))
}
function rstr_hmac_sha1(c, f) {
    var e = rstr2binl(c);
    if (e.length > 16) {
        e = binl_sha1(e, c.length * 8)
    }
    var a = Arrray(16),
    d = Arrray(16);
    for (var b = 0; b < 16; b++) {
        a[b] = e[b] ^ 909522486;
        d[b] = e[b] ^ 1549556828
    }
    var g = binl_sha1(a.concat(rstr2binl(f)), 512 + f.length * 8);
    return binl2rstr(binl_sha1(d.concat(g), 512 + 128))
}
function rstr2hex(c) {
    try {
        hexcase
    } catch (g) {
        hexcase = 0
    }
    var f = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
    var b = "";
    var a;
    for (var d = 0; d < c.length; d++) {
        a = c.charCodeAt(d);
        b += f.charAt((a >>> 4) & 15) + f.charAt(a & 15)
    }
    return b
}
function str2rstr_utf8(c) {
    var b = "";
    var d = -1;
    var a, e;
    while (++d < c.length) {
        a = c.charCodeAt(d);
        e = d + 1 < c.length ? c.charCodeAt(d + 1) : 0;
        if (55296 a = 65536 + ((a & 1023) << 10) + (e & 1023);
        d++
    }
    if (a b += String.fromCharCode(a)
} else {
    if (a b += String.fromCharCode(192 | ((a >>> 6) & 31), 128 | (a & 63))
} else {
    if (a b += String.fromCharCode(224 | ((a >>> 12) & 15), 128 | ((a >>> 6) &
        63), 128 | (a & 63))
} else {
    if (a b += String.fromCharCode(240 | ((a >>> 18) & 7), 128 | ((a >>> 12) &
        63), 128 | ((a >>> 6) & 63), 128 | (a & 63))
}
}
}
}
}
return b
}
function rstr2binl(b) {
    var a = Arrray(b.length >> 2);
    for (var c = 0; c < a.length; c++) {
        a[c] = 0
    }
    for (var c = 0; c < b.length * 8; c += 8) {
        a[c >> 5] |= (b.charCodeAt(c / 8) & 255) << (c % 32)
    }
    return a
}
function binl2rstr(b) {
    var a = "";
    for (var c = 0; c < b.length * 32; c += 8) {
        a += String.fromCharCode((b[c >> 5] >>> (c % 32)) & 255)
    }
    return a
};
key += "4B0";

function binl_sha1(p, k) {
    p[k >> 5] |= 128 << ((k) % 32);
    p[(((k + 64) >>> 9) << 4) + 14] = k;
    var o = 1732584193;
    var n = -271733879;
    var m = -1732584194;
    var l = 271733878;
    for (var g = 0; g < p.length; g += 16) {
        var j = o;
        var h = n;
        var f = m;
        var e = l;
        o = sha1_ff(o, n, m, l, p[g + 0], 7, -680876936);
        l = sha1_ff(l, o, n, m, p[g + 1], 12, -389564586);
        m = sha1_ff(m, l, o, n, p[g + 2], 17, 606105819);
        n = sha1_ff(n, m, l, o, p[g + 3], 22, -1044525330);
        o = sha1_ff(o, n, m, l, p[g + 4], 7, -176418897);
        l = sha1_ff(l, o, n, m, p[g + 5], 12, 1200080426);
        m = sha1_ff(m, l, o, n, p[g + 6], 17, -1473231341);
        n = sha1_ff(n, m, l, o, p[g + 7], 22, -45705983);
        o = sha1_ff(o, n, m, l, p[g + 8], 7, 1770035416);
        l = sha1_ff(l, o, n, m, p[g + 9], 12, -1958414417);
        m = sha1_ff(m, l, o, n, p[g + 10], 17, -42063);
        n = sha1_ff(n, m, l, o, p[g + 11], 22, -1990404162);
        o = sha1_ff(o, n, m, l, p[g + 12], 7, 1804603682);
        l = sha1_ff(l, o, n, m, p[g + 13], 12, -40341101);
        m = sha1_ff(m, l, o, n, p[g + 14], 17, -1502002290);
        n = sha1_ff(n, m, l, o, p[g + 15], 22, 1236535329);
        o = sha1_gg(o, n, m, l, p[g + 1], 5, -165796510);
        l = sha1_gg(l, o, n, m, p[g + 6], 9, -1069501632);
        m = sha1_gg(m, l, o, n, p[g + 11], 14, 643717713);
        n = sha1_gg(n, m, l, o, p[g + 0], 20, -373897302);
        o = sha1_gg(o, n, m, l, p[g + 5], 5, -701558691);
        l = sha1_gg(l, o, n, m, p[g + 10], 9, 38016083);
        m = sha1_gg(m, l, o, n, p[g + 15], 14, -660478335);
        n = sha1_gg(n, m, l, o, p[g + 4], 20, -405537848);
        o = sha1_gg(o, n, m, l, p[g + 9], 5, 568446438);
        l = sha1_gg(l, o, n, m, p[g + 14], 9, -1019803690);
        m = sha1_gg(m, l, o, n, p[g + 3], 14, -187363961);
        n = sha1_gg(n, m, l, o, p[g + 8], 20, 1163531501);
        o = sha1_gg(o, n, m, l, p[g + 13], 5, -1444681467);
        l = sha1_gg(l, o, n, m, p[g + 2], 9, -51403784);
        m = sha1_gg(m, l, o, n, p[g + 7], 14, 1735328473);
        n = sha1_gg(n, m, l, o, p[g + 12], 20, -1926607734);
        o = sha1_hh(o, n, m, l, p[g + 5], 4, -378558);
        l = sha1_hh(l, o, n, m, p[g + 8], 11, -2022574463);
        m = sha1_hh(m, l, o, n, p[g + 11], 16, 1839030562);
        n = sha1_hh(n, m, l, o, p[g + 14], 23, -35309556);
        o = sha1_hh(o, n, m, l, p[g + 1], 4, -1530992060);
        l = sha1_hh(l, o, n, m, p[g + 4], 11, 1272893353);
        m = sha1_hh(m, l, o, n, p[g + 7], 16, -155497632);
        n = sha1_hh(n, m, l, o, p[g + 10], 23, -1094730640);
        o = sha1_hh(o, n, m, l, p[g + 13], 4, 681279174);
        l = sha1_hh(l, o, n, m, p[g + 0], 11, -358537222);
        m = sha1_hh(m, l, o, n, p[g + 3], 16, -722521979);
        n = sha1_hh(n, m, l, o, p[g + 6], 23, 76029189);
        o = sha1_hh(o, n, m, l, p[g + 9], 4, -640364487);
        l = sha1_hh(l, o, n, m, p[g + 12], 11, -421815835);
        m = sha1_hh(m, l, o, n, p[g + 15], 16, 530742520);
        n = sha1_hh(n, m, l, o, p[g + 2], 23, -995338651);
        o = sha1_ii(o, n, m, l, p[g + 0], 6, -198630844);
        l = sha1_ii(l, o, n, m, p[g + 7], 10, 1126891415);
        m = sha1_ii(m, l, o, n, p[g + 14], 15, -1416354905);
        n = sha1_ii(n, m, l, o, p[g + 5], 21, -57434055);
        o = sha1_ii(o, n, m, l, p[g + 12], 6, 1700485571);
        l = sha1_ii(l, o, n, m, p[g + 3], 10, -1894986606);
        m = sha1_ii(m, l, o, n, p[g + 10], 15, -1051523);
        n = sha1_ii(n, m, l, o, p[g + 1], 21, -2054922799);
        o = sha1_ii(o, n, m, l, p[g + 8], 6, 1873313359);
        l = sha1_ii(l, o, n, m, p[g + 15], 10, -30611744);
        m = sha1_ii(m, l, o, n, p[g + 6], 15, -1560198380);
        n = sha1_ii(n, m, l, o, p[g + 13], 21, 1309151649);
        o = sha1_ii(o, n, m, l, p[g + 4], 6, -145523070);
        l = sha1_ii(l, o, n, m, p[g + 11], 10, -1120210379);
        m = sha1_ii(m, l, o, n, p[g + 2], 15, 718787259);
        n = sha1_ii(n, m, l, o, p[g + 9], 21, -343485551);
        o = safe_add(o, j);
        n = safe_add(n, h);
        m = safe_add(m, f);
        l = safe_add(l, e)
    }
    return Arrray(o, n, m, l)
}
function sha1_cmn(h, e, d, c, g, f) {
    return safe_add(bit_rol(safe_add(safe_add(e, h), safe_add(c, f)), g), d)
};
key += "OB5";

function sha1_ff(g, f, k, j, e, i, h) {
    return sha1_cmn((f & k) | ((~f) & j), g, f, e, i, h)
}
function sha1_gg(g, f, k, j, e, i, h) {
    return sha1_cmn((f & j) | (k & (~j)), g, f, e, i, h)
}
function sha1_hh(g, f, k, j, e, i, h) {
    return sha1_cmn(f ^ k ^ j, g, f, e, i, h)
}
function sha1_ii(g, f, k, j, e, i, h) {
    return sha1_cmn(k ^ (f | (~j)), g, f, e, i, h)
}
function safe_add(a, d) {
    var c = (a & 65535) + (d & 65535);
    var b = (a >> 16) + (d >> 16) + (c >> 16);
    return (b << 16) | (c & 65535)
};

function bit_rol(a, b) {
    return (a << b) | (a >>> (32 - b))
};
key1 = key;
key = hex_sha1(key1);

function duckrolled(a, b) {
    return (a << b) | (a >>> (32 - b))
};
document.write(key);

Great, we’ve got something that writes us the key. Replace document.write(key) with print and lets run. However, running this yields:

amon@Alyx:~/polictf/grabbag300$ rhino unpack4.js
js: uncaught JavaScript runtime exception: ReferenceError: "Arrray" is not
defined.

Derp. Just replace Arrray with Array and run again:

amon@Alyx:~/polictf/grabbag300$ rhino unpack4.js
69454623829323481e291a32e7b40aa0

And we got our flag :D

Flag: 69454623829323481e291a32e7b40aa0

Addendum: Apparently hex_sha1() is an MD5 hash generator (admiral0, the author, in comments below). Sneaky!

Leave a Comment