1. 취약점 설명
Google Chrome의 CSS 렌더링 엔진에서 발견된 Use-After-Free 취약점. 악성 웹페이지를 방문하는 것만으로 Chrome 샌드박스 안에서 임의 코드가 실행된다. Chrome뿐 아니라 Chromium 기반 브라우저(Edge, Brave, Opera 등) 전체에 영향을 미치며, 패치 전부터 실제 공격에 악용된 제로데이다.
2. 취약점 개요
| 항목 | 내용 |
| CVE 번호 | CVE-2026-2441 |
| CVSS | 8.8 (High) |
| 발표일 | 2026년 2월 13일 |
| CWE | CWE-416 (Use After Free) |
| 영향 제품 | Google Chrome 145.0.7632.75 미만, Chromium 기반 브라우저 전체 |
| 취약 조건 | 사용자가 악성 웹페이지를 방문 (추가 상호작용 불필요) |
| 보고자 | Shaheen Fazim (2026년 2월 11일 제보) |
Use-After-Free(UAF)란? 프로그램이 메모리를 해제(free)한 후에도 해당 주소를 계속 참조(use)하는 취약점. 해제된 메모리에 공격자가 원하는 데이터를 심으면 프로그램 실행 흐름을 조작할 수 있다.
메모리는 창고 같은 것이다. 프로그램이 데이터를 쓸 때 창고에서 빈 공간을 빌리고(메모리 할당), 다 쓰면 반납한다(메모리 해제). UAF는 반납한 후에도 그 주소를 계속 사용하는 것이다. 이미 반납한 공간에 다른 누군가가 들어와 있을 수 있는데, 거기서 뭔가를 읽거나 쓰면 예측 불가능한 동작이 발생한다.
동일한 취약점 유형(CWE-416)이 실제 적용된 또 다른 사례 → CVE-2026-53359: Linux KVM Use-After-Free 취약점 분석
Firefox는 왜 영향 없냐면? Firefox의 스타일링 엔진(Stylo)은 Rust로 작성됐기 때문이다. Rust는 컴파일 타임에 소유권과 빌림 규칙을 강제해서 Use-After-Free 버그 자체를 원천 차단한다. Chrome의 Blink 엔진은 C++로 작성되어 이런 메모리 안전 보장이 없다.
3. 익스플로잇 방식
취약점의 근본 원인
취약점은 Chrome Blink 렌더링 엔진의 CSSFontFeatureValuesMap 구현 안에 있다. 이터레이터가 CSSFontFeatureValuesMap 객체를 순회하는 중에 내부 HashMap이 변경되면 리해시(rehash) 작업이 발생하고, 이 과정에서 원본 메모리가 해제되지만 이터레이터는 해제된 주소를 가리키는 댕글링 포인터를 그대로 보유한다.
CSSFontFeatureValuesMap이란? CSS에는 폰트를 세밀하게 제어하는 기능이 있다.
@font-feature-values MyFont { @styleset { a: 1; b: 2; } }"MyFont 폰트에서 a는 스타일 1번, b는 스타일 2번으로 표시해줘" 라는 의미다. CSSFontFeatureValuesMap은 이 폰트 피처 값들을 저장하는 자료구조(일종의 사전)로, 브라우저가 이 CSS를 처리할 때 내부적으로 생성해서 메모리에 저장한다.
이터레이터란? 자료구조를 순서대로 훑는 포인터다. CSSFontFeatureValuesMap = {a:1, b:2, c:3} 이 있을 때 이터레이터를 생성하면 "첫 번째 항목(a:1)을 가리키는 포인터"가 만들어지고, a→b→c 순서로 하나씩 읽어나간다. 이터레이터는 내부적으로 "지금 메모리 어디를 보고 있는지" 주소를 기억하고 있다.
리해시(rehash)란? HashMap은 데이터를 빠르게 찾기 위해 버킷이라는 공간에 분산해서 저장한다. 버킷이 너무 차면 더 큰 공간으로 이사하면서 데이터를 재배치하는데 이게 리해시다. 리해시가 일어나면 원래 있던 메모리 공간은 해제되고 데이터는 새 공간으로 이동한다. 문제는 이터레이터가 원래 공간 주소를 기억하고 있다는 것이다. 리해시 후 원래 공간은 해제됐는데 이터레이터는 해제된 주소를 계속 가리키게 된다.
댕글링 포인터란? 포인터는 메모리 주소를 저장하는 변수다. 정상 상태에서는 포인터가 유효한 데이터를 가리키지만, 메모리가 해제됐는데 포인터가 여전히 그 주소를 가리키는 경우를 댕글링 포인터라고 한다. 친구 집 주소를 메모해뒀는데 친구가 이사를 가버린 상황과 같다. 그 주소로 찾아가면 전혀 다른 사람이 살고 있다.
취약한 코드 흐름:
@font-feature-values CSS 규칙 처리
↓
CSSFontFeatureValuesMap 이터레이터 생성 (메모리 할당)
↓
이터레이터 순회 중 set() 또는 delete()로 맵이 변경
↓
HashMap 리해시 → 원본 메모리 해제
↓
이터레이터는 해제된 주소(댕글링 포인터) 계속 사용
↓
Use-After-Free → 힙 메모리 손상
↓
공격자가 재할당된 메모리에 악성 데이터 심기
↓
Chrome 샌드박스 안에서 임의 코드 실행
패치 내용 (커밋 63f3cb4864c6):
이터레이터의 raw pointer를
deep copy (const FontFeatureAliases aliases_)로 교체
→ 원본 메모리가 해제돼도 복사본을 사용하므로 UAF 불가
공격자가 어떻게 코드 실행으로 연결하는가 (힙 그루밍)
메모리가 해제되면 바로 사라지는 게 아니라 "재사용 가능한 공간"으로 표시만 된다. 공격자는 이 타이밍을 이용한다.
힙 그루밍 과정:
1. 공격자가 같은 크기의 객체를 일부러 많이 생성
→ 해제될 메모리 자리를 공격자 객체가 차지하도록 준비
2. CSSFontFeatureValuesMap 메모리 해제
→ 해제된 자리에 공격자가 준비한 객체가 들어옴
3. 댕글링 포인터가 그 자리를 읽으면
→ 공격자 데이터를 정상 데이터인 것처럼 처리
비유:
이사 간 친구 집에 내가 먼저 이사 들어감
그 집 주소를 아직 가진 사람이 찾아오면
나를 친구로 착각함 → 내가 원하는 대로 조작 가능
이 과정을 통해 공격자는 함수 포인터를 자신의 악성 코드 주소로 덮어씌워 Chrome 렌더러 프로세스 안에서 임의 코드를 실행하게 된다.
실제 공격 방식 (드라이브바이 다운로드)
악성 웹페이지에 조작된 CSS 규칙이 포함되어 있으면, 스타일 재계산 또는 렌더링 과정에서 취약점이 트리거된다. 브라우저 렌더링 프로세스 안에 결함이 있기 때문에 웹페이지 방문 외에 추가적인 사용자 상호작용이 필요 없다.
공격자가 이 취약점을 활용하는 방식은 크게 세 가지다.
| 방식 | 설명 |
| 멀버타이징 | 정상 광고 네트워크를 통해 악성 광고 삽입, 광고 렌더링만으로 트리거 |
| 워터링홀 | 타겟이 자주 방문하는 사이트를 미리 침해해두고 대기 |
| 스피어피싱 | 이메일이나 메신저로 악성 링크 전달 |
익스플로잇 체인
샌드박스 안에서의 코드 실행이 끝이 아니다. 공격자는 이 취약점을 시작으로 다음 단계로 이어지는 공격 체인을 구성할 수 있다.
CVE-2026-2441 (Chrome 샌드박스 RCE)
↓
Mojo IPC 익스플로잇 (샌드박스 탈출)
↓
브라우저 프로세스 RCE
↓
커널 익스플로잇 (권한 상승)
↓
완전한 시스템 장악
→ 악성코드 설치, 자격증명 탈취, 내부망 이동
비슷한 브라우저 UAF를 활용한 실제 공격 사례: NSO Pegasus — WebKit UAF + 샌드박스 탈출 + 커널 익스플로잇 Intellexa Predator — Chrome UAF + Android 커널 익스플로잇 APT28 — Chrome 0-day + Windows LPE 체인
4. 어디서 흔적이 남는가 (로그 소스 매핑)
브라우저 취약점은 클라이언트 사이드에서 발생하기 때문에 서버 레벨 로그보다는 엔드포인트 레벨 탐지가 핵심이다.
| 로그 소스 | 탐지 가능한 흔적 |
| EDR / Sysmon | Chrome 렌더러 프로세스에서 비정상 자식 프로세스 생성 |
| 프록시/웹 필터 로그 | 악성 도메인 접근, 비정상 Content-Type 응답 |
| 윈도우 이벤트 로그 | Chrome 프로세스 크래시 (이벤트 ID 1000, 1001) |
| 네트워크 로그 | 샌드박스 탈출 후 C2 아웃바운드 연결 |
| 브라우저 크래시 리포트 | Chrome "Aw, Snap!" 페이지 반복 발생 |
⚠️ 탐지의 핵심 어려움 취약점이 브라우저 렌더러 프로세스 메모리 안에서 발생하기 때문에 네트워크 레벨에서는 직접 탐지가 불가능하다. 공격 트래픽이 일반 HTTPS처럼 보이기 때문에 패킷 분석으로도 구분하기 어렵다. 샌드박스 탈출 이후 발생하는 프로세스 행위 패턴을 탐지하는 것이 현실적인 접근이다.
5. SPL 탐지 쿼리
기본형: Chrome 렌더러에서 비정상 자식 프로세스 생성 탐지
index=sysmon OR index=wineventlog
| where EventCode IN (1, 4688)
| eval suspicious=if(
match(parent_process, "(chrome\.exe|msedge\.exe|brave\.exe|opera\.exe)") AND
match(process_name, "(cmd\.exe|powershell|wscript|mshta|rundll32|certutil|bitsadmin)"),
1, 0)
| where suspicious=1
| stats count as spawn_count by src_host, parent_process, process_name, command_line
| sort -spawn_count
| table src_host, parent_process, process_name, command_line, spawn_count
쿼리 설명:
- Chrome, Edge, Brave 같은 Chromium 기반 브라우저 프로세스에서 cmd, powershell 같은 의심 프로세스가 직접 자식으로 생성되는 패턴을 탐지한다
- 정상적인 브라우저 사용에서는 이런 프로세스 생성이 거의 발생하지 않는다
- 샌드박스 탈출 후 공격자가 시스템에서 명령을 실행하는 패턴
⚠️ 인덱스명: sysmon, wineventlog는 예시다. 실제 환경의 인덱스명으로 변경 후 사용할 것. Sysmon이 설치되어 있어야 command_line 필드까지 확인 가능하다.
개선형: Chrome 크래시 반복 + 이후 비정상 아웃바운드 연결 연계 탐지
index=wineventlog
| where EventCode IN (1000, 1001) AND match(param1, "(chrome|msedge|brave)")
| stats count as crash_count by src_host, _time span=10m
| where crash_count >= 3
| join type=left src_host
[search index=firewall_logs OR index=proxy_logs
| where direction="outbound"
| where NOT match(dest_domain, "(google|microsoft|chromium|brave)\.com$")
| eval suspicious_port=if(dest_port IN (4444, 8080, 9001, 1080, 31337), 1, 0)
| where suspicious_port=1
| stats count as c2_count by src_host]
| where isnotnull(c2_count)
| table src_host, crash_count, c2_count
쿼리 설명:
- EventCode 1000/1001은 윈도우 응용 프로그램 크래시 이벤트다
- PoC 실행 시 브라우저 탭이 크래시("Aw, Snap!" 페이지)되므로 짧은 시간 안에 크래시가 반복되면 익스플로잇 시도로 볼 수 있다
- join으로 크래시 반복 + 비표준 포트 아웃바운드 연결을 연계해 샌드박스 탈출 후 C2 통신 패턴을 포착
취약한 Chrome 버전 보유 자산 탐지
index=endpoint_inventory OR index=software_inventory
| search product="Google Chrome" OR product="Microsoft Edge" OR product="Brave"
| eval vulnerable=if(
(match(product, "Google Chrome") AND version < "145.0.7632.75") OR
(match(product, "Microsoft Edge") AND version < "145.0.3800.58") OR
(match(product, "Brave") AND version < "1.76.0"),
1, 0)
| where vulnerable=1
| stats count as vulnerable_count by hostname, product, version
| sort -vulnerable_count
| table hostname, product, version, vulnerable_count
쿼리 설명:
- 취약한 버전의 Chrome/Edge/Brave가 설치된 자산을 한눈에 파악
- 패치 적용 우선순위 결정에 활용 가능
- 자산 인벤토리 데이터가 Splunk로 수집되는 환경에서 사용 가능
6. 오탐지 줄이는 팁
- 브라우저 자동 업데이트: Chrome은 업데이트 후 재시작 전까지 구버전이 실행된다. 버전 탐지 쿼리에서 재시작 여부도 함께 확인하는 것이 정확도를 높임
- 크래시 반복 기준: 브라우저 크래시는 메모리 부족, 플러그인 오류 등 정상적인 원인으로도 발생한다. 크래시 단독으로는 오탐이 많으므로 반드시 아웃바운드 연결과 연계해서 판단
- 자식 프로세스: 일부 브라우저 확장 프로그램이나 PDF 뷰어가 외부 프로세스를 실행할 수 있다. 알려진 정상 프로세스는 화이트리스트로 제외
7. 임시 대응 / 패치 정보
패치 버전:
| 브라우저 | 패치 버전 | 배포일 |
| Chrome (Windows/macOS) | 145.0.7632.75 이상 | 2026년 2월 13일 |
| Chrome (Linux) | 144.0.7559.75 이상 | 2026년 2월 13일 |
| Microsoft Edge | 145.0.3800.58 이상 | 2026년 2월 14일 |
| Edge Extended Stable | 144.0.3719.130 이상 | 2026년 2월 17일 |
버전 확인 방법:
Chrome: chrome://version
Edge: edge://version
즉시 적용 가능한 완화 조치:
- 브라우저 즉시 업데이트 및 재시작 (업데이트만으로는 부족, 반드시 재시작해야 적용)
- 헤드리스 Chrome 배포 환경 우선 점검 - CI/CD 파이프라인, PDF 생성 서버, 웹 스크래핑 서버 등 비대화형 환경도 취약
- 엔터프라이즈 환경: Google Admin Console 또는 Microsoft Intune으로 버전 컴플라이언스 강제 적용
- 웹 필터: 알려진 악성 도메인 및 비정상 Content-Type 차단
⚠️ 이 취약점은 클라우드 환경에서도 특히 위험하다. 헤드리스 Chrome이 PDF 생성, 웹 미리보기 등 비대화형 용도로 컨테이너나 CI/CD 파이프라인에 임베드된 경우, 악성 콘텐츠를 렌더링하는 것만으로 취약점이 트리거된다. 일반 엔드포인트 패치보다 이쪽이 더 빠르게 우선 대응이 필요하다.
8. 마무리 / 한계점
이 SPL 쿼리의 한계:
- 취약점이 브라우저 메모리 안에서 발생해 직접 탐지가 불가능하고, 샌드박스 탈출 이후 행위만 탐지 가능
- Sysmon이 설치되지 않은 환경에서는 프로세스 생성 상세 정보 확인 불가
- 공격자가 정상 포트(443 등)로 C2 통신을 위장하면 아웃바운드 연결 기반 탐지 우회 가능
"CSS는 그냥 화면을 예쁘게 만드는 것" 이라는 인식이 이번 취약점으로 완전히 깨졌다. 선언형 언어도 충분히 공격 벡터가 될 수 있고, 특히 C++로 구현된 파서는 메모리 안전성이 보장되지 않는다. 브라우저처럼 매일 사용하고 신뢰하는 소프트웨어 안에 이런 취약점이 존재한다는 점에서 패치 속도와 버전 관리의 중요성을 다시 한번 확인할 수 있다.
더 나은 탐지 방법이나 실제 운영 환경에서의 개선점이 있다면 댓글로 공유해주세요.
이 블로그의 관련 글:
- Use-After-Free(CWE-416)가 실제 적용된 또 다른 사례 → CVE-2026-53359: Linux KVM Use-After-Free 취약점 분석
- UAF의 개념적 기반이 되는 취약점 유형 정리 → CWE-121/122: 버퍼 오버플로우 완전 정리
참고 자료: