32C3 CTF – Android Reverse-Engineering libdroid

January 9, 2016

I unfortunately didn’t have time to participate to the CCC CTF this year, but I wanted to look at the android reverse challenge and see if I could solve it using the Xposed Framework. So here we go, same toolkit as last time, Jadx, Genymotion and Android Studio (see here)

Firing up an emulator with API 23 and starting the app shows a keypad with cute little smileys. After clicking on “bear”, “bear”, “ghost”, “monkey”,”heart”, “burger”, a message appears, “no rootkit for you”. Same happens after pressing the “get flag” button.

app

So let’s open the app in jadx. There we can see an obfuscated class with native calls

    public static native String generateConfusion();

    public static native String getFlag();

    public static native String getOperatingSystem();

    public static native String getPhoneNumber();

    public static native String installRootkit();

    public static native String installiOS();

    public static native String obtainWorldDomination();

    private static native void phoneHome(byte[] bArr, byte[] bArr2);

    static {
        System.loadLibrary("libdroid");
        a = a(getOperatingSystem(), 1);
        b = a(getPhoneNumber(), 1);
        c = a(installRootkit(), 1);
        d = a(generateConfusion(), 1);
        f = a(obtainWorldDomination(), 1);
        g = a(installiOS(), 1);
        flag = a(getFlag(), 1);
    }

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView((int) R.layout.activity_a);
        try {
            a(a);
        } catch (Exception e) {
        }
        this.e = BuildConfig.FLAVOR;
    }

    void a(String a) throws Exception {
        InputStream b = getAssets().open(a);
        ByteArrayOutputStream b2 = new ByteArrayOutputStream();
        byte[] data = new byte[AccessibilityNodeInfoCompat.ACTION_COPY];
        while (true) {
            int nRead = b.read(data, 0, data.length);
            if (nRead == -1) {
                break;
            }
            b2.write(data, 0, nRead);
        }
        b2.flush();
        BufferedReader b4 = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(a(b2.toByteArray(), b))));
        while (true) {
            String c = b4.readLine();
            if (c != null) {
                if (c.startsWith(g)) {
                    g = c.substring(g.length());
                }
                if (c.startsWith((String) f)) {
                    f = Base64.decode(c.substring(((String) f).length()), 0);
                }
            } else {
                return;
            }
        }
    }

So when the app is starting, some static values are initialized and then modified again in the a(a) method.
The interesting part is the click event handler a(View v).


public void a(View v) {
        if (v.getId() == R.id.button) {
            this.e += d.charAt(1);
        }
        if (v.getId() == R.id.button2) {
            this.e += d.charAt(2);
        }
        if (v.getId() == R.id.button3) {
            this.e += d.charAt(3);
        }
        if (v.getId() == R.id.button4) {
            this.e += d.charAt(4);
        }
        if (v.getId() == R.id.button5) {
            this.e += d.charAt(5);
        }
        if (v.getId() == R.id.button6) {
            this.e += d.charAt(6);
        }
        if (v.getId() == R.id.button7) {
            this.e += d.charAt(7);
        }
        if (v.getId() == R.id.button8) {
            this.e += d.charAt(8);
        }
        if (v.getId() == R.id.button9) {
            this.e += d.charAt(9);
        }
        if (v.getId() == R.id.button10) {
            this.e += d.charAt(0);
        }
        if (this.e.length() == 6 || v.getId() == R.id.button11) {
            CharSequence flag = flag;
            try {
                InputStream b = getAssets().open(g);
                ByteArrayOutputStream b2 = new ByteArrayOutputStream();
                byte[] data = new byte[AccessibilityNodeInfoCompat.ACTION_COPY];
                while (true) {
                    int nRead = b.read(data, 0, data.length);
                    if (nRead == -1) {
                        break;
                    }
                    b2.write(data, 0, nRead);
                }
                b2.flush();
                byte[] j = b2.toByteArray();
                byte[] f_ = new byte[16];
                System.arraycopy((byte[]) f, 0, f_, 0, ((byte[]) f).length);
                System.arraycopy(this.e.getBytes(), 0, f_, 10, this.e.getBytes().length);
                phoneHome(j, f_);
                if (new String(j).startsWith(c)) {
                    flag = new String(j);
                }
            } catch (Exception e1) {
                e1.printStackTrace();
            }
            Snackbar.make(v, flag, 0).setAction((CharSequence) "Action", null).show();
            this.e = BuildConfig.FLAVOR;
        }
    }

So everytime a key is pressed, a new character will be appended to the attribute “e”. If its length is 6 or the “get flag” button is pressed, some magic is done and a result is displayed in a message using a Snackbar.

Let’s bruteforce it! There are only 999999 possibilities.
The idea is then to set the variable “e” and simulate a click on the “get flag” button.

We first need to find the charset for “e”, ie we need the value of “d” (this.e += d.charAt(x);). We have seen above that “d” is set in the onCreate method, so let’s hook it


findAndHookMethod("ctf.stratumauhhur.libdroid.a", lpparam.classLoader, "onCreate", Bundle.class, new XC_MethodHook() {

	@Override
	protected void afterHookedMethod(MethodHookParam param) throws Throwable {
	    String d = (String) XposedHelpers.getObjectField(param.thisObject,"d");
	    XposedBridge.log("d= " + d);
	}
});

Running the app returns

I/Xposed: d=  1234567890

So the charset is only digits, isn’t it? Wrong, wrong, wrong! (it did take me some time to realize my mistake…) It’s not “1234567890” but ” 123456789″…

Now we are nearly ready to start the brute force. We need somehow to log the popup message to get the flag. So let’s hook the make() method of the snackbar and log everything that doesn’t start with the default error message:

findAndHookMethod("android.support.design.widget.Snackbar", lpparam.classLoader, "make", View.class, CharSequence.class, int.class, new XC_MethodHook() {

    @Override
    protected void afterHookedMethod(MethodHookParam param) throws Throwable {
        if (!((String) param.args[1]).startsWith("Sorry")) {
            XposedBridge.log("message= " + param.args[1]);
        }
    }
});

Here the pseudo-code for the brute force

String formatted = String.format("%06d", cnt.getAndIncrement());
if (formatted.endsWith("00000")) {
    XposedBridge.log("Count= " + formatted);
}
formatted = formatted.replaceAll("0"," ");
XposedHelpers.setObjectField(object, "e", formatted);
XposedHelpers.callMethod(object, "a", p.args[0]);

And then we start the app and press the “get flag” button to start the brute force. In less than 2 minutes, we get the flag

flag

Thanks again 0x3cute for showing me this framework!

0

SECCON 2015 Online CTF – Reverse-Engineering Android APK 1

December 7, 2015

After reading a write up of the Trend Micro CTF about someone discovering the Xposed Framework and wanting to use it to solve CTF challenges, I decided to do the same.
In short, the Xposed framework allows to hook methods from an android application without having to modify the app.

What I used:
Genymotion (site) (android emulator faster than the stock one)
Xposed Framework (site)
jadx (site) dex to java decompiler
Android Studio (to write the Xposed module)

After opening the apk in jadx, the relevant parts are found in the MainActivity

Screen Shot 2015-12-07 at 12.56.16

In particularly:

if (1000 == MainActivity.this.cnt) {
    tv3.setText("SECCON{" + String.valueOf((MainActivity.this.cnt + MainActivity.this.calc()) * 107) + "}");
}

So after winning 1000 times in a row, the flag is displayed. It is calculated based on the counter and the result of the calc() method. Unfortunately here, the calc method is a native method.Instead of starting Hopper and reversing the native lib or patching the apk to display directly the flag, let’s try to write a Xposed module for it.

Idea is then to hook up the onClick method and set the attributes to the correct values (ie set count to 999 and the attribute m and n in order to make it a wining move)

public class RPS implements IXposedHookLoadPackage {
    public void handleLoadPackage(final LoadPackageParam lpparam) throws Throwable {

        if (!lpparam.packageName.equals("com.example.seccon2015.rock_paper_scissors"))
            return;
        findAndHookMethod("com.example.seccon2015.rock_paper_scissors.MainActivity", lpparam.classLoader, "onClick", View.class, new XC_MethodHook() {
            @Override
            protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                // set the modified values
                XposedHelpers.setIntField(param.thisObject, "cnt", 999);
                XposedHelpers.setIntField(param.thisObject, "m", 0);
                XposedHelpers.setIntField(param.thisObject, "n", 1);
            }
        });
}

Finally we start the application in the emulator, click on any button and the flag is displayed!

Screen Shot 2015-12-07 at 12.55.38

1

Squareroots CTF‑Workshop – November 2015

October 15, 2015

This page is intentionally left german.

Auch dieses Semester veranstalten wir, die squareroots, am Wochenende von Freitag den 6. bis Sonntag den 8. November 2015 einen Workshop in den Räumlichkeiten der Universität Mannheim.
Wie bereits bei unseren vorherigen Workshops werden wir neben Einführungen in verschiedene Technologien und Werkzeuge auch den Ablauf von CTF-Wettbewerben vorstellen.

Anmeldungen sind ab sofort möglich auf http://anmeldung.sqrts.de/!

(more…)

0

ASIS CTF Finals 2015 – Giloph (crypto 300)

October 13, 2015

In this challenge we were given a normal network capture file of some “TCP” traffic.

topsecret

After carefully looking at the capture you can guess that it is not actually just TCP traffic but rather TLS traffic. Fix that in wireshark by right-clicking on a packet and choose to “decode as… > ssl”
(That step actually took way more time for me than it should have, I searched for the string “http/1.1 spdy/3.1h2-14h2uP” in packet #4 and found some other captures online that were about TLS traffic so I figured that must be it, but there are many ways to see or guess the traffic type…)

Now we can see this challenge is about TLSv1.2. We have to think about ways to decrypt the traffic. First I looked at the certificate but nothing seemed to be “attackable” there (like a weak modulus, this one was 1024bits). Also the CipherSuite of the conversation was “TLS_DHE_RSA_WITH_AES_128_CBC_SHA” which means a private key wouldn’t actually be very helpful since the “DHE” part provides forward secrecy.
At least in theory. So how can we break the encryption? The RSA part is not attackable – Let’s take a look at the DHE (Diffie-Hellman) part:

Observe packet #6, the ServerKeyExchange part of the packet

dhe1

We can actually see the parameters of the Diffie-Hellman Key-Exchange, which is normal. The abnormal part of the exchange are the values of the parameters. A quick look at wikipedia reveals:

https://en.wikipedia.org/wiki/Diffie%E2%80%93Hellman_key_exchange#Security
“The protocol is considered secure against eavesdroppers if G and g are chosen properly.”

I’m not a math person and to be honest I had no idea what “chosen properly” means but went ahead and looked at the attack mentioned in the next sentences, since I assumed those parameters were not “proper”.

https://en.wikipedia.org/wiki/Pohlig%E2%80%93Hellman_algorithm

After studying that, it seems that the key exchange is attackable if (p-1) can be factored into “small numbers”. I was not sure what exactly that means, but I tried to factor (p-1) with my favourite algorithm I copied from stackexchange and it turns out it has quite a lot of small factors!

[2, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 17, 17, 37, 37, 37, 37, 37, 37, 43, 43, 43, 43, 43, 43, 43, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 67, 67, 67, 67, 67, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 83, 83, 83, 83, 103, 103, 107, 107, 107, 107, 107, 109, 109, 109, 109, 109, 109, 127, 127, 127, 127, 127, 127, 127, 163, 167, 167, 167, 167, 167, 179, 179, 179, 179, 179, 197, 197, 197, 197, 197, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199L]

Okay, now we know there is a Diffie-Hellman Key-Exchange with some bad numbers and there exists some algorithm to break it. We could try to implement it, or just search for someone else who did it. I found this website:
http://www.case.edu/artsci/math/singer/Sage/pohligHellman.shtml

And indeed: It takes less than a second for it to return the (secret!) exponents. If you want to understand what happened there, go through my code, I tried to explain it in the comments there a bit. If you know how Diffie-Hellman works and get what this attack does already, you can skip it.

diffie.java

Thanks Java for this BigInteger number, it’s our secret!

9776722676843184390957456547012361603291708320301362890309561267000575367544550
0525763667655689728731939280038960341016957753437419456873567523867888108036157
3256299267775932146754989582312932406500348168366159589926388819530482546040056
20980597958229509174130519560051229345937360583880913807554453333987854

Feels good to break an algorithm that is used millions of times everyday… but now what. We have a big number but we still can’t see what’s in those packets. It’s time to dig into the RFCs about TLS:
https://www.ietf.org/rfc/rfc5246.txt

Take a look at “8. Cryptographic Computations” and the points below it. That seems relevant! We find out that:
“The negotiated key (Z) is used as the pre_master_secret, and is converted into the master_secret, as specified above.”

Okay, now we know what that big number is and roughly what to do with it. It’s our “Pre-Master-Secret” and we need to somehow derive the “Master-Secret” (which is used to derive even more stuff). A teammate luckily found this tutorial, which shows you how to precicely do what we need:
https://www.adayinthelifeof.nl/2013/12/30/decoding-tls-with-php/

After filling the script with the right numbers (client/server-secret from the pcap) we arrive at the following:

tls.php

which gives us… The “MASTER-SECRET”:

599dae45bea108da405cebfa1fd4414acccdc24ab4262c1c
1e86af60da2de9712933343d3c6f92486db4bdbda9d693f8

Another number, WOW!

At this point I tried feeding the values we have to wireshark, for some reason it didn’t decode the packets though. This is supposed to work:
https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/Key_Log_Format
if you give it the CLIENT_RANDOM (used in computation above) and the Master-Secret.

So we tried a solution without wireshark: we followed the tutorial above further and wrote this script (or rather copied and filled in the right numbers) to derive the symmetric keys:
derive.php

output:
 clientkey 872f0e68167f5201bbc661417f148421
 clientiv  4eaaf3d612cb2629767aa11e1601a7ef
 serverkey 73eb5d0236c76e957381ff269081fba4
 serveriv  1dac9c3db71249572009791f82b5e87b

Now we have symetrically encrypted ciphertexts (ApllicationData in the pcap) and symmetric keys/ivs! Let’s try to decrypt it:
http://aes.online-domain-tools.com/

Just put the ciphertext (hex) into it, choose AES-CBC and give it the client/server “write key” and iv from the script (depending on if you want to decrypt the data coming from or going to the server/client).

decrypt1decrypt2

(source .dat files in zip. link at the top)

We can see a GET-request and some answer, almost done!
Last step is to uncompress the gzip from the GET-response. I just used “binwalk -e” (overkill but it worked) on the .dat file and got the HTML-response:


<html><head><title>Top Secret</title></head>
<body><h1>Flag is ASIS{f702d759801533096be29291fd6e82c3}</h1></body></html>

Now that’s quite a topsecret website, and a flag too!
This was one of the most interesting challenges I solved in quite a while! It was very interesting to find out about so many of the internals of TLS and actually calculating all relevant keys “by hand”. Think about it: all of those calculations (and more) are done every time (close enough) you open a website that uses HTTPS!

Thanks ASIS!

0

ASIS CTF Finals 2015 – 10-SED (crypto 175)

October 12, 2015

In this challenge we were given the source of a server which encrypts and decrypt messages for us with DES, a ciphertext and some kind of key.

server.py

After looking at the source, you can observe how it handles the userinput, namely using the “key” to generate indices for reading from its own private key-list and en/decrypting a given text with said keys.
For a given key the server also checks the length of the input to match (after hex-decoding) 3 (a valid key would thus e.g. be “001122”; more on that later).

An example run would look like this: send the key “414243”, server takes the “subkeys” (his private keys) at those positions from its key-list and en/decrypts userinput with those and returns

Ek3=keys[43](Ek2=keys[42](Ek1=keys[41](input))).encode(“hex”)

Nothing special about it yet. Special is the fact that there is a comment in the source “assert len(key) == 3#10″. If you take a look at the given “enc” file you can see the “key” in there is (after hex-decoding) of length 10.
What this means is that the ciphertext we have is the flag encrypted, 10 (!) times with some keys we don’t know (only the server does, we know the *positions* of those keys though). Luckily we can tell the server to decrypt it for us. If it wasn’t for the fact that the number got change from 10 to 3.
The decryption seems impossible now because the server only does decryption for us if we provide exactly 3 key-positions. Apparently we can “reduce” the encryption of the flag to 1 instead of 10 encryption rounds like this:

cipher = "7f62a70857410e0e9c2bb283fc9807f8b1d34bcf7a2b456e965e860e5c6818b40ac596fa43492c30"
# originalkey = 97c4b5a27177406c404f --- this means 10 positions for keys were given and the flag encrypted accordingly
first = interact(s, cipher, "6c404f", "DEC")
second = interact(s, first, "717740", "DEC")
third = interact(s, second, "c4b5a2", "DEC")

(short explanation: slice the given key(s) into groups of 3 and do decryption with them – full script will be shown in the end)

However what we get from that is a flag that is still encrypted with the key at position 97. There is no way to get a completely unencrypted flag it seems, until you check out the wiki article about DES:

https://en.wikipedia.org/wiki/Data_Encryption_Standard#Minor_cryptanalytic_properties

And this is the whole trick of the challenge:
There seem to exist some keys which have the property Ek(Ek(input)) = input (Equivalent to Dk(Dk(input)) = input)

This fits exactly what we need. The only thing left is running the decryption in a loop with all keys like “000097”, “010197”, “020297”,…
To be more precise: decrypt our flag which is only decrpyted with key at position “97” with the keys at following positions: “97” “00” “00” etc. etc. and wait.
Sadly, we don’t get a flag. It turned out, for some reason, ASIS didn’t go with this vulnerability but chose to take the one which requires much more bruteforcing:
We need to find two distinct keys k1, k2 which have the property Ek1(Ek2(plain)) = plain (the second “weakness” described in the wiki article).
So, same as above, but instead of iterating over 256 keys, we iterate over 256*256 keys… But after a while we get:

[#] testing da4f97
[enc] recv: Enter your key(hex encoded):
[enc] sent: da4f97
[enc] recv: Enter your message(hex encoded):
[enc] sent: 15c876f9e9a20af6d2e40c5fc7de5e721f50460969a5be17fdb8c5b5a01e70da05944650ae57e2b3
[enc] recv: Enter your command(ENC/DEC):
[enc] sent: DEC
[enc] recv: message(hex encoded):
[enc] result: 0e4eca072f192237f7dddb44aa441f06479fff6ee8f628b2e8e2e540d7062f56fd8293c249069f2c
[#] GOT PLAIN: '\x0eN\xca\x07/\x19"7\xf7\xdd\xdbD\xaaD\x1f\x06G\x9f\xffn\xe8\xf6(\xb2\xe8\xe2\xe5@\xd7\x06/V\xfd\x82\x93\xc2I\x06\x9f,'
[#] testing da5097
[enc] recv: Enter your key(hex encoded):
[enc] sent: da5097
[enc] recv: Enter your message(hex encoded):
[enc] sent: 15c876f9e9a20af6d2e40c5fc7de5e721f50460969a5be17fdb8c5b5a01e70da05944650ae57e2b3
[enc] recv: Enter your command(ENC/DEC):
[enc] sent: DEC
[enc] recv: message(hex encoded):
[enc] result: 415349537b39303135326333643665363635386632303537626261346338383965356364617d0000
[#] GOT PLAIN: 'ASIS{90152c3d6e6658f2057bba4c889e5cda}\x00\x00'

… a flag! :D

Apparently keys at possitions 0xda and 0x50 fullfil the second property!
Really a very interesting challenge overall! I would have liked if the possitions of the keys were a bit more bruteforce-“friendly” though (like “02” “05”), since I almost gave up after a couple of tousand iterations.
This is how I got the flag a bit more quickly, professional multithreading.
asis-multi-sed

Full script here, as of writing this, the ASIS service is still up for testing :)
asisfinal15-sed.py

4

ASIS CTF Finals 2015 – Strange (misc 150)

October 12, 2015

After downloading the ASIS typical .tar.xz archive, we got a png file with 14MB. After extracting we noticed that the file has dimensions of 344987×344987 pixels. OK that is huge! Since there was no preview of the picture generated, which would indicate a normal picture with something attached after the picture, we tried to open it with Photoshop and Matlab but that resulted, in my case, with my MacBook telling me there is no memory left while Matlab used 60GB memory. Now let’s inspect the file with a hex-editor:

stange-topsectionstrange-datasection

Here we see there are just zeros, but somewhere in the middle there is some data. So we used binwalk to extract the zlib part of the png file and decompressed the raw zlib stream. This gave us a the raw image data file with a size of 14GB. From inspecting the image with tweakpng we knew the png file uses a palette with two colors and 1bit per pixel.

To get the important information we opened the raw image data file in a hex-editor and search for data. Since the data in the file is a 1 dimensional array which will be converted into a 2 dimensional array by the rendering routine, we have to look for several positions with data. In the middle of the file we found the hex strings shown below. To find them we searched the file for the bit sequence “11” and got several positions.

So we extracted the following hex strings:

“F1FC1F783F7FFC7C3FCF38FFFE3F1EFFFDE3E07E7FFFFC3F1F8F9FFC0FF7E7F3E3FCF38F87C3FCCF”
“F1F98F731EFFF1399FCE627FFC9E4EFFFD89EFFE7FFFF99E4E273FFDFFF7E7F3C9FCE7273399FCE7″
“F5F3EF67DEFFF39BCF8E673FF9CCE6FFFD9CCFFC7FFFFBCCE6733FF9FFF7C7E39CF8E67379BCF8F7″
“E4F3EF67DEF8F39BCF0C27383BFDE68F859CCFF878783BEDE6FA1839FE1787C3BFF0C2F379BEF0F7″
“ECF1FF63FEF273BFDF4E67713A3DF623319DC1FA73313BCDF7F331383CC7A7D3A3F4E6FBFBBCF4F7″
“EEF83F707EE7383F1ECE7077989DF67379C1CCF677B799CDF7F337999DE767B389ECE6FBE39CECF7″
“CE7F0F7E1EEFB39FCCCE673F99CDF6FB799CFE6677FF9C0DF7C73F9FCDE667339CCCE6FBF9C0CCF7″
“C07FE77FCCE037DFCDCE6FB81BEDF6FA7DBEFF6E77F81FEDF79F381FE9F6E773BEDCE6FBF9FEDCF3″
“9F77E76FCDEFF7DBE8066FB39BEDE6FB79BEFF4037F39FCDE73F339FEDE40201BE8066F37DFC8073″
“9F33E767CEEFB79BCFCE6F3799CCE67379BCDE7E77B799DCE67F379BCDE7E7F39CFCE673799DFCF7″
“BF38CF719EF271399FCE62731C9E4E271189CCFE7333199E4E7F33199C47E7F3C9FCE7273399FCF7″
“3FBC1F783EF8F87C3FCE70F8CE3F1E8F85C3E1FE7878CC3F1E0338CC3E17E7F3E3FCE78F87C3FCF7″
“FFFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7″
“FFFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7″
“FFFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE7″
“FFFFFFFFFF7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCF”

 

The end was quite simple: loop through each string, convert it to binary, check if value is 1, if so draw a black pixel, open picture and copy and submit the flag.

from PIL import Image
lines = [
"F1FC1F783F7FFC7C3FCF38FFFE3F1EFFFDE3E07E7FFFFC3F1F8F9FFC0FF7E7F3E3FCF38F87C3FCCF",
"F1F98F731EFFF1399FCE627FFC9E4EFFFD89EFFE7FFFF99E4E273FFDFFF7E7F3C9FCE7273399FCE7",
"F5F3EF67DEFFF39BCF8E673FF9CCE6FFFD9CCFFC7FFFFBCCE6733FF9FFF7C7E39CF8E67379BCF8F7",
"E4F3EF67DEF8F39BCF0C27383BFDE68F859CCFF878783BEDE6FA1839FE1787C3BFF0C2F379BEF0F7",
"ECF1FF63FEF273BFDF4E67713A3DF623319DC1FA73313BCDF7F331383CC7A7D3A3F4E6FBFBBCF4F7",
"EEF83F707EE7383F1ECE7077989DF67379C1CCF677B799CDF7F337999DE767B389ECE6FBE39CECF7",
"CE7F0F7E1EEFB39FCCCE673F99CDF6FB799CFE6677FF9C0DF7C73F9FCDE667339CCCE6FBF9C0CCF7",
"C07FE77FCCE037DFCDCE6FB81BEDF6FA7DBEFF6E77F81FEDF79F381FE9F6E773BEDCE6FBF9FEDCF3",
"9F77E76FCDEFF7DBE8066FB39BEDE6FB79BEFF4037F39FCDE73F339FEDE40201BE8066F37DFC8073",
"9F33E767CEEFB79BCFCE6F3799CCE67379BCDE7E77B799DCE67F379BCDE7E7F39CFCE673799DFCF7",
"BF38CF719EF271399FCE62731C9E4E271189CCFE7333199E4E7F33199C47E7F3C9FCE7273399FCF7",
"3FBC1F783EF8F87C3FCE70F8CE3F1E8F85C3E1FE7878CC3F1E0338CC3E17E7F3E3FCE78F87C3FCF7",
"FFFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7",
"FFFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7",
"FFFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE7",
"FFFFFFFFFF7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCF",

]

im = Image.new("RGB",(400,50),"white")
imo = im.load()
for y,l in enumerate(lines):
  g = bin(int(l, 16))[2:]
  for x,b in enumerate(g):
    if b == "1":
      imo[x,y] = (0,0,0)
im.save("strange-flag.png")

strange-flag

Thanks ASIS for this quite nice challenge :)

0

Squareroots & kitCTF @ GPN15

May 31, 2015

This post intentionally left german.

Bereits gegen 5 schleichen sich die ersten Sonnenstrahlen in unser HLab und sorgen für unschöne Reflektionen auf unseren Monitoren. Das wohl beste Mittel dagegen? – Gulasch!

Zusammen mit dem Team kitCTF veranstalten wir auch in diesem Jahr einen CTF im Rahmen der GPN15. Wir bieten euch spannendes Echtzeit Hacking, Nervenkitzel und riskantes Live-Patching. Kurzum dieses Jahr ist unser CTF serverbased (mehr zu den Regeln und dem Ablauf).

Also schnappt euch ein Team, packt neben eurem Gulasch-Löffel noch einen Laptop ein und kommt zu unserer Eröffnungsveranstaltung (05.06.2015 20:00) nach Karlsruhe. Ach und meldet euch doch bitte schon einmal an, damit wir besser skalieren können :).

Wir freuen uns auf euch :)

0

0ctf 2015 quals – forward (web250)

March 30, 2015

At the start we’ve only got an url to our target webserver:

Bildschirmfoto 2015-03-30 um 18.14.33

When we click on “Login” we get a javascript popup which tells us “You Are Not Authorized!”. Then we click on “FLAG”, because that’s what we want. Unfortunately we don’t get a flag yet, but the source code of admin.php is revealed.


<?php
    if (isset($_GET['view-source'])) {
        show_source(__FILE__);
        exit();
    }
    include("./inc.php"); // key & database config

    function err($str){ die("<script>alert(\"$str\");window.location.href='./';</script>"); }

    $nonce = mt_rand();

    extract($_GET); // this is my backdoor :)
    
    if (empty($_POST['key'])) {

        err("Parameter Missing!");
    }

    if ($_POST['key'] !== $key) {
        err("You Are Not Authorized!");
    }

    $conn = mysql_connect($host, $user, $pass);

    if (!$conn) {
        err("Database Error, Please Contact with GameMaster!");
    }

    $query = isset($_POST['query']) ? bin2hex($_POST['query']) : "SELECT flag FROM forward.flag";
    $res = mysql_query($query);
    if (FALSE == $res) {
        err("Database Error, Please Contact with GameMaster!");
    }

    $row = mysql_fetch_array($res);

    if ($debug) {
        echo "HOST:\t{$host}<br/>";
        echo "USER:\t{$user}<br/>";
    }

    echo "<del>FLAG:\t0ctf{</del>" . sha1($nonce . md5($row['flag'])) . "<del>}</del><br/>"; // not real flag

    mysql_close($conn);

Now we inspect the code and find the comment “this is my backdoor :)”. After looking up what extract($_GET) does, it was clear that we can (re)define our own variables and values here using GET parameters. We’re also able to overwrite values which were set earlier. Inserting our own sql query however is not possible. If we alter the query in any way it is replaced by its hex-representation. Since it should be impossible to generate a valid query using the “bin2hex” php function, this is also a dead end.

Now we asked ourselves why it is possible to display the IP and the username of the mysql connection. If we change the host-parameter for the mysql_conenct() function maybe we can get mysql credentials. It really seemed like this was the case, because the remote host had an open mysql port. It turns out we can recover the username, but it was impossible to get the password (Checking the protocol specification would have helped).

So what do we have? We can make mysql user connect (with the correct password) to a server of our choice but not recover the password and also can’t directly alter the query. This was the point when we finally came up with the right idea: Use a proxy which forwards the mysql connection back to the original server.

php with our $host and unaltered user/password -> mysql-proxy (WE) -> remote host -> “select flag from forward.flag” -> [plaintext flag] -> mysql-proxy -> php

Because we didn’t think the flag is really in “forward.flag” we set up “mysql-proxy” to alter the query if we need to. After changing the host value via the get parameter to our server ip, we got a result from the php script, which means the connection was successful by using our mysql-proxy. To get the result of the query we just had to start tcpdump and capture all traffic from and to the mysql-proxy.

After inspecting the dump in wireshark we found this string “0ctf{w3ll_d0ne_guY}”

This is (roughly) how everything looked like in the end (The Firefox addon “HttpRequester” on the right to alter http requests) :

final

0

Squareroots CTF‑Workshop – März 2015

March 5, 2015

This page is intentionally left german.

Auch dieses Semester veranstalten wir, die squareroots, am Wochenende von Freitag den 20. bis Sonntag den 22 März 2015 einen Workshop in den Räumlichkeiten der Universität Mannheim.
Wie bereits bei unseren vorherigen Workshops werden wir neben Einführungen in verschiedene Technologien und Werkzeuge auch den Ablauf von CTF-Wettbewerben vorstellen.

Anmeldungen sind ab sofort möglich auf http://anmeldung.sqrts.de/!

(more…)

2

Squareroots hacking the Himalaya

November 4, 2014

Himalaya, 24th of October 2014

We are in one of the world’s most targeted areas when it comes to cyber attacks:
Dharamsala, India, HQ of the Tibetan Exile Government is one of the main targets
of Chinese hackers.

Here, over 70 hackers from all over the world met for the hillhacks hacking
conference (hillhacks.in). One of the participating hackers was our member
floatec, who not only attended the event but also held a small lecture about CTF
hacking-competitions.
Although we all know, that CTFs are not only a fun hobby, but also a good way to
motivate people to get in touch with topics of IT security, in India, these
kinds of competitions are mostly unknown.
So we needed to start from the very beginning: What are CTFs? How do they work?
What different types of CTFs exist and how can you organize your own one?

In the end, we showed and explained a bunch of useful tools for CTFs.
In only 60 minutes, we rushed through all the relevant topics.

But after all, the lecture was a success, as great parts of the audience were
really interested in the topic and planned to try out a CTF in the future.

A record of the talk will be made available in the near future by hasgeak from
Bangalore. We can just recommend you to visit the next hillhacks, which will
take place in May 2015.

 

floatec (r) gives a lecture about CTFs at hillhacks (photo © David Huang)

0
Get Adobe Flash player