flawopen.com/보안 사고/BlueMoon 제로데이 풀체인: Chrome V8 및 Windows ALPC
방탄유리(Chrome 샌드박스) 뒤에 갇혀 금고 열쇠가 없는 은행 창구 직원을 상상해보세요. 공격자가 직원을 속여 창구 내부를 장악했습니다(Chrome V8 RCE). 하지만 방탄유리 때문에 외부로 나갈 수 없습니다. 이때 공격자는 지하 메인 기계실로 연결된 압축공기 우편 파이프(Windows ALPC)를 발견하고, 규격을 초과하는 거대한 금속 캡슐을 파이프에 밀어 넣어 기계실을 파괴하고 건물 전체의 마스터 도어를 강제로 개방했습니다(SYSTEM 권한 획득).
In early September 2026, cybersecurity researchers and threat intelligence teams discovered a sophisticated, actively exploited zero-day attack campaign dubbed 'BlueMoon'. The threat actors deployed a zero-click/one-click exploit chain targeting fully updated installations of Google Chrome on Microsoft Windows.
The attack chained two zero-days patched within days of each other:
153.0.8010.36/.37.This incident is a textbook illustration of modern systems exploitation. Because Chromium enforces rigorous process sandboxing—locking renderer processes in low-integrity AppContainers with restricted system call tables—compromising the browser engine alone was insufficient for the attackers to steal files or persist on the victim's machine. To escape containment, the attackers weaponized the ALPC subsystem exposed to the sandboxed renderer, compromising the Windows NT kernel and achieving unconstrained NT AUTHORITY\SYSTEM privileges.
In V8's JIT optimization pipeline, the compiler optimizes array operations by calculating integer range bounds. Due to an arithmetic truncation bug in Turbofan's Typer phase when folding 64-bit integer bitwise operations, the engine erroneously concluded that an array index could never exceed array.length. It eliminated runtime bounds checks, allowing a crafted JavaScript loop to write arbitrary pointers past the end of the backing store on the V8 heap.
Once remote code execution was achieved inside the renderer process, the attacker encountered Chrome's defense-in-depth perimeter: Win32k system calls were blocked, direct disk writes were denied by Windows Mandatory Integrity Control (Low Integrity), and outbound raw socket creation was prohibited. The attacker could not run cmd.exe or persist.
To escape the sandbox, the attacker leveraged the fact that sandboxed renderers must still communicate with system IPC endpoints via ALPC. The attacker sent an intricately malformed ALPC message structure with mismatched message length headers. In ntoskrnl.exe, the message handling routine allocated a kernel pool buffer based on the declared data size, but copied data based on the total message length, triggering an out-of-bounds heap write into the adjacent Paged Pool. The attacker corrupted an adjacent security token object to grant themselves SeDebugPrivilege and SYSTEM credentials.
// 1. Conceptual V8 Turbofan Typer Flaw (CVE-2026-87491)
Type Typer::Visitor::TypeSpeculativeNumberBitwiseOr(Node* node) {
// Bug: Underflow/truncation in 64-bit range inference
// Compiler statically infers range [0, 10], but runtime value can reach 0x7FFFFFFF!
return Type::Range(min_val, max_val, zone());
}
// 2. Conceptual Windows Kernel ALPC Heap Copy Flaw (CVE-2026-85880)
NTSTATUS AlpcpCopyMessageData(PALPC_MESSAGE Msg, PVOID Buffer) {
// Bug: Buffer allocated from declared DataLength, but copy uses TotalLength
ULONG allocSize = Msg->Header.u1.s1.DataLength;
PVOID poolBlock = ExAllocatePoolWithTag(PagedPool, allocSize, 'CplA');
// HEAP OVERFLOW: TotalLength > DataLength overwrites adjacent pool memory!
RtlCopyMemory(poolBlock, Msg->PortMessage.Data, Msg->Header.u1.s1.TotalLength);
return STATUS_SUCCESS;
}
// 1. Fixed V8 Turbofan Bounds Validation
Type Typer::Visitor::TypeSpeculativeNumberBitwiseOr(Node* node) {
// Fix: Strict conservative bounding preventing speculative check elimination
if (!IsSafeIntegerRange(min_val, max_val)) return Type::Any();
return Type::Range(SafeClamp(min_val), SafeClamp(max_val), zone());
}
// 2. Fixed Windows Kernel ALPC Size Verification
NTSTATUS AlpcpCopyMessageData(PALPC_MESSAGE Msg, PVOID Buffer) {
// Fix: Explicit sanity check validating header length consistency
if (Msg->Header.u1.s1.TotalLength < Msg->Header.u1.s1.DataLength) {
return STATUS_INVALID_PARAMETER;
}
// Allocate buffer matching the actual copy length, strictly bounded
PVOID poolBlock = ExAllocatePoolWithTag(PagedPool, Msg->Header.u1.s1.TotalLength, 'CplA');
if (!poolBlock) return STATUS_INSUFFICIENT_RESOURCES;
RtlCopyMemory(poolBlock, Msg->PortMessage.Data, Msg->Header.u1.s1.TotalLength);
return STATUS_SUCCESS;
}
# Sysmon Event ID 1: Detect suspicious child processes spawned from chrome.exe
EventID=1 AND ParentImage="*\chrome.exe" AND Image IN ("*\cmd.exe", "*\powershell.exe", "*\whoami.exe")
# ETW: Microsoft-Windows-Kernel-Memory: Monitor NonPaged/Paged Pool ALPC corruption
logman start AlpcPoolTrace -p "Microsoft-Windows-Kernel-Memory" 0x80 -ets
# Yara: Rule targeting the BlueMoon V8 JIT shellcode loader stage
rule BlueMoon_V8_Stage1 { strings: $c = { 48 8B 04 24 48 83 C0 ?? 48 89 04 24 } condition: $c }
Win32kLockdown and RendererAppContainer policies are actively enabled via enterprise GPO.chrome.exe or any sandboxed utility process spawns system binaries (cmd.exe, powershell.exe).