STACK the Flags is a Cybersecurity Capture-the-Flag (CTF) competition organised by GovTech Cyber Security Group. 2020 marks the first year where this competition was held. It spanned 3 days, with the challenge portal opening from 2100h on Friday, 4 Dec 2020 up to 2245h on Sunday, 6 Dec 2020. Although initially intented to only last 48 hours, a 1h 45min extension was added to the total competition duration as the file-hosting server became unavailable due to quota limits.
Table of Contents
About our Team
I participated in the Junior College category under the team name 0x4841434b.
Challenges
There were 11 different categories of challenges, namely:
- Binary Exploitation
- Cloud
- Cryptography
- Forensics
- Internet of things (IoT)
- Miscellaneous
- Mobile
- Open Source Intelligence (OSINT)
- Reverse Engineering (RE)
- Social Engineering (SE)
- Web
The challenges were not released all at once, but instead form a “prerequisite tree”. Hence, we were unable to view some challenges by the end of the competition. There was also dynamic scoring — the more teams solved a particular challenge, the less points it awards. The challenges we were able to solve are listed below:
Forensics
[Forensics-1] Walking down a colourful memory lane
“Walking down a colourful memory lane” is a challenge listed under the category “Forensics”. It was initially worth 1000 points, and by the end of the CTF, 6 teams had solved it and as a result it was down due to 992 points due to dynamic scoring.
Challenge Description
We are trying to find out how did our machine get infected. What did the user do?
How we did it
We were given a memory dump, forensics-challenge-1.mem. As such, I fired up my Ubuntu Virtual Machine and examined it using volatility -f forensics-challenge-1.mem imageinfo. This returned the profile of Win7SP1x64. Using this together with the pslist plugin, we were given a list of processes.
$ volatility -f forensics-challenge-1.mem --profile=Win7SP1x64 pslist
Volatility Foundation Volatility Framework 2.6
Offset(V) Name PID PPID Thds Hnds Sess Wow64 Start Exit
------------------ -------------------- ------ ------ ------ -------- ------ ------ ------------------------------ ------------------------------
0xfffffa8018dac040 System 4 0 86 572 ------ 0 2020-12-03 08:51:24 UTC+0000
0xfffffa8019355b30 smss.exe 240 4 2 29 ------ 0 2020-12-03 08:51:24 UTC+0000
0xfffffa8019f07950 csrss.exe 324 316 9 458 0 0 2020-12-03 08:51:25 UTC+0000
0xfffffa8018db2060 wininit.exe 376 316 3 75 0 0 2020-12-03 08:51:25 UTC+0000
0xfffffa8018db15d0 csrss.exe 388 368 13 611 1 0 2020-12-03 08:51:25 UTC+0000
0xfffffa801a12c060 winlogon.exe 424 368 5 112 1 0 2020-12-03 08:51:25 UTC+0000
0xfffffa801a12fb30 services.exe 480 376 7 207 0 0 2020-12-03 08:51:25 UTC+0000
0xfffffa801a1797c0 lsass.exe 496 376 6 569 0 0 2020-12-03 08:51:25 UTC+0000
0xfffffa801a17db30 lsm.exe 504 376 10 146 0 0 2020-12-03 08:51:25 UTC+0000
0xfffffa801a1c7b30 svchost.exe 612 480 10 356 0 0 2020-12-03 08:51:25 UTC+0000
0xfffffa801a1aa780 vm3dservice.ex 672 480 3 45 0 0 2020-12-03 08:51:25 UTC+0000
0xfffffa801a234060 svchost.exe 708 480 8 285 0 0 2020-12-03 08:51:25 UTC+0000
0xfffffa801a2592a0 svchost.exe 756 480 22 515 0 0 2020-12-03 08:51:25 UTC+0000
0xfffffa801a2cf5f0 svchost.exe 868 480 15 370 0 0 2020-12-03 08:51:26 UTC+0000
0xfffffa801a2fab30 svchost.exe 908 480 32 948 0 0 2020-12-03 08:51:26 UTC+0000
0xfffffa801a331230 svchost.exe 252 480 22 770 0 0 2020-12-03 08:51:26 UTC+0000
0xfffffa801a34cb30 svchost.exe 500 480 19 478 0 0 2020-12-03 08:51:26 UTC+0000
0xfffffa801a3ee4c0 spoolsv.exe 1184 480 13 261 0 0 2020-12-03 08:51:26 UTC+0000
0xfffffa801a408b30 taskhost.exe 1196 480 8 154 1 0 2020-12-03 08:51:26 UTC+0000
0xfffffa801a4158a0 svchost.exe 1244 480 19 313 0 0 2020-12-03 08:51:26 UTC+0000
0xfffffa801a51eb30 VGAuthService. 1448 480 3 84 0 0 2020-12-03 08:51:27 UTC+0000
0xfffffa801a55f630 vmtoolsd.exe 1472 480 10 270 0 0 2020-12-03 08:51:27 UTC+0000
0xfffffa801a4c4630 sppsvc.exe 1672 480 5 151 0 0 2020-12-03 08:51:27 UTC+0000
0xfffffa801a643b30 WmiPrvSE.exe 1852 612 9 196 0 0 2020-12-03 08:51:27 UTC+0000
0xfffffa801a6695e0 dllhost.exe 1912 480 13 185 0 0 2020-12-03 08:51:27 UTC+0000
0xfffffa801a6ae380 svchost.exe 2024 480 7 97 0 0 2020-12-03 08:51:28 UTC+0000
0xfffffa801a6ba060 msdtc.exe 1276 480 12 145 0 0 2020-12-03 08:51:29 UTC+0000
0xfffffa801917f060 WmiPrvSE.exe 2264 612 6 206 0 0 2020-12-03 08:51:47 UTC+0000
0xfffffa801a54e130 dwm.exe 2444 868 5 118 1 0 2020-12-03 08:51:58 UTC+0000
0xfffffa801a3dd7f0 explorer.exe 2460 2432 32 905 1 0 2020-12-03 08:51:58 UTC+0000
0xfffffa801a846b30 vm3dservice.ex 2548 2460 2 53 1 0 2020-12-03 08:51:59 UTC+0000
0xfffffa801a8ceb30 vmtoolsd.exe 2556 2460 8 166 1 0 2020-12-03 08:51:59 UTC+0000
0xfffffa801a91a060 SearchIndexer. 2704 480 13 648 0 0 2020-12-03 08:52:05 UTC+0000
0xfffffa801a9bfb30 wmpnetwk.exe 2876 480 15 226 0 0 2020-12-03 08:52:05 UTC+0000
0xfffffa801a9cab30 svchost.exe 2964 480 18 246 0 0 2020-12-03 08:52:05 UTC+0000
0xfffffa801a5d27c0 svchost.exe 1240 480 13 332 0 0 2020-12-03 08:53:27 UTC+0000
0xfffffa801a84e060 audiodg.exe 2376 756 5 126 0 0 2020-12-03 09:08:22 UTC+0000
0xfffffa80199e6a70 chrome.exe 2904 2460 33 1694 1 0 2020-12-03 09:10:20 UTC+0000
0xfffffa801a1d5b30 chrome.exe 852 2904 10 170 1 0 2020-12-03 09:10:20 UTC+0000
0xfffffa801998bb30 chrome.exe 1392 2904 10 274 1 0 2020-12-03 09:10:20 UTC+0000
0xfffffa801a91d630 chrome.exe 692 2904 13 225 1 0 2020-12-03 09:10:20 UTC+0000
0xfffffa8019989b30 chrome.exe 1628 2904 8 152 1 0 2020-12-03 09:10:21 UTC+0000
0xfffffa801a84cb30 chrome.exe 1340 2904 13 280 1 0 2020-12-03 09:10:24 UTC+0000
0xfffffa801acbeb30 chrome.exe 1112 2904 14 251 1 0 2020-12-03 09:10:27 UTC+0000
0xfffffa801acd8b30 chrome.exe 272 2904 14 239 1 0 2020-12-03 09:10:27 UTC+0000
0xfffffa801acd1060 chrome.exe 1648 2904 13 227 1 0 2020-12-03 09:10:28 UTC+0000
0xfffffa801acedb30 chrome.exe 3092 2904 13 212 1 0 2020-12-03 09:10:28 UTC+0000
0xfffffa801ad0eb30 chrome.exe 3160 2904 15 286 1 0 2020-12-03 09:10:29 UTC+0000
0xfffffa801ad3cb30 chrome.exe 3220 2904 15 295 1 0 2020-12-03 09:10:30 UTC+0000
0xfffffa801ad3ab30 chrome.exe 3240 2904 13 218 1 0 2020-12-03 09:10:30 UTC+0000
0xfffffa801ad8d060 chrome.exe 3320 2904 13 218 1 0 2020-12-03 09:10:32 UTC+0000
0xfffffa801ad9eb30 chrome.exe 3328 2904 13 231 1 0 2020-12-03 09:10:33 UTC+0000
0xfffffa801addfb30 chrome.exe 3380 2904 13 304 1 0 2020-12-03 09:10:34 UTC+0000
0xfffffa801ad9ab30 chrome.exe 3388 2904 13 283 1 0 2020-12-03 09:10:34 UTC+0000
0xfffffa801ae269e0 chrome.exe 3444 2904 13 231 1 0 2020-12-03 09:10:38 UTC+0000
0xfffffa801ae2e7d0 chrome.exe 3456 2904 12 196 1 0 2020-12-03 09:10:42 UTC+0000
0xfffffa801ae63060 chrome.exe 3568 2904 12 222 1 0 2020-12-03 09:10:44 UTC+0000
0xfffffa801ae89b30 chrome.exe 3584 2904 9 173 1 0 2020-12-03 09:10:45 UTC+0000
0xfffffa801aed8060 notepad.exe 3896 2460 5 286 1 0 2020-12-03 09:10:52 UTC+0000
0xfffffa801aeb5b30 chrome.exe 2492 2904 12 171 1 0 2020-12-03 09:10:58 UTC+0000
0xfffffa801af22b30 chrome.exe 1348 2904 12 171 1 0 2020-12-03 09:10:59 UTC+0000
0xfffffa801af63b30 chrome.exe 3232 2904 12 182 1 0 2020-12-03 09:11:00 UTC+0000
0xfffffa801af9d060 chrome.exe 4192 2904 12 168 1 0 2020-12-03 09:11:02 UTC+0000
0xfffffa801afaf630 chrome.exe 4268 2904 12 171 1 0 2020-12-03 09:11:04 UTC+0000
0xfffffa801afa6b30 chrome.exe 4324 2904 14 180 1 0 2020-12-03 09:11:04 UTC+0000
0xfffffa801afbeb30 chrome.exe 4380 2904 12 179 1 0 2020-12-03 09:11:04 UTC+0000
0xfffffa801ac4d060 RamCapture64.e 4832 2460 6 70 1 0 2020-12-03 09:11:24 UTC+0000
0xfffffa80199c3060 conhost.exe 4840 388 2 50 1 0 2020-12-03 09:11:24 UTC+0000
0xfffffa801ae055d0 dllhost.exe 4508 612 6 57728600 1 0 2020-12-03 09:12:23 UTC+0000
We see that there are a lot of chrome.exe processes. Given this is not normal of an idle machine, and the hint in the challenge description to find out “What did the user do?”, I did some online research, and discovered a Volatility plugin we can download called chromehistory. This gave us a list of visited URLs, among which, this MediaFire Link.
Downloading the PNG file gives us the following image:

(Yes, it is a really small image).
Judging from the small image, especially the fact that it is only 1 pixel high, I had the guess that the flag must be hidden inside this image, since poking around the rest of the memory dump did not give me anything.
So I fired up Python:
from PIL import Image
import numpy as np
pic = Image.open('This is a png file.png') file
pix = np.array(pic.getdata()).reshape(pic.size[0], pic.size[1], 3)
for pixel in pix:
print(''.join([chr(o) for o in pixel[0].tolist()]), end = '')
Using some Python programming to convert the R, G and B values of the individual pixels into ASCII, we got the flag.
Flag: govtech-csg{m3m0ry_R3dGr33nBlu3z}
[Forensics-2] Voices in the head
“Voices in the head” is a challenge listed under the category “Forensics”. It was initially worth 2000 points, but by the end of the CTF, 26 teams had solved it and it was down due to 1692 points due to dynamic scoring, although it should be noted that almost no one got it for the first 2 days; it was only after they released a free hint on the morning on the last day that other teams started to be able to solve it.
Challenge Description
We found a voice recording in one of the forensic images but we have no clue what’s the voice recording about. Are you able to help?
How we did it
We were given a WAV file. Again with the voice recordings, much like Beep Boop, I don’t know what I was on that first day, but simply from hearing the WAV file we were given, I immediately knew we had to get a spectrogram of the image. After searching online for quite a while, we finally found this site, which gave us the following spectrogram:

The = sign made it look like a Base64 code. Sure enough, copying the characters by hand and plugging it in into a Base64 decoder, we got https://pastebin.com/jETj2uUb as the decoded text.
However, the pastebin contents were:
++++++++++[>+>+++>+++++++>++++++++++<<<<-]>>>>++++++++++++++++.------------.+.++++++++++.----------.++++++++++.-----.+.+++++..------------.---.+.++++++.-----------.++++++.
It took us way too long to realise it was the Brainfuck programming language. I had had a hunch that it was a programming language just purely from its syntax, but I could not figure out which programming language it was. In fact, we only figured it out after the CTF was over.
Passing it through an online Brainfuck decoder, we get thisisnottheflag as the decoded string.
This is where the free hint came into play. It turns out, upon inspection using Xiao Steganography, we discovered that there was a hidden ZIP file inside our original WAV file given to us. Using thisisnottheflag as the extraction password, we got a ZIP file successfully extracted.
This ZIP file contained a Microsoft Word document, however it was unfortunately encrypted, but since we got the ZIP file already, we could try to run strings on it, and sure enough, it gave us the flag:
$ strings extracted.zip | grep -i govtech
govtech-csg{Th1sisn0ty3tthefl@g}PK
Flag: govtech-csg{Th1sisn0ty3tthefl@g}
Miscellaneous
[Misc-1] Welcome Challenge
Description
Welcome to STACK the Flags 2020! This is a welcome challenge to get you started. Can you find the flag hidden on our website? (Please DO NOT attack/scan the web service! The challenge does not require you to attack the site, subdomains, or root domain!)
Key points
- The aim of the challenge was to find a flag hidden on the website: https://ctf.tech.gov.sg/. My first instinct was that the flag was hidden in a comment in the page’s source code, but a quick check revealed that this was not the case. Closer inspection of the source code revealed no suspicious links that could have been used to hide the flag.
- The next thing I tried was looking for subpages on the website such as appending “/2020/welcome” to the end of the url, given that other subpages were in a similar format, but trying some common ones yielded nothing.
- With the admins’ hint to treat this as an OSINT challenge and their phrasing of “find the flag hidden ‘on’ the website”, I began to suspect that the flag was not actually hidden on the website itself but another related website instead. Inspection of the website showed links to other websites under the “About Us” section, which did not seem to contain the flag. The bottom left of the home page also revealed that the website was built using Isomer. I then accessed the Isomer website to see if there was any information about the CTF website that I could exploit.
- The guide to Isomer provided a few links, such as a Facebook group (which I concluded did not contain the flag) as well as a GitHub page. Scrolling through led to a repository titled `, which contained the flag in the README.
Flag: govtech-csg{W3lcom3_to_ST4CK_TH3_FL4GS_2o2o!}
[Misc-2] Beep Boop
“Beep Boop” is a challenge listed under the category “Miscellaneous”. It was initially worth 1000 points, but by the end of the CTF, 32 teams had solved it and it was down due to 730 points due to dynamic scoring.
Challenge Description
As part of forensic investigations into servers operated by COViD, an investigator found this sound file in a folder labeled “SPAAAAAAAAAAAAAAAAAACE”. Help us uncover the secret of the file.
How we did it
We were given an attached WAV file.
I solved this challenge almost immediately after the competition started. For some reason, the moment I played the WAV file and listened to it, I just knew that it was a Slow-Scan Television (SSTV) signal, and the description only made me a bit more confused. I knew that we could use the Robot36 Android App to decode such signals, so I immediately tried to load it on my phone. However, I couldn’t seem to find a way to open a file on Robot36, instead of just using the device microphone.
Eventually, after 10 minutes of searching online, trying to figure out how to do it, I still couldn’t figure out how to do it… and then I remembered I had a spare phone. Luckily, this competition was done online and not at a physical venue, so I could just blast the painful audio over the speakers and let my phone mic pick it up.
As expected, it decoded the signal and I got the flag.

Flag: govtech-csg{C00L_SL0w_Sc4n_T3L3v1S1on_tR4nsM1ss10N}
P.S.: I only found out that SSTV was used in Space transmissions only later when I was researching more about the clue. Now that I think of it, the description would have made the chalenge much easier for those who were not familiar with slow-scan television signals.
[Misc-3] FWO FWF
Description
As part of forensic investigations into servers operated by COViD, an investigator found this web server containing a hidden secret. Help us find the contents of this secret.
Solution
Upon entering the link given in the challenge description, we can see the following letters :
A quick inspection of the page source shows that there are three different font styles, with only the third being visible.
When we edit it such that only selector .a is visible,
we get the message : THE FLAG IS HIDDEN IN A FILE
When we edit it such that only selector .b is visible, we get the message: CSG.TXT
Initially, we tried adding CSG.txt to the end of the URL (which did not work). However, upon adding CSG.TXT, we get the following: Rmo0Y19HdTNfZkdsWTNfaTFmMW8xWTFHbAo=.
The ‘=’ at the end of the string indicates that it is probably Base64 encoded. Hence, we decrypted it from Base64 using CyberChef to get : Fj4c_Gu3_fGlY3_i1f1o1Y1Gl.
We then carried out ROT13 to get the flag.
Flag: Sw4p_Th3_sTyL3_v1s1b1L1Ty
[Misc-5] Emmel
Note that this challenge involves a web server. It has been cloned to create a preview-only and non-functional mockup.
“Emmel” is a challenge listed under the category “Miscellaneous”. It was initially worth 2000 points, but by the end of the CTF, 39 teams had solved it and it was down due to 1216 points due to dynamic scoring.
Challenge Description
Investigators have found a curious login page on a server belonging to COViD. Can you get past the login screen? Login Page
How we did it
Upon clicking the link, we were greeted by the following page:

As usual with web challenges, I started by trying out what the web page is like in order to get a sensing on how it functions. The website gives you a form where you can upload JPG files and a “Submit” button that’s greyed out. Trying to upload a JPG file will enable the “Submit” button, allowing us to upload the file, and returning us with a similarity score. I tried uploading a PNG file it resulted in a still greyed out button and a prompt “only .jpg files are allowed”.
Looking at the page sources in the Chrome DevTools panel, we can take a look inside the <script> tag in index.html and see that the check whether we are submitting a JPG file or not is done client-side, and the check is purely based on the file extension. With this knowledge, I tried to rename a PNG file as a JPG and it still works to give me the similarity score. However, trying to do the same with a text file of any kind will result in an internal server error. This led me to believe that the image comparison may be an actual algorithm that compares the two images.
At first, I tried to Google how image comparison algorithms in PHP work, but I realised that a lot of them involve a lot of their own code instead of one from predefined libraries, and each implementation seems to also be unique in how they actually do the image comparison. As such, this led me to believe that it is unlikely that the actual comparison algorith will be one that is easily found online. I also had guesses whether it has something to do with palindromes (as “Emmel” almost sounds like one–emmel~lemme~let me) but it was a bit far-fetched. Lastly, I had a hunch that this challenge had something to do with Machine Learning (ML) as Emmel sounds like ML (this turned out to be true as the challenge author subsequently mentioned as follows, but at that point in time, it was just a guess).
Regardless, after analysing the actual packets that were sent when I submit the form using Wireshark, I tried to create a Python program using PIL and requests to automate the same process and obtain the similarity score, using a bunch of grayscale images.
Version 1 - Grayscale Images
import requests
from PIL import Image
headers = {
'Connection': 'keep-alive',
'Cache-Control': 'max-age=0',
'Upgrade-Insecure-Requests': '1',
'Origin': 'http://yhi8bpzolrog3yw17fe0wlwrnwllnhic.alttablabs.sg:40751',
'Referer': 'http://yhi8bpzolrog3yw17fe0wlwrnwllnhic.alttablabs.sg:40751/recognize',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',
'Accept-Encoding': 'gzip, deflate',
'Host': 'yhi8bpzolrog3yw17fe0wlwrnwllnhic.alttablabs.sg:40751',
}
for val in range(256):
print(f"testing val={val}...", end = '')
img = Image.new('RGB', (256, 256), color = (val, val, val))
img.save(f'test{val}.jpg')
files = {
'file': (f'test{val}.jpg', open(f'test{val}.jpg', 'rb'), 'image/jpeg'),
}
response = requests.post('http://yhi8bpzolrog3yw17fe0wlwrnwllnhic.alttablabs.sg:40751/recognize', files=files, headers=headers)
print([l for l in response.text.splitlines() if 'Similarity' in l])
I saw that the accuracy finally got up from 0.0%, but not that high (at most 17.85%), as shown:
...
testing val=129...[' <p id="score">Error, you must get 50% or higher! Similarity: 15.95%</p>']
testing val=130...[' <p id="score">Error, you must get 50% or higher! Similarity: 16.66%</p>']
testing val=131...[' <p id="score">Error, you must get 50% or higher! Similarity: 17.25%</p>']
testing val=132...[' <p id="score">Error, you must get 50% or higher! Similarity: 17.6%</p>']
testing val=133...[' <p id="score">Error, you must get 50% or higher! Similarity: 17.78%</p>']
testing val=134...[' <p id="score">Error, you must get 50% or higher! Similarity: 17.85%</p>']
testing val=135...[' <p id="score">Error, you must get 50% or higher! Similarity: 17.68%</p>']
testing val=136...[' <p id="score">Error, you must get 50% or higher! Similarity: 17.25%</p>']
testing val=137...[' <p id="score">Error, you must get 50% or higher! Similarity: 16.63%</p>']
testing val=138...[' <p id="score">Error, you must get 50% or higher! Similarity: 15.66%</p>']
...
As such, I decided to change it to send an RGB image. However, with the way I initially coded it, it was just way too slow as it had to send all 256^3 = 16777216 possible images.
Version 2 - RGB Images (Sequential)
min = 127
max = 140
for r in range(min, max):
for g in range(min, max):
for b in range(min, max):
print(f"testing ({r},{g},{b})...", end = '')
img = Image.new('RGB', (512, 512), color = (r, g, b))
img.save(f'test2_({r},{g},{b}).jpg')
However, this was just way too slow. I then tried to assign a random colour to each pixel as opposed to the whole image being the same colour, but this made it even slower and also less accurate (I was averaging 10%). But then, noticing that the required similarity threshold of 50% or higher is actually quite low, I decided to modify the code from Version 2, except that the program will generate images of random colours instead of trying each one sequentially:
Version 3 - RGB Images (Randomised)
import random
while True:
r = random.randint(0,255)
g = random.randint(0,255)
b = random.randint(0,255)
print(f"testing ({r},{g},{b})...", end = '')
img = Image.new('RGB', (512, 512), color = (r, g, b))
img.save(f'testrandom_({r},{g},{b}).jpg')
...
print([l for l in response.text.splitlines() if 'Similarity' in l])
I ran the program for more than 10 minutes to no avail and nearly gave up. However, after taking a break, I decided to give it one more chance before I searched for another alternative means to solving the challenge. Luckily, not 10 seconds later, this happened in the output:
...
testing (199,82,111)...[' <p id="score">Error, you must get 50% or higher! Similarity: 0.16%</p>']
testing (16,43,4)...[' <p id="score">Error, you must get 50% or higher! Similarity: 0.01%</p>']
testing (71,255,194)...[' <p id="score">Error, you must get 50% or higher! Similarity: 0.07%</p>']
testing (106,44,107)...[]
testing (45,79,142)...[' <p id="score">Error, you must get 50% or higher! Similarity: 0.09%</p>']
testing (103,203,19)...[' <p id="score">Error, you must get 50% or higher! Similarity: 0.05%</p>']
...
Well hello there!
I then sent that exact image with values R=106, G=44, B=107 manually in the browser, and voila!


Flag: govtech-csg{I_L0V3_G00D_D0GG0S!}
OSINT
[OSINT-1] What is he working on? Some high value project?
Description
The lead Smart Nation engineer is missing! He has not responded to our calls for 3 days and is suspected to be kidnapped! Can you find out some of the projects he has been working on? Perhaps this will give us some insights on why he was kidnapped…maybe some high-value projects! This is one of the latest works, maybe it serves as a good starting point to start hunting. Flag is the repository name!
Solution
Upon entering the given link, we proceed to view the page source. We can observe various comments within the page source, including this one (line 869):
We then go on to Gitlab and search for @joshhky. This gives us the user Josh Hong who works for KoroVax, indicating that we have found the correct account.
As the flag name is the project repository name, we went on to try the various projects shown above. However, they were not the flag.
As the description hints at a high value project, we go on to further inspect his various projects including the korovax-employee-wiki:
We then come across the latest document added which mentions a ‘krs-admin-portal’ that is not in the list of public repositories, indicating that it is potentially high value.

Upon trying govtech-csg{krs-admin-portal}, we find that it is indeed the flag!
Flag: govtech-csg{krs-admin-portal}
[OSINT-2] Where was he kidnapped?
Description
The missing engineer stores his videos from his phone in his private cloud servers. We managed to get hold of these videos and we will need your help to trace back the route taken he took before going missing and identify where he was potentially kidnapped!
You only have a limited number of flag submissions!
Flag Format:
govtech-csg{postal_code}
Attached Files
Attached were 3 video files, which appeared to be Instagram stories taken by the missing engineer.
Write-up
Inspecting the first video, we noticed that:
- The engineer took the bus 117 toward Punggol interchange
- The bus stop was opposite an MRT station with the tracks above ground
- The buildings in the background near the MRT station
Immediately, we searched for the route of bus 117 online. Looking at all the possible bus stops, we deduced that the bus stop could be:
- Opp Sembawang Stn
- Opp Canberra Stn
- Opp Yishun Stn
- Opp Khatib Stn
- (And we kept Soo Tech Stn as a potential option)
Using Google Maps, we checked the street view of each of these bus stops, and found that Opp Khatib Stn was the bus stop we were looking for!!
Inspecting the second video, we immediately noticed:
- These iconic yellow pillars
- “Not even near the MRT…”
- The location is at a HDB block
- Looking at how the person was walking, it was likely that he/she just got off the bus and was leaving the bus stop
Using Google Maps street view once again, we followed the direction of the bus route and looked at the bus stops after Opp Khatib Stn. Upon spotting the iconic yellow pillars at bus stop Blk 871, we hollered in victory and moved on to the third video.
Staring intensely at the third video, we observed that:
- There was a framed community garden of sorts beside the location
- There was a green expanse beside the garden
- The flooring of the location was tiled and not normal concrete
- The semicircle shape of the chair
- The bench beside the chair
We inferred that the location was within walking distance of the bus stop and should be within the cluster of HDB blocks beside the bus stop. We tried to use Google Earth to inspect the HDB blocks but sadly it wasn’t made 3D yet.
Yet again, using our newfound best friend Google Maps street view, we jumped around the roads surrounding the HDB blocks. Voila!
Indeed, looking at the block numbers, the place we were looking for was at Blk 870.
Flag: govtech-csg{760870}
[OSINT-6] Only Time Will Tell
Description
This picture was taken and sent to us! It seems like a bomb threat! Are you able to tell where and when this photo was taken? This will help the investigating officers to narrow down their search! All we can tell is that it’s taken during the day!
If you think that it’s 7.24pm in which the photo was taken. Please take the associated 2 hour block. This will be 1900-2100. If you think it is 10.11am, it will be 1000-1200.
Flag Example:
govtech-csg{1.401146_103.927020_1990:12:30_2000-2200}Flag Format:
govtech-csg{lat_long_date_[two hour block format]}Use this calculator!
This challenge:
- Unlocks other challenge(s)
- Is eligible for Awesome Write-ups Award
- Prerequisite for Mastery Award - Intelligence Officer
Attached Files
Write-up
I have no idea why but we initially thought that the sign was in the UK, but after some googling, we found that the picture was taken at the Speakers’ Corner at Hong Lim Park in Singapore.
Undoubtedly, our next instinct was to scan the barcode at the bottom left corner of the image. After all, when you see a barcode, you have to scan it!!!
We specifically cropped out the barcode to make sure that it can be scanned.
Next, we then used an online barcode scanner https://online-barcode-reader.inliteresearch.com/, and obtained the date 25 October 2020.
After that, we used EXIF data to find the latitude and longitude. We tried to do this in a few ways:
First, uploading the cropped image to online EXIF data viewers gave us a latitude and longitude of 1.286648 degrees N and 103.846835 degrees E.
Second, right-clicking and looking at the properties of the downloaded image, also gave us the latitude and longitude as shown below.
After putting these values in the calculator linked in the challenge description, we also obtained a latitude of 1.286648 degrees and a longitude of 103.846835 degrees.
Looking at the direction of the sun, we deduced that the timing was in the afternoon, but we tried to key in the flag using all the possible combinations of 2 hour blocks.
However, we were confounded.
govtech-csg{1.286648_103.846835_2020:10:25_two hour time period} ⇒ WRONG
Despite checking many times, our flag was still incorrect. We clutched our heads, wondering what went wrong.
After a period of confusion, we used a command line tool called exiftool to extract the location. We finally figured out what the coordinates should be, and it turned out to be a difference in the 6th decimal place to our initial answer.
At long last, our flag was correct.
govtech-csg{1.286647_103.846836_2020:10:25_1500-1700}
One of our teammates who solved the final part of the challenge said that technically the command line tool only gives the latitude and longitude up to two decimal places, making the result less precise compared to other tools which can give a more precise measurement. Hence, it is strange that the organisers would opt to use the less precise value.
Flag: govtech-csg{1.286647_103.846836_2020:10:25_1500-1700}
[OSINT-7] Sounds of Freedom!
Description
In a recent raid on a suspected COViD hideout, we found this video in a thumbdrive on-site. We are not sure what this video signifies but we suspect COViD’s henchmen might be surveying a potential target site for a biological bomb. We believe that the attack may happen soon. We need your help to identify the water body in this video! This will be a starting point for us to do an area sweep of the vicinity! Flag is the postal code.
Solution
The task comes with a video file. Upon playing the video, we first notice a few things.
- The location looks to be a park with a circular lake/pond.
- There is a bus stop with a white roof below the filming location.
- There is a red building next to the pond.
- There are green/ blue HDB blocks in the background.
- There is the sound of fighter jet engines in the background. This along with the name suggests that the location is near an army air base.


A teammate who lived near Punggol Park commented that the location looked exceedingly like Punggol Park, before proceeding to convince herself and everyone else that the location could NOT be Punggol Park.
Hence, we went on to use Google Maps to look at the satellite images of parks near the main army air bases in Singapore (Changi Air Base (East and West), Paya Lebar, Sembawang and Tengah). However, there were no parks that could fit the above description.
We then decided to relook Punggol Park. Using Google Maps’ Street View function from the bus stop at Opp Blk 475B, we can see the following :
Since the features observed matched up with the ones in the video, we did a quick google search to find the postal code for Punggol Park and got the flag (as well as an existential crisis for the teammate who lived near Punggol Park).
Flag: govtech-csg{538768}