Skip to content

Latest commit

 

History

History
170 lines (141 loc) · 6.87 KB

greatsuccess.md

File metadata and controls

170 lines (141 loc) · 6.87 KB

GreatSuccess

Task

Great React! I'm very excite!

File: memeApp.ipa

Solution

We start off by extracting the zip archive:

$ unzip memeApp.ipa

We now can start digging around the application. Things of interest:

  • memeApp - Mach-O universal binary with 2 architectures: [armv7:Mach-O armv7 executable, flags:<NOUNDEFS|DYLDLINK|TWOLEVEL|WEAK_DEFINES|BINDS_TO_WEAK|PIE>] [arm64]
  • main.jsbundle - ASCII text, with very long lines
  • AccessibilityResources.bundle/en.lproj/Localizable.strings - ASCII text
  • assets/app.json - JSON data

The last two don't contain anything interesting. Before we start with the binary, let's look at the jsbundle.

Fast scrolling through yields this:

  • SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED
  • FxUCKisKTiwHHjRVFi8GHgtFQEoJFhcXKjYOBQRFnMcIgAfAFVtYCIEBhw9NhcKFAgYRywTBAQTWFceMAweFGA2MwkuBApJZRMKBAhUbVI4AS0KKww8CDI6H0UlIUc0LFdtWiI6HBY6NhAQKAYcXzpgNiI6RltfOjoQHBEMGwAoEA1JZzE=
  • https://greatsuccess.appsecil.ctf.today/key -> VeryNiceKey,ILike123

The second one is base64 but the decoded version is useless. We need to find out what's going on here.

This might be worth reading: https://github.com/OWASP/owasp-mstg/blob/master/Document/0x06c-Reverse-Engineering-and-Tampering.md#patching-react-native-applications

I started of beautifying the jsbundle, JStillery threw me errors but there are lots of other beautifiers out there.

I then opened it in an IDE that analyzes the script and supports me with Find Usage and Go to declaration tools. After all, I didn't really need them but it is a good starting point.

We find our URL again:

__d(function(g, r, i, a, m, e, d) {
    var t = r(d[0]),
        c = r(d[1]);
    Object.defineProperty(e, "__esModule", {
        value: !0
    }), e.default = void 0;
    var n = c(r(d[2])),
        l = t(r(d[3])),
        f = r(d[4]),
        o = r(d[5]),
        u = {
            uri: "[not useful]"
        };

    function s(t) {
        var c = (0, l.useState)("Get Memed"),
            f = (0, n.default)(c, 2),
            o = f[0],
            u = f[1],
            s = (0, l.useState)(!1),
            h = (0, n.default)(s, 2),
            I = h[0],
            w = h[1];
        return (0, l.useEffect)(function() {
            fetch(t).then(function(t) {
                return t.text()
            }).then(function(t) {
                console.log('response: ' + t), u(t)
            }).catch(function() {
                w(!0)
            })
        }, [t]), [o, I]
    }
    var h = o.StyleSheet.create({...stuff...}),
        I = function() {
            var t = s("https://greatsuccess.appsecil.ctf.today/key"),
                c = (0, n.default)(t, 2),
                I = c[0],
                w = (c[1], (0, f.decrypt)(I, "FxUCKisKTiwHHjRVFi8GHgtFQEoJFhcXKjYOABQRFnMcIgAfAFVtYCIEBhw9NhcKFAgYRywTBAQTWFceMAweFGA2MwkuBApJZRMKBAhUbVI4AS0KKww8CDI6H0UlIUc0LFdtWiI6HBY6NhAQKAYcXzpgNiI6RltfOjoQHBEMGwAoEA1JZzE="));
            return console.log(w), l.default.createElement(o.View, {
                style: h.container
            }, l.default.createElement(o.ImageBackground, {
                source: u,
                style: h.image
            }, l.default.createElement(o.Text, {
                style: h.text
            }, w)))
        };
    e.default = I
}, 402, [9, 1, 14, 55, 403, 2]);

If you've ever looked at bundled javascript it's not that hard to figure out what's going on here.

We have a module resolver function __d which provides it's first parameter with the dependencies listed in the third parameter. The position of the module itself is the second parameter.

So we have module 402 that depends on [9, 1, 14, 55, 403, 2]. The inner function s seems to fetch the content of a page. The fetched content is probably used as a key in the call to decrypt the base64. f = r(d[4]) is module 403:

__d(function(g, r, i, a, m, e, d) {
    var t = r(d[0]);
    Object.defineProperty(e, "__esModule", {
        value: !0
    }), e.encrypt = function(t, o) {
        return xorEncrypt = function(t, c) {
            return n.default.map(c, function(n, c) {
                return n.charCodeAt(0) ^ t.charCodeAt(Math.floor(c % t.length))
            })
        }, b64Encode = function(t) {
            var n, o, h, u, f, l, p = 0,
                A = '';
            if (!t) return t;
            do {
                n = (f = t[p++] << 16 | t[p++] << 8 | t[p++]) >> 18 & 63, o = f >> 12 & 63, h = f >> 6 & 63, u = 63 & f, A += c.charAt(n) + c.charAt(o) + c.charAt(h) + c.charAt(u)
            } while (p < t.length);
            return ((l = t.length % 3) ? A.slice(0, l - 3) : A) + '==='.slice(l || 3)
        }, o = this.xorEncrypt(t, o), this.b64Encode(o)
    }, e.decrypt = function(t, o) {
        return b64Decode = function(t) {
            var n, o, h, u, f, l, p = 0,
                A = [];
            if (!t) return t;
            t += '';
            do {
                n = (l = c.indexOf(t.charAt(p++)) << 18 | c.indexOf(t.charAt(p++)) << 12 | (u = c.indexOf(t.charAt(p++))) << 6 | (f = c.indexOf(t.charAt(p++)))) >> 16 & 255, o = l >> 8 & 255, h = 255 & l, A.push(n), 64 !== u && (A.push(o), 64 !== f && A.push(h))
            } while (p < t.length);
            return A
        }, xorDecrypt = function(t, c) {
            return n.default.map(c, function(n, c) {
                return String.fromCharCode(n ^ t.charCodeAt(Math.floor(c % t.length)))
            }).join('')
        }, o = this.b64Decode(o), this.xorDecrypt(t, o)
    }, e.b64Table = void 0;
    var n = t(r(d[1])),
        c = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
    e.b64Table = c
}, 403, [1, 404]);

We see some xor-ing going on. Let's write a function to xor the base64 decoded chars and print them out:

// Extracted from above
var decrypt = function(t) {
            var n, o, h, u, f, l, p = 0,
                A = [];
            if (!t) return t;
            t += '';
            do {
                n = (l = c.indexOf(t.charAt(p++)) << 18 | c.indexOf(t.charAt(p++)) << 12 | (u = c.indexOf(t.charAt(p++))) << 6 | (f = c.indexOf(t.charAt(p++)))) >> 16 & 255, o = l >> 8 & 255, h = 255 & l, A.push(n), 64 !== u && (A.push(o), 64 !== f && A.push(h))
            } while (p < t.length);
            return A
}

var c = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
var k = "VeryNiceKey,ILike123";

var chars = decrypt("FxUCKisKTiwHHjRVFi8GHgtFQEoJFhcXKjYOABQRFnMcIgAfAFVtYCIEBhw9NhcKFAgYRywTBAQTWFceMAweFGA2MwkuBApJZRMKBAhUbVI4AS0KKww8CDI6H0UlIUc0LFdtWiI6HBY6NhAQKAYcXzpgNiI6RltfOjoQHBEMGwAoEA1JZzE=").map(x => String.fromCharCode(x));

var out = [];
for (var i = 0; i < chars.length; i++) {
  out.push(String.fromCharCode(chars[i].charCodeAt(0) ^ k.charCodeAt(i % k.length)));
}

console.log(out.join(''));

We get: AppSec-IL{My_country_send_me_to_United_States_to_make_movie-film._Please,_come_and_see_my_film._If_it_not_success,_I_will_be_execute.}