The following post by 0xb0b is licensed under CC BY 4.0
Initial Alert and File Inspection
In the challenge, we have a PCAPNG file. In the story of the challenge, we are a senior DFIR specialist and were alerted by a junior colleague that some suspicious traffic was being generated. The SIEM had failed to capture the network traffic, but the network capture device was still working.
We open the pcapng file using Wireshark and detect a small amount of HTTP traffic, followed by pure TCP traffic on port 1337.
Traffic Analysis
We inspect the HTTP traffic by right-clicking on the packet and following the HTTP stream. We see that a PowerShell script gets downloaded.
Follow -> HTTP Stream
Inspecting the TCP traffic on port 1337 we see a bunch of base64 encoded data.
Follow -> TCP Stream
First, lets export the script. Via
File -> Export Objects -> HTTP...
We are able to retrieve the script xxxmmdcclxxxiv.ps1.
PowerShell Script Functionality
This script is a PowerShell script for dumping the memory of a KeePass process using procdump.exe and performing additional actions, including XOR encoding the dump files and sending them over a network. We look through it section by section.
It checks if KeePass is running, and if KeePass is running, it uses procdump.exe to generate a memory dump.
That memory dump is stored in 1337.dmp and the data is XOR-encoded using the key 0x41. The XOR-encoded content is converted into a Base64 string and then sent over a TCP connection to a remote server on port 1337.
If a file named Database1337.kdbx is found, it follows similar steps for XOR encoding, Base64 encoding, and sending the file over the network. The data is XOR-encoded using the key 0x42, then converted into a Base64 string.
The data is then sent over a TCP connection over port 1338.
Extracting Data from TCP Port 1337
On port 1337, a lot of data is being transmitted. Following the traffic via Follow->TCP Stream is not recommended. But we already know what the data might be. Let's get that via tshark.
Via tshark we retrieve the raw data send over port 1337 and store it into the file dump_1337.raw.
The next thing we do is to craft a script to restore the data, which, when retrieved via the tshark method, is still in raw hex. With that script, the data gets first converted from hex to ASCII, then base64 decoded and then XORed with the extracted key 0x41.
This script was generated using ChatGPT. In the end, it would have been faster to write it independently, since GPT made a lot of mistakes
stream2dmp.py
import base64defprocess_raw_data(file_path,output_file,xor_key=None):try:# Step 1: Read raw hex data from filewithopen(file_path, 'r')as file: hex_data = file.read().strip()# Step 2: Convert hex to ASCII (to look for Base64 content) ascii_data =bytes.fromhex(hex_data).decode("utf-8", errors="ignore")# Step 3: Extract potential Base64 content base64_content =Nonefor line in ascii_data.splitlines():iflen(line)>50andall(c.isalnum() or c in'+/='for c in line): base64_content = linebreakifnot base64_content:print("No Base64 content found.")return# Step 4: Decode Base64 content first decoded_base64 = base64.b64decode(base64_content)# Step 5: XOR decryption on Base64-decoded content (if a key is provided) decrypted_data = decoded_base64if xor_key isnotNone: decrypted_data =bytearray([byte ^ xor_key for byte in decoded_base64])# Step 6: Write the XOR-decrypted data to a .dmp filewithopen(output_file, 'wb')as output: output.write(decrypted_data)print(f"Decrypted data written to {output_file}")exceptExceptionas e:print(f"An error occurred: {str(e)}")# Usagefile_path ='dump_1337.raw'# Path to your raw hex data fileoutput_file ='output_1337.dmp'# Path to save the output filexor_key =0x41# Replace with your XOR keyprocess_raw_data(file_path, output_file, xor_key)
After running the script, we check if we successfully decoded the data. Using the file command, we see it is a Mini Dump. The conversion was successful.
Extracting the Master Key
We seem to have no chance with grep getting the initial part of the software. After some research, we come across the keepass-dump-masterkey tool by matro7sh.
With that, we are able to retrieve the initial password and are able to answer the first question.
Retreiving the KeePass File
Still, we are missing the KeePass file, to test that password. Let's get back to our Wireshark session. By excluding the traffic on port 1337, we see at the end the transmission of the KeePass file on port 1338, as seen in the script.
Via
Follow -> TCP Stream
We are able to extract the base64-encoded data.
Using the following script, or CyberChef, we are able to retrieve the transmitted KeePass file.
dec-keepass.py
import base64# Base64 encoded stringencoded_string = "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"
# Hexadecimal keyhex_key =0x42# Key 42 in hexadecimal# Decode the base64 stringdecoded_bytes = base64.b64decode(encoded_string)# XOR decryption functiondefxor_decrypt(data,key):returnbytes([b ^ key for b in data])# Decrypt the data using XOR with the hex keydecrypted_data =xor_decrypt(decoded_bytes, hex_key)withopen("dec.kdbx", "wb")as file: file.write(decrypted_data)
After extracting the keepass file via the script we are able to check via the file command if the conversion was successful.
Using CyberChef:
Brute-Forcing the KeePass Password
Using the initial password, we are prompted that the file may be corrupt or the password incorrect.
As a reminder, the first character cannot be found in the dump, and for the second the script will only give you a few possibilities, in any case we recommend you to run the bruteforce on 2 chars with the script below
The first character cannot be found and needs to be brute-forced. To craft a wordlist we use the following script t prepend any printable character. Keep in mind, that the base password in this script is redacted and need to be changed.
wordlist.py
#!/usr/bin/env python3import string# Target string, CHANGE MEtarget ="REDACTED"# Open a file to write the resultswithopen("wordlist.txt", "w")as f:# Loop through all printable characters and prepend them to the target stringfor char in string.printable: result = char + target f.write(result +"\n")print("Wordlist generated and written to wordlist.txt")
We use the suggested script from the repository, but it fails.
#!/bin/sh
# Usage: ./keepass-pwn.sh Database.kdbx wordlist.txt (wordlist with 2 char)
while read i
do
echo "Using password: \"$i\""
echo "$i" | kpcli --kdb=$1 && exit 0
done < $2
After another research we come across keepass4brute, which lets us brute force the password.
After a short execution, we receive the correct password.
We are now able to unlock the KeePass file and find the final flag in the Notes of the entry You win!.