[2] CSP object-src empty-source-list bypass for no-URL plugin elements
Severity: Medium | Component: WebCore Content Security Policy enforcement | c6228ab
이 diff는 CSP object-src 정책의 bypass를 수정합니다. URL 없이 사용된 <object>/<embed> 인스턴스화에 대해 빈 source list가 deny-all이 아닌 허용으로 처리되던 문제였습니다. 이 버그는 배포된 정책에 의존하는 페이지의 plugin attack surface를 확대하지만, 직접적인 memory primitive는 발생하지 않습니다. Exploit을 위해서는 이미 HTML injection이 가능한 상태가 전제되어야 하며, 영향 범위는 'none' 대신 빈 source list 형태를 사용하는 CSP에 한정됩니다.
<object> 또는 <embed> 엘리먼트에 data/src 속성이 없는 경우, 기존 WebKit은 빈 URL을 CSP 검사에 전달했습니다. 이 경로에는 'none' 키워드가 명시된 경우에만 차단하는 특수 로직이 적용되어 있었습니다. 빈 source list(object-src;)는 CSP Level 3 §6.7.2.7에 따라 'none'과 동일하게 취급해야 하지만, 기존 코드는 이를 잘못 허용했습니다. 이번 패치는 §6.1.9 특수 케이스를 완전히 제거합니다. 대신 URL이 없는 엘리먼트에 대해서는 document 자체의 URL을 source-list 매칭의 fallback으로 사용합니다. Document URL은 빈 source list와 'none'에 대해 자연스럽게 매칭되지 않아 차단되고, 'self'나 wildcard에 대해서는 허용됩니다. 새로 추가된 WPT 테스트 3개는 URL 없이 사용된 <object>/<embed>가 object-src 'none'과 빈 source list 모두에서 차단되는지 검증합니다.
Source/WebCore/page/csp/ContentSecurityPolicySourceListDirective.cpp
-bool ContentSecurityPolicySourceListDirective::allows(const URL& url, bool didReceiveRedirectResponse, ShouldAllowEmptyURLIfSourceListIsNotNone shouldAllowEmptyURLIfSourceListEmpty)
+bool ContentSecurityPolicySourceListDirective::allows(const URL& url, bool didReceiveRedirectResponse)
{
if (url.isEmpty())
- return shouldAllowEmptyURLIfSourceListEmpty == ShouldAllowEmptyURLIfSourceListIsNotNone::Yes && !m_sourceList.isNone();
+ return false;
return m_sourceList.matches(url, didReceiveRedirectResponse);
}
Source/WebCore/page/csp/ContentSecurityPolicy.cpp
- if (m_policies.isEmpty() || LegacySchemeRegistry::schemeShouldBypassContentSecurityPolicy(url.protocol()))
+ const auto& urlToCheck = url.isEmpty() ? m_protectedURL : url;
+ if (m_policies.isEmpty() || LegacySchemeRegistry::schemeShouldBypassContentSecurityPolicy(urlToCheck.protocol()))
return true;
- // ... 'MUST be blocked if object-src's value is 'none', but will otherwise be allowed' ...
String sourceURL;
- const auto& blockedURL = !preRedirectURL.isNull() ? preRedirectURL : url;
+ const auto& blockedURL = !preRedirectURL.isNull() ? preRedirectURL : urlToCheck;
...
- return allPoliciesAllow(handleViolatedDirective, &ContentSecurityPolicyDirectiveList::violatedDirectiveForObjectSource, url, redirectResponseReceived == RedirectResponseReceived::Yes, ContentSecurityPolicySourceListDirective::ShouldAllowEmptyURLIfSourceListIsNotNone::Yes);
+ return allPoliciesAllow(handleViolatedDirective, &ContentSecurityPolicyDirectiveList::violatedDirectiveForObjectSource, urlToCheck, redirectResponseReceived == RedirectResponseReceived::Yes);
LayoutTests/imported/w3c/web-platform-tests/content-security-policy/object-src/object-src-no-url-empty-source-list-blocked.html
+<meta http-equiv="Content-Security-Policy" content="object-src; script-src 'self' 'unsafe-inline';">
+<object type="text/html"></object>
Patch Details
패치는 ContentSecurityPolicySourceListDirective::allows에서 ShouldAllowEmptyURLIfSourceListIsNotNone 파라미터를 제거합니다. 이제 이 함수는 빈 URL에 대해 무조건 false를 반환합니다. ContentSecurityPolicy::allowObjectFromSource에서는 전달된 url이 비어 있을 때(data/src가 없는 <object>/<embed>의 경우), 정책 매칭을 수행하기 전에 m_protectedURL(document 자체의 URL)로 대체합니다. 해당 파라미터는 checkSource(), violatedDirectiveForObjectSource(), 그리고 관련 allows() 오버로드 시그니처에서도 함께 제거되었습니다. checkFrameAncestors()의 두 오버로드에서도 제거된 세 번째 인자가 삭제되었습니다.
source-list 매칭의 empty-URL fast path에서 두 CSP 상태('none' 키워드와 빈 source list)를 혼용하여, 빈 source list가 deny-all이 아닌 허용으로 동작한 패턴.
Background
Content Security Policy는 HTTP 헤더 또는 meta 태그를 통해 전달되는 정책으로, document가 각 리소스 유형에 대해 허용할 source를 제한하는 데 사용됩니다. object-src 디렉티브는 <object>/<embed> plugin 로딩을 제어합니다. source list는 디렉티브의 오른쪽 값으로, 'self'·'none'·scheme/host 표현식 등의 토큰을 포함하거나 비어 있을 수 있습니다. CSP Level 3 명세에 따르면 빈 source list는 어떤 URL도 매칭하지 않으며, 'none'과 동일하게 동작합니다.
WebKit에서는 ContentSecurityPolicySourceList로 source list를 모델링합니다. isNone()은 리터럴 'none' 토큰에 대해서만 true를 반환하며, 빈 리스트에 대해서는 false를 반환합니다. <object>/<embed>는 URL 없이도 plugin을 인스턴스화할 수 있는데, data와 src 속성이 모두 없는 경우에는 type 속성만으로 plugin이 선택됩니다. 초기 CSP 초안에는 이러한 no-URL plugin에 대해 'none'이 명시되지 않은 한 허용하는 특수 조항이 있었으며, 이것이 이번 버그의 배경이 되었습니다.
Analysis
패치 이전 allows() 내부 조건은 shouldAllowEmptyURLIfSourceListEmpty == Yes && !m_sourceList.isNone()이었습니다. 이 조건의 목적은 §6.1.9 특수 케이스, 즉 URL 없는 plugin은 'none'이 명시된 경우에만 차단하고 그 외에는 허용한다는 원칙을 구현하는 것이었습니다. 그러나 이 조건은 두 가지 서로 다른 CSP 상태를 혼용합니다. (a) 명시적인 'none' 키워드와 (b) 빈 source list(object-src;)입니다.
CSP Level 3 §6.7.2.7에 따르면 이 둘은 의미상 동일합니다. 둘 다 어떤 URL도 매칭하지 않아야 합니다. 그러나 m_sourceList.isNone()은 리터럴 'none' 토큰에 대해서만 true를 반환합니다. 결과적으로 object-src; 정책에서는 Yes && !false = true가 되어, 배포된 정책이 모든 object source를 금지하는 의도임에도 no-URL <object>/<embed>가 기본 plugin을 로딩할 수 있었습니다.
Aaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaa Aaa Aaaaa Aaaa Aaaaaaaaaa Aaa Aaaa Aaaa Aaaaaaaaaaaa Aa Aaaaaaaa Aaaaaaaaaaaaaaaaaaaaa Aa Aaaaaaa Aaaaaaaaaaaaa Aaa a Aaaaaa Aa Aaaa a Aaa Aaa Aaaaaaa Aaaa Aaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaa Aaaaaa Aaaaaaa Aaaaaa Aaaa Aaa Aaaaaaa Aaaaaaaa Aaa Aaaa Aaa Aaa a Aaaaaa
Aa Aaa Aa Aaa a Aaa Aaaa Aa Aa a Aaaaaaaa Aaa Aaaa Aaaa Aaa Aa Aaa Aaaaaa Aaa Aa Aa Aa Aaa Aaaaaaaa Aaaaaa Aaaa Aaa a Aa Aaaa Aaaaaaaa Aaaa Aaaaaaaaa a Aaaaaa Aaaaa Aa Aaaaa Aaaa Aaaa Aaaaaaaaa Aaaaaaaaa Aaaa Aaaaaa
a Aaaaaaaaaaaaaa Aaa Aaaaaaaaaaaa Aaaaaaaaaaaaaaaa Aaa Aaaaaaa Aaaaaaaa Aaa Aaaaaaaaaaaaaa Aaaaaaaaaaa Aaaaaaaa Aaaa Aaaaa a a Aa Aaaaaa Aaaaaa Aaaa Aaaa Aa Aaaa Aaaaaa Aaaaaa Aaaa a Aa Aaa Aaaaaaaa Aaaaaaaaaaaaaa Aaa Aaaaaaaaa Aaaaaaa Aaaaaaaaaaaaa Aaaaaaaa Aaaaaaaaaaaaaaaaaaaaaa Aaaaa Aaaaa Aa Aaaaa Aa Aaaaaaa Aaaaaaaa Aaa a Aaaaaa Aa Aaaa Aaaaaaaaaa Aaa Aaaaa Aaa Aaaaa Aaaa Aaaa Aaaaaa Aaaaaa Aaaaaaaa Aaaa Aa Aaaaa Aaaaaaa Aa Aaaaaaaaaaaa Aa Aaaaaaaa Aaaaa Aa Aaa Aaaaaaaaaaa Aaaaa Aaaaaaaaa
🔒The policy-enforcement implications of conflating two CSP states in a single matcher predicate, and the boundary this weakens when the policy is deployed as a deny-all.
더 확인하려면 구독해 주세요
Audit directions
a Aaaaa Aaaaaaaaaa Aaaa Aa Aaaaaaa Aaaaaaa Aaa Aa Aaaaaa Aaaa Aa Aaaa Aaaa Aaaaaaaaaaaaaaaaaaa Aaaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa a Aa Aaaaaaaaaaa Aaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaa Aaaaaa Aaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaa Aa Aaaa Aaaa Aa a Aaa Aaaaaaaa Aaa a Aaaaaa Aaaa Aaaa Aaa Aaaa Aaa Aaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaa Aaaaaaaaaa Aa Aaa Aaaaaa
a Aaaaaaa Aa Aaa Aa Aaaaaaaaaaaaa Aaa Aaa a Aa Aaa Aaaaa Aa Aaa Aaaaaaaaa Aaaa Aaaa Aaaaa Aaa a Aa Aa Aaaa Aa Aaaa Aaa Aa Aa Aa Aaaa Aaaa Aaaaaaaaaa Aaaaaaa Aa Aaaa Aaa Aa Aa Aaaa Aaaaa Aaa Aaaaaaaa Aaa Aaa a Aaaa Aaaaaaa Aaa Aaaaaaa Aa Aaa Aaaa Aaaaaaa Aa Aaa Aaaaaaaaaaaaaa Aaaaaaaaaaaaaaa Aaaaaaa Aaaa Aaaaaa
a Aaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaa a Aa Aaaaaaaaaaaaaaaaaa Aaa Aa Aaaaa a Aaaaaa Aaaa Aaaa Aaa Aaaa a Aa Aaaaa Aaaaaaaa Aaa Aaaaaaaa Aa Aa Aaa Aaa Aaaaaaaa a Aaaa Aa Aaaaaa Aaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaa Aaaaaa
a Aaaaaa Aa Aaa Aaaa Aa Aa Aaa Aa Aa Aaaaaa Aa Aaa Aaa Aaaa a Aa Aa Aa Aaa Aaaaaa Aa Aaaa Aaa Aaaaaaaaaaaa Aaa Aaaaaaaaaaaa Aaaaaaaaaaaa Aaaaaaaaaaaaaa Aaaaaaaaa a Aaaaaa Aaaaa Aa Aaa Aa Aaa Aaaa Aaaaaa
a Aaaaaaaaa Aa Aaaaaaaaaaaaaaaaaaaaaaaaaaa Aaa Aaaaaaaa Aaa Aaaa Aaaaa Aaaaa Aa Aaaaaaaaaaaaaaaaaaaaaaaa Aa Aa Aaa Aaaaaaaa Aa Aaaa Aaaaa Aa Aaaa Aaaa Aaaaa Aaa Aa Aaaaa Aaa Aaaaa Aa Aaaaaa Aaaaa a a Aaaaaa Aaaaa Aa Aaa Aaaa Aaa Aaa Aaa Aaa Aaaa Aa Aa Aaaaaa
🔒Multiple reusable audit patterns identified across CSP source-list matching and similar two-condition policy gates, with concrete starting points for variant discovery.
더 확인하려면 구독해 주세요