I participated with Defenit and CTF.SG and mostly worked on the Web. Or I should say Misc, since all the Web felt like Misc. The 3-part OSINT series was fun though!
I also solved the programming challenges in Misc, and some other Guess challenges…
We finished 6th place with only about 6 challenges unsolved, so I’m pretty happy with our performance!
Web
Tic Tac Toe
Category: Web | 333 solves | 50 points
Challenge Description
Hello there, I invite you to one of the largest online global events in history ... the Tic Tac Toe World Championships! http://challenge.rgbsec.xyz:8974/~BobbaTea#6235, bAse#0001
This is a javascript game cheating challenge.
We noticed that window["_0x2c3b80"]
contains the game state in base64. Decoding it, we got the flag!
FLAG
rgbCTF{h4h4_j4v42cr1p7_ev3n72_AR3_c00L}Typeracer
Category: Web | 184 solves | 119 points
Challenge Description
I AM SPEED! Beat me at TypeRacer and the flag is all yours! http://challenge.rgbsec.xyz:8973/~BobbaTea#6235, bAse#0001
This is another javascript game cheating challenge, but let’s see if we can solve it without calling any function.
Turning on the debugger, add a breakpoint when the time is changed.
This will cause the game to freeze with the text visible.
Type the text into your favourite text editor, then use an automation tool like AutoHotKey send it back to the browser. There’s a function checking for keystrokes, so directly copy-pasting it would not work.
This was my code which led me to the flag:
Sleep 1000
Send loose rather smaller ever proper plane yes letter heard draw available least gulf grow deal please accurate ear result whose judge modern bound generally seen jack why cup keep union saved fish harbor eleven depth door excellent go nothing tomorrow cotton problem finger charge trip knowledge nervous ahead due carried
Btw you can also beat it manually if you have a wpm of around 200. I tried and failed it with 163:
Congrats you have a WPM of 163.33333333333334! Unfortunately, only keyboarding legends can redeem the flag. Perhaps, try again?
And we got a flag encoded in base64:
FLAG
rgbCTF{w40w_j4v42cr1p7_12_4nn0y1ng}Countdown
Category: Web | 63 solves | 455 points
Challenge Description
This challenge is simple. All you have to do is wait for the countdown to end to get the flag. The countdown ends one second before the end of the CTF, but you have fast fingers right? http://challenge.rgbsec.xyz:5000/~Quintec#0689
We are given a page where there is a countdown that ends 1 second before the CTF ends, as the challenge description says.
First I tried modifying my system clock, but it only led the timer to count down to negative.
This led me to believe that it is not a javascript game cheating challenge like the previous two, but one that involves server-side processing instead.
Taking a look at the session cookie and the logic of the javascript on the page:
window.addEventListener('load', function() {
var cookie = Cookies.get('session');
var data = cookie.split('.')[0].replace(/_/g, '/').replace(/-/g, '+');
var data = JSON.parse(atob(data));
var end = moment.utc(data['end']);
setInterval(function() {
var dur = moment.duration(end.diff(moment.utc()));
var disp = dur.get("days") + "d " + dur.get("hours") + "h " + dur.get("minutes") + "m " + dur.get("seconds") + "s";
document.getElementById("countdown").innerHTML = disp;
}, 1000);
});
We see that the variable end
is a date hardcoded into the cookie. This means we can modify it and the server will hopefully read our input without sanitization.
However, we notice that the cookie has some extraneous characters at the end eyJlbmQiOiIyMDIwLTA3LTEzIDE2OjU5OjU5KzAwMDAifQ.Xwu32g.eGyibsd0-7vE_XZSZrczJOWT7pU
, which was not JWT as JWT uses dot separators.
Doing a quick google search, it turned out to be a flask cookie. But we needed a key for that!
This was where Justin came in and said
But what if
Time
is key?
That’s right, the key was hiding in plainsight just above the countdown timer.
Using the code of this stackoverflow answer, we write the following script:
from flask import Flask, session
from flask.sessions import SecureCookieSessionInterface
app = Flask("example")
app.secret_key = "Time"
session_serializer = SecureCookieSessionInterface() \
.get_signing_serializer(app)
@app.route("/")
def test():
session["end"] = "2020-07-10 16:59:59+0000"
session_cookie = session_serializer.dumps(dict(session))
return session_cookie
app.run(host="0.0.0.0")
Which will change the end date and encrypt it into this flask cookie: eyJlbmQiOiIyMDIwLTA3LTEwIDE2OjU5OjU5KzAwMDAifQ.XwpcKA.JP9x_llcMpcXoTJdt1KRF7nxEMg
Which gave us the flag!
FLAG
rgbCTF{t1m3_1s_k3y_g00d_j0k3_r1ght}Imitation Crab
Category: Web | 68 solves | 448 points
Challenge Description
Flag should be modified to fit the rgbCTF format (rgbCTF{flag}, underscores between words) http://challenge.rgbsec.xyz:7939/~BobbaTea#6235
I’ll be honest, we got stuck on it until our pwn guy Lord_Idiot came in and showed us the robots.txt
link: http://167.172.123.213:7939/robots.txt
Which showed a path to the /static/export.har
, where a .har
file could be downloaded.
Trawling through the large file, we find the text: RGBCTF H4R F1L3S 4R3 2UP3R US3FU1
And encoded it in the flag format, which gave us the 13th solve.
FLAG
rgbCTF{H4R_F1L3S_4R3_2UP3R_US3FU1}Keen Eye
Category: Web | 30 solves | 490 points
Challenge Description
Pay close attention... http://challenge.rgbsec.xyz:8376/~BobbaTea#6235, bAse#0001
Credits go to @03sunf for this one.
There was a cdn
link to a suspicious JS called poppers.min.js
. The usual package is popper.min.js
so there was an extra s
in there.
By grepping it, we found the flag.
What we tried and failed:
- Request smuggling (it worked randomly)
- HTTP headers
FLAG
rgbctf{n073_7h3_d1ff}Misc
[insert creative algo chall name]
Category: Misc | 67 solves | 449 points
Challenge Description
https://pastebin.com/pKNVLkTs~BobbaTea#6235, qpwoeirut#5057
This was a programming challenge, as you can tell from the description.
So basically n
generates a list of n
elements, and x
defines the number of partitions.
Using this code I wrote myself I got from stackoverflow:
import sys
def clusters(l, K):
if l:
prev = None
for t in clusters(l[1:], K):
tup = sorted(t)
if tup != prev:
prev = tup
for i in xrange(K):
yield tup[:i] + [[l[0]] + tup[i],] + tup[i+1:]
else:
yield [[] for _ in xrange(K)]
def neclusters(l, K):
for c in clusters(l, K):
if all(x for x in c): yield c
if __name__ == "__main__":
maxi = int(sys.argv[1])
K = 4
L = [1,2,4,8,16,32,64,128,256,512,1024,2048]
clust = neclusters(L,K)
ans = [clust.next() for _ in xrange(maxi)]
print(len(ans))
We can start off with a large xrange number 10000000
and decrease it by one digit until we get a number returned. Then we increase it back up to half of the previous (in this case, 500000
) and keep increasing the first digit until it hits StopIteration
again. After we have fixed the first digit, move on to the next digits and do the same.
Eventually you’ll reach the answer which is 611501
. Wrap it in flag tags and we’ve got the flag.
FLAG
rgbCTF{611501}[another witty algo challenge name]
Category: Misc | 90 solves | 409 points
Challenge Description
This is pretty simple. You get a list of 5000 by 5000 grid of ones and zeros, and you have to print the number of islands in the grid.An island is a collections of ones where each one is adjacent to another one in the island. For a cell to be adjacent to another cell, they must share an edge.
Submit the number wrapped in the flag format, like rgbCTF{123}
~ BobbaTea#6235, qpwoeirut#5057
This was another programming challenge, as you can tell from the description.
It is the classic iteration of the very common interview question ‘Count the Islands’, down to the detail where one ‘1’ is one island, and to be counted as an island, they must be touching either horizontally or vertically.
You can find solutions in your favourite scripting language online because it’s just that common.
And you don’t even need to optimize it, since you are running it locally with a fixed input.
class Solution(object):
def numIslands(grid):
cnt_land = 0
q = []
d = [[-1,0,1,0],[0,-1,0,1]]
len_y = len(grid)
if len_y ==0 :
return 0
len_x = len(grid[0])
if len_x == 0:
return 0
for i in range(len_y):
for j in range(len_x):
if grid[i][j] == "1":
grid[i][j] = "-1"
q.append((i,j))
while q :
cur = q.pop(0)
for k in range(4):
next_y = cur[0]+d[0][k]
next_x = cur[1]+d[1][k]
if next_y >= 0 and next_y <len_y and next_x >=0 and next_x <len_x and grid[next_y][next_x] == "1":
grid[next_y][next_x] ="-1"
q.append((next_y, next_x))
cnt_land +=1
return cnt_land
f = open('grid.txt')
grid = []
for i in range(5000):
a = list(f.readline()); del a[-1]; grid.append(a)
g = Solution.numIslands(grid)
print(g)
Code by @realsung
This prints 119609
. Wrap it in flag tags and we’ve got the flag.
FLAG
rgbCTF{119609}Secure RSA
Category: Misc | 16 solves | 497 points
Challenge Description
We have created the securest possible RSA algorithm! (Put your flag in the flag format.)~Quintec#0689
The attached file is a text file, so I’ll paste it here:
Secure RSA (SRSA) is a new, revolutionary way to encrypt your data that ensures the original message is unrecoverable by hackers. Top scientists from around the world have confirmed this mathematically irrefutable fact. 3 of our very own RGBSec members have developed this method, and we present it to you here. Granted, the method is very simple, and we aren't quite sure why nobody has thought of it before. Levying the power of uninjectivity, we set e to a small number. 0, in fact. Let's calculate the cipher now: (anything)^0 = 1. Since the message before encryption could have been any number in the world, the ciphertext is uncrackable by hackers.
n: 69696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969
e: 0
c: 1
Any crypto guy would know that there is no way of solving with such e
and c
values. Hence we figured out that the answer must be in the text.
If you take the first letter of each sentence, it returns ST3GL0LS
.
Wrap that in flag tags and you’ve got the answer!
FLAG
rgbCTF{ST3GL0LS}Ye Old PRNG
Category: Misc | 38 solves | 484 points
Challenge Description
I found a really old prng... can you exploit it? nc challenge.rgbsec.xyz 23456~qpwoeirut#5057
Actually we solved this 3 minutes after the ctf ended but it was too late to submit the flag.
Anyway, we would still be 6th so no difference I guess.
Because Base100 is used, you just need to take the number, square it on wolfram, then take the middle 100 numbers, which gives us the flag rgbCTF{d0nt_us3_midd13_squ4r3}
.
FLAG
rgbCTF{d0nt_us3_midd13_squ4r3}Forensics/OSINT
PI 1: Magic in the Air
Category: Forensics | 160 solves | 468 points
Challenge Description
We are investigating an individual we believe is connected to a group smuggling drugs into the country and selling them on social media. You have been posted on a stake out in the apartment above theirs and with the help of space-age eavesdropping technology have managed to extract some data from their computer. What is the phone number of the suspect's criminal contact?flag format includes country code so it should be in the format: rgbCTF{+00000000000}
~Klanec#3100
The goal is to find the phone number of our target.
This is a btsnoop file, so basically bluetooth capture. I have solved a similar challenge back in ISITDTU Finals 2019 last year, where we had to decode a mouse capture based on hex values.
Decoding it, we get a conversation with the phone number.
YOO MAN
SORRRY FOR THHE DELAY LOL
TRYIINNG TO GEET TYITHHIS KEYBBOARD WORKIINNNNN
YEEEAA ITS NNEEW WIRELESS MANG
BEEEN MMOVVIINNG PPRODUCT
SSPEAAKIINNNN OF YOOU NNEEDED TOO CCOONNTAACT MMY BOY RIGHT
YE
SHOOULDD BBE FFIINE JUST SSAAY JOHNNY H SENT YOOU
ALRIGHHT LEMME GEET YOOUU THEE NUMBEER
HHOLD UUP IMM LOOOKIINGG FORR IT
ITSS HHISS BBURNER GOTT IIT WWRITTTENN DOWNN SSOOMMEWHERE
YEEAHH GOT IT
00736727859
MIIND IT IS AA SSWWWEDISHH NNUMBEER HE GGOT IT OONN HHOLLIDAAY THEERE FFEWW MMOONTHHS BBACCK
YEAHH YOOUU CAN BUUY BBURNNEERS SSUUPPER EAASIILY THEERE
AALRIGHHT G
YEEAAH ITS DONNY L
RREMEMBEER TO TELL HIIMM I SENT YOOU
PEEACE
XED[UP]G3KLID
The file ends at ..x
, so everything on the last line is some leftover artifact from our decoding.
Adding the Swedish country code (hinted in the decoded text) to the phone number, we got the flag!
FLAG
rgbCTF{+46736727859}PI 2: A Series of Tubes
Category: OSINT | 22 solves | 495 points
Challenge Description
Use the personal information uncovered from PI 1 to find out where our suspect's contact lives, his full name and the next flight he is taking.The flag for this challenge is in the following format: rgbCTF{firstnamelastname:homecity:countrycode:flightnumber} where countrycode is the ISO 3166-1 Alpha 2 code
all lowercase, no whitespace or symbols
~Klanec#3100
The goal is to find the firstname/lastname, the home city, the country code and the flight nubmer of our target.
As the only personal information we uncovered was the phone number, I eventually figured out that the answer was to search for it on social media, as it was hinted in the first challenge of the series.
Adding the phone number as a phone contact for easier access, I opened Whatsapp to find this:
It says No business on whatsapp. Hit me on sc
.
Now, although I might be a boomer, I do know that SC refers to Snapchat. Hence I redownload the app after years of not using it, and went to search for contacts on my phone (Remember, I added him as a new contact on my phone).
And we’ve got his profile name donnylockheart
. As I am too boomer to know how to view a snap, I went straight to instagram instead of wasting time trying to figure it out.
Searching on instagram returns 2 accounts: one with the display name donnylockheart
and username donnylockheart9
, and another with the display name Donny Lockhart
and username donnylockheart
. Naturally, I checked the latter one first, but it appears to be a red herring (or a real person!) as it had no further hints.
Checking the former account, we find that although there are no posts, there are 2 highlights that contained a lot of information.
The first is his flight ticket.
Although the flight number and departure airport were censored, we can easily find any flight just with the destination airport and time of arrival. Doing that, I found 3 results: Birmingham KL1426, KLM Royal Dutch Airlines KL1630, Qatar Airways QR 273
. Let’s save this for later.
Moving on, we find another story where Donny posted a location and the caption Got this crazy town right on my doorstep
.
Tapping on the location named Digbeth
, a map of the city of Birmingham is shown.
Thus we can deduce that the target lives in Birmingham, and that the first flight number above is the correct one.
Putting it all together, we have:
- First name: Donny
- Last name: Lockheart
- Home City: Birmingham
- Country Code: GB (stands for Great Britain, which is the country Birmingham is in)
- Flight number: KL1426
Change them all to lowercase and wrap it in flag tags and we’ve got the answer!
FLAG
rgbCTF{donovanlockheart:birmingham:gb:kl1426}PI 3: This isn’t a secure line
Category: Forensics | 8 solves | 499 points
Challenge Description
Our suspect is getting noided. We've managed to retrieve this from his computer. What can you find?The flag format is the MD5 hash of what you will find. rgbCTF{hash}
~Klanec#3100
Placeholder
FLAG
rgbCTF{md5(applepie)}Insanity Check
Category: OSINT | 16 solves | 497 points
Challenge Description
There's a flag in our Discord server. Can you get it? Server Link: https://discord.gg/CfhfYPQ~Quintec#0689
After much futile searching, I eventually realised that the answer can only be in the Discord server icon of the CTF.
Downloading the PNG icon, I ran stegsolve and LSB steg but it doesn’t seem like there’s anything hidden in the image.
As such, I followed up with zsteg which returned the flag.