[3] HTMLPlugInElement skips content extensions for embed/object loads
<embed>/<object> 마크업을 사용할 수 있는 모든 페이지에서 항상 재현 가능한 content blocker bypass를 수정하는 패치입니다. 영향 범위는 memory corruption이 아닌 policy 우회(사용자 설치 규칙을 벗어나는 tracker/malware/광고 로드)이므로 Medium으로 평가합니다.
<embed>, <object> 요소를 통해 로드되는 plugin content가 content extension 규칙 검사를 전혀 거치지 않았습니다. 이번 수정에서는 HTMLPlugInElement::canLoadURL에 content extension 검사가 추가되어, updateWidget의 wouldLoadAsPlugIn 지연 처리 이전에 차단 대상 URL을 거부하도록 변경되었습니다.
Source/WebCore/html/HTMLPlugInElement.cpp
LayoutTests/http/tests/contentextensions/block-embed-element.html
Patch Details
ENABLE(CONTENT_EXTENSIONS)가 활성화된 상태에서 URL이 유효한 경우, canLoadURL은 document의 Page, DocumentLoader, UserContentProvider를 조회합니다. 이후 UserContentProvider::processContentRuleListsForLoad(page, completeURL, ContentExtensions::ResourceType::Other, documentLoader)를 호출하며, 반환된 결과의 shouldBlock()이 true이면 canLoadURL이 false를 반환하여 호출 측에서 plugin 리소스 로드를 건너뜁니다. 회귀 테스트는 .*should-be-blocked URL 필터에 매칭되는 <embed>, <object> 리소스를 로드하여, 표준 'Content blocker prevented frame...' 콘솔 메시지가 발생하는지 확인합니다.
리소스 로드 경로에서 content policy 검사가 누락되어, plugin element 클래스가 다른 element 유형에 균일하게 적용되는 필터를 우회할 수 있는 패턴.
Background
Content Extensions(content blocker라고도 불림)는 WebKit 확장 기능 및 관리 구성에 선언형 규칙 목록 API를 제공합니다. 각 규칙은 trigger(URL 패턴, 리소스 유형, 로드 컨텍스트)와 action(block, block-cookies, css-display-none, make-https)의 쌍으로 이루어집니다. 로드 발생 시 UserContentProvider::processContentRuleListsForLoad가 모든 활성 규칙 목록을 평가하고 집계된 actions를 반환하는데, 호출 측은 results.shouldBlock()을 확인해 로드를 중단할지 결정합니다. HTMLPlugInElement는 <embed>, <object>, 레거시 <applet>의 C++ 베이스 클래스로, canLoadURL은 두 서브클래스 모두가 updateWidget 진입 전에 반드시 거치는 단일 진입점입니다. SubframeLoader에 plugin 또는 subframe widget 생성을 요청하기 이전에 이 함수를 통과하는 셈입니다. 한편 <iframe>, <img>, <script>, XHR/fetch 로드에 대한 content extension 훅은 각각의 loader 경로에 별도로 구현되어 있습니다.
Analysis
HTMLPlugInElement::canLoadURL은 URL에 대해 self-reference 검사만 수행했을 뿐, content extension 엔진은 전혀 거치지 않았습니다. <img>, <script>, <iframe>, XHR 등 다른 리소스 유형에는 이미 규칙 목록 평가 경로가 존재했지만, plugin element 클래스에는 해당 경로가 연결되지 않은 상태였습니다.
공격자가 HTML을 직접 작성하거나 주입할 수 있는 상황을 가정합니다. 사용자가 설치한 content blocker가 차단했을 URL을 <embed src="BLOCKED_URL"> 또는 <object data="BLOCKED_URL"> 형태로 삽입하면, 패치 이전에는 그 로드가 그대로 진행되었습니다. HTMLPlugInElement::canLoadURL이 UserContentProvider를 전혀 거치지 않고 true를 반환했기 때문입니다. 그 결과 서드파티 tracker, malware 호스팅 도메인, 또는 사용자가 차단한 도메인의 PDF가 페이지 안에서 렌더링될 수 있었습니다. 추가된 processContentRuleListsForLoad(..., ResourceType::Other, ...) 호출은 SubframeLoader와 동일한 차단 지점에서 해당 로드를 거부합니다. 테스트 케이스는 .*should-be-blocked 필터에 대해 일반 <embed>, <object> 마크업을 실행하고, 새 경로에서 'Content blocker prevented frame...' 콘솔 메시지가 발생하는지 검증합니다.
이 vulnerability는 특정 URL이 페이지 내에서 로드되는 것을 막기 위해 사용자와 관리형 디바이스 정책이 의존하는 content blocker 신뢰 경계를 약화시켰습니다. HTML content extension 모델은 plugin element 로드를 포함한 모든 subresource 로드가 활성 규칙 목록에 대해 평가된다고 가정하는데, 패치 이전에는 <embed>와 <object>에서 이 불변 조건이 성립하지 않았습니다. WebKit의 content extension 적용은 단일 로드 게이트에 집중되어 있지 않고, 네트워크 요청을 시작하는 각 element/loader 클래스에 개별적으로 복제됩니다. 이 구조가 적용 누락을 반복적으로 발생시키는 근본 원인입니다.
Audit directions
- Content extension 엔진을 거치지 않고 리소스 로드를 시작하는 HTML element 클래스. 자체적인
canLoad*또는requestResource방식의 게이트를 가진 모든 element/loader를 점검하여UserContentProvider::processContentRuleListsForLoad호출 여부를 확인합니다. 먼저HTMLTrackElement::canLoadURL(text-track 로드),HTMLFrameElementBase::canLoadURL,HTMLMediaElement리소스 선택,<link rel=preload>에 대한HTMLLinkElement, Service Worker / WorkerimportScripts경로를 우선적으로 살펴볼 필요가 있습니다.processContentRuleListsForLoad를 검색하여 기존 훅 위치를 파악하고,canLoadURL/canLoad/isAllowedToLoad호출 지점과 비교합니다. ResourceType오분류를 통한 무음 우회. 각 호출 지점에서ContentExtensions::ResourceType이 어떻게 선택되는지 점검합니다. 잘못된 bucket에 배치된 로드는 규칙을 너무 적게 또는 너무 많이 매칭하게 됩니다. Plugin 로드, PDF embed, MathML/SVG 외부 로드,<object>-as-image fallback 경로가 올바른 유형을 사용하는지 확인합니다.- Feature flag로 제어되는 보안 검사.
canLoadURL호출 측에서ENABLE(CONTENT_EXTENSIONS)가 실제 배포 포트(macOS, iOS, visionOS)에서 사용하는 빌드 구성인지 확인합니다. 해당 flag로 보호되는 검사는 flag가 꺼진 포트에서 dead code가 됩니다. - 새 검사가 subframe 생성 이전에 실행되는지 확인합니다.
HTMLPlugInElement::updateWidget과SubframeLoader::requestObject/requestEmbed를 추적하여, content extension 엔진을 재확인하지 않고 진행되는 두 번째 리소스 로드 경로(fallback content, type-detection sniff fetch, PDF preview thumbnail)가 없는지 검증합니다.