Status DataClose notification

Coin98 Disclosed Report

Bug bounty report Coin98

Keplr/EVM Provider Hijack via localStorage["override"] Poisoning

Company
Created date
Mar 23 2026

Target

https://chrome.coin98.com

Vulnerability Details

The Coin98 Chrome Extension protects users by injecting non-writable wallet provider objects (like window.keplr and window.ethereum) into every webpage. This prevents malicious scripts from replacing the wallet with a fake one to steal funds.

However, the extension's core content script (takePlaces.f90f5315.js) contains a critical logic flaw: before it injects these secure objects, it reads a key called override from the website's own localStorage. If this key is set to enable: false for a specific provider, the extension skips its security protection entirely.

Since localStorage is completely controlled by the website (or any script running on it), a malicious DApp can "poison" this key on a user's first visit. On all subsequent visits, the extension will fail to inject its secure provider, allowing the attacker to inject a fake wallet that intercepts and modifies the user's transaction signing requests.

how do you reproduce it?

Prepare the PoC: Host the provided poc_coin98_keplr_hijack.html(atatched below in poc.md) on a local server (e.g., python3 -m http.server 8080).

  1. Phase 1 (Poison): Visit the page (http://localhost:8080/poc_coin98_keplr_hijack.html). The script will immediately execute localStorage.setItem("override", '[{"key":"keplr","enable":false}]').
  2. Phase 2 (Bypass): Reload the page. The Coin98 content script reads the poisoned override at document_start and decides not to inject window.keplr.
  3. Phase 3 (Exploit): Because the namespace is now empty, the PoC script defines its own window.keplr.
  4. Phase 4 (Impact): Click the simulation triggers. Observe that when the DApp (the PoC) calls keplr.getOfflineSigner(), it receives an attacker-controlled address, and the signAmino()call is intercepted by the malicious script.

Validation steps

  1. Control Group Verification The provided PoC includes a Control Phase. When first loaded (or after a reset), it confirms that window.ethereum.isCoin98 AND window.keplr.isCoin98 are both true. This proves the extension is installed and its security features are normally active.

  2. Bypass Verification After poisoning localStorage and reloading, the PoC check shows:

  • window.ethereum.isCoin98: true (Proving the extension is still active)
  • window.keplr present: false (Proving the bypass was successful and specifically targeted)
  1. Exploitation Evidence The console logs clearly show the transition from extension-protected state to attacker-controlled state.
  • Log Proof: [HIJACK] signAmino() INTERCEPTED — signer: cosmos1ATTACKER...
  • Logical Chain: The fact that any website can modify localStorage to disable an extension's security injection is a fundamental business logic failure. The extension should never trust page-level storage to decide whether to provide its own security features.

Attachments

hidden
CommentsReport History
Comments on this report are hidden
Details
Statedisclosed
Severity
None
Bounty$0
Visibilitypartially
VulnerabilityBusiness Logic Errors
Participants (3)
manager
author
triage team