I could have been 5th place but unfortunately I did not officially qualify to be a real participant in this event. Still had a good time though, notwithstanding the fact that there was so much ‘steganography’ and quite a bit of guesswork at certain points :)
Details | Links |
---|---|
Official Event Information Page | https://www.csit.gov.sg/tisc/tisc-home |
Official Event Landing Page | https://www.tisc.csit-events.sg/ |
Official Event Summary | https://www.csit.gov.sg/tisc/tisc-2021-summary |
Level 1 (Forensics)
Scratching the Surface - Scenario 1
You have been roped in to investigate this attack, and CSIT SOC team have collected, detected and triaged the data and as you are new to the team, they will like you to look into these 3 particular interesting files, are you able to help?
Challenge 1
We’ve sent the following secret message on a secret channel.
Submit your flag in this format: TISC{decoded message in lower case}
Attached: file1.wav
“Channel"s in this case could refer to the Left/Right audio channels. Here, I’ll view the channels in iZotope RX 8 Advanced Audio Editor but feel free to use anything else as it doesn’t really matter:
The Right audio channel seems to have what looks like morse code. Decoding it in manually in CyberChef, we get the flag.
Flag: TISC{csitislocatedinsciencepark}
Challenge 2
This is a generic picture. What is the modify time of this photograph?
Submit your flag in the following format: TISC{YYYY:MM:DD HH:MM:SS}
Attached: file2.jpg
Just run exiftool file2.jpg
and it’s right in the output:
|
|
Flag: TISC{2003:08:25 14:55:27}
Challenge 3
Nothing unusual about the Singapore logo right?
Submit your flag in the following format: TISC{ANSWER}
Attached: file3.jpg
binwalk on the image shows that there is a zip file containing picture_with_text.jpg
, so let’s extract that with -e
.
|
|
|
|
picture_with_text.jpg
contains some suspicious text at the beginning, which turned out to be ROT13 ciphered.
|
|
Decoding “NAFJRE GB GUVF PUNYYRATR VF URER NCCYRPNEEBGCRNE” in CyberChef gives
ANSWER TO THIS CHALLENGE IS HERE APPLECARROTPEAR
.
Flag: TISC{APPLECARROTPEAR}
Scratching the Surface - Scenario 2
Excellent! Now that you have show your capabilities, CSIT SOC team have given you an .OVA virtual image in investigating a snapshot of a machine that has been compromised by PALINDROME. What can you uncover from the image?
Once you download the VM, use this free flag TISC{Yes, I’ve got this.} to unlock challenge 4 - 10.
hxxps://transfer.ttyusb.dev/I6aQoOSuUuAoIIaqMWWkCcKyOk/windows10.ova
Check MD5 hash: c5b401cce9a07a37a6571ebe5d4c0a48 For guide on how to import the ova file into VirtualBox, please follow the VM importing guide attached.
Please download and install Virtualbox ver 6.1.26 instead of ver 6.1.28, as there has been reports of errors when trying to install the Win 10 VM image. https://www.virtualbox.org/wiki/Download_Old_Builds_6_1
Attached: VM Importing Guide.txt
For this forensics scenario, I will be demonstrating how to solve all of the following challenges without running the actual VM (Virtual Machine) at all. First, you will need to import the given windows10.ova
into VirtualBox as a VMDK and not a VDI. Simply untick the “Additional Options” checkbox and you are good to go.
We will then be conducting most of our analysis directly on the VMDK file through Autopsy. Create a new case in Autopsy and add the VMDK file as a “Disk Image or VM File” type data source.
Challenge 4
What is the name of the user?
Submit your flag in the format: TISC{name}.
Autopsy will display “Operating System User Account” information under Extracted Content after running the default selected ingest modules. In there, you can see “adam” as the user account name.
Flag: TISC{adam}
Challenge 5
Which time was the user’s most recent logon? Convert it UTC before submitting.
Submit your flag in the UTC format: TISC{DD/MM/YYYY HH:MM:SS}.
For this, we will use a special ingest module Python plugin called “SAM Parse” which is available to install from https://github.com/markmckinnon/Autopsy-Plugins. Rerun your ingest modules (making sure SAM Parse is selected) and under Extracted Content the SAM file will be parsed for accounts and their last login times.
For adam
, the last login Unix timestamp is 1623897697
. This can be converted to UTC with date -d @1623897697 -u
:
|
|
Flag: TISC{17/06/2021 02:41:37}
Challenge 6
A 7z archive was deleted, what is the value of the file CRC32 hash that is inside the 7z archive?
Submit your flag in this format: TISC{CRC32 hash in upper case}.
Autopsy shows the Recycle Bin’s contents under Extracted Content and in it we can see $R31T54D.7z
.
Export it out of Autopsy, open in 7-zip/WinRAR (not extract) and the CRC32 value will be displayed in its own column.
Flag: TISC{040E23DA}
Challenge 7
Question1: How many users have an RID of 1000 or above on the machine?
Question2: What is the account name for RID of 501?
Question3: What is the account name for RID of 503?
Submit your flag in this format: TISC{Answer1-Answer2-Answer3}. Use the same case for the Answers as you found them.
Here we can reuse the “Operating System User Account Information” under Extracted Content seen in Challenge 4.
The first column displays the SIDs of the accounts. The RID is the last portion of the SID (separated by hyphens -
).
- Question 1: For users with RID of 1000 or above on the machine, we can only see
adam
with an RID of 1002. - Question 2: Account name with RID 501 is
Guest
- Question 3: Account name with RID 503 is
DefaultAccount
Flag: TISC{1-Guest-DefaultAccount}
Challenge 8
Question1: How many times did the user visit https://www.csit.gov.sg/about-csit/who-we-are ?
Question2: How many times did the user visit https://www.facebook.com ?
Question3: How many times did the user visit https://www.live.com ?
Submit your flag in this format: TISC{ANSWER1-ANSWER2-ANSWER3}.
Under Extracted Content in Autopsy, Web History will be of use.
Ignoring the WebCacheV01.dat
files and only focusing on “History”, you’ll see that the “CSIT | Who We Are” page is visited twice while facebook.com
and live.com
is never visited.
Flag: TISC{2-0-0}
Challenge 9
A device with the drive letter “Z” was connected as a shared folder in VirtualBox. What was the label of the volume? Perhaps the registry can tell us the “connected” drive?
Submit your flag in this format: TISC{label of volume}.
For VirtualBox Windows guests, shared folders are “implemented as a pseudo-network redirector”. Simply put, they are mounted as a mapped network drive with the fixed UNC path prefix \\VBoxSF\
, \\VBoxSvr\
or \\VBoxSrv\
followed by the shared folder name, e.g. \\VboxSvr\MyShare
. This is gleaned from the VirtualBox User Manual.
Where can we find information about the mapped network drives in a system? Microsoft documentation and numerous other StackOverflow answers (one, two, three) point to this registry key HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\MountPoints2
where mapped drives information is stored in this format ##Server_Name#Share_Name
. So in the case of \\VboxSvr\MyShare
, we should see something like ##VboxSvr#MyShare
.
Additionally, as seen from the StackOverflow answers and this other outdated Microsoft documentation under “Drive Label”, the _LabelFromReg
value in this MountPoints2
registry key can be used to specify custom volume labels.
But when this value is not set, the default label used is actually just the share name itself. I’ve proved this by searching for image examples online like this TenForums tutorial for mapped network drives and other miscellaneous VirtualBox shared folder tutorials (one, two, three). So with the shared folder name MyShare
and no _LabelFromReg
value set, the volume label will be MyShare
.
Ok, enough explanation and time to examine the registry of the VMDK in Autopsy! The MountPoints2
key we want is from HKEY_CURRENT_USER
which can be retrieved from the user’s NTUSER.DAT
file.
No _LabelFromReg
value is set for the ##VBoxSrv#vm-shared
key, which means the shared folder name vm-shared
will by default be our volume label!
Flag: TISC{vm-shared}
Challenge 10
A file with SHA1 0D97DBDBA2D35C37F434538E4DFAA06FCCC18A13 is in the VM… somewhere. What is the original name of the file that is of interest?
Submit your flag in this format: TISC{original name of file, include file extension}.
I added the VMDK into FTK Imager as an Image File and then I did an “Export File Hash List”.
I then searched for the given SHA1 hash in the exported list and found that it corresponded to otter-singapore.lnk
under the Recent
folder.
If we now check “Recent Documents” under Extracted Content in Autopsy, we can see that otter-singapore.lnk
points to the original file otter-singapore.jpg
.
Flag: TISC{otter-singapore.jpg}
Level 2 (Network Forensics)
Dee Na Saw as a need
We have detected and captured a stream of anomalous DNS network traffic sent out from one of the PALINDROME compromised servers. None of the domain names found are active. Either PALINDROME had shut them down or there’s more to it than it seems.
This level contains 2 flags and both flags can be found independently from the same pcap file as attached here.
Flag 1 will be in this format, TISC{16 characters}.
Attached: traffic.pcap
I noticed a pattern in the DNS queries: they all began with a hardcoded prefix d33d
followed by a string of only uppercase letters and numbers. I suspected this string to be Base32-encoded because a usual Base64-encoding would instead use both lowercase and uppercase letters. But when I tried extracting all these strings out, Base32 decoding failed.
Upon closer anaysis, I realised the 2 numbers right after d33d
would range from 0-9 even though the Base32 character set only has numbers 2-7. Hence, these 2 numbers probably belonged to a separate encoded message which will form the other independent flag that can be found in this level.
Part 1
I extracted the 2 numbers after d33d
out from all of the DNS queries and I noticed that the 2 numbers, when taken together, go from 01
to 64
. This character set of 64 could mean Base64-encoding, so I mapped each number to the standard Base64 character set A-Za-z0-9+/=
. In the script below I also parsed out the Base32-encoded string after the 2 numbers at the same time for Part 2.
|
|
Base64-decoding the contents of dns_extracted_numbers_mapped.txt
, we actually get a Microsoft Word .docx
file. I extracted the contents of the file and found the flag inside word/theme/theme1.xml
.
Flag: TISC{1iv3_n0t_0n_3vi1}
Part 2
The extracted Base32-encoded string from the same script in Part 1 decodes to text with the flag within.
Flag: TISC{n3vEr_0dd_0r_Ev3n}
Level 3 (Reverse Engineering)
Needle in a Greystack
An attack was detected on an internal network that blocked off all types of executable files. How did this happen?
Upon further investigations, we recovered these 2 grey-scale images. What could they be?
Attached files: 1.bmp, 2.bmp
Rearranging 1.bmp
I opened 1.bmp
in 010 Editor and I could see chunks of a PE file being stored as BITMAPLINE structures in reverse order. See that the section information (rdata
), PE
signature, and MZ
signature are appearing in the wrong order?
I exported the file’s bitmap structures to CSV in 010 Editor, deleted the unneeded structures (BITMAPFILEHEADER
, BITMAPINFOHEADER
, RGBQUAD
), and then made a quick Python script to read the remaining BITMAPLINE structures in the CSV for their offsets and sizes so as to construct a new file containing the PE chunks in the correct order.
Sample CSV contents:
Name,Value,Start,Size,Color,Comment
struct BITMAPLINE lines[0],,436h,94h,Fg: Bg:,
struct BITMAPLINE lines[1],,4CAh,94h,Fg: Bg:,
struct BITMAPLINE lines[2],,55Eh,94h,Fg: Bg:,
struct BITMAPLINE lines[3],,5F2h,94h,Fg: Bg:,
struct BITMAPLINE lines[4],,686h,94h,Fg: Bg:,
struct BITMAPLINE lines[5],,71Ah,94h,Fg: Bg:,
struct BITMAPLINE lines[6],,7AEh,94h,Fg: Bg:,
However, I did not get a valid PE this way on my first try, because I realized that the padBytes
(3 bytes) in each BITMAPLINE structure should be ignored. So this is the script that accounts for that:
|
|
Analyzing 1.exe
Now it’s time to analyze 1_rearranged.exe
in IDA Pro!
|
|
The first part of the code in main
takes in a command-line argument for a .txt
filename to read from and store in a buffer.
Then, the file’s contents stored in the buffer are processed in chunks of 64 bytes with each mini-chunk of 16 bytes XOR’d with some different 16-byte keys.
Frankly, I don’t really fully understand how this decoding routine worked — especially the code at the beginning where the address of buf
is used as a negative index for arrays. Nevertheless, and crucially, I noticed through dynamic analysis that all the 16-byte keys used were sequentially retrieved from .rdata
.
After XOR decoding the buffer, the buffer and its size are then passed to sub_931360
where the buffer is checked to be a valid PE, copied into a new allocated memory region, parsed for various image data directories, and then executed.
|
|
Known-Plaintext Attack
Given that we know the final XOR-decoded buffer will be a valid PE with a standard MS-DOS MZ Header (64 bytes) at the beginning, we can conduct a known-plaintext attack: take the 64 bytes of a standard MS-DOS MZ Header and XOR it again with the hardcoded 16-byte keys stored in .rdata
to get back the data that would have been (or was expected to be) in the buffer at the beginning. This is the result in CyberChef!
Whoa, aren’t those the words in 2.bmp
?! More specifically, the MS-DOS MZ header maps to the last BITMAPLINE structure in 2.bmp
.
What this means is that 2.bmp
probably contains another PE stored as BITMAPLINE structures in reverse, similar to 1.bmp
, except that the chunks are all XOR-encoded. If we feed these BITMAPLINE chunks in the correct order as a .txt
file to 1_rearranged.exe
, 1_rearranged.exe
will XOR-decode these chunks to form the new PE that will be checked and executed.
Rearranging 2.bmp
Hence, I exported the BITMAPLINE structures’ offsets and sizes in 2.bmp
to CSV the same way I did for 1.bmp
and modified my earlier script to rearrange the structures. This time, padBytes
is only 1 byte.
|
|
The chunk that will XOR-decode to the MS-DOS MZ Header is now at the start of 2_rearranged.txt
.
I pass in 2_rearranged.txt
as the command-line argument to 1_rearranged.exe
and let the decoding of the buffer be done. I then ran pe-sieve with /data 3
to scan non-executable pages to dump out the PE that was in memory.
DLL
The dumped PE is actually a DLL. The entry point (DllEntryPoint
) calls the standard functions _security_init_cookie
, dllmain_dispatch
, etc., so let’s just focus on DllMain
.
DllMain
calls sub_10001050
which contains the main code we’re interested in.
At the start, the contents of key.txt
are read into a buffer.
|
|
The buffer is compared with the string Words of the wise may open many locks in life.
and then used to RC4-drop[32]-decrypt 38 bytes of ciphertext. I tried running the DLL with that string inside key.txt
, but it didn’t work so we need something else. After all, based on analysis of the RC4 code the key length should be 14.
What could “words of the wise” mean? I remembered that 2.bmp
was filled with many relatively complex words, so maybe it could be one of them? I made a quick Python to script to print out any words of length 14, and one of them stood out:
|
|
Using !t4ttaRRatt4t!
as the contents for key.txt
and running the DLL (with rundll32.exe
, and I also had to set the instruction pointer to DllMain
since the fwdReason
check in dllmain_dispatch
would fail), the ciphertext is properly RC4-decrypted into the flag!
Flag: TISC{21232f297a57a5a743894a0e4a801fc3}
Level 4 (Web Pentesting)
The Magician’s Den
One day, the admin of Apple Story Pte Ltd received an anonymous email.
===
Dear admins of Apple Story,
We are PALINDROME.
We have took control over your system and stolen your secret formula!
Do not fear for we are only after the money.
Pay us our demand and we will be gone.
For starters, we have denied all controls from you.
We demand a ransom of 1 BTC to be sent to 1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2 by 31 dec 2021.
Do not contact the police or seek for help.
Failure to do so and the plant is gone.
We planted a monitoring kit so do not test us.
Remember 1 BTC by 31 dec 2021 and we will be gone.
Muahahahaha.Regards,
PALINDROME
===Management have just one instruction. Retrieve the encryption key before the deadline and solve this.
hxxp://wp6p6avs8yncf6wuvdwnpq8lfdhyjjds.ctf.sg:14719
Note: Payloads uploaded will be deleted every 30 minutes.
PALINDROME’s “Photo of us” shows a mage in a cart which is a reference to the infamous Magecart threat actors who deploy credit card skimming malware on e-commerce sites. The malware is often smartly concealed in CSS, SVG, PNG or JPG files.
In this case, PALINDROME states that they have “planted a monitoring kit” so it is probably hidden in a similar fashion.
Hidden Base64 string
Inspecting the network traffic when visiting the site, we see a suspicious eval
and Base64 string hidden in the website’s favicon.ico
.
Decoding in CyberChef, we get the following PHP code (that I formatted) that is getting eval
’d:
|
|
New Endpoint
The above PHP code sends a POST request with some form data to a previously-unseen endpoint hxxp://s0pq6slfaunwbtmysg62yzmoddaw7ppj.ctf.sg:18926/xcvlosxgbtfcofovywbxdawregjbzqta.php
. If we try sending this request on our own with the same form key 14c4b06b824ec593239362517f538b29
and value Hi from scada
, we get back the following HTTP response which informs us that a HTML page has been created:
Request:
|
|
Response:
|
|
Visiting this page at data/331be6ac94793c631e6c87d186bcc11d.html
shows us that our original POST form value data Hi from scada
has been inserted as HTML:
|
|
This means that we can probably inject arbitrary javascript into the page for XSS. But for what purpose? Going back to the new endpoint’s root at hxxp://s0pq6slfaunwbtmysg62yzmoddaw7ppj.ctf.sg:18926/
, we see that this website is actually PALINDROME’s own homepage.
The “Latest sample data” link on the top right leads to /data.php
. This page logs the dates and times that “admin” viewed sample data.
XSS for cookie-stealing
Remember that we could inject arbitrary HTML and Javascript into data pages like data/331be6ac94793c631e6c87d186bcc11d.html
? These are likely the sample data pages that “admin” is monitoring and visiting. So, we can steal the “admin” cookie (or some other data if that’s not the goal) through XSS.
Instead of using a harmless message like “Hi from scada” as my POST form value, it’ll be my XSS payload.
|
|
When the “admin” views the sample data at data/331be6ac94793c631e6c87d186bcc11d.html
, the following HTML will render and the Javascript will grab his/her cookie and send it back to my Burp Collaborator instance.
|
|
And as expected, I soon caught a response from “admin” with their PHPSESSID
cookie.
|
|
Now where should I use this PHPSESSID
cookie? I did a Burp Suite content discovery scan on s0pq6slfaunwbtmysg62yzmoddaw7ppj.ctf.sg:18926
and it found a login page at login.php
. Using the cookie on my GET request to this page gave me a 302 redirect to landing_admin.php
.
Request:
|
|
Response:
|
|
SQL Injection
The admin landing page shows a list of targets (presumably victims of PALINDROME) and allows filtering them based on their isALIVE/isDEAD status via a dropdown menu.
Behind the scenes, the filtering is done through a POST request like so:
Request:
|
|
Response snippet:
|
|
The web application probably executes an SQL SELECT statement to retrieve rows based on our filter query, so let’s try modifying the filter with the end goal of exploiting an SQL injection to retrieve all rows (of targets/victims).
With a test query like filter=junk
, we will see that the HTML response will include the following:
|
|
However, when we try a single quote like this filter='
, the HTML response will completely omit the “Filter applied” <div>
and the filter results <table>
/<div>
. Our input has probably successfully triggered an SQL syntax error which confirms the web application is likely vulnerable to SQL injections.
Through more test payloads, you’ll notice that spaces are stripped from your payload and your query value cannot be more than 7 characters. As such, I used %a0
instead of a regular whitespace and 1
instead of the usual 1=1
to retrieve all rows.
Request:
|
|
Response snippet with flag on line 12:
|
|
Flag: TISC{H0P3_YOu_eNJ0Y-1t}
Level 5 (Binary Manipulation, IoT Analytics)
Need for Speed
We have intercepted some instructions sent to an autonomous bomb truck used by PALINDROME. However, it seems to be just a BMP file of a route to the Istana!
Analyze the file provided and uncover PALINDROME’s instructions. Find a way to kill the operation before it is too late.
Ensure the md5 checksum of the given file matches the following before starting:
26dc6d1a8659594cdd6e504327c55799Submit your flag in the format: TISC{flag found}.
Note: The flag found in this challenge is not in the TISC{…} format. To assist in verifying if you have obtained the flag, the md5 checksum of the flag is: d6808584f9f72d12096a9ca865924799.
Attached: route.bmp
There were no funny words or reverse PE chunks in the given .bmp
this time. Instead, I noticed data in the later sections of the BMP appeared to consistently have some of its least significant bits (LSBs) flipped.
I uploaded the image to StegOnline to browse the bit planes for further analysis.
Sure enough, the Red 0, Blue 0, and Green 0 (LSBs for RGB) planes all had this interesting noise that was abruptly cut off at around ⅔ of the image from the top, thus pointing to some kind of LSB steganography at work.
When I first tried to extract the LSBs of the R, G, B values of pixels and concatenate them together to form a binary string, the resulting data did not make much sense. I was stuck here for long and searched lots online until I came across this writeup for CSAW Quals 2016 where skipping every 9th extracted bit was necessary. Soon after I also found this really old tool called Stepic which uses the same technique, but nothing else apart from that. Is this something obscure?
Anyway, skipping the 9th bit worked wonders and the resulting file was a
7z
.
|
|
|
|
The extracted archive contains update.log
and candump.log
. The former tells us to check the turn signals:
see turn signals for updated abort code :)
- P4lindr0me
while the latter contains CAN traffic likely dumped by candump.
However, because we aren’t provided with any CAN database files (DBC) to decode this raw CAN bus traffic, nor do we have any information on what kind of vehicle generated this traffic, we can’t know for sure which message identifier is mapped to turn signals.
Thus, I just sorted the message IDs present in the traffic capture file by frequency and manually examined the traffic for message IDs that appeared the least often. My reasoning was that these message IDs were probably more special.
|
|
Starting from the bottom (and viewing these logs in Wireshark), message ID 0x5A1
did not contain any significant as its data just alternated between 3 different values.
However, for 0x0C7
, I noticed there were ASCII characters consistently in the 3rd byte of the data. Let’s try extracting this out with pyshark and see what it gives:
|
|
|
|
This output gives the MD5 checksum of d6808584f9f72d12096a9ca865924799
which matches what the challenge’s description is looking for!
Flag: TISC{l1f3_15_wh47_h4pp3n5_wh3n_y0u'r3_bu5y_m4k1n6_07h3r_pl4n5.-j_0_h_n_l_3_n_n_0_n}
Level 7 (Steganography, Android Security, Cryptography)
The Secret
Our investigators have recovered this email sent out by an exposed PALINDROME hacker, alias: Natasha. It looks like some form of covert communication between her and PALINDROME.
Decipher the communications channel between them quickly to uncover the hidden message, before it is too late.
Submit your flag in the format: TISC{flag found}.
Attached: Bye for now.eml
LSB Steganography
The given .eml
file contains a long Base64-encoded string as a HTML comment at the bottom. This string decodes to a PNG image.
Since the email says that we can sometimes “find the most valuable things hidden in the least significant places”, and the PNG has the EXIF image description of “瑞恩的巨蟒只喜欢最后一个比特” which translates to “Ryan’s Python likes the last bit”, we pretty much know that there is LSB steganography at play.
I ran zsteg on the image and a URL was found encoded in the least significant bits of the R, G, B, and A planes.
|
|
Downloading data.zip
from the URL and running 7z l -slt
on it to view detailed information, we see that it appears to be an encrypted zip with app.apk
inside compressed using Deflate.
|
|
There is also a comment at the end of the zip file which when reversed says THINK AGAIN BEFORE CRACKING FIKIR DULU SEBELUM MEMBOBOL
. The second part FIKIR DULU SEBELUM MEMBOBOL
is just the Indonesian translation of the message in the first part. This means that we should not be trying to bruteforce for the encrypted zip’s password.
Zip Pseudo-Encryption
I spent many hours trying random things before I thought: what if the compressed app.apk
data wasn’t actually encrypted? Instead, maybe the encrypted file general purpose bit flag (bit 0) was just set in the zip file header to fool any zip utilities trying to read/extract this zip file? With that, I tried unsetting the bit in both the ZIPFILERECORD
and ZIPDIRENTRY
structures and extraction worked without hiccups. Hooray!
|
|
APK Analysis
Launching app.apk
in https://appetize.io/, we can see that the app retrieves and checks the current time and location upon clicking on I'M IN POSITION
. It seems that the location needs to be near the CSIT building while the time needs to be somewhere around 1 hour before sunrise. If these checks aren’t fulfilled, an error is shown at the bottom and Data
and Flag
remain as N.A
.
Writeup to be continued…
I hope you enjoyed!