# `@bsv/wallet-toolbox` / `-client` / `-mobile` don't verify storage-supplied recipient output scripts against caller-requested outputs in createAction

BSV wallet-toolbox signs recipient lockingScripts returned by remote storage without comparing them to caller-requested outputs, letting a malicious storage operator redirect payments.

- Payout: Research
- Severity: high
- Program: BSV wallet-toolbox
- Found by: echennells
- Disclosed: 2026-09-24
- CVE: CVE-2026-56744
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-36f9-7rg5-cpf8
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-36f9-7rg5-cpf8
- Tags: area:web3 domain:web impact:funds-loss impact:integrity kind:research platform:ghsa sev:high tech:nodejs technique:source-review vuln:access-control vuln:business-logic

**Target:** createAction / buildSignableTransaction in @bsv/wallet-toolbox with remote StorageClient
**Root cause:** buildSignableTransaction takes each non-change output's lockingScript from the storage server response and signs it without comparing to args.outputs; WalletPermissionsManager.createAction only reads tx.inputs and getFee(), never tx.outputs.
**Steps:**
1. Configure wallet with a malicious remote StorageClient (BRC-103 AuthFetch) as active storage provider.
2. Call createAction with args.outputs specifying the intended recipient lockingScript.
3. Storage server returns a response whose non-change output lockingScript is a substituted attacker address.
4. buildSignableTransaction signs the storage-supplied script; signAction/completeSignedTransaction broadcast it.
5. Wallet UI still displays the originally requested recipient; funds go to the substituted address.
**How found:** Code review of the signing path: noticed buildSignableTransaction sources lockingScript from the storage response and that WalletPermissionsManager.createAction parses the tx but only uses inputs and getFee(), never tx.outputs. Confirmed by running a storage server returning a substituted output against an unmodified yours-wallet build.
**Exploitation:** Storage operator (or whoever compromises it) stores a substituted recipient script from the start; the wallet builds, signs and broadcasts a payment to the attacker address while the app shows the requested recipient. A response-only MITM is rejected because StorageServer.processAction compares signed rawTx outputs to stored data.
**Insight:** When a wallet delegates tx construction to a remote service, audit every field the signer consumes against the caller's original request — especially output scripts — and check whether the permission layer parses the built tx but ignores outputs.

