Ph0wn is a Capture the Flag and workshop event dedicated to smart devices. Our CTF and workshops are consequently connected (sometimes a bit loosely!) to smart devices. For example, we have had CTF challenges involving connected coffee machines, drones, and satellites, as well as workshops on Ghidra for ARM and Hydrabus. Like last year, ph0wn offers a pre-challenge. Here is the write-up.

Step 1

The CTF start on ph0wn teaser website with this instructions :

Yesterday, I observed the sky. I spotted a new Exifplanet. Or is it a constellation of satellites? or aliens? [Download].
Bruteforce is useless. Use your brains.

As the hint ‘Exifplanet’ in the instructions suggests, one solution is to look at the image’s metadata. This is a classic scenario in CTF: step1

In the teaser discord channel, I see two things, a picture sent by a user named “Erwin Tubble” (below) and an instruction (“Please DM me for firmware downloads. Specify brand and model as precisely as possible. And begin your message with SEND FIRMWARE: xxxx”).

step1

With Google Lens, we can find the same image on Pinterest along with all the information needed to complete the challenge. I sent the following message on discord and received a zip file:

SEND FIRMWARE : 
Sky-Watcher Star Adventurer GTi

The zip file have two files. On is a release note : step1

The first flag and instructions can be found with a simple string in the firmware file:

strings MC021_Ver0356.mcf
[...]
=======================
ph0wn{t0_the_skY_&_beyonD}
======================================
Congratulations!
Submit your flag to https://teaser.ph0wn.org/hurrayifoundtheflag/submit
Level Up!
Pico le Croco launched a new constellation of satellites. They are reachable via the Android app.
Download the app from https://teaser.ph0wn.org/static/picostar_edcfccda7e90c553e4485cdfe3fbb6d4815c503e2a7e13d3cea47e4fb5c4bc73.apk
sha256: edcfccda7e90c553e4485cdfe3fbb6d4815c503e2a7e13d3cea47e4fb5c4bc73
This application is Pico-certified for Android 10 or 11 emulators x86_64 with Google APIs. 
=======================================

Step 2

Following previous instruction, the next step required to download an apk file:

$ wget --no-check-certificate https://teaser.ph0wn.org/static/picostar_edcfccda7e90c553e4485cdfe3fbb6d4815c503e2a7e13d3cea47e4fb5c4bc73.apk

An interesting thing to do during Android reverse engineering is to use a tool that can find URLs present in the APK, facilitating analysis and further research. Here, I use apk2url :

$ apk2url ./picostar_edcfccda7e90c553e4485cdfe3fbb6d4815c503e2a7e13d3cea47e4fb5c4bc73.apk
$ cat endpoints/picostar_edcfccda7e90c553e4485cdfe3fbb6d4815c503e2a7e13d3cea47e4fb5c4bc73_*
https://34.163.87.133:9950
http://schemas.android.com/aapt
http://schemas.android.com/apk/res/android
http://schemas.android.com/apk/res-auto
https://34.163.87.133:9950
https://34.163.87.133:9950
http://schemas.android.com
https://34.163.87.133:9950
https://34.163.87.133:9950

The IP returns a message indicating that there is a web server listening behind the URI https://34.163.87.133:9950 :

$ curl -k https://34.163.87.133:9950
No such satellite

I’m not sure why, but I can’t find the strings.xml file with Jadx. However, it is possible to obtain it using APKtool.I lost quite a bit of time on this because, not finding the information in this file, I attempted dynamic analysis, which yielded nothing. I also looked into the program’s execution traces to see if the flag might be somewhere on the phone. The mistake was that I should have tried according to the method I know: searching by all means after reading the Android manifest. Here is how to decompile using APKTool:

apktool d picostar_edcfccda7e90c553e4485cdfe3fbb6d4815c503e2a7e13d3cea47e4fb5c4bc73.apk

Here, there is indeed a strings.xml file, and the IP identified via apk2url helps identify an internal variable storing this value :

$ grep -R "https://34.163.87.133:9950"
res/values/strings.xml:    <string name="server_ip">https://34.163.87.133:9950</string>

When opening the APK with Jadx, searching for the string server_ip reveals this interesting class:

    [...]
    public final byte[] ph0wnIsAwes0me = {80, 105, 99, 111, 83, 116, 42, 114, 43, 43, 67, 97, 118, 105, 97, 114};
    public final byte[] comebacknextyear = {83, 105, 120, 116, 101, 101, 110, 32, 98, 121, 116, 101, 32, 73, 86, 33};

    public static String alien(String str, byte[] bArr, byte[] bArr2) {
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(2, new SecretKeySpec(bArr, "AES"), new IvParameterSpec(bArr2));
        return new String(cipher.doFinal(Base64.getDecoder().decode(str)));
    }

    private void getFlag() {
        try {
            superstar(getResources().getString(R.string.server_ip) + "/" + alien("+60nXLtfb249m+F94blHhMZnUQs13OCFcLFIcSwXjQE=", this.ph0wnIsAwes0me, this.comebacknextyear));
        } catch (Exception e3) {
            e3.printStackTrace();
        }
    }
    [...]
    public final JSONObject superstar(String str) {
        try {
            HttpURLConnection httpURLConnection = (HttpURLConnection) new URL(str).openConnection();
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(httpURLConnection.getInputStream()));
            StringBuilder sb = new StringBuilder();
            while (true) {
                String readLine = bufferedReader.readLine();
                if (readLine == null) {
                    bufferedReader.close();
                    httpURLConnection.disconnect();
                    return new JSONObject(alien(sb.toString(), this.ph0wnIsAwes0me, this.comebacknextyear));
                }
                sb.append(readLine);
            }
        } catch (IllegalArgumentException | Exception e3) {
            e3.printStackTrace();
            return null;
        }
    }
    

A function named getFlag appears to be the solution to the challenge. It calls another function named superstar, which makes a simple API call and returns a JSON. The string passed as a parameter to the superstar function is the URL of the endpoint that allows us to retrieve the flag. The endpoint is generated by the alien function, which simply performs AES CBC deciphering with the parameters str (the secret to decrypt), bArr (the key), and bArr2 (the initialization vector). The AES key is contained in the variable ph0wnIsAwes0me with the value icoSt*r++Caviar (converted from ASCII to string) and the initialization vector in comebacknextyear with the value Sixteen byte IV! (converted from ASCII to string).

The analysis allows us to write the following code to retrieve the secret endpoint.

from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad
import base64

def decrypt_aes_cbc_base64(encoded_message, key, iv):
    message = base64.b64decode(encoded_message)
    cipher = AES.new(key, AES.MODE_CBC, iv)
    decrypted_data = unpad(cipher.decrypt(message), AES.block_size)
    return decrypted_data.decode('utf-8')

bArr = b'PicoSt*r++Caviar'
bArr2 = b'Sixteen byte IV!'
encrypted_str = '+60nXLtfb249m+F94blHhMZnUQs13OCFcLFIcSwXjQE='

# Call the function with appropriate arguments
result = decrypt_aes_cbc_base64(encrypted_str, bArr, bArr2)
print(result)

# $ python3 decipher.py
# $ we_like_satellite1337 

All that remains is to call the secret endpoint to obtain the flag and the subsequent instructions :

$ curl -k  https://34.163.87.133:9950/we_like_satellite1337 
Congratulations, you validated STAGE 2.
Submit your flag to https://teaser.ph0wn.org/hurrayifoundtheflag/submit
Flag: ph0wn{theSt4rsSh1neInTh3SkY}

=== STAGE 3 ===

With his constellation of satellites, Pico le Croco has managed to contact Aliens. This is their message:


"We, inhAbiTantz of Tenda, h4ve patChed ouR b3st r0uter tech.
On http://34.155.175.156
We beLi3ve in iT. U donT hav3 n0 sk1LLs to haCk."


Pico managed to grab the httpd daemon they use and a pcap.
Download both (stage3.tar.gz) from https://teaser.ph0wn.org/673d27d84d17ef194b0dbe4ac02d85a40d75d8e12310cdd538551bef0fecc333

SHA256:

2c989c7116cabd8b57c987e503e85bd625d62421d6e408a6ea839b03d4086b72  httpd
3562fa5c4034ae1fc4219f1e58151cacb5fd9c8b450b28d5e94d5a0782605f06  router.pcapng

================

Conclusion

I’ll stop this first article here, as it is getting lengthy. There is one stage remaining out of the three proposed in the pre-challenge. The first stage, the OSINT part, was simple but demonstrates how OSINT can be used to gather intelligence for attacking a target, even through photos. The second part, involving reverse engineering, was more technical but relatively straightforward. This challenge has been very educational and interesting so far. I’m approaching the next stage with a bit more apprehension since it involves exploiting vulnerabilities on embedded systems, a type of exercise I have never done before.