← ALL FILES REPAIR · Analysis
Qilin win_enc Analysis
Static analysis · Qilin (Agenda) ransomware

Qilin win_enc encryptor

A keyed Rust encryptor for Windows. It disables recovery, stops backup and database services, encrypts local and network files with ChaCha20, and wraps each file key with an embedded RSA-2048 public key.

SHA-256 56e1d092…f7e7Method: REA MCP + Ghidra 12.1.2 + PythonSample never executed
C confirmed in code or dataI inferred from decompilationU unverified

Summary

RoleEncryptor with spreader
CipherChaCha20 + RSA-2048 OAEP
Extension.jtu5s6r
Activation44-character --key

The binary needs an activation key. The key decrypts an embedded blob that holds the ransom note, and a wrong key stops the run before any system change. Encrypted files get the extension .jtu5s6r and a note named RECOVER-jtu5s6r-DATA.txt. Only the operators hold the RSA private key, so a decryptor cannot be built from this sample.

Sample identification

PropertyValue
SHA-25656e1d092c07322d9dad7d85d773953573cc3294b9e428b3bbbaf935ca4d2f7e7
MD559d756280b06cf113ca43abc0050edd5
Imphashd5bbe0ab32fe5cd8bfc57bbea724fe95
Size / format7,363,640 bytes · PE32+ x86-64 console · 10 sections · stripped, Rust symbols retained
ToolchainRust (rustc 129f3b9964af…), built under /root/.cargo, so cross-compiled on Linux
Entropy6.30 overall, not packed
Crateschacha20 0.9.1 · rsa 0.7.2 · sha2 0.10.8 · rand 0.8.5 · base64 0.21.7 · clap 2.34.0 · threadpool 1.8.1 · regex 1.10.4 · zeroize
ImportsSmall, Rust std style: file and process APIs, Winsock, BCryptGenRandom. No service-control, Restart Manager or crypto-API imports; all crypto is in the binary.

Command line C

OptionMeaning
--key KEYRequired. Activation key, exactly 44 characters
-p, --pathFile or directory to encrypt
-s, --sleepDelay in seconds before acting
--no_localSkip local data
--no_netSkip network data
--no_implDo not use impersonation
--no_notesDo not write ransom notes

Execution flow

From win_enc::main, run_command and check_key_and_get_rec_text.

  1. Parse arguments with clap. C
  2. Validate the key. C
    • The key must be 44 characters: a 32-byte ChaCha20 key plus a 12-byte nonce. I
    • It decrypts the embedded 323-byte base64 blob, then base64-decodes the result to UTF-8 text, which is the ransom note.
    • The text must contain the marker ***is_ok***. Otherwise the sample prints "Key is invalid" and exits.
  3. Impersonate unless --no_impl is set, through two unnamed helper functions. U
  4. Sleep for --sleep seconds. C
  5. Inhibit recovery. Each command runs as cmd /C … with CREATE_NO_WINDOW. C
    • fsutil behavior set SymlinkEvaluation R2L:1 and R2R:1
    • iisreset.exe /stop
    • vssadmin.exe Delete Shadows /all /quiet and wmic.exe Shadowcopy Delete
    • bcdedit /set {default} and bcdedit /set {default} recoveryenabled No
    • reg add HKLM\SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters /v MaxMpxCt /d 65535 /t REG_DWORD /f
    • wevtutil.exe cl on every log
    • PowerShell Get-VM | Stop-VM -Force (the $excludedVMs list is empty)
  6. Stop services and kill processes. C
    • net stop <name> /y over about 120 service names
    • taskkill /IM <name> /F over about 125 process names
    • sc stop for every running service
  7. Encrypt local data unless --no_local: list drives, walk files, send them to a 50-thread pool that calls encrypt_file. C
  8. Encrypt network data unless --no_net. C
    • arp -a lists neighbors and a regex extracts IPv4 addresses. For each address, cmd /C net view \\<ip> lists its shares (output captured). A separate cmd /C chcp 65001 >nul runs first.
    • A batch file is written, run with cmd, then deleted.
    • The sample copies itself to C:\Users\Public\<its own file name>. C
    • The embedded PsExec is dropped to C:\Users\Public\psexec0.exe.
    • Reconstructed command: psexec0.exe \\<host> -accepteula -s -d "<copy path> <original arguments>" --no_impl. The arguments are the sample's own command line, so --key is probably forwarded. I
    • A second 50-thread pool fans out. Nothing in the code copies the payload to the remote host (no -c, no xcopy, robocopy, net use or C$ strings), so the copy path must already exist on each target. U
    • --no_impl sits outside the quotes; whether PsExec passes it to the payload is unknown. U
  9. Write the note RECOVER-jtu5s6r-DATA.txt in each directory unless --no_notes. C

Network behavior

The sample has no network code of its own. Every network action goes through Windows tools. No broadcast, multicast or command-and-control traffic was found in the binary. This is static analysis only; no traffic was observed.

ActivityProtocol and scopeSource
arp -aReads the local ARP cache; sends no packets itselfSample runs the command
net view \\<ip>Unicast to one host at a time, SMB (TCP 445). If Windows must resolve a name it may use NetBIOS (UDP 137, TCP 139), which can broadcastSample runs the command; any NetBIOS traffic comes from Windows
PsExec to each hostUnicast SMB (TCP 445), then a named pipe to PSEXESVCEmbedded Sysinternals PsExec
Encryption of network filesSMB file access to shares the token can reachStandard file I/O

Checked in the binary

  • No 255.255.255.255, multicast (224.0.0.x, 239.255.x.x) or ff02:: string, and no 0.0.0.0 or 127.0.0.1.
  • No IPv4 literal, domain, .onion address or URL for the sample to contact. The https:// strings belong to Rust libraries.
  • The imported socket functions (sendto, connect, getaddrinfo, setsockopt) are used only by Rust standard library code (UdpSocket::send_to, Socket::connect, LookupHost), which has no direct callers: linked but apparently unused. I Indirect calls would not show in the caller list, so this is not proven.

Expected on the wire: SMB connections (TCP 445) from the infected host to many neighbors in turn, PSEXESVC installs on those hosts, and no beaconing or broadcast storms. I No leak-site or negotiation address is stored in plain text; any such address is probably in the encrypted ransom note, which the victim reads.

File encryption

Per-file algorithm

  1. Generate a random 32-byte key and 12-byte nonce with OsRng. C
  2. Skip the file if its name is the note name, its extension is jtu5s6r, or its last 7 bytes are already jtu5s6r. C
  3. Rename file to file.jtu5s6r and encrypt it in place through 8 KiB buffers. C
  4. Choose a mode and encrypt with ChaCha20 (expand 32-byte k). I
  5. Append the 276-byte footer, then write the note into the directory. C

Footer layout I

encrypted bodysame length as original
RSA-OAEP(key)256 B
nonce12 B
mode1 B
jtu5s6r7 B

Mode selection I

ModeConditionBehavior
0File under 100 MiBFull encryption in 2 MiB chunks
3Extension in the document, database or image list2 MiB blocks, up to 100 per round, stride size/20
2Default for files of 100 MiB or more with other extensions1 MiB blocks, stride size/30
1Handled by the dispatcher, not chosen by encrypt_file1 MiB blocks, stride size/15

Mode 3 wins over mode 0, but a small listed file still ends up fully encrypted because it fits in one block. The exact stride semantics of modes 1 to 3 are unverified. U

Listed extensions: sql, doc, rtf, xls, jpg, jpeg, png, gif, webp, tiff, psd, raw, bmp, pdf, docx, docm, dotx, dotm, odt, xlsx, xlsm, xltx, xltm, xlsb, xlam, ods, pptx, pptm, potx, potm, ppsx, ppsm, odp, mdf, txt.

Embedded RSA public key C

-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA7cu+Khl+gDf5QWsaPS+6
WkRgKUjnkvz+ZVL0HDDCGy6z5Qe59vwONZlvp/10nO7ZcsXLD7uKEiO9eS0l0E8o
PgPD9EipHyNIz6KRks28D6Ciq9txrgwbiqDhh4EcCroeRVWXDJgQFkztWgaHVBCP
ks4JOC0MKAVqm0LPuEyx+EnPlzcbsLDyU0Ryh7Ti7WyEX+4QPyQ250i4o1lO54v0
UD5aHppAl3BfgwSI+QvdKa0/D28H6eRSQWS5g0UyWUuOxwNZ9KcT9edqtzPNdlou
ZOPsVaDElJdc06TvUwSpr1rOzwkhClZr/WXgEYKtu7UDs0ral0O2TOdVVx6A4S8I
2QIDAQAB
-----END PUBLIC KEY-----

RSA cannot be attacked at this size. The OAEP hash (SHA-256 or SHA-512) is not determined. U

Possible keystream reuse. I The decompilation shows the ChaCha20 block counter set to 0 for every chunk and every block. If that is right, all chunks of a file share one keystream. A synthetic model confirmed the effect: a zero-filled file gives identical ciphertext chunks. Predictable plaintext (zero runs, known headers) could then expose keystream, though the key itself stays safe. The decompilation had type-propagation warnings and the sample was not run, so this needs checking against real output.

Embedded tooling C

A hex-encoded PE is stored in the binary and written to C:\Users\Public\psexec0.exe. It is a Sysinternals PsExec: 32-bit, 716,176 bytes, with the Sysinternals licence text and the psexec.pdb and psexesvc.pdb paths in its strings.

Indicators of compromise

Sample56e1d092c07322d9dad7d85d773953573cc3294b9e428b3bbbaf935ca4d2f7e7
Imphashd5bbe0ab32fe5cd8bfc57bbea724fe95
RSA key DER94c557feef7fdb9750cc9b43ebf4f811417830ca9c339fcc2b71d5fb3a5f72ce
PsExec SHA-256078163d5c16f64caa5a14784323fd51451b8c831c73396b967b4e35e6879937b
PsExec MD524a648a48741b1ac809e47b9543c6f12

Host artifacts

  • Extension .jtu5s6r and note RECOVER-jtu5s6r-DATA.txt
  • Dropped file C:\Users\Public\psexec0.exe, and a temporary .bat file that is written and then deleted
  • Last 7 bytes of an encrypted file are jtu5s6r

Behavioral

  • Several recovery-inhibition commands (vssadmin, wmic, bcdedit, wevtutil cl, many net stop) from one parent process within seconds
  • arp -a, then cmd /C net view \\<ip> for each address found, from the same process
  • psexec0.exe \\<host> -accepteula -s -d … against many hosts in quick succession
  • Registry write LanmanServer\Parameters\MaxMpxCt = 65535
  • High file rename rate with a new extension, across 50 threads

This build has no domain, credential, .onion address or leak-site string. The ransom text sits in the encrypted blob.

Targeted services, processes and paths C

Services (partial)

veeam* Veeam*Svc backup* memtas mepocs sql$ mysql mysql$ sophos MSExchange* WSBExchange PDVFSService BackupExec* Gx* (CommVault) SAP* QB* (QuickBooks) AcronisAgent AcrSch2Svc DefWatch ccEvtMgr ccSetMgr SavRoam RTVscan zhudongfangyu stc_raw_agent CASAD2DWebSvc CAARCUpdateSvc MSSQLSERVER SQLSERVERAGENT SQLBrowser MSSQLServerOLAPService ReportServer MsDtsServer SQLWriter WSearch wuauserv eventlog

Processes (partial)

agntsvc dbeng50 dbsnmp encsvc excel firefox infopath isqlplussvc msaccess mspub notepad ocautoupds ocomm ocssd onenote oracle outlook powerpnt sql steam synctime thunderbird visio winword wordpad xfssvccon *sql* bedbh vxmon benetns bengien beserver TeamViewer* tv_w32 tv_x64 CVMountd cvd saphostexe saposcol sapstartsrv avagent avscc

Never encrypted

Files *.exe *.dll *.sys *.lnk *.ini *.inf *.pol *.cmd *.ps1 *.vbs *.bat *.drv *.msc *.msu *.lock *.search-ms pagefile.sys hiberfil.sys NTUSER.DAT autorun.inf boot.ini desktop.ini. Directories Windows.old, $Windows.~WS, $Windows.~BT, System Volume Information, Boot, PerfLogs, parts of Program Files*, ProgramData\Microsoft\*, AppData\Local\Temp and others.

MITRE ATT&CK mapping

TechniqueEvidence
T1486 Data Encrypted for ImpactChaCha20 with RSA-OAEP key wrapping
T1490 Inhibit System Recoveryvssadmin, wmic, bcdedit
T1489 Service Stopnet stop, sc stop, taskkill, Stop-VM
T1070.001 Clear Windows Event Logswevtutil cl
T1135 / T1018 Share and remote system discoverynet view, arp -a
T1021.002 / T1569.002 SMB and service executionPsExec -s
T1134 Access Token ManipulationImpersonation helpers (unverified)
T1059.003 Windows Command Shellcmd /C with hidden window
T1027 / T1140 Obfuscated or encoded payloadEncrypted note blob, hex-encoded embedded PE

Detection and response

  • Alert on several recovery-inhibition commands from one parent process within a few seconds.
  • Block or alert on C:\Users\Public\psexec0.exe and on PsExec started with -accepteula -s -d from that path.
  • Hunt for files ending in jtu5s6r and for RECOVER-jtu5s6r-DATA.txt.
  • File signatures: the strings jtu5s6r, RECOVER-, -DATA.txt, ***is_ok***, win_enc, no_impl, and the public key above.
  • Restore from offline backups. The sample carries only a public key. Pursue the keystream-reuse lead only with real ciphertext and a known original.

Open items

ItemStatus
Activation key and ransom textU Not available
How the payload gets onto each target (no copy code found)U
Whether --key is passed to the PsExec-launched copy (probably, through the copied argument list)I
The two unnamed impersonation helpersU
Block semantics of modes 1 to 3, and the OAEP hashU
Keystream reuse against real outputI Model only
Dynamic confirmationU Needs an isolated VM, which was not set up

A Python model of the container (throwaway RSA key, synthetic files) round-trips for modes 0 to 3. That shows the model is self-consistent. It does not validate the format against the sample.