
CVE-2026-74943, Firefox RasterImage의 use after free (보안 심각도 높음)
이 디렉터리에는 mozilla::image::RasterImage::OnSurfaceDiscarded에서 발생하는
스레드 간 use-after-free에 대한 정직한 재현 스캐폴드가 들어 있습니다. 이는
합성 이미지 데이터만을 사용하여 로컬에 다운로드한 취약한 Firefox 빌드에
대해 실행되도록 만들어졌습니다. 어떠한 라이브 시스템이나 제3자 시스템도 건드리지
않습니다.
일치하는 PoC 저장소: https://github.com/defineid/Revenant
분석 문서: 이 포트폴리오의 writeup-rasterimage-uaf.html.
이 사실들은 원래 연구 중 취약한 빌드(libxul.so, Firefox 140.9.0esr)를
디스어셈블하여 확인되었으며, 커밋된 스크린샷 assets/writeup-uaf-disasm.png가
바로 그 분석입니다. 함수 이름은 해당 정확한 빌드에 대한 공식 Mozilla Breakpad
심볼 파일에서 가져온 것입니다. 배포되는 libxul.so는 스트립되어 있습니다.
이 PoC 패키지를 조립한 환경에서는 다시 디스어셈블되지 않았습니다
(검증 상태 참조).
| 사실 | 증거 | 출처 |
|---|---|---|
취약한 함수는 RasterImage::OnSurfaceDiscarded(SurfaceKey const&) | FUNC 3eba200 16c 0 mozilla::image::RasterImage::OnSurfaceDiscarded(...) | Mozilla 심볼 서버 .sym |
this+0xC0에서의 초기 바이트 읽기 (분석 문서: mIsBeingDestroyed) | 3eba20a: cmp BYTE PTR [rdi+0xc0],0x0 (rdi = this) | objdump -d libxul.so |
this+0x40에서의 RasterImage 참조 카운트에 대한 AddRef | 3eba25a: lock inc QWORD PTR [rbx+0x40] (두 번 발행됨), rbx = this | objdump -d libxul.so |
0x20바이트 크기의 runnable이 할당되고 this를 캡처함 | 3eba264: mov edi,0x20 → call moz_xmalloc@plt; 3eba280: mov QWORD PTR [rax+0x10],rbx | objdump -d libxul.so |
runnable이 디스패치된 후 this가 Release됨 | 3eba298: call QWORD PTR [rcx+0x28] (가상 디스패치); 3eba29b: lock dec QWORD PTR [rbx+0x40] | objdump -d libxul.so |
참조 카운트가 0에 도달하면 ~RasterImage를 실행한 후 free | je 3eba345 → call 3ebbe90 <~RasterImage> → call free@plt | objdump + .sym |
디스패치된 runnable이 OnSurfaceDiscarded::$_0 람다를 캡처함 | FUNC 5279d90 ... RunnableFunction<...OnSurfaceDiscarded(...)::$_0>::Run() | Mozilla 심볼 서버 .sym |
| 필드 | 값 |
|---|---|
| CVE | CVE-2026-74943 |
| 구성 요소 | Core / Graphics: ImageLib |
| 분류 | Use-After-Free (CWE-416) |
| 심각도 | sec-high (웹에서 도달 가능한 메모리 안전성 문제) |
| 수정 버전 | Firefox 154 · ESR 140.14 |
| 버그 | bugzilla.mozilla.org/show_bug.cgi?id=2057308 (보안 제한됨) |
SurfaceCacheImpl::Remove는 표면 캐시 뮤텍스를 보유한 상태에서 디코더 /
ImageIO 스레드 상에서 원시(raw) 비소유(non-owning) ImageKey 포인터를 통해
OnSurfaceDiscarded를 호출함으로써, 디코딩된 표면이 폐기되었음을 소유 이미지에
통지합니다. 취약한 빌드에서 이 핸들러는 다음과 같이 동작합니다:
this+0xC0에서 단일 바이트 플래그를 읽습니다 (cmp BYTE PTR [rdi+0xc0],0x0).
분석 문서는 이를 이전의 완화책인 mIsBeingDestroyed로 식별합니다.this+0x40에서 RasterImage 참조 카운트를 증가시켜 this에 대한 강한 참조를
획득합니다 (lock inc QWORD PTR [rbx+0x40]). 이는 단순한 add-ref이며,
원자적 add-ref-if-nonzero가 아닙니다.moz_xmalloc(0x20)으로 this를 캡처하는 RunnableFunction을 할당하고
(mov QWORD PTR [rax+0x10],rbx), 이를 전역 이벤트 타깃으로 디스패치한 후
(call QWORD PTR [rcx+0x28]), 로컬 참조를 해제합니다
(lock dec QWORD PTR [rbx+0x40]).플래그를 읽는 것과 카운트를 증가시키는 것은 락프리 참조 카운트에 대한
두 개의 별개 연산이기 때문에, 메인 스레드가 마지막 참조를 드롭하는 순간을
가로지를 수 있습니다. 그러면 디코더 스레드는 0에서 AddRef를 수행하여, 소멸자가
이미 커밋된 객체를 되살립니다. 소멸자가 완료되어 RasterImage를 free하면,
대기 중이던 runnable이 나중에 메인 스레드에서 실행되어 해제된 저장소를
읽습니다.
수정은 이미지를 직접 참조하는 것을 중단합니다: OnSurfaceDiscarded는 별도로 참조 카운트되는 ProgressTracker에 대한 강한 참조를 캡처하고, 트래커의 약한 역참조를 통해 메인 스레드에서 이미지를 복구하며, 원자적 add-ref-if-live로 이를 승격합니다; mIsBeingDestroyed 플래그는 제거됩니다.
콘텐츠 프로세스의 일반 웹 페이지가 이미지 요청을 소유한 프레임을 해제하면서 표면 캐시 압력(다수의 폐기 가능한 디코딩된 표면)을 유발합니다. 클릭, 권한, 확장 프로그램 또는 환경 설정 변경은 필요하지 않습니다. 경쟁은 디코더 스레드의 폐기 알림과 동일한 이미지에 대한 메인 스레드의 최종 해제 사이에서 발생합니다.
https://ftp.mozilla.org/pub/firefox/releases/140.9.0esr/에서 일치하는 플랫폼 빌드를 다운로드하십시오.
ASAN 빌드(Nightly asan 또는 로컬 --enable-address-sanitizer 빌드)를 사용하면 use-after-free가 간헐적인 무음 크래시 대신 깔끔한 보고서로 관찰됩니다.<canvas>로 자체 이미지를 생성하며, 네트워크에서 아무것도 로드하지 않습니다.취약한 빌드에서 trigger.html을 여십시오. 이상적으로는 file:// 경로 또는 사용자가 제어하는 로컬 정적 서버에서 여십시오:
/path/to/firefox --new-instance --profile ./poc-profile ./trigger.html
기본적으로 드라이버는 디코드/릴리스 중첩을 최대화하기 위해 촘촘한 마이크로태스크 루프를 실행합니다. URL에 ?tick=N을 추가하면 양보하는 setTimeout(N) 루프로 전환되는데, 이 루프는 똑같이 격렬하게 돌지만 이벤트 루프에 숨 쉴 틈을 주므로, 자동화된 드라이버가 실행 중인 페이지를 단계별로 진행시키고 스크린샷을 찍을 수 있게 해줍니다. 커밋된 trigger-running.png는 정확히 그런 방식으로, 로컬 140.11.0esr 빌드를 Marionette를 통해 ?tick=4로 구동하여 캡처한 것입니다. 이 이미지는 하네스가 실시간으로 동작하는 모습(사이클과 티어다운이 증가하는 모습)을 보여줍니다. 해당 빌드는 패치되어 있으므로 크래시는 발생하지 않으며, 이 이미지는 드라이버가 실행된다는 것만 증명합니다. 헤드리스에서는 화면 밖 이미지가 그려지지 않으므로 "images decoded" 카운터가 0으로 유지됩니다. 취약한 헤드리스 빌드는 이미지를 디코드하며, 바로 이곳에서 레이스가 발생합니다.
페이지를 계속 실행하도록 두십시오. 페이지는 서로 다른 대형 이미지 여러 개를 반복적으로 디코드하여(서피스 캐시를 채우고 강제로 축출하기 위해) 동시에 그 이미지들을 소유한 DOM 서브트리를 해체합니다(소유 이미지의 최종 릴리스를 유도하기 위해). 이는 디코더 스레드의 "surface discarded"와 메인 스레드의 "last release" 간의 중첩을 최대화합니다. 레이스는 확률적이므로 계속 실행해 두십시오.
heap-use-after-free가 나타나며, 그 READ 프레임은 RasterImage::OnSurfaceDiscardedInternal / 디스패치된 러너블 내부에 있고, freed 프레임은 RasterImage::Release 티어다운에 있습니다. ASAN 없이는 간헐적인 콘텐츠 프로세스 크래시로 나타날 수 있습니다.ProgressTracker 약한 역참조를 거치며, 파괴가 이미 확정된 경우 알림을 버립니다.libxul.so(140.9.0esr)를 디스어셈블했으며, 위의 첫 번째 표에 있는 모든 바이너리 수준 사실을 공식 Mozilla 심볼 파일과 바이트 단위로 대조하여 확인했습니다. 커밋된 assets/writeup-uaf-disasm.png가 바로 그 작업의 결과물입니다. 라이트업에서 나온 세 가지 구조적 주장(초기 this+0xC0 플래그 읽기, this+0x40에서의 lock inc AddRef, this를 캡처하고 디스패치한 뒤 Release하는 moz_xmalloc(0x20) 러너블)은 모두 성립합니다.이것은 이미 패치된 CVE에 대한 라이트업을 위해, 합성된 자체 생성 데이터로 로컬에 다운로드한 빌드를 대상으로 합니다. 어떤 라이브 또는 제3자 서비스도 공격하지 않으며, 결함을 트리거하는 것 외의 어떤 익스플로잇 프리미티브도 포함하지 않습니다.