Full Report
Metamask is a popular crypto wallet in the web browser. Even if you're not using it to store your funds, it's likely interacting with your hardware wallet. Obviously, having a safe crypto wallet is a must. Metamask supports snaps, which are modules built to extend the functionality of Metamask. This is ran in a sandboxed environment with very serious permission boundaries. Metamask warns users of each permission that the snap wants, putting the burden of security onto the user in this case. The sandbox is composed of three parts: iframe, LavaMoat and SES sandbox. Browser iFrames are a well known way to isolate the risk of code. In this case, Metamask has written an API that allows the iFrame to communicate with Metamask to perform various actions. Lavamoat is a policy mechanism that limits the permissions heavily that a given snap can run. To prevent supply chain attacks there are limitations on which packages can interact with the Metamask post message API. The final layer of protection is Secure EcmaScript (SES) sandbox. The first part of this locks the JavaScript builtins to prevent prototype pollution bugs and removes sensitive info from some functions and objects. The SES has compartments to force the globalThis variable to only be available for secure functions. With all of these protections in mind, they set out to try to break the security model. When processing an incoming call from the sandbox, much validation is done on this. However, we can do some schengians with JSON objects to cause problems! Using the Metamask iFrame API, we can overwrite a call to toJSON() with our malicious content. Since this function is used later in the process, we pull the ol' switcheroo on the running code! The impact of this is quite severe. The promised validation and permissions model has been broken. A prompt to sign a malicious transaction can be done from the snap, even if the permissions say that it can't. If you're reading this and are confused then go read the proof of concept in the post. This was helpful for seeing what's going on. Overall, a good breakdown of the Metamask snap security model. Even with this, arbitrary transactions cannot be ran, since it requires users to sign off on it.
Analysis Summary
# Vulnerability: Property Spoofing in MetaMask Snaps API Communication
## CVE Details
- CVE ID: Not explicitly provided in the article.
- CVSS Score: Not explicitly provided in the article.
- CWE: CWE-20 (Improper Input Validation) or CWE-829 (Improper Restriction of Delegation) due to manipulation of object serialization/deserialization leading to privilege misuse.
## Affected Systems
- Products: MetaMask Snaps execution environment.
- Versions: Versions of MetaMask affected prior to the patch referenced (Commit `168ff08`).
- Configurations: Snaps utilizing the iFrame API for communication with the main MetaMask process.
## Vulnerability Description
The vulnerability resides in the validation step for incoming RPC requests originating from a MetaMask Snap communicated via the iFrame API. A malicious snap could exploit the timing between JSON sanitization (`getSafeJson`) and final argument validation (`assertEthereumOutboundRequest`). By defining a custom `toJSON()` function on the arguments object sent from the snap, the attacker could cause the object to serialize into malicious content *after* preliminary safe JSON processing but *before* the final, stricter assertion check. Specifically, the attack involved setting the initial method (e.g., `snap_dialog`) but using a malicious `toJSON` implementation to secretly transform the request parameters into a call intended for `eth_sendTransaction`, effectively bypassing intended permission checks. This allowed a snap to prompt the user to sign a malicious transaction even if its permissions should have prevented such an action.
## Exploitation
- Status: PoC available (The article describes the necessary steps and provides a snippet of the proof-of-concept code).
- Complexity: Low/Medium (Requires understanding the communication flow and timing attack vector related to object serialization).
- Attack Vector: Adjacent (Interaction via an installed, potentially malicious Snap).
## Impact
- Confidentiality: Low/Medium (Depends on data exposed via the successfully spoofed transaction/request).
- Integrity: High (Allows unauthorized actions, specifically tricking the user into signing an arbitrary transaction, bypassing permission controls).
- Availability: Low (No direct impact on service availability).
## Remediation
### Patches
- The issue was mitigated by reordering validation steps. The patch asserts the arguments *after* they have been safely serialized/sanitized by `getSafeJson`.
- Patch Reference: Commit `168ff082102a65e2aad428f44c5b10f9a100c689` in the MetaMask Snaps repository.
- The patched logic ensures: `const sanitizedArgs = getSafeJson(args) as RequestArguments; assertEthereumOutboundRequest(sanitizedArgs);`
### Workarounds
- Do not install or use Snaps from untrusted or unverified sources until your MetaMask version is updated. (General security hygiene recommended when dealing with sandboxed extensions).
## Detection
- Indicators of Compromise: Unusual initiation of transaction signing prompts not directly attributed to user-initiated actions within the main MetaMask interface or known trusted Snap functions.
- Detection Methods and Tools: Monitoring RPC requests originating from the Snap execution context for arguments whose structure changes significantly between initial parsing and final validation stages (though this is difficult without access to the underlying implementation details).
## References
- Vendor Advisories: Discussed within the referenced blog post detailing the vulnerability discovery.
- Relevant Links:
- osec dot io slash blog slash 2023 slash 11 slash 01 slash metamask-snaps slash