CSAW CTF 2013 - Web 300 - herpderper

Monday, September 23, 2013 » csaw, ctf, stratum auhuur

Web 300 challenge of CSAW CTF 2013

Description

herpderper.apk

late hint: "Hint for herpderper: Simple web bugs are simple."

Tools:

  • dex2jar
  • jd-gui
  • apktool

Convert the apk using dex2jar, unzip, analyze app stucture using jd-gui. The only request that is ever send is done in ops.black.herpderper.AuthRequest.doInBackground(String[]) which for some reason cannot be decompiled. (We couldn't get the app to install on an Android emulator and on several phones indicating that the bytecode had been manipulated). However, disassembling was possible:

I disected that method using the disassembled dex file (apktool), as I found it easier to understand (Dalvik is a register machine with virtually unlimited registers, while JVM is a stack machine). As I never read more than minimal examples of assembler before, I turned doInBackground to pseudocode first:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
protected doInBackground(String[] uri) {
    v14 = android.os.Debug.isDebuggerConnected();
    if (!v14) {
        short v14 = 0x539;
        v14 = v14 / 0;
    } else {
        java.net.URL v11 = 0;
        try {
            v14 = 0;
            v14 = uri[v14];
            v12 = new java.net.URL(v14);
        } catch (java.net.MalformedURLException) {
        }
        v11 = v12;
        java.net.HttpURLConnection v13 = 0;
        try {
            v14 = v11.openConnection();
            v0 = v14;
            assert v0 instanceof java.net.HttpURLConnection;
            v13 = v0;
            v14 = 1;
            v13.setDoOutput(v14);
            v14 = "POST";
            v13.setRequestMethod(v14);
            ops.black.herpderper.TrustModifier.relaxHostChecking(v13);
            v5 = v13.getOutputStream();
            v14 = 1;
            v14 = uri[v14];
            v15 = "UTF-8";
            v14 = v14.getBytes(v15);
            v15 = 0;
            String v2 = android.util.Base64.encodeToString(v14, v15);
            v14 = 2;
            v14 = uri[v14];
            v15 = "UTF-8";
            v14 = v14.getBytes(v15);
            v15 = 0;
            String v8 = android.util.Base64.encodeToString(v14, v15);
            v14 = "\n";
            v15 = "";
            v14 = v2.replace(v14, v15);
            v15 = "\r";
            v16 = "";
            v2 = v14.replace(v15, v16);
            v14 = "\n";
            v15 = "";
            v4 = v8.replace(v14, v15);
            v15 = "\r";
            v16 = "";
            v8 = v14.replace(v15, v16);
            v14 = new StringBuilder();
            v15 = "identity=";
            v14 = v14.append(v15);
            v14 = v14.append(v2);
            v15 = "&secret=";
            v14 = v14.append(v15);
            v14 = v14.append(v8);
            v15 = "&integrityid=";
            v14 = v14.append(v15);
            v15 = 3;
            v15 = uri[v15];
            v14 = v14.append(v15);
            v9 = v14.toString();
            v14 = v9.getBytes();
            v5.write(v14);
            v5.close();
            v13.connect();
        } catch (Exception) {
        }
        v0 = p0;
        v7 = v0;
        try {
            v14 = v13.getInputStream();
            v3 = new BufferedInputStream(v14);
            v14 = new java.io.InputStreamReader(v3);
            v6 = new BufferedReader(v14);
            v10 = new java.lang.StringBuilder();
            while (true) {
                v4 = v6.readLine();
                if (!v4 == 0) { // break? -> :cond_1
                    v10.append(v4);
                } else {
                    v7 = v10.toString();
                    v13.disconnect();
                    return v7;
                }
            }
        } catch (org.apache.http.client.ClientProtocolException v14) {
            v13.disconnect();
        } catch (java.lang.Exception) {
        }
    }
}

doInBackground takes an array of strings representing the URL, the mail address, password and apk signature.
It does only post requests to one URL: https://webchal.isis.poly.edu/csaw.php
parameters:

1
2
3
identity = base64(email)
secret = base64(password)
integrityid = apk signature

I used a Java program I found somewhere to generate the needed apk signature:

1
3082019f30820108a0030201020204522f840b300d06092a864886f70d0101050500301431123010060355040b1309426c61636b204f7073301e170d3133303931303230343134375a170d3338303930343230343134375a301431123010060355040b1309426c61636b204f707330819f300d06092a864886f70d010101050003818d0030818902818100cf6ecf73522d132c654ba9d9448e3051099e16283b68ef7872779e29cf517cbdb9dbeadced28147b8bc0e2cf93a02aff855561258a20cf107fe79fc1b56479fd706760f8a6a5bdeba2dc9ea810c5b7954fea9b62d96f3d66743b7723f57578e814939a23262be7bdd0aca74cfc0bd06ec8e267861161075d00edd29e1ed7d29d0203010001300d06092a864886f70d0101050500038181003289f625b0d425dd9eb49c7d5113f3f9f39d72dd56c56684aeeede3e8e99aaf279b9e5c994b4f8f1d5ecb0941ffb7cb8dd3fa58c60926127ebe2a85531c1c1885f9ae588af1bd91ebc3ce41259818569663d9ec66cdbfb08993e20c046b2dcd0ca54e52e84dc1866c824a586ce452750b9df09c2a5fca4a05e3746db3aae9fa9

Requests which lack all three parameters are replied with:

1
<html><body>please access the site using the mobile application</body></html>

Unless the integrityid is correct, the reply is:

1
{"response":{"status":"failure","msg":"Client integrity fault"}}

Logging in with any identity and secret returns:

1
{"response":{"status":"failure","msg":"Login failed"},"timeStamp":"1379429423","tZ":"America/New_York","reqResourceId":"webchal.isis.poly.edu","clientId":{"identitySig":"d033e22ae348aeb5660fc2140aec35850c4da997","role":"anonymous","accessToken":"YWRtaW46YW5vbnltb3VzOndlYmNoYWwuaXNpcy5wb2x5LmVkdQ=="}}

After some fiddling we found out that you could set an additional parameter "role" to base64('admin') which in combination with both identity and secret set to the same value yielded the key:

1
{"response":{"status":"success","msg":"Key: Yo dawg I heard you leik to derp so i put a herp in your derp so you could herpderp while you derpderp"},"timeStamp":"1379429491","tZ":"America/New_York","reqResourceId":"webchal.isis.poly.edu","clientId":{"identitySig":"d033e22ae348aeb5660fc2140aec35850c4da997","role":"admin","accessToken":"YWRtaW46YWRtaW46d2ViY2hhbC5pc2lzLnBvbHkuZWR1"}}

The site uses a self-signed certificate, but which you could have mitm'ed because of bad certificate checking in the app (only reproducible parts of the certificate are checked), but we didn't successfully pursue that.

+= 300