Closed Bug 1554110 (CVE-2020-12389) Opened 2 years ago Closed 8 months ago

Windows sandbox: renderer processes can open each and unrelated Chromium processes

Categories

(Core :: Security: Process Sandboxing, defect, P1)

defect

Tracking

()

RESOLVED FIXED
Tracking Status
firefox-esr60 --- wontfix
firefox-esr68 76+ fixed
firefox67.0.1 --- wontfix
firefox68 + wontfix
firefox69 + wontfix
firefox70 + wontfix
firefox71 --- wontfix
firefox72 --- wontfix
firefox73 --- wontfix
firefox74 --- wontfix
firefox75 --- wontfix
firefox76 --- fixed

People

(Reporter: niklas.baumstark, Assigned: bobowen)

References

Details

(Keywords: csectype-priv-escalation, sec-high, Whiteboard: [reporter-external] [client-bounty-form][post-critsmash-triage][adv-main76+][adv-ESR68.8+])

Attachments

(1 file, 1 obsolete file)

Chromium uses the following code to prevent access between renderers, and from renderers to other low integrity processes: https://cs.chromium.org/chromium/src/services/service_manager/sandbox/win/sandbox_win.cc?l=430

  result = policy->SetTokenLevel(sandbox::USER_RESTRICTED_SAME_ACCESS,
                                 sandbox::USER_LOCKDOWN);
  if (result != sandbox::SBOX_ALL_OK)
    return result;
  // Prevents the renderers from manipulating low-integrity processes.
  result = policy->SetDelayedIntegrityLevel(sandbox::INTEGRITY_LEVEL_UNTRUSTED);

In SandboxBroker::SetSecurityLevelForContentProcess of Firefox with aSandboxLevel < 20 however, the delayed integrity level is set to INTEGRITY_LEVEL_LOW: https://dxr.mozilla.org/mozilla-central/source/security/sandbox/win/src/sandboxbroker/sandboxBroker.cpp#432

This means that content processes can open and manipulate each other, and also to some other Chromium-based processes such as the Chrome or Electron GPU process (an example is the Slack desktop application). I verified this in Firefox 67 by attaching to a renderer process with WinDbg and calling OpenProcess(PROCESS_VM_WRITE, 0, <PID>) to check if the result is non-zero. The steps for doing so are as follows (on Windows 10):

bp kernel32!ReadFile
g  
# wait for breakpoint to hit
eq KERNEL32!_imp_ReadFile kernel32!OpenProcessStub
eq @rsp deadbeef
r @rcx=0x20
r @rdx=0
r @r8=0n<PID>
g
# will crash at 0xdeadbeef because we changed the return address
r @rax   # non-zero if opening was successfully

One direct consequence of this within Firefox itself is that a web renderer process can escalate privileges to file and web extension processes which are potentially more trusted. See also bug 1538028, file processes can read the entire filesystem.

Another consequence is that we can get control of a process before it calls LowerToken(). This clearly goes against the idea of the sandbox, even though it probably only means a minor increase of kernel attack surface in the case of Firefox. If a Chromium-like win32k lockdown mechanism will ever be introduced in the future, this would be a bypass.

Also, I could imagine that this could affect site isolation in the future.

Flags: sec-bounty?
Group: firefox-core-security → dom-core-security
Type: task → defect
Component: Security → Security: Process Sandboxing
Product: Firefox → Core

Thanks Niklas, it's useful having this explicitly in a bug (which I don't think we already have), but this is a known issue.
We are working towards moving to untrusted integrity level at the same time as win32k lockdown.
Many of the things that block that also block us from using untrusted integrity.

Need to hook this up to a INTEGRITY_LEVEL_UNTRUSTED bug.

Priority: -- → P2

In the meanwhile, you're fine with essentially presenting the entire host filesystem to a compromised renderer?

he did say "working towards moving to untrusted integrity level" -- that's not "fine with it", it's just hard retrofitting into a decade plus of the wrong architecture.

If a Chromium-like win32k lockdown mechanism will ever be introduced in the future, this would be a bypass.

We're working hard on that, too.

Status: UNCONFIRMED → NEW
Ever confirmed: true
Keywords: sec-high

I guess I misposed the question: Unless Firefox requires doing OpenProcess for legitimate purposes, shouldn't it be possible to fix this bug without moving to untrusted integrity, and would that not be a warranted approach to this issue?

Somebody already wrote about the fact that this is possibly publicly, probably without realizing the security implications: https://github.com/0vercl0k/CVE-2019-9810#organization

(In reply to Daniel Veditz [:dveditz] from comment #4)

he did say "working towards moving to untrusted integrity level" -- that's not "fine with it", it's just hard retrofitting into a decade plus of the wrong architecture.

If a Chromium-like win32k lockdown mechanism will ever be introduced in the future, this would be a bypass.

We're working hard on that, too.

I'd also add that it's not quite as simple as presenting the entire host filesystem to a compromised renderer.
The vast majority of the time the file content process will not be running and, other bugs that need fixing aside, in theory a compromised renderer should not be able to force the parent process to start it.

That said my thanks in comment 1 was genuine, this is one of the things that we knew would be addressed by moving to untrusted, but having this bug filed has raised the awareness of that within Mozilla.

(In reply to Niklas Baumstark from comment #5)

I guess I misposed the question: Unless Firefox requires doing OpenProcess for legitimate purposes, shouldn't it be possible to fix this bug without moving to untrusted integrity, and would that not be a warranted approach to this issue?

If you have ideas about how we can prevent this without going to untrusted then that would be great.

I was thinking about a ObRegisterCallbacks hook on PsProcessType, which I believe is the canonical anti-debugging trick on Windows.

Maybe this can also be done with just normal ACLs, unfortunately I'm not an expert in Windows security

(In reply to Niklas Baumstark from comment #9)

I was thinking about a ObRegisterCallbacks hook on PsProcessType, which I believe is the canonical anti-debugging trick on Windows.

Ah right we don't currently use kernel drivers, so this solution would not be straight forward to roll out and indeed could introduce worse vulnerabilities.
It might also be possible for the process to work around such hooking.

Oh I didn't realize this is a kernel API, please ignore. Anyways, will let you know if I come across anything useful.

(In reply to Niklas Baumstark from comment #10)

Maybe this can also be done with just normal ACLs, unfortunately I'm not an expert in Windows security

It looks like you can do exactly this, thanks!
The CreateProcessAsUser function can take two LPSECURITY_ATTRIBUTES arguments, which allows you to set the security descriptor for the process and thread.
If I pass a security descriptor with just the user in the dacl, then the OpenProcess fails, because the normal content processes have the user removed from their access token.

Then I found a setting that had been added to the sandbox policy a while ago that doesn't add the Restricted SID and removes the Logon SID from the ACL on the access token used to create the process.
This is what is used to get the security descriptor if null is passed for the LPSECURITY_ATTRIBUTES for the process and thread, so this is a really simple change to achieve the same result.

If I understand you correctly, then taking the approach of modifying the token privileges rather than the process ACL is preferrable because it would also deal with the issue of the Chromium/Electron renderers.

Assignee: nobody → bobowencode
Status: NEW → ASSIGNED
Priority: P2 → P1

Bob, is it possible for me to get access to this patch to test it on my local build, just out of curiosity?

(In reply to Niklas Baumstark from comment #14)

If I understand you correctly, then taking the approach of modifying the token privileges rather than the process ACL is preferrable because it would also deal with the issue of the Chromium/Electron renderers.

Well it would potentially prevent other low integrity processes from opening our process (if they didn't have the User SID).
This doesn't affect whether our process can open other low integrity processes, we would need untrusted or a more restrictive access token for that, but it's a big step in the right direction.
For example the low integrity internet explorer process still has the Logon SID in its ACL with read access.
We still have that SID in our access token (this just removes it from our processes ACL), so we should still be able to get read access to that IE process.

(In reply to Niklas Baumstark from comment #16)

Bob, is it possible for me to get access to this patch to test it on my local build, just out of curiosity?

Given that this is just a one line policy change, I'll paste it in here.

diff --git a/security/sandbox/win/src/sandboxbroker/sandboxBroker.cpp b/security/sandbox/win/src/sandboxbroker/sandboxBroker.cpp
--- a/security/sandbox/win/src/sandboxbroker/sandboxBroker.cpp
+++ b/security/sandbox/win/src/sandboxbroker/sandboxBroker.cpp
@@ -502,6 +502,8 @@ void SandboxBroker::SetSecurityLevelForC
       sandbox::SBOX_ALL_OK == result,
       "SetDelayedIntegrityLevel should never fail, what happened?");

+  mPolicy->SetLockdownDefaultDacl();
+
   sandbox::MitigationFlags mitigations =
       sandbox::MITIGATION_BOTTOM_UP_ASLR | sandbox::MITIGATION_HEAP_TERMINATE |
       sandbox::MITIGATION_SEHOP | sandbox::MITIGATION_DEP_NO_ATL_THUNK |

Comment on attachment 9069004 [details]
Bug 1554110: SetLockdownDefaultDacl for content process sandbox policy. r=jmathies!

Security Approval Request

  • How easily could an exploit be constructed based on the patch?: Not by itself, this is something that could contribute to a sandbox escape.
    It wouldn't take long for someone with the necessary understanding to work out what this protects against.
    I think landing this under a separate non security bug would be a good idea.
  • Do comments in the patch, the check-in comment, or tests included in the patch paint a bulls-eye on the security problem?: No
  • 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?: If the patch doesn't apply or rebase, then it will be trivial to create new patches.
  • How likely is this patch to cause regressions; how much testing does it need?: Unlikely, because sandboxed processes shouldn't be attempting to open each other.
    There is a very small risk that this could interfere with other software that might try to open our processes, but I think that is unlikely because they would tend to be running with more privileges anyway.
Attachment #9069004 - Flags: sec-approval?

Comment on attachment 9069004 [details]
Bug 1554110: SetLockdownDefaultDacl for content process sandbox policy. r=jmathies!

sec-approval+ for trunk. We'll want this on beta and ESR60 as well so please create and nominate patches for those branches.

Attachment #9069004 - Flags: sec-approval? → sec-approval+

(In reply to Al Billings [:abillings] from comment #19)

Comment on attachment 9069004 [details]
Bug 1554110: SetLockdownDefaultDacl for content process sandbox policy. r=jmathies!

sec-approval+ for trunk. We'll want this on beta and ESR60 as well so please create and nominate patches for those branches.

Landed via bug 1557282 (deliberately not linked).

The patch for m-c imports cleanly for beta, I'll add one for ESR60 on bug 1557282.

NI to jcristau and RyanVM, so they're aware of this for the approval requests, which I'll make on that bug.

Flags: needinfo?(ryanvm)
Flags: needinfo?(jcristau)
Flags: needinfo?(ryanvm)

This has just been backed out, looks like some audio/media issue.

I guess this won't make 68 at this point.

In bug 1557282 I'm going to try landing this again just for windows 10.
I've filed bug 1564842 for landing this for earlier versions, possibly once audio remoting has been enabled.

Comment on attachment 9069004 [details]
Bug 1554110: SetLockdownDefaultDacl for content process sandbox policy. r=jmathies!

Revision D33301 was moved to bug 1557282. Setting attachment 9069004 [details] to obsolete.

Attachment #9069004 - Attachment is obsolete: true

Bob, want to give this another try for 71/70?

Flags: needinfo?(bobowencode)

Following up in email with Bob and jimm.

From email with :gcp, this is blocked partly by bug 1432303, and possibly by bug 1567385 as well.

Following up in email with :kinetik and :drno.

(In reply to Liz Henry (:lizzard) from comment #30)

From discussion with kinetik, we are aiming a fix at 71.

Thanks lizzard (sorry, must have missed this comment first time, only noticed with the email from the CCs being added).

One idea we had (if the audio issue is fixed), is to only call SetLockdownDefaultDacl on the processes that we want to protect.
So, the file content process and certain other "privileged" content processes. In the hope that we can live with the performance and profiling issues in those processes.
I think it should be fairly straight forward to pass through the remote type instead of just a bool for the file content process to [1]

tjr - I think you're more familiar with these new remote types - which ones do you think we would need to protect? (Also, do you think that we might live with the other issues in these process types?)

[1] https://searchfox.org/mozilla-central/rev/1fe0cf575841dbf3b7e159e88ba03260cd1354c0/security/sandbox/win/src/sandboxbroker/sandboxBroker.cpp#419

Flags: needinfo?(bobowencode) → needinfo?(tom)

We have three types of content processes we would want to protect: file, privilegedabout, and privilegedmozillacontent. privilegedabout hosts about:newtab and that's got to be fast. privilegedmozillacontent hosts FXA which does in-browser crypto so while it's not a bright red flag like newtab, it is something to be somewhat concerned about.

Note that once we get Fission, we would also need to apply this to regular content processes too. If that isn't done in this bug, it should be a separate bug that blocks Bug 1505832

It's an ugly dependency graph to draw out "If we apply it to X but not Y we're leaving ourselves vulnerable to Z which negates the benefit of applying it to X" - but I don't think that's necessary. If we can apply it to X, we should, even if we don't derive the benefit today.

Flags: needinfo?(tom)

(In reply to Ryan VanderMeulen [:RyanVM] from comment #33)

Should we mark this bug as stalled?

No; it's blocked on Audio IPC but there is no additional information we need (that we can't get) to justify marking it stalled.

Flags: sec-bounty? → sec-bounty+

Update on recent progress on regressions caused by the SetLockdownDefaultDacl fix:

  • Audio remoting is riding the trains.
  • I have a patch for the profiler stack issue.
  • Performance issues appear to be multifaceted:
    • OpenProcessToken - reasonably good fix for this to prevent brokering to the parent.
    • Appears to break DWrite cache (which we think we need to do deliberately at some point) - this does not affect us when there is no acceleration or when webrender is enabled, so could enable in those cases.
    • Two above appear to fix tabswitch performance regressions (bug 1567385), but not tp6 cold start regressions (bug 1566744), need to investigate those further.
See Also: → CVE-2020-12388

Bob, has this been fixed now that bug 1618911 landed?

Flags: needinfo?(bobowencode)

(In reply to Gian-Carlo Pascutto [:gcp] from comment #36)

Bob, has this been fixed now that bug 1618911 landed?

Yes, I've resolved instead of duplicated, because the attacks were different even though the underlying cause and fix were the same.

Status: ASSIGNED → RESOLVED
Closed: 8 months ago
Flags: needinfo?(bobowencode)
Resolution: --- → FIXED
Group: dom-core-security → core-security
Group: core-security → core-security-release

If the fix in bug 1618911 was backported to ESR68, is this bug also fixed in ESR68? It got marked "wontfix" a couple months ago for some reason.

Flags: needinfo?(bobowencode)

(In reply to Daniel Veditz [:dveditz] from comment #38)

If the fix in bug 1618911 was backported to ESR68, is this bug also fixed in ESR68? It got marked "wontfix" a couple months ago for some reason.

yeah, I guess it was originally a won't fix, but that changed with the severity being increased due to the full escape

Flags: needinfo?(bobowencode)
Flags: qe-verify-
Whiteboard: [reporter-external] [client-bounty-form] [verif?] → [reporter-external] [client-bounty-form] [verif?][post-critsmash-triage]
Whiteboard: [reporter-external] [client-bounty-form] [verif?][post-critsmash-triage] → [reporter-external] [client-bounty-form][post-critsmash-triage][adv-main76+]
Whiteboard: [reporter-external] [client-bounty-form][post-critsmash-triage][adv-main76+] → [reporter-external] [client-bounty-form][post-critsmash-triage][adv-main76+][adv-ESR68.8+]
Attached file advisory.txt
Alias: CVE-2020-12389

Same root cause as bug 1618911, different exploit strategy. Also opening up.

Group: core-security-release
You need to log in before you can comment on or make changes to this bug.