The Joy Of Nostalgia
Description
A data leak is suspected to stem from poor configuration management on a WebOS workstation. The engineer in charge reported executing a Registry Wipe procedure to sanitize sensitive data related to ticket SEC-2024-1837.
The ticket is closed. All bootstrap configuration keys have been purged from HKCU and HKLM. The system is deemed ‘clean.’ However, forensic analysis revealed traces of a command-line tool execution. We suspect a temporary snapshot was created for debugging purposes and was not safely deleted.
Locate the remaining ghost in the machine.
Files: memory.dmp
Tool Used: volatility3, RegRipper.
- A quick analysis of the RunMRU key and
Windows Powershell.evtxshows traces of a script executionstage2.ps1at around 17:28.
----------------------------------------
runmru v.20200525
(NTUSER.DAT) Gets contents of user's RunMRU key
RunMru
Software\Microsoft\Windows\CurrentVersion\Explorer\RunMRU
LastWrite Time 2026-01-18 17:27:59Z
MRUList = cba
a gpedit.msc\1
b services.msc\1
c powershell.exe -NoP -NonI -W Hidden -ExecutionPolicy Bypass -File C:\Temp\LG\stage2.ps1\1
----------------------------------------<EventData>
<Data>
Stopped, Available, NewEngineState=Stopped
PreviousEngineState=Available
SequenceNumber=15
HostName=ConsoleHost
HostVersion=5.1.19041.1682
HostId=ca814c95-5933-4d87-b844-d43f85dca2b4
HostApplication=C:\Windows\system32\WindowsPowerShell\v1.0\PowerShell.exe -NoP -NonI -W Hidden -ExecutionPolicy Bypass -File C:\Temp\LG\stage2.ps1
EngineVersion=5.1.19041.1682
RunspaceId=f6980405-2f10-4081-a369-223aa8f05385
PipelineId=
CommandName=
CommandType=
ScriptName=
CommandPath=
CommandLine=
</Data>
<Binary></Binary>
</EventData>The process tree at around the same time shows activities from cmd.exe and conhost.exe but these processes can not be dumped for further analysis, likely because these processes were short-lived (1-2s runtime).
$ vol.py -f memory.dmp windows.psscan | grep 17:28:..
8780 772 MoUsoCoreWorke 0xa58a6935e0c0 12 - 0 False 2026-01-18 17:28:23.000000 UTC N/A Disabled
5772 5140 conhost.exe 0xa58a6a158080 0 - 1 False 2026-01-18 17:28:03.000000 UTC 2026-01-18 17:28:05.000000 UTC Disabled
5140 8276 cmd.exe 0xa58a6a969080 0 - 1 False 2026-01-18 17:28:03.000000 UTC 2026-01-18 17:28:05.000000 UTC Disabled
8704 652 svchost.exe 0xa58a6b220080 4 - 0 False 2026-01-18 17:28:23.000000 UTC N/A Disabled
1084 5140 PING.EXE 0xa58a6b892080 0 - 1 False 2026-01-18 17:28:04.000000 UTC 2026-01-18 17:28:05.000000 UTC Disabled
2496 772 RuntimeBroker. 0xa58a6ba66080 12 - 1 False 2026-01-18 17:28:41.000000 UTC N/A Disabled- By analyzing the memory dump itself, we can see fragments of the (supposedly) ran script.
$ strings memory.dmp
$user = "User1"
$domain = $env:COMPUTERNAME
$sid = ([System.Security.Principal.NTAccount]("$domain\$user")).
Translate([System.Security.Principal.SecurityIdentifier]).Value
$dir = "C:\Temp\LG"
New-Item -ItemType Directory -Force $dir | Out-Null
# save carriers
& reg.exe save "HKU\$sid" "$dir\HKCU_SAVED.hiv" /y *> $null
& reg.exe save "HKU\.DEFAULT" "$dir\DEFAULT_SAVED.hiv" /y *> $null
# warm-up cache (1MB)
$N = 1048576
Get-Content "$dir\HKCU_SAVED.hiv" -Encoding Byte -TotalCount $N | Out-Null
Get-Content "$dir\DEFAULT_SAVED.hiv" -Encoding Byte -TotalCount $N | Out-Null
# self-delete
$me = $PSCommandPath
Start-Process -FilePath "cmd.exe" -ArgumentList "/c ping 127.0.0.1 -n 2 >nul & del /f /q `"$me`"" -WindowStyle Hidden
# requires -RunAsAdministrator
$ErrorActionPreference = 'SilentlyContinue'- The developer used
reg.exeto forcibly load 1MB of the hives to the memory.
$dir = "C:\Temp\LG"
$N = 1048576
Get-Content "$dir\HKCU_SAVED.hiv" -Encoding Byte -TotalCount $N | Out-Null
Get-Content "$dir\DEFAULT_SAVED.hiv" -Encoding Byte -TotalCount $N | Out-Null- A filescan confirms the existance of
DEFAULT_SAVED.hiv.
$ vol.py -f memory.dmp windows.filescan | grep DEFAULT_SAVED.hiv
0xa58a6b704760 \Temp\LG\DEFAULT_SAVED.hiv- Viewing the file with a registry viewer shows nothing of interest. But by analysing the hive raw, we can find the secret bootstrap key that was deleted.
$ strings -n 1 file.0xa58a6b704760.0xa58a6c87fad0.DataSectionObject.DEFAULT_SAVED.hiv.dat | tr -d \n | grep --ignore-case bootstrap
...
ssh://18.36.108.72:3636@vkTicket SEC-2024-1837(vkRetryCountD\}O vk jE&BootstrapLGW0Gh05t_1n_Th3_Tr4n54ct10n
...Flag: VSL{Gh05t_1n_Th3_Tr4n54ct10n}
Accidental
Description
While working on my project, a friend sent me a suspicious file and asked me to analyze it. After downloading and extracting the archive, I followed the included instructions. Unfortunately, I made a critical mistake and accidentally executed the malware on my system, causing my entire project to be encrypted.
Your task is to analyze the provided files, recover the encrypted data, and uncover the hidden secret.
Tool Used: FTK Imager, Ghidra, openssl.
Overview
- Navigating to the user’s directory (named
employee)shows us many encrypted files with the extension.vsl. - A tiny hexdump of the encrypted file shows a
CRYPT_V1header along with what seems to be the original extension at offset0x20.
$ xxd 24122024.vsl | head
00000000: 4352 5950 545f 5631 0000 0000 0100 0000 CRYPT_V1........
00000010: aefa 0600 b0fa 0600 9936 e428 1e8b dc01 .........6.(....
00000020: 2e72 6172 0000 0000 0000 0000 0000 0000 .rar............- Navigating to the
Desktopdirectory gives us a ransomware note.
========================================
YOUR FILES HAVE BEEN ENCRYPTED
by VSL RANSOMWARE
========================================
Victim ID: VSL-E9F224E1-DESKTOP-8DLHUJ4
All your documents, photos, databases
have been encrypted with AES-128.
Your files now have the .vsl extension.
========================================- Seems like the files are encrypted with AES-128-XXX.
- Analysis by parsing Window’s
$MFTshows that the files were encrypted around04:43:50or21:43:50UTC
- An interesting note is before the files were encrypted, a binary called
WindowsSecurityService.exewas created in the user’sTempfolder, which should raise up some red flags as this is commonly where malware move themselves to gain persistence.
- Uploading this file onto VirusTotal
confirms that is malicious.

Note
You can get the analysis results here .
Malware Analysis
Static analysis using DetectItEasy tells us that this malware was written in C/C++. Which we will reverse engineer using Ghidra.

Overview of some interesting functions:
InitializeStringsInjectIntoExplorerInstallPersistenceDeriveKeyFromSystemencryptFile
The malware begins by initializing some strings.
void InitializeStrings(void)
{
if (g_Initialized == 0) {
_C2Host = 0x36372b31342b3433;
_DAT_1400130c8 = 1026698038;
DAT_1400130cc = 0;
XorDecode(&C2Host,12,5);
_g_C2Port = 825307441;
DAT_1400130e4 = 0;
XorDecode(&g_C2Port,4,5);
__mingw_snprintf(&g_RansomNote,2048,
"========================================\n YOUR FILES HAVE BEEN ENCRYPTED \n by VSL RANSOMWARE \n================================= =======\n\nVictim ID: %%s\n\nAll your documents, photos, databases\nhave been e ncrypted with AES-128.\nYour files now have the %s extension.\n\n============== ==========================\n"
,PTR_DAT_14000e000);
g_Initialized = 1;
}
return;
}The strings includes:
- The previously seen ransom note.
- The C2 Server’s IP and Port, which is XOR-encrypted by
0x5, which decodes to61.14.233.78:4444 - This C2 Server address is also consistent with the VirusTotal analysis.
Next, in an attempt to gain persistence, the malware calls
StartC2Communications(), which then inject Shellcode intoexplorer.exe.
undefined8 InjectIntoExplorer(void)
{
undefined8 uVar1;
int ProcAddress;
ProcAddress = FindProcessByName("explorer.exe");
if (ProcAddress == 0) {
ProcAddress = FindProcessByName("svchost.exe");
uVar1 = 0;
}
else {
uVar1 = InjectShellcode(ProcAddress,&g_ShellcodeX64,460);
}
return uVar1;
}- Furthermore, the malware calls
InstallPersistence(), which in turn callsCopyToTempAndPersist().
undefined8 CopyToTempAndPersist(void)
{
uStack_10 = 5368731472;
GetModuleFileNameA((HMODULE)0,local_1008,260);
DVar1 = GetTempPathA(260,local_2008);
if (DVar1 == 0) {
uVar4 = 0;
}
else {
__mingw_snprintf((char *)local_3008,4096,g_PersistPath,local_2008);
BVar2 = CopyFileA(local_1008,(LPCSTR)local_3008,0);
if ((BVar2 == 0) && (DVar1 = GetLastError(), DVar1 != 80)) {
return 0;
}
SetFileAttributesA((LPCSTR)local_3008,128);
LVar3 = RegOpenKeyExA((HKEY)18446744071562067969,g_RunKey,0,2,&local_3010);
if (LVar3 == 0) {
sVar5 = strlen((char *)local_3008);
RegSetValueExA(local_3010,g_RunValue,0,1,local_3008,(int)sVar5 + 1);
RegCloseKey(local_3010);
}
uVar4 = 1;
}
return uVar4;
To summarise, this function:
- Get the
Temppath of the user, then move the malware toPersistPath, consistent with where we foundWindowsSecurityService.exe. - Modify the
Software\Microsoft\Windows\CurrentVersion\Runkey toWindowsSecurityUpdate, which will callWindowsSecurityService.exe.
- Get the
Finally, the heart of the malware, the
encryptFile()function and theDeriveKeyFromSystem()function, which is called by theInitializeCrypto()function.
...
local_3c = g_RawAESKey;
local_34 = DAT_140013d08;
BVar1 = CryptImportKey(g_hCryptProv,local_48,28,0,0,&g_hAESKey);
...- This key is then passed to
encryptFile().

undefined4 encryptFile(char *file)
{
...
data[0] = '\x02';
data[1] = '\0';
data[2] = '\0';
data[3] = '\0';
CryptSetKeyParam(key,4,data,0);
...
}- Here, the data is set to
0x02, which in turn set the encryption algorithm to AES-128-ECB. - Now, the key is derived from the
DeriveKeyFromSystem()function.

undefined4 DeriveKeyFromSystem(void)
{
...
BVar1 = GetComputerNameA(&g_KeyComputerName,&local_4c);
if (BVar1 == 0) {
_g_KeyComputerName = 22051046311022165;
}
local_4c = 256;
BVar1 = GetUserNameA(&g_KeyUserName,&local_4c);
if (BVar1 == 0) {
_g_KeyUserName = 22051046311022165;
}
iVar2 = GetCurrentUserSID(&g_KeyUserSID,256);
if (iVar2 == 0) {
_g_KeyUserSID = 3544945563108715859;
_DAT_140013e28 = 3471483858412449837;
DAT_140013e30 = 0;
}
local_5c = 0;
BVar1 = GetVolumeInformationA("C:\\",(LPSTR)0,0,&local_5c,(LPDWORD)0,(LPDWORD)0,(LPSTR)0,0);
if (BVar1 == 0) {
_g_KeyVolumeSerial = 3472328296227680304;
DAT_140013f28 = 0;
}
else {
__mingw_snprintf(&g_KeyVolumeSerial,32,"%08X",(ulonglong)local_5c);
}
_g_KeyMachineGUID = 3472328296227680304;
_DAT_140013f48 = 3472324997692796973;
_DAT_140013f50 = 3256155514113699888;
_DAT_140013f58 = 206966894640;
_DAT_140013f5d = 3158064;
uRam0000000140013f60 = 808464432;
LVar3 = RegOpenKeyExA((HKEY)18446744071562067970,"SOFTWARE\\Microsoft\\Cryptography",0,131353,
&local_58);
if (LVar3 == 0) {
local_4c = 64;
RegQueryValueExA(local_58,"MachineGuid",(LPDWORD)0,(LPDWORD)0,&g_KeyMachineGUID,&local_4c);
RegCloseKey(local_58);
}
GetTempPathA(260,local_168);
__mingw_snprintf(&g_KeyPersistPath,260,g_PersistPath,local_168);
__mingw_snprintf(&g_KeySourceData,1024,"%s|%s|%s|%s|%s|%s",&g_KeyComputerName,&g_KeyUserName, &g_KeyUserSID,&g_KeyVolumeSerial,&g_KeyMachineGUID,&g_KeyPersistPath);
local_10 = BCryptOpenAlgorithmProvider(&local_18,L"SHA256",(LPCWSTR)0,0);
...
}- So the key is created from the:
- Computer’s name.
- The username.
- The user’s SID.
- The Volume Serial of Drive
C. - The Machine’s GUID inside
SOFTWARE\Microsoft\Cryptography - The previously found
PersistPath. - All of these are separated with a
|, then goes through Sha256 to get the 16-byte key.
__mingw_snprintf(&g_KeySourceData,1024,"%s|%s|%s|%s|%s|%s",&g_KeyComputerName,&g_KeyUserName, &g_KeyUserSID,&g_KeyVolumeSerial,&g_KeyMachineGUID,&g_KeyPersistPath);- The computer name can be retrieved from the ransomware note.
Victim ID: VSL-E9F224E1-DESKTOP-8DLHUJ4- The username is
employee. - The user’s SID can be found inside
C:\Users\employee\AppData\Roaming\Microsoft\Crypto\RSA\%SID%which isS-1-5-21-2194485576-443945188-1043422397-1001.

- The Volume Serial can be found using
fsstat.
$ sudo ewfmount challenge.E01 /mnt
$ sudo fsstat /mnt/ewf1
FILE SYSTEM INFORMATION
--------------------------------------------
File System Type: NTFS
Volume Serial Number: 01DC87F8E9F59FE0
OEM Name: NTFS
Version: Windows XP- Since Windows only uses the lower 32-bits, our volume serial is
E9F59FE0 - The machine GUID’s can be found by inspecting the hive.
dca5645e-d0dc-4f10-ac03-59fe070380cd.
- And the
PersistPathis the path where we found the malware.C:\Users\employee\AppData\Local\Temp\WindowsSecurityService.exe - Which then we can derive the key:
$ cat key.txt | tr -d \n | sha256sum
da67fc3366bbe437aec7163db7fc2b744a0e60b5f48dda14585af83bf69079b0 -- Since this is AES-128, the key will be 16-byte, assuming it’s the first 16-byte.
- The encrypted file is structured like so.
---
config:
layout: elk
---
flowchart TB
subgraph H["Custom Header (68 bytes)"]
H1["0x00 – 0x07<br>Signature: CRYPT_V1"]
H2["0x08 – 0x1F<br>Metadata / Unknown Fields"]
H3["0x20 – 0x27<br>Original Extension<br>(e.g. .rar)"]
H4["0x28 – 0x43<br>Padding / Reserved"]
end
A["Encrypted .vsl File"] --> H & C["0x44 → EOF<br>AES-128-ECB Ciphertext"]
- Quick decrypt script:
#!/usr/bin/env sh
if [ -z $1 ]
then
echo "Usage: ./decrypt.sh filename.vsl"
exit 1
fi
HEXDUMP=$(cat $1 | xxd -p -c 0)
SIZE=$(( $(du -b $1 | awk '{print $1}') * 2 + 1 ))
CIPHERTEXT="$(echo $HEXDUMP | cut -c137-$SIZE)"
ORIGINAL_EXT="$(echo $HEXDUMP | cut -c65-73 | xxd -r -p)"
ORIGINAL_FILE=$(echo $1 | sed -e "s/\.vsl/$ORIGINAL_EXT/")
KEY="da67fc3366bbe437aec7163db7fc2b74"
echo $CIPHERTEXT | xxd -r -p | openssl aes-128-ecb -d -K $KEY > $ORIGINAL_FILE- The flag is located inside
ProjectFinal.vsl, which will get decrypted to a zip file, extracting gives usMOCK_DATA.sqlwhere the flag lies.
$ cat MOCK_DATA.sql | grep "VSL"
insert into MOCK_DATA (id, first_name, last_name, email, gender, ip_address) values (700, 'VSL{y0u_c4n_f1nd_4nd_r3c0v3ry_1t_12112}', 'Pifford', 'ipiffordjf@huffingtonpost.com', 'Male', '91.246.75.218');Flag: VSL{y0u_c4n_f1nd_4nd_r3c0v3ry_1t_12112} and VSL{61.14.233.78:4444}
Comments