Huntress 2023
Forensics - Bad Memory
A user came to us and said they forgot their password. Can you recover it? The flag is the MD5 hash of the recovered password wrapped in the proper flag format.
We get a zip file with a file inside called “image.bin”. As this looks like a memory dump file, we can use volatility3 to analyse and dump data.
Volatility3 setup
We clone the repo and then download the needed symbol files, which is metadata that enabled volatility3 to decode and interpret accurately, for our operating system (linux) in the “volatility3/volatility3/symbols” folder.
1$ sudo git clone https://github.com/volatilityfoundation/volatility3.git
2[sudo] password for kali:
3Cloning into 'volatility3'...
4remote: Enumerating objects: 31324, done.
5remote: Counting objects: 100% (2168/2168), done.
6remote: Compressing objects: 100% (908/908), done.
7remote: Total 31324 (delta 1516), reused 1777 (delta 1246), pack-reused 29156
8Receiving objects: 100% (31324/31324), 6.31 MiB | 416.00 KiB/s, done.
9Resolving deltas: 100% (23697/23697), done.
10$ cd volatility3/symbols
11$ wget https://downloads.volatilityfoundation.org/volatility3/symbols/linux.zip
Volatility3 usage
We can now enumerate the image.bin file and gather info by calling windows.info.Info
.
1$ sudo python3 vol.py -f /media/sf_Shared/image.bin windows.info.Info
2[sudo] password for kali:
3Volatility 3 Framework 2.5.2
4Progress: 100.00 PDB scanning finished
5Variable Value
6
7Kernel Base 0xf8047e200000
8DTB 0x1aa000
9Symbols file:///opt/volatility3/volatility3/symbols/windows/ntkrnlmp.pdb/81BC5C377C525081645F9958F209C527-1.json.xz
10Is64Bit True
11IsPAE False
12layer_name 0 WindowsIntel32e
13memory_layer 1 FileLayer
14KdVersionBlock 0xf8047ee0f2a8
15Major/Minor 15.19041
16MachineType 34404
17KeNumberProcessors 1
18SystemTime 2020-10-03 11:45:39
19NtSystemRoot C:\Windows
20NtProductType NtProductWinNt
21NtMajorVersion 10
22NtMinorVersion 0
23PE MajorOperatingSystemVersion 10
24PE MinorOperatingSystemVersion 0
25PE Machine 34404
26PE TimeDateStamp Sun Aug 11 05:47:24 2069
We can dump the SAM hashes via windows.hashdump.Hashdump
.
1$ sudo python3 vol.py -f /media/sf_Shared/image.bin windows.hashdump.Hashdump
2Volatility 3 Framework 2.5.2
3...
4...
5...
6Progress: 100.00 PDB scanning finished
7User rid lmhash nthash
8
9Administrator 500 aad3b435b51404eeaad3b435b51404ee 31d6cfe0d16ae931b73c59d7e0c089c0
10Guest 501 aad3b435b51404eeaad3b435b51404ee 31d6cfe0d16ae931b73c59d7e0c089c0
11DefaultAccount 503 aad3b435b51404eeaad3b435b51404ee 31d6cfe0d16ae931b73c59d7e0c089c0
12WDAGUtilityAccount 504 aad3b435b51404eeaad3b435b51404ee 4cff1380be22a7b2e12d22ac19e2cdc0
13congo 1001 aad3b435b51404eeaad3b435b51404ee ab395607d3779239b83eed9906b4fb92
Cracking the hash and finding the flag
We can crack the hash for the “congo” user via john
as these are nthashes
we can provide the option --format=NT
.
1$ john --format=NT hashdump.txt --wordlist=~/wordlists/rockyou.txt
2Using default input encoding: UTF-8
3Loaded 1 password hash (NT [MD4 128/128 SSE2 4x3])
4Warning: no OpenMP support for this hash type, consider --fork=3
5Press 'q' or Ctrl-C to abort, almost any other key for status
6goldfish# (congo)
71g 0:00:00:00 DONE (2023-10-24 22:16) 1.250g/s 9785Kp/s 9785Kc/s 9785KC/s
8Session completed.
We now have the plaintext password, but the flag needs to be an MD5 hash, so we can hash plaintext password, and we get the flag.
1$ echo -n "goldfish#" | md5sum
22eb53da441962150ae7d3840444dfdde
flag{2eb53da441962150ae7d3840444dfdde}
Malware - Opendir
A threat actor exposed an open directory on the public internet! We could explore their tools for some further intelligence. Can you find a flag they might be hiding?
We get a link to an open directory, which is a directory that is available to access via a URL, with a username and
password.
We can download the directory via wget
and then search through to files with grep
to find the hidden flag.
1$ wget --user opendir --password opendir --no-parent -r http://chal.ctf.games:30169/
2$ cd chal.ctf.games:30169
3$ ls
4def1.bat hyp.bat LOGOFALL1.bat NG1.bat NG3.bat poshC2.bat sir
5dropper_cs.exe index.html LOGOFALL.bat NG2.bat ngrok.exe RDPtoALL.bat VmManagedSetup.exe
6$ grep -r "flag*"
7grep: sir/hydra/hydra.exe: binary file matches
8sir/LOGOFF.bat: set flag=false
9sir/LOGOFF.bat: if "%username%" neq "!user!" (set flag=true) else (if "!status!" neq "Active" set flag=true)
10sir/LOGOFF.bat: if !flag!==true (logoff !ID!& Echo user=!user! ID=!ID! Status=!status! was log off.)
11sir/64_bit_new/oui.txt: Chiefland FL 32626
12sir/64_bit_new/oui.txt:44-4A-65 (hex) Silverflare Ltd
13sir/64_bit_new/oui.txt:444A65 (base 16) Silverflare Ltd
14sir/64_bit_new/oui.txt:00-0A-68 (hex) Solarflare Communications Inc.
15sir/64_bit_new/oui.txt:000A68 (base 16) Solarflare Communications Inc.
16sir/64_bit_new/oui.txt: Efland NC 27243
17sir/64_bit_new/oui.txt:flag{9eb4ebf423b4e5b2a88aa92b0578cbd9}
18sir/64_bit_new/oui.txt:00-0F-53 (hex) Solarflare Communications Inc.
19sir/64_bit_new/oui.txt:000F53 (base 16) Solarflare Communications Inc.
Malware - BlackCat
We’ve been hit by the infamous BlackCat Ransomware Group! We need you to help restore the encrypted files. Please help! My favorite rock got encrypted and I’m a wreck right now!
We get a zipped file that contains a ransomware decryption tool. The files contain an executable file that asks for a key to decrypt the files, and if a key is provided, tries to decrypt the files in the “victim-files” directory.
├── DecryptMyFiles.exe
├── NOTE.png
└── victim-files
├── Bliss_Windows_XP.png.encry
├── flag.txt.encry
├── Huntress-Labs-Logo-and-Text-Black.png.encry
├── my-favorite-rock.jpg.encry
├── the-entire-text-of-hamlet.txt.encry
When disassembling the executable, we find out the XOR cipher is being used. XOR is considered a weak cipher because it’s vulnerable to Known-plain-text attacks (KPA), which is basically when you have two values you can always derive the third value:
- plain-text x key = encrypted_text
- encrypted_text x plain-text = key
- encrypted_text x key = plain-text If the key is smaller than the plaintext, the key is repeated.
We now have to find out if we can find out plaintext that we can use in this attack. We see a bunch of images being encrypted, we know that the magic bytes (file signatures) at the start of a file are always the same. We can find the hex value by extracting them from any PNG file, in this case we use “notes.png” that was also supplied in the compressed archive.
1$ xxd ../NOTE.png| head -1
200000000: 8950 4e47 0d0a 1a0a 0000 000d 4948 4452 .PNG........IHDR
We can use this hex value as the plaintext and any encrypted png in CyberChef while decrypting using the XOR cipher.
encrypted_text x plain-text = key
-> contentOf(Huntress-Labs-Logo-and-Text-Black.png.encry) x 89504e470d0a1a0a = key
We find the key is “cosmoboi”, located on the location where the magic bytes would be (.PNG).
When putting in the key in “DecryptMyFiles.exe”, we get the decrypted flag.txt:
Keeping my flag here so it's safe:
flag{092744b55420033c5eb9d609eac5e823}
Malware - BlackCat II
Be advised analyst: BlackCat is back! And they’re mad. Very mad. Help our poor user recover the images that they downloaded while browsing their favorite art site. Quickly!
We’ve received a zipped file that contains a decryption tool for ransomware. Inside the files, we find an
executable Decryptor.exe
that launches a GUI prompting for a decryption key. If a valid key is provided, it attempts
to decrypt the files located in the “victim-files” directory.
Directory Structure:
├── Decryptor.exe
└── victim-files
├── A_Sunday_Afternoon_on_the_Island_of_La_Grande_Jatte_by_Georges_Seurat_5773ff06-a03e-401b-8914-6106bc277bfd_large.jpg.encry
├── Cafe_Terrace_at_Night_by_Vincent_van_Gogh_large.jpg.encry
├── flag.txt.encry
├── Guernica_by_Pablo_Picasso_large.jpg.encry
├── Impression_Sunrise_by_Claude_Monet_large.jpg.encry
└── Wanderer_above_the_Sea_of_Fog_by_Caspar_David_Friedrich_large.jpg.encry
Upon loading “Decryptor.exe” in DotPeek, we can access the source code, revealing a couple of noteworthy methods.
The AESDecryptFile
method stands out, utilizing a hardcoded initialization vector (IV):
1 private static void AESDecryptFile(string inputFile, string outputFile, string key, byte[] iv)
2 {
3 try
4 {
5 using (Aes aes = Aes.Create())
6 {
7 byte[] aesKeyFromPassword = DecryptorUtil.GenerateAesKeyFromPassword(key);
8 aes.Key = aesKeyFromPassword;
9 aes.IV = iv;
10 aes.Mode = CipherMode.CFB;
11 aes.Padding = PaddingMode.Zeros;
12 using (FileStream fileStream1 = new FileStream(inputFile, FileMode.Open))
13 {
14 using (FileStream fileStream2 = new FileStream(outputFile, FileMode.Create))
15 {
16 using (ICryptoTransform decryptor = aes.CreateDecryptor())
17 {
18 using (CryptoStream cryptoStream = new CryptoStream((Stream) fileStream2, decryptor, CryptoStreamMode.Write))
19 {
20 byte[] buffer = new byte[4096];
21 int count;
22 while ((count = fileStream1.Read(buffer, 0, buffer.Length)) > 0)
23 cryptoStream.Write(buffer, 0, count);
24 }
25 }
26 }
27 }
28 }
29 }
However, the most interesting one is DecryptFiles
, which utilizes
the SHA256 hash of the plaintext file as a decryption key when provided with the
filepath`.
1 public static void DecryptFiles(string directoryPath, string decryptionKey)
2 {
3 string[] files = Directory.GetFiles(directoryPath, "*.encry");
4 if (files.Length == 0)
5 return;
6 string filePath = (string) null;
7 foreach (string str in files)
8 {
9 string key = filePath != null ? DecryptorUtil.CalculateSHA256Hash(filePath) : decryptionKey;
10 string outputFile = Path.Combine(directoryPath, Path.GetFileNameWithoutExtension(str) + ".decry");
11 DecryptorUtil.AESDecryptFile(str, outputFile, key, DecryptorUtil.hardcodedIV);
12 filePath = outputFile;
13 }
14 Console.WriteLine("[*] Decryption completed.");
15 }
16
17 private static string CalculateSHA256Hash(string filePath)
18 {
19 using (SHA256 shA256 = SHA256.Create())
20 {
21 using (FileStream inputStream = File.OpenRead(filePath))
22 return BitConverter.ToString(shA256.ComputeHash((Stream) inputStream)).Replace("-", "").ToLower();
23 }
24 }
If we have the plaintext version of those encrypted files, we could decrypt all of our victim-files. We see a bunch of images in the “victim-files” folder. One that sticks out is “A_Sunday_Afternoon_on_the_Island_of_La_Grande_Jatte_by_Georges_Seurat_5773ff06-a03e-401b-8914-6106bc277bfd_large.jpg” as appears to contain a universally unique identifier (UUID).
UUIDs are 128-bit values typically represented as a hexadecimal string with hyphens to separate different parts of the identifier. UUIDs are often used to uniquely identify resources or entities, and they have a very low probability of collision, making them suitable for various applications like database records, filenames, or other scenarios where unique identification is important.
We search these images on Google Images, and as we stumble on one website that seems to have all of them: atxfinearts.com. Upon saving “A Sunday Afternoon on the Island of La Grande Jatte,” we observe that the downloaded file matches the encrypted file’s name but with a .webp extension instead of .jpg.
We can calculate the SHA256
hash of the downloaded webp image to obtain the decryption key.
Linux
1$ sha256sum A_Sunday_Afternoon_on_the_Island_of_La_Grande_Jatte_by_Georges_Seurat_5773ff06-a03e-401b-8914-6106bc277bfd_large.webp
280d60bddb3b57a28d7c7259103a514cc05507c7b9cf0c42d709bdc93ffc69191 A_Sunday_Afternoon_on_the_Island_of_La_Grande_Jatte_by_Georges_Seurat_5773ff06-a03e-401b-8914-6106bc277bfd_large.webp
Windows
1> Get-FileHash -Algorithm SHA256 -Path .\A_Sunday_Afternoon_on_the_Island_of_La_Grande_Jatte_by_Georges_Seurat_5773ff06-a03e-401b-8914-6106bc277bfd_large.webp
2Algorithm Hash
3--------- ----
4SHA256 80d60bddb3b57a28d7c7259103a514cc05507c7b9cf0c42d709bdc93ffc69191
With this decryption key and the filepath to the victim-files folder, we can input them into the Decryptor executable.
Opening “flag.txt.decry” in a text editor reveals the flag:
Keeping another flag here for safe keeping again!
flag{03365961aa6aca589b59c683eecc9659}
Miscellaneous - Who is Real?
This is not a technical challenge, but it is a good test of your eye!
Now we live in a world of generative AI, for better or for worse. The fact of the matter is, threat actors can scheme up fake personas to lure you into a scam or social engineering… so, can you determine which profile picture is real and which is fake?
Play a game to train yourself on identifying what stands out for AI generated people. After a streak of 10 correct selections, you’ll receive the flag!
We get a webpage where we need to choose between an AI-generated picture and a real picture. The method that worked best for me is to focus on the background as the AI pictures often have backgrounds that have little flaws in them, as you can see in the screenshot below.
Miscellaneous - Indirect Payload
We saw this odd technique in a previous malware sample, where it would uncover it’s next payload by… well, you’ll see.
We accessed a website featuring a button that redirects users to a “/flag.php” endpoint. Following this initial redirection, a series of subsequent redirects occurred, each leading to a distinct hash-based URL. Within the response of each redirection, a fragment of the flag was provided.
Request URL: http://chal.ctf.games:30483/site/flag.php
Status code: 302
Date: Wed, 18 Oct 2023 13:36:12 GMT
Location: /site/fe3cbf06ef09be78eb8ae144888eeeae.php
Response Content:
Redirecting to: http://chal.ctf.games:30483/site/fe3cbf06ef09be78eb8ae144888eeeae.php
Request URL: http://chal.ctf.games:30483/site/fe3cbf06ef09be78eb8ae144888eeeae.php
Status code: 302
Date: Wed, 18 Oct 2023 13:36:12 GMT
Location: /site/f99cc7e975c1fdfd1b803bd248bac515.php
Response Content:
Redirecting to: http://chal.ctf.games:30483/site/f99cc7e975c1fdfd1b803bd248bac515.php
Request URL: http://chal.ctf.games:30483/site/f99cc7e975c1fdfd1b803bd248bac515.php
Status code: 302
Date: Wed, 18 Oct 2023 13:36:13 GMT
Location: /site/0eb108f40ad71158d396d396e825fab7.php
Response Content:
character 0 of the payload is f
The Python script below helps navigate a website’s redirects and gather parts of a hidden flag from the response. By following the redirects, the script extracts characters from the response text, revealing the flag.
1import requests
2import sys
3from urllib.parse import urljoin
4
5base_url = sys.argv[1]
6url = urljoin(base_url, "site/flag.php")
7flag = ''
8while '}' not in flag:
9 try:
10 response = requests.get(url, allow_redirects=False)
11 print(f"flag: {flag} - URL: {response.url}")
12 if response.text:
13 flag += response.text.split()[-1]
14
15 # Check if it's a redirect
16 if response.status_code == 302:
17 location = response.headers['Location']
18 url = urljoin(base_url, location)
19 else:
20 break # Stop if it's not a redirect
21 except requests.exceptions.RequestException as e:
22 print(f"An error occurred: {e}")
23 break
24if flag:
25 print(f"FLAG FOUND: {flag}")
Output:
flag: - URL: http://chal.ctf.games:32522/site/flag.php
flag: - URL: http://chal.ctf.games:32522/site/fe3cbf06ef09be78eb8ae144888eeeae.php
flag: - URL: http://chal.ctf.games:32522/site/f99cc7e975c1fdfd1b803bd248bac515.php
flag: f - URL: http://chal.ctf.games:32522/site/0eb108f40ad71158d396d396e825fab7.php
flag: f - URL: http://chal.ctf.games:32522/site/e318c81f0211a5b17060ddab1fcc8fb0.php
flag: fl - URL: http://chal.ctf.games:32522/site/bdbbadb4fe344b998f98ca54c2e97b01.php
...
...
...
flag: flag{448c05ab3e3a7d68e3509eb85e87206 - URL: http://chal.ctf.games:32522/site/bc3d27d4ea43d4da9464eb03d753db61.php
flag: flag{448c05ab3e3a7d68e3509eb85e87206f - URL: http://chal.ctf.games:32522/site/1fb764ff4dd3eb7bcfb0a3e7c064f975.php
flag: flag{448c05ab3e3a7d68e3509eb85e87206f - URL: http://chal.ctf.games:32522/site/b9e277d8277ae70b3402edff6a4a6764.php
FLAG FOUND: flag{448c05ab3e3a7d68e3509eb85e87206f}
Miscellaneous - Rock, Paper, Psychic
Wanna play a game of rock, paper, scissors against a computer that can read your mind? Sounds fun, right?
We can open the binary with IDA and go through the source code. In the main
function, we see a jnz
(jump if not
zero) check that jumps to playerWins
if it’s not zero otherwise it continues in main
and calls computerWins
.
As the computer knows our choice, this check is always true and the computer always wins. We can patch the source code
(Edit > Patch > Assemble) and change it to jz
(jump if zero).
We can now debug through the program (or apply the patches and run the patched exe file) and get the flag even though we lose.
Miscellaneous - Discord Snowflake Scramble
Someone sent message on a Discord server which contains a flag! They did mention something about being able to embed a list of online users on their own website…
Can you figure out how to join that Discord server and see the message?
Note: Discord phone verification is NOT required for this challenge.
Connect here: https://discord.com/channels/1156647699362361364/1156648139516817519/1156648284237074552
We get a discord link that redirects to an empty space.
We have a URL with IDs. We can look around for tools that might give us more information about the discord server. We come across discordlookup.com (GitHub).
We can use the Snowflake IDs in the URL to retrieve this information. Discord uses Snowflake IDs to uniquely identify various entities like users, channels, servers, messages, and more.
In the URL that was provided (https://discord.com/channels/1156647699362361364/1156648139516817519/1156648284237074552), these IDs represent the following:
- 1156647699362361364: Server (guild) ID.
- 1156648139516817519: Channel ID.
- 1156648284237074552: Message ID.
We can follow the “Instant Invite URL” and gain access to the server that contains the flag message.
OSINT - Operation Not Found
We get a link to a challenge on osint.golf.
We see construction happening on a build by the company “Brasfield & Gorrie”. The picture seems to date from maps 2019.
We can go to their portfolio page and look through the different projects they have done.
Brasfield & Gorrie Blog Post Google Maps location
OSINT - Under The Bridge
Can you find this iconic location? We again get a link to a challenge on osint.golf.
We can see a number on the bridge “HC13”, additionally, all the warning signs are in English, and the plates on the cars seem to indicate that this bridge is located in England. When we search “HC13 bridge England” we find out that this is the bridge that appears in the Rick Roll music video. This bridge is located on “150 Freston Rd, London”.
Steganography - Land Before Time
This trick is nothing new, you know what to do: iSteg. Look for the tail that’s older than time, this Spike, you shouldn’t climb.
The flag can be extracted using iSteg.
1~/Downloads ➜ cd iSteg-v2.01_CLI
2~/Downloads/iSteg-v2.01_CLI ➜ java -jar iSteg-v2.01_CLI.jar
3iSteg CLI v-2.01
4Enter your choice:
5 1. Hide a file with Steg
6 2. Hide a message with Steg
7 3. Extract stuff from Steg
8 Enter any things to exit.
93
10Enter file name with extension:
11dinosaurs1.png
12Password (Press enter if the steganographic data wasn't encrypted):
13
14Message extraction successful. The text is:
15flag{da1e2bf9951c9eb1c33b1d2008064fee}