Einladen is a captivating medium difficulty Sherlock scenario where your skills are put to the test in a cybersecurity incident involving diplomacy and intrigue. Recently, our team received an invite to a prestigious event at the German embassy to bid farewell to the ambassador. However, suspicions arose when our SIEM tools triggered alerts following the receipt of the email, suggesting it was a phishing attempt. Provided for your analysis are diverse artifacts including numerous binaries, a network capture, DLLs from the host system, and a critical .hta file. Dive into these resources and unravel the mystery.
A phishing email posing as an embassy farewell invitation sent the victim to a web page that smuggled a ZIP archive to disk through an embedded byte array. The archive held a decoy PDF and an HTA dropper, which wrote a Microsoft-signed executable beside a malicious DLL so the signed binary would sideload it. A separate JavaScript sample carried a Base64-encoded .NET client built on the AsyncRAT design, with an encrypted configuration, a geolocation check that stops it in one country, and a debugger check. It persisted through a Run key and a scheduled task, and its network capture shows it resolving a chat platform subdomain hosted on AWS as its command channel.
Invitation.pdf and the HTA. Inspect statically onlytshark or Wiresharkprocmon-parser, or open in ProcmonFormat note: Several answers are masked (e.g.
****v,******t, a registry path with a leading hive). Count the asterisks before submitting.
downloader.html is an HTML smuggling page: it carries the ZIP as a JavaScript byte array (d[]) and rebuilds the file in the browser before offering it as a download. Pull the array out, rebuild the bytes and hash the result. The hash is of the rebuilt ZIP, not of the HTML.
python3 -I -c "import re,hashlib;s=open('downloader.html').read();d=bytes(int(x) for x in re.search(r'd\s*=\s*\[([\d,\s]+)\]',s).group(1).split(','));open('Invitation_Farewell_DE_EMB.zip','wb').write(d);print(hashlib.sha256(d).hexdigest())"
The ZIP contains a decoy Invitation.pdf next to the HTA. Extracting its text shows the letterhead of an embassy, which names the country the lure impersonates.
pdftotext Invitation.pdf - | head
The HTA drops several files by writing byte arrays held in variables. Parse the arrays out of the script and identify each payload by its header and signature. One of them is msoev.exe, the Microsoft-signed binary that is later used for DLL sideloading next to the malicious mso.dll; the variable holding its bytes is the answer.
python3 -I parse_hta.py invitation.hta
The JavaScript sample unc.js starts with obfuscated variables, but the tail of the script holds the logic that writes the script to disk as a batch file. Decode the strings in the tail and read the target file name.
head -c 2000 unc.js; tail -c 2000 unc.js
unc.js embeds one very large Base64 blob (line 28). Decode it, write the bytes out as an EXE and hash it. This is the .NET RAT that the remaining questions examine.
sed -n '28p' unc.js | base64 -d > dec.exe && sha256sum dec.exe
Load Logfile.PML with a Procmon parser and list the process records. msoev.exe appears as PID 10044 and its record carries the parent PID of the process that launched it, which is the Explorer shell that opened the sample.
python3 -I pml.py Logfile.PML --processes | grep -i msoev
Tip: Procmon’s Process Tree view shows the same parent relationship if you prefer the GUI.
From Q9 we know the user profile, so search the Procmon events for mso.dll on that Desktop. A Load Image event for it with result SUCCESS means the sideloaded DLL was mapped into msoev.exe; a NAME NOT FOUND would have meant the load failed.
python3 -I pml.py Logfile.PML --ops | grep -i 'mso.dll' | grep -i 'Load Image'
The anti-analysis routine looks for a running process by name. Among the user strings is the name of a .NET debugger and decompiler, which is what it hunts for.
python3 -I us.py dec.exe | grep -iE 'dnspy|debug'
The same user strings hold the country checks that follow the geolocation call: a set of country codes and one country name that, on a match, ends the process.
python3 -I us.py dec.exe | grep -iE 'russia|country'
Persistence is a Run key. The path is stored reversed in the user strings to avoid a plain string match, so reverse it to read the key.
python3 -I us.py dec.exe | grep -i 'nuR' | rev
Gotcha: The format mask shows the hive abbreviation at the start and a trailing backslash. A bare
Software\...\Run\path is rejected: include theHKCUprefix.
Alongside the Run key, the malware builds a schtasks command line. Read the /rl switch in that command for the run level.
python3 -I us.py dec.exe | grep -i schtasks
Filter the Procmon events to RegSetValue and count the rows that carry a data value. Counting the rows in the capture gives the total.
python3 -I pml.py Logfile.PML --ops | grep -c RegSetValue
The same Procmon capture records the host and user in each process entry. The computer name is the host the capture was taken on.
python3 -I pml.py Logfile.PML --processes | grep -i msoev
The process record for msoev.exe lists the account it ran as; the username is the profile folder seen in the paths, e.g. C:\Users\<user>\Desktop\mso.dll.
python3 -I pml.py Logfile.PML --processes | grep -i msoev
The malware’s user strings include one external geolocation service that returns JSON with the country of the caller. That URI is the one it queries to find where the victim sits.
python3 -I us.py dec.exe | grep -i http
In the packet capture, list the DNS queries and pick out the one that is not a Windows or Microsoft service. A single chat platform subdomain stands out among the lookups the sample made.
tshark -r msoev.pcapng -Y 'dns.flags.response==0' -T fields -e dns.qry.name | sort | uniq -c | sort -rn
Look at the DNS response for the domain from Q4 and count the answer records of type A.
tshark -r msoev.pcapng -Y 'dns.qry.name=="toyy.zulipchat.com" && dns.flags.response==1' -T fields -e dns.a
From Q4 and Q5 we have the chat server addresses. The capture also holds PTR lookups, and the reverse record for one of those addresses is an AWS EC2 public hostname, which embeds the IP address in dashed form: ec2-<a>-<b>-<c>-<d>. Match the PTR response to the address the malware connected to.
tshark -r msoev.pcapng -Y 'dns.ptr.domain_name' -T fields -e dns.qry.name -e dns.ptr.domain_name
The Ports setting in Client.Settings follows the AsyncRAT configuration layout: a Base64 string decrypted with AES-256, with the key derived from a Base64 master key through PBKDF2. No .NET decompiler was on hand, so read the Base64 strings from the user-string heap with dnfile and decrypt them with the AsyncRAT salt and 50000 iterations. The result is a comma-separated list of ports.
python3 -I dec.py dec.exe
| Phase | Action |
|---|---|
| Initial Access | Phishing email with an embassy farewell lure leads to downloader.html |
| Initial Access | HTML smuggling rebuilds Invitation_Farewell_DE_EMB.zip in the browser |
| Execution | HTA dropper writes several files, including the signed msoev.exe |
| Defense Evasion | msoev.exe loads the malicious mso.dll from its own folder (sideload) |
| Execution | JavaScript sample writes itself as a batch file and decodes an embedded .NET EXE |
| Discovery | Client queries a public geolocation API and exits on a country match |
| Defense Evasion | Client checks for a running debugger by process name |
| Persistence | Run key (stored reversed in the binary) plus a scheduled task at the highest run level |
| Command and Control | DNS lookups of a Zulip chat subdomain on AWS; AES-encrypted port list in the client config |
| Type | Value |
|---|---|
| File (ZIP SHA-256) | 5D4BF026FAD40979541EFD2419EC0B042C8CF83BC1A61CBCC069EFE0069CCD27 |
| File (decoded EXE SHA-256) | DB84DB8C5D76F6001D5503E8E4B16CDD3446D5535C45BBB0FCA76CFEC40F37CC |
| File | Invitation_Farewell_DE_EMB.zip, msoev.exe, mso.dll, richpear.bat |
| Domain | toyy[.]zulipchat[.]com |
| Host | ec2-35-171-197-55[.]compute-1[.]amazonaws[.]com |
| URL | hxxp[://]ip-api[.]com/json/ |
| Ports | 666,777,111,5544 |
| Registry | HKCU\Software\Microsoft\Windows\CurrentVersion\Run\ |
| Technique | ID | Description |
|---|---|---|
| Obfuscated Files or Information: HTML Smuggling | T1027.006 | ZIP rebuilt from a byte array in downloader.html |
| System Binary Proxy Execution: Mshta | T1218.005 | HTA dropper writes the payload files |
| Command and Scripting Interpreter: JavaScript | T1059.007 | unc.js decodes and drops the .NET client |
| Command and Scripting Interpreter: Windows Command Shell | T1059.003 | Script rewrites itself as a .bat |
| Hijack Execution Flow: DLL Side-Loading | T1574.002 | Signed msoev.exe loads the malicious mso.dll |
| Virtualization/Sandbox Evasion | T1497.001 | Debugger process check |
| System Location Discovery: System Language Discovery | T1614.001 | Geolocation lookup and country check; closest ID for a country-based kill switch |
| Boot or Logon Autostart: Registry Run Keys | T1547.001 | Reversed Run key path written for persistence |
| Scheduled Task/Job: Scheduled Task | T1053.005 | schtasks entry at the highest run level |
| Web Service: Bidirectional Communication | T1102.002 | Zulip chat platform as the command channel |
Browser-built downloads bypass gateway inspection. The ZIP never crosses the mail gateway or proxy as a file; JavaScript assembles it inside the page. Detect the delivery with web proxy rules on large inline byte arrays, and on archives written to Downloads by a browser process that then contain an .hta.
Block or alert on HTA execution. An archive carrying an HTA that writes an EXE and DLL together is a clear chain. Remove mshta.exe from user reach or alert on it spawning file writes in user profiles.
A signed binary next to an unsigned DLL is the sideload signal. msoev.exe is Microsoft-signed, so reputation checks pass. The detection is an image load of mso.dll from a user-writable folder rather than from the signed binary’s expected location, which Procmon and Sysmon event 7 both show.
Chat platforms are not free of DNS scrutiny. The client used a Zulip subdomain as its channel, and the lookup plus the AWS reverse record are the only network indicators. Alert on workstations resolving chat or collaboration subdomains that the organisation does not use.
Watch Run keys and schtasks /rl highest from user processes. Both persistence routes were written by the same unsigned process within seconds of start. A user-level process creating a high run level task is rare enough to alert on.
The lure, the loader and the client were all built from common parts, and the chain only works because a signed Microsoft binary was allowed to load a DLL from a user folder. The cheapest points to break it are the download of an HTA in an archive and the sideload image load, both of which were visible well before the RAT connected out.