Closed Bug 1798741 Opened 2 years ago Closed 2 years ago

Reflected XSS without CSP bypass in Socorro via controllable parsed.path in views.highlight_url

Categories

(Socorro :: General, defect, P1)

Tracking

(Not tracked)

RESOLVED FIXED

People

(Reporter: 40826d, Assigned: willkg)

References

()

Details

(Keywords: sec-low, wsec-xss, Whiteboard: [reporter-external] [web-bounty-form] [verif?])

Attachments

(2 files)

Summary

Bug 1389217 introduced syntax highlighting of generated source files in Socorro. The view function takes the url query parameter and validates it such that only files under http[s]://gecko-generated-sources.s3.amazonaws.com will be fetched and highlighted with Pygments.

For example:

https://crash-stats.mozilla.org/sources/highlight/?url=https://gecko-generated-sources.s3.amazonaws.com/7a1db5dfd0061d0e0bcca227effb419a20439aef4f6c4e9cd391a9f136c6283e89043d62e63e7edbd63ad81c339c401092bcfeff80f74f9cae8217e072f0c6f3/x86_64-pc-windows-msvc/release/build/swgl-59e3a0e09f56f4ea/out/brush_solid_DEBUG_OVERDRAW.h

I was unable to bypass the validation or traverse anywhere meaningful (the only allowed host is a dedicated S3 bucket) but did find an interesting behaviour giving us control of the parsed.path that is passed to the title option of Pygments' HtmlFormatter which is ultimately returned unescaped:

def highlight_url(request):
    # ...
    url = request.GET.get("url")
    # ...
    parsed = urlparse(url)
    # ...
    filename = parsed.path.split("/")[-1]
    # ...
    formatter = HtmlFormatter(
        full=True, title=parsed.path, linenos="table", lineanchors="L", hl_lines=lines
    )
    return http.HttpResponse(
        highlight(resp.text, lexer, formatter), content_type="text/html"
    )

Browsing to the following URL will trigger a CSP error (unless you disabled CSP using a WebExtension or intercept proxy, in which case you will observe an alert dialog with the value of document.domain):

https://crash-stats.mozilla.org/sources/highlight/?url=https://gecko-generated-sources.s3.amazonaws.com/7a1db5dfd0061d0e0bcca227effb419a20439aef4f6c4e9cd391a9f136c6283e89043d62e63e7edbd63ad81c339c401092bcfeff80f74f9cae8217e072f0c6f3/x86_64-pc-windows-msvc/release/build/swgl-59e3a0e09f56f4ea/\%3Ch1%3ECheck%20DevTools%3Csvg%20onload=confirm(document.domain)%3E\out/../out/brush_solid_DEBUG_OVERDRAW.h

Environment

  • Confirmed on: crash-stats.allizom.org, crash-stats.mozilla.org
  • Browser: Firefox Nightly 108.0a1 (2022-10-27)

Impact

An attacker can execute arbitrary JavaScript in the context of another user on Socorro to exfiltrate Protected Data and perform actions as that user provided that CSP is unsupported or disabled in the browser. The impact is otherwise limited to HTML and CSS injection.

Flags: sec-bounty?
Attached image Socorro_Highlight_XSS_noCSP.png (deleted) —
Group: websites-security → client-services-security
Type: task → defect
Component: Other → General
Product: Websites → Socorro

Hello Viridian,

Thank you for your detailed and well written report.

I can confirm that the added HTML is injected on the page and I can see the CSP violation in the console.

Thanks,
Frida

Hello Will,

I found that you worked on previous security reports for Socorro, can you please take a look at this report?

Thanks,
Frida

Flags: needinfo?(willkg)

Thanks Frida, appreciate it!

By the way, I wondered if the Socorro team could help with a couple questions:

  • How are raw crash data validated upon ingestion?
  • Could an attacker influence the content type of an attachment they submit and and then fetch it back intact via the RawCrash API?

Yuck! Thank you for finding this! I'm grabbing this to fix now.

How are raw crash data validated upon ingestion?

Crash report payload structure is specified here:

https://socorro.readthedocs.io/en/latest/spec_crashreport.html

If you're planning to experiment with crash report payloads, please don't do it with our production system. Please use the stage environment instead.

Could an attacker influence the content type of an attachment they submit and and then fetch it back intact via the RawCrash API?

I don't think so. The RawCrash API returns either application/json or application/octet-stream and doesn't infer the content type from the content being requested.

The RawCrash API was implemented as a "model" with an api wrapper. That predates me and I'm not sure why it's squirrely and not more straight-forward.

Here's what determines the content type for the crash annotations (a set of key/values returned as JSON via the raw crash api):

https://github.com/mozilla-services/socorro/blob/36a52199561af50f6e1d23944e34101073b96569/webapp-django/crashstats/api/views.py#L201

Here's what determines the content type for all the other things attached to a crash report (minidump, memory_report, etc):

https://github.com/mozilla-services/socorro/blob/36a52199561af50f6e1d23944e34101073b96569/webapp-django/crashstats/api/views.py#L333-L338

Assignee: nobody → willkg
Status: NEW → ASSIGNED
Flags: needinfo?(willkg)
Priority: -- → P1

Thanks very much for the pointers Will (and the fast fix!)

If you're planning to experiment with crash report payloads, please don't do it with our production system. Please use the stage environment instead.

Of course. I haven't looked much at the rest of the pipeline yet, just the web app, but I'll make sure to test on stage when I do (or my local setup, if I get those parts working).

This was pushed to prod just now in bug #1798949. Marking as FIXED.

Thank you for reporting!

Status: ASSIGNED → RESOLVED
Closed: 2 years ago
Resolution: --- → FIXED
Flags: sec-bounty?
Flags: sec-bounty-hof+
Flags: sec-bounty+
Keywords: sec-low, wsec-xss
Group: client-services-security
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: