MetaMask JavaScript Security Stack (Part 2 - Snow) [๐]
07 Jul 2023Originally posted on X
In Last week's tweet (โ) we discussed "scuttling", a LavaMoat ๐ feature we enable on MetaMask ๐ฆ for enhanced supply chain security.
However, near the end I hinted scuttling isn't so simple in reality..
Browser JS security folks? Come learn why, and how Snow โ๏ธ saves the day:
First, it's important to clarify there are multiple groups of properties we don't/can't scuttle, each group for its own reasons (scuttling is hard to achieve!)
Today I'll focus on the "non-configurable window object properties" group.
But first - how does scuttling even work?
For starters, we need to understand what to scuttle.
To do that, we'll collect all the properties that are accessible via the window.
That must include own properties of the window itself, but also own properties of every one of the prototypes in its prototype chain:
Once we have that list, it's time to scuttle!
Pretty simple, we just iterate the props we collected, and redefine their getters to deny access to their real values, and instead return "undefined" (for the sake of the example).
But as you can see, the scuttling attempt fails...
In JS (by legacy), some props of the window object are non-configurable by definition (we're not gonna get into why).
This means that some properties aren't going to cave in under scuttling - ever.
Question is - which ones? And how useful are they going to be for attackers?
These are the 8 props that are non-configurable in the browser, and cannot be redefined whatsoever.
* Infinity, NaN, undefined - are primitive values and therefore are not very powerful for attackers ๐
* window, top - refer back to the window object, which we already scuttle ๐
* chrome - very powerful for attackers (will elaborate on another thread one day)
Luckily, even though is not configurable, it is writable, meaning we can't redefine its getter, but we can redefine its value to be undefined ๐
Which leaves us with only two: document & location.
And unfortunately, if code escapes LavaMoat sandbox and gets access to either one - it can be pretty bad.
Today, we'll focus on document access, why it's dangerous, and how we use our very own Snow JS โ๏ธ to reduce that risk.
document access is dangerous for obvious reasons:
DOM access allows attackers to intercept the interaction users have with the app. They can change its layout deceitfully, thus tricking the user to take actions that might compromise its account or its private key.
That's bad, but mitigating that isn't our top priority, because:
1. Fully sandboxing the DOM was proven before as practically impossible.
Projects have tried achieving {parts of} that before (e.g. CaJa) and have learned how tangled up the DOM really is.
It'll take some time before we decide to retake on that challenge.
But most importantly:
2. We're less worried of DOM interception, as it ultimately requires UI (=user interaction) to compromise MetaMask.
We're far more worried about powerful JS capabilities the window object grants, with which attackers can breach MetaMask without any UI - which is why we scuttle!
So... what's so bad about it?
If that's the "obvious" and "less prioritized" reason we're worried about DOM access - what's the less obvious, more dangerous reason?
Short answer? iframes.
Long(er) answer?
iframes introduce new realms, which in the browser translate to...
... New windows! Meaning, more windows to scuttle! ๐ฑ
So in reality, assuming attackers escaped LavaMoat sandbox, bypassing scuttling is simple - just create a new iframe and steal the scuttled props from its window!
And what's the single prop needed to create a new iframe? ๐
Does that make scuttling useless? KINDA.
Does that make my last tweet a lie? Am I just a big phoney?
Well, not quite. By integrating scuttling with Snow โ๏ธ, we're eliminating the threat, but in a different, also advanced way.
Getting 2 long though, so wait for nxt week's ๐งต ๐
There it is, part 3
However, near the end I hinted scuttling isn't so simple in reality..
Browser JS security folks? Come learn why, and how Snow โ๏ธ saves the day:
First, it's important to clarify there are multiple groups of properties we don't/can't scuttle, each group for its own reasons (scuttling is hard to achieve!)
Today I'll focus on the "non-configurable window object properties" group.
But first - how does scuttling even work?
For starters, we need to understand what to scuttle.
To do that, we'll collect all the properties that are accessible via the window.
That must include own properties of the window itself, but also own properties of every one of the prototypes in its prototype chain:
Once we have that list, it's time to scuttle!
Pretty simple, we just iterate the props we collected, and redefine their getters to deny access to their real values, and instead return "undefined" (for the sake of the example).
But as you can see, the scuttling attempt fails...
In JS (by legacy), some props of the window object are non-configurable by definition (we're not gonna get into why).
This means that some properties aren't going to cave in under scuttling - ever.
Question is - which ones? And how useful are they going to be for attackers?
These are the 8 props that are non-configurable in the browser, and cannot be redefined whatsoever.
* Infinity, NaN, undefined - are primitive values and therefore are not very powerful for attackers ๐
* window, top - refer back to the window object, which we already scuttle ๐
* chrome - very powerful for attackers (will elaborate on another thread one day)
Luckily, even though is not configurable, it is writable, meaning we can't redefine its getter, but we can redefine its value to be undefined ๐
Which leaves us with only two: document & location.
And unfortunately, if code escapes LavaMoat sandbox and gets access to either one - it can be pretty bad.
Today, we'll focus on document access, why it's dangerous, and how we use our very own Snow JS โ๏ธ to reduce that risk.
document access is dangerous for obvious reasons:
DOM access allows attackers to intercept the interaction users have with the app. They can change its layout deceitfully, thus tricking the user to take actions that might compromise its account or its private key.
That's bad, but mitigating that isn't our top priority, because:
1. Fully sandboxing the DOM was proven before as practically impossible.
Projects have tried achieving {parts of} that before (e.g. CaJa) and have learned how tangled up the DOM really is.
It'll take some time before we decide to retake on that challenge.
But most importantly:
2. We're less worried of DOM interception, as it ultimately requires UI (=user interaction) to compromise MetaMask.
We're far more worried about powerful JS capabilities the window object grants, with which attackers can breach MetaMask without any UI - which is why we scuttle!
So... what's so bad about it?
If that's the "obvious" and "less prioritized" reason we're worried about DOM access - what's the less obvious, more dangerous reason?
Short answer? iframes.
Long(er) answer?
iframes introduce new realms, which in the browser translate to...
... New windows! Meaning, more windows to scuttle! ๐ฑ
So in reality, assuming attackers escaped LavaMoat sandbox, bypassing scuttling is simple - just create a new iframe and steal the scuttled props from its window!
And what's the single prop needed to create a new iframe? ๐
Does that make scuttling useless? KINDA.
Does that make my last tweet a lie? Am I just a big phoney?
Well, not quite. By integrating scuttling with Snow โ๏ธ, we're eliminating the threat, but in a different, also advanced way.
Getting 2 long though, so wait for nxt week's ๐งต ๐
There it is, part 3