BsidesSF CTF — Challenge Write-up Part 2

Weather Companion — 350 points, 11 solves — Mobile, Reversing

“A simple weather application that fetches and displays the weather. What hides within?”

After loading and running the application on Android Studio, you will see that it displays the weather. The applications makes one request to pull down a json file — https://storage.googleapis.com/weather-companion/weather.json?GoogleAccessId=weather-companion-service-acco@bsides-sf-ctf-2019.iam.gserviceaccount.com&Expires=1554057388&Signature=Zwv1...snip…

This is a signed URL, it is a form of authenticating a request to fetch a resource from a Google storage bucket. Let’s dive into the decompiled application to understand what is going on under the hood. Proguard was used to obfuscate the application’s code, which makes it fairly hard to read the code. The classes in the package — com.example.myapplication — are the ones to focus on. The class identified as “a” is most interesting, it includes strings that seem to be part of the JSON credentials file.

The private key is split into four parts (java source, JNI source) -

String keyPart1 = decodeString(part1,0);
String keyPart2 = new String(dks());
String keyPart3 = new String(ss(part3,part3.length()));
String keyPart4 = decodeString(part4,1);
String key = keyPart1 + keyPart2 + keyPart3 + keyPart4;

The parts were altered as follows -

  1. Part 1 — base64 encoded in java
  2. Part 2 — shifted with ROT13 in JNI
  3. Part 3 — base32 encoded in java
  4. Part 4 — uses a character array in JNI

Some players reversed the application and walked through the smali code to put together the key. Others patched the smali to dump the key into logcat, this is the approach I’ll be demonstrating here.

After decompiling the application using apktool, I updated the smali code to dump the key into into logcat (reference). The end of the credentials string is indicated by “\”}” on line 415. Adding the following lines does the trick -

const-string v8, “json”
invoke-static {v8, v5}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I

Once this is complete, the application is repacked using apktool, then following the steps from this blog does the trick. First you create a key using keytool, then sign the application using jarsigner and then ensure the data is aligned correctly using zipalign.

keytool -keystore keystore_name.jks -genkey -keyalg RSA -keysize 2048 -validity 30 -alias key_alias
jarsigner -keystore keystore_name.jks weather-companion-signed.apk key_alias
$Android_SDK_Path$/Sdk/build-tools/28.0.3/zipalign -f -v 4 weather-companion-signed.apk weather-companion-final.apk

Running the updated APK will now log the key as follows,

9–03–17 11:10:39.440 8224–8243/com.example.myapplication D/json: {“type”: “service_account”,”project_id”: “bsides-sf-ctf-2019”,”private_key_id”: “6dd7fc48a8b1d49edf7f03f74bc47713bec7d989”,”private_key”: “ — — -BEGIN PRIVATE KEY — — -\nMIIEv…snip…. — — -END PRIVATE KEY — — -\n”,”client_email”: “weather-companion-service-acco@bsides-sf-ctf-2019.iam.gserviceaccount.com”,”client_id”: “116037946827001874660”,”auth_uri”: “https://accounts.google.com/o/oauth2/auth","token_uri": “https://oauth2.googleapis.com/token","auth_provider_x509_cert_url": “https://www.googleapis.com/oauth2/v1/certs","client_x509_cert_url" : “https://www.googleapis.com/robot/v1/metadata/x509/weather-companion-service-acco%40bsides-sf-ctf-2019.iam.gserviceaccount.com"}

Gsutil can be used to pull the flag from the bucket as follows,

gcloud auth activate-service-account — key-file=output.json
gsutil -m cp -r -p bsides-sf-ctf-2019 gs://weather-companion/flag.txt .

This challenge took a while to put together, shout-out to @IAmMandatory for his suggestions and brainstorming aid. This was heavily inspired by real world issues of hardcoded / obfuscated keys in the Android application.

I recommend reading @aaditya_purani’s great mobile challenges write-up for different approaches to solving the challenges.

Pcap challenge

Table Tennis — 50 points, 172 solves — Forensics

“The flag is in the Pcap, can you find it?”

Loading the pcap into wireshark shows DNS, TCP, ICMP and HTTPS packets. If you looked through the packets, the ICMP packets contain HTML.

Initially my write-up was going to use wireshark for exporting packets and processing them, but @ikuamike’s write-up inspired me to use scapy instead.

from scapy.all import *
output = “”
for packet in packets:
if packet.haslayer(ICMP) and packet[ICMP].type == 0:
output += packet[ICMP].load[-8:]
print output
>> ‘<html>\n\t<head>\n\t<title> I ❤ Corgi </title>\n\t\t<script>\ndocument.write(atob(“Q1RGe0p1c3RBUzBuZ0FiMHV0UDFuZ1Awbmd9”));\n\t\t</script>\n\n\t</head>\n\n\t<body>\n\n\t\t<h1> Woof!! </h1>\n\n\t</body>\n\n</ht’
import base64
base64.b64decode(“Q1RGe0p1c3RBUzBuZ0FiMHV0UDFuZ1Awbmd9”)
>> ‘CTF{JustAS0ngAb0utP1ngP0ng}’

And that wraps up the author write-ups for 2019, I look forward to coming up with new challenges and returning with loads of energy for BSidesSF CTF 2019.