Closed Bug 1770094 (CVE-2022-40956) Opened 2 years ago Closed 2 years ago

XS-Leaks with CSP (base-uri) bypass by img tag.

Categories

(Core :: DOM: Security, defect, P3)

defect

Tracking

()

VERIFIED FIXED
105 Branch
Tracking Status
firefox-esr91 --- wontfix
firefox-esr102 105+ verified
firefox104 --- wontfix
firefox105 + verified

People

(Reporter: t.satoki111, Assigned: tschuster)

References

(Blocks 1 open bug)

Details

(Keywords: csectype-disclosure, sec-low, Whiteboard: [domsecurity-backlog1] [adv-main105+][adv-esr102.3+][post-critsmash-triage])

Attachments

(8 files)

User Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:100.0) Gecko/20100101 Firefox/100.0

Steps to reproduce:

I have a Content-Security-Policy that restricts reading of the base tag to 'self'.
And checked if this restriction is effective by injecting HTML tags.

Actual results:

CSP appeared to have successfully prevented base tag injection.
However, from the img tag, it was confirmed that the request was sent to the URI specified in the base.
Only FireFox could get the secret path of a user's img tag by XS-Leaks.
The attached poc.html confirms the leakage of confidential information (uuid).
I am assuming an attack scenario where the profile URL is secret and the tag injection occurs on the top page.

Expected results:

A base tag blocked by CSP is invalid, and the img tag should issue a request to the original URI.

Group: firefox-core-security → dom-core-security
Component: Untriaged → DOM: Security
Product: Firefox → Core

The attached testcase works correctly for me -- the two links (the image and the navigation) are relative to our attachment domain and ignore the base setting. I see CSP errors for both. Were you running two test servers in the same file directory? Could you capture a HAR log in DevTools showing it doing the wrong thing?

Flags: needinfo?(t.satoki111)
Attached image leak1.png
Attached image leak2.png

Thank you for verifying my report.
When you open the file you attached with "bmoattachments.org", other CSP settings (e.g. img-src) get in the way and disable base.
I would like you to try either temporarily saving it locally as a poc.html file or in an environment with CSP settings turned off except for base-uri.

I am attaching a screenshot of the actual screenshot I verified to show that I am not mistaken in my perception.
One is the same html that was submitted as a PoC on the site where the base-uri was specified.
leak1.png
The other is the server log to capture the request (requestbin).
leak2.png

Flags: needinfo?(t.satoki111)

As far as I know, if default-src 'none' and img-src 'none' are valid, there is no problem.
However, if they are not, a GET request is sent to the URI specified in base, which is leak even if base-uri 'none' is set.
I have tried with the following CSP enabled, but it leaks.

base-uri 'none'; worker-src 'none'; connect-src 'none'; font-src 'none'; object-src 'none'; script-src 'none'; style-src 'none'; frame-src 'none'; frame-ancestors 'none'; form-action 'none'
Flags: needinfo?(dveditz)

I'll take a look.

Flags: needinfo?(dveditz) → needinfo?(fbraun)
Flags: needinfo?(dveditz)

Need any additional information?
I want to create a CTF XS-Leaks problem using this behavior and am waiting for a fix.

Attached file log.txt

I'm attaching some logs that I gathered by running firefox with MOZ_LOG="CSPParser:5,CSPContext:5,CSMLog:5" firefox https://localhost:8000.

It seems we are loading the image twice. I am quoting the most interesting bits here:
First, we parse the CSP and then do a TYPE_INTERNAL_IMAGE_PRELOAD of the URL at the domain with the injected base URL

#DebugDoContentSecurityCheck Begin
[Child 22315: Main Thread]: V/CSMLog doContentSecurityCheck:
[Child 22315: Main Thread]: V/CSMLog   processType: "webIsolated=http://localhost"
[Child 22315: Main Thread]: V/CSMLog   channelURI: "https://attack.example.com/profile/img/b0190b16-41fb-9059-a680-9e5b47f24ce5.png"
[Child 22315: Main Thread]: V/CSMLog   httpMethod: GET
[Child 22315: Main Thread]: D/CSMLog   loadingPrincipal: "http://localhost:8000/poc.html"
[Child 22315: Main Thread]: D/CSMLog   triggeringPrincipal: "http://localhost:8000/poc.html"
[Child 22315: Main Thread]: D/CSMLog   principalToInherit: nullptr
[Child 22315: Main Thread]: V/CSMLog   redirectChain:
[Child 22315: Main Thread]: V/CSMLog   internalContentPolicyType: TYPE_INTERNAL_IMAGE_PRELOAD
[Child 22315: Main Thread]: V/CSMLog   externalContentPolicyType: TYPE_IMAGE
[Child 22315: Main Thread]: V/CSMLog   upgradeInsecureRequests: false
[Child 22315: Main Thread]: V/CSMLog   initialSecurityChecksDone: false
[Child 22315: Main Thread]: V/CSMLog   allowDeprecatedSystemRequests: false
[Child 22315: Main Thread]: D/CSMLog   CSP:
[Child 22315: Main Thread]: V/CSMLog   securityFlags:
[Child 22315: Main Thread]: V/CSMLog     - SEC_ALLOW_CROSS_ORIGIN_INHERITS_SEC_CONTEXT
[Child 22315: Main Thread]: V/CSMLog     - SEC_ALLOW_CHROME
[Child 22315: Main Thread]: V/CSMLog   httpsOnlyFirstStatus:
[Child 22315: Main Thread]: V/CSMLog     - HTTPS_ONLY_UNINITIALIZED
[Child 22315: Main Thread]: V/CSMLog     - HTTPS_ONLY_EXEMPT
[Child 22315: Main Thread]: D/CSMLog
#DebugDoContentSecurityCheck End

Then we ask the CSP if that location is allowed to be loaded - there is no CSP directive that disallows this:

[Child 22315: Main Thread]: D/CSPContext nsCSPContext::ShouldLoad, aContentLocation: https://attack.example.com/profile/img/b0190b16-41fb-9059-a680-9e5b47f24ce5.png
[Child 22315: Main Thread]: D/CSPContext >>>>                      aContentType: 38
[Child 22315: Main Thread]: D/CSPContext nsCSPContext::ShouldLoad, decision: load, aContentLocation: https://attack.example.com/profile/img/b0190b16-41fb-9059-a680-9e5b47f24ce5.png
[Child 22315: Main Thread]: D/CSPContext nsCSPContext::ShouldLoad, aContentLocation: https://attack.example.com/profile/img/b0190b16-41fb-9059-a680-9e5b47f24ce5.png
[Child 22315: Main Thread]: D/CSPContext >>>>                      aContentType: 38
[Child 22315: Main Thread]: D/CSPContext nsCSPContext::ShouldLoad, decision: load, aContentLocation: https://attack.example.com/profile/img/b0190b16-41fb-9059-a680-9e5b47f24ce5.png

After that, we see just another image request, but with a different content policy type, TYPE_INTERNAL_IMAGE:

#DebugDoContentSecurityCheck Begin
[Child 22315: Main Thread]: V/CSMLog doContentSecurityCheck:
[Child 22315: Main Thread]: V/CSMLog   processType: "webIsolated=http://localhost"
[Child 22315: Main Thread]: V/CSMLog   channelURI: "http://localhost:8000/profile/img/b0190b16-41fb-9059-a680-9e5b47f24ce5.png"
[Child 22315: Main Thread]: V/CSMLog   httpMethod: GET
[Child 22315: Main Thread]: D/CSMLog   loadingPrincipal: "http://localhost:8000/poc.html"
[Child 22315: Main Thread]: D/CSMLog   triggeringPrincipal: "http://localhost:8000/poc.html"
[Child 22315: Main Thread]: D/CSMLog   principalToInherit: nullptr
[Child 22315: Main Thread]: V/CSMLog   redirectChain:
[Child 22315: Main Thread]: V/CSMLog   internalContentPolicyType: TYPE_INTERNAL_IMAGE
[Child 22315: Main Thread]: V/CSMLog   externalContentPolicyType: TYPE_IMAGE
[Child 22315: Main Thread]: V/CSMLog   upgradeInsecureRequests: false
[Child 22315: Main Thread]: V/CSMLog   initialSecurityChecksDone: false
[Child 22315: Main Thread]: V/CSMLog   allowDeprecatedSystemRequests: false
[Child 22315: Main Thread]: D/CSMLog   CSP:
[Child 22315: Main Thread]: D/CSMLog   - "base-uri 'self'"
[Child 22315: Main Thread]: V/CSMLog   securityFlags:
[Child 22315: Main Thread]: V/CSMLog     - SEC_ALLOW_CROSS_ORIGIN_INHERITS_SEC_CONTEXT
[Child 22315: Main Thread]: V/CSMLog     - SEC_ALLOW_CHROME
[Child 22315: Main Thread]: V/CSMLog   httpsOnlyFirstStatus:
[Child 22315: Main Thread]: V/CSMLog     - HTTPS_ONLY_UNINITIALIZED
[Child 22315: Main Thread]: V/CSMLog     - HTTPS_ONLY_EXEMPT
[Child 22315: Main Thread]: D/CSMLog
#DebugDoContentSecurityCheck End
[Child 22315: Main Thread]: D/CSPContext nsCSPContext::ShouldLoad, aContentLocation: http://localhost:8000/profile/img/b0190b16-41fb-9059-a680-9e5b47f24ce5.png
[Child 22315: Main Thread]: D/CSPContext >>>>                      aContentType: 37
[Child 22315: Main Thread]: D/CSPContext nsCSPContext::ShouldLoad, decision: load, aContentLocation: http://localhost:8000/profile/img/b0190b16-41fb-9059-a680-9e5b47f24ce5.png
[Child 22315: Main Thread]: D/CSMLog

Again, the CSP does not disallow the load.

In summary, it seems that the image preloader is accepting the base tag without (or before) asking the CSP.

Flags: needinfo?(fbraun)

Thank you for reporting this bug and please apologize the long wait.
I'm marking this as sec-low because this CSP bug does not lead to XSS and it assumes an injection in the page.
Please let us know when and where you intend to cover this in your CTF.

Keywords: sec-low

Thank you for your detailed understanding.
I do not think it is critical as it assumes HTML injection.
I decided it was a bit risky and reported it :)
The CTF will take place in November or January and we are thinking of XS-Leaks the secret profile page (protected by CSP).

Emilio, from looking at Document::ResolvePreloadImage, it seems we never respected the base tag protection from CSP here.

When does the preloader logic kick in and does it know about the CSP?
Wondering what the right hooks should be..

Flags: needinfo?(emilio)

Preloads are triggered by the speculative parser, so before the DOM is built from the meta element.

It seems we apply the CSP meta tag once it's added to the DOM which is too late: https://searchfox.org/mozilla-central/rev/15b656909e77d3048d4652b894f79a8c719b4b86/dom/html/HTMLMetaElement.cpp#90-113

However it seems the speculative parser does know about the CSP meta tag:

https://searchfox.org/mozilla-central/rev/15b656909e77d3048d4652b894f79a8c719b4b86/parser/html/nsHtml5TreeOpExecutor.cpp#1330-1354

So the question I guess is why the preload CSP is not applying.

Flags: needinfo?(emilio)

Aha! Looking at `BaseURIForPreload´, it seems we just don't check the CSP.

We likely need a call into CSP like we have in SetBaseURIUsingFirstBaseWithHref.

@Tom Schuster: Maybe you can add this to your backlog, but it shouldn't have to block the other important stuff you're doing.

Severity: -- → S3
Priority: -- → P3
Whiteboard: [domsecurity-backlog1]

Taking this, but I am currently still working on higher priority stuff.

Assignee: nobody → tschuster
Status: UNCONFIRMED → ASSIGNED
Ever confirmed: true
Attachment #9289658 - Attachment description: WIP: Bug 1770094 → Bug 1770094 r?emilio!,freddyb

Depends on D154518

Attachment #9289658 - Attachment description: Bug 1770094 r?emilio!,freddyb → Bug 1770094 r?emilio!,freddyb!

https://hg.mozilla.org/integration/autoland/rev/3849f10b51546b8d2e92ba4b1c6b088f625b6d64

I just realized that this doesn't work when the CSP is delivered via the Content-Security-Policy header instead of the meta tag. Even though the comment in nsHtml5TreeOpExecutor::AddSpeculationCSP seemed to suggest the opposite to me.

Flags: needinfo?(dveditz)
Keywords: leave-open
Flags: needinfo?(tschuster)

This is leave open to have another version of the test that uses a Content-Security-Policy header instead of meta tag. Sadly I don't think we can just set this header on the main test page?

Why do ^headers^ files not work?

Why do ^headers^ files not work?

It does indeed work! Somehow I assumed it only works for other test files. I've updated the test. I am not quite sure what flags we usually use to remember checking in the security tests.

Flags: in-testsuite-
Keywords: leave-open
Version: Firefox 100 → Firefox 105
Status: ASSIGNED → RESOLVED
Closed: 2 years ago
Resolution: --- → FIXED
Target Milestone: --- → 105 Branch
Version: Firefox 105 → unspecified

https://groups.google.com/a/mozilla.org/g/dev-platform/c/QznyG9gzwYc/m/aGS52shgAAAJ has a suggestion for how to be reminded to land the test later. Also, please request ESR102 approval on this when you get a chance.

Group: dom-core-security → core-security-release
Flags: needinfo?(tschuster)
Flags: in-testsuite?
Flags: in-testsuite-

Comment on attachment 9289658 [details]
Bug 1770094 r?emilio!,freddyb!

ESR Uplift Approval Request

  • If this is not a sec:{high,crit} bug, please state case for ESR consideration: This is a simple patch that fixes an cross-site leak and CSP bypass that would be easy to recognize by looking at the already landed patch.
  • User impact if declined:
  • Fix Landed on Version: 105
  • Risk to taking this patch: Low
  • Why is the change risky/not risky? (and alternatives if risky):
Flags: needinfo?(tschuster)
Attachment #9289658 - Flags: approval-mozilla-esr102?
Attachment #9289668 - Flags: approval-mozilla-esr102?
Attachment #9289668 - Flags: approval-mozilla-esr102?

Comment on attachment 9289658 [details]
Bug 1770094 r?emilio!,freddyb!

Approved for 102.3esr.

Attachment #9289658 - Flags: approval-mozilla-esr102? → approval-mozilla-esr102+

I have the utmost respect for your excellent work and obsession with safety.
Thank you very much.

By the way, can a CVE or something be assigned to this issue?
I want to use it when explaining to others :)

We usually decide over CVE assignment when the fix is available in Firefox release. This bug is tracking 105, which means that it will become available to our release population starting Tuesday, September 20th.

Whiteboard: [domsecurity-backlog1] → [domsecurity-backlog1] [reminder-test 2022-10-18]
Whiteboard: [domsecurity-backlog1] [reminder-test 2022-10-18] → [domsecurity-backlog1] [reminder-test 2022-10-18][adv-main105+][adv-esr102.3+]
Attached file advisory.txt
Flags: qe-verify+
Whiteboard: [domsecurity-backlog1] [reminder-test 2022-10-18][adv-main105+][adv-esr102.3+] → [domsecurity-backlog1] [reminder-test 2022-10-18][adv-main105+][adv-esr102.3+][post-critsmash-triage]
QA Whiteboard: [qa-triaged]
Attached image image.png

Hello! I tried verifying this issue today but I'm not sure if the following results are correct:
On Windows 10x64 with the affected build 102.0a1 (20220518214245) when loading the attached poc.html file on an HTTP local server, I see a second request for the .png file that has attack.example.com domain in the Network tab.

On fixed builds Firefox 105.0 (20220915150737) and Firefox 102.3.0esr (20220912135840) I can no longer see the attack.example.com request when loading the attached poc.html. This was tested on Windows 10x64, macOS 11 and Ubuntu 21.

I have attached a screenshot with my results for a better understanding. Note that I have also added a random .png file inside the server path (http://127.0.0.1:8080/profile/img/b0190b16-41fb-9059-a680-9e5b47f24ce5.png). Is this verification correct or do I need to follow other steps in order to verify this issue? If so can you please provide if possible another set of steps? Thank you in advance!

Flags: needinfo?(tschuster)

Thanks for the confirmation.
As you can see in your image, 105.0 does not request attack.example.com, so the security risk I reported has been fixed.

Many sites these days have user profiles created by UUID (and secret URLs).
This eliminates the risk of leaking paths that should be kept secret by the user.

(In reply to Satoki Tsuji from comment #31)

Thanks for the confirmation.
As you can see in your image, 105.0 does not request attack.example.com, so the security risk I reported has been fixed.

Many sites these days have user profiles created by UUID (and secret URLs).
This eliminates the risk of leaking paths that should be kept secret by the user.

Thank you as well for the confirmation and explanation.

I am closing this issue as verified fixed based on comment 30 and comment 31. Removing the ni? request as well.

Status: RESOLVED → VERIFIED
QA Whiteboard: [qa-triaged]
Flags: qe-verify+
Flags: needinfo?(tschuster)
Alias: CVE-2022-40956

Thanks Satoki Tsuji for your confirmation.

Flags: needinfo?(tschuster)
Flags: needinfo?(tschuster)
Flags: in-testsuite?
Flags: in-testsuite+
Group: core-security-release

4 months ago, Tom Schuster (MoCo) placed a reminder on the bug using the whiteboard tag [reminder-test 2022-10-18] .

tschuster, please refer to the original comment to better understand the reason for the reminder.

Flags: needinfo?(tschuster)
Whiteboard: [domsecurity-backlog1] [reminder-test 2022-10-18][adv-main105+][adv-esr102.3+][post-critsmash-triage] → [domsecurity-backlog1] [adv-main105+][adv-esr102.3+][post-critsmash-triage]
Flags: needinfo?(tschuster)
Flags: sec-bounty?
Flags: sec-bounty? → sec-bounty+
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Creator:
Created:
Updated:
Size: