HexBrain

Monero · Report #3648638

monero:// deeplink parsing accepts tx_amount=(all) and can trigger send-all transaction mode

Validstatus
mediumseverity
21upvotes
20 Aug 2026disclosed

How it was found

Summary

Monero GUI has two different URI ingestion paths.

QR/manual URI handling uses the canonical backend parser, but external monero:// deeplinks are parsed by a separate QML routine in main.qml. That deeplink path forwards the raw tx_amount string into the transfer model without canonical numeric validation.

The transfer model reserves the literal string "(all)" as a privileged internal control value meaning "send all unlocked balance". Because the external deeplink path does not reject that sentinel, an attacker can supply a crafted URI such as:

monero://<attacker_address>?tx_amount=(all)&tx_description=test

and the wallet will enter the send-all transaction path for the attacker-controlled address.

Releases Affected

Observed in the current Monero GUI source checkout corresponding to 0.18.4.6.

Affected code paths:

  • main.qml:447-478
  • pages/Transfer.qml:95-105
  • pages/Transfer.qml:203-212
  • main.qml:964-980

Comparison path showing the stricter canonical parser:

  • src/libwalletqt/WalletManager.cpp:406-423

Steps To Reproduce

  1. Inspect main.qml:447-478. onUriHandler(uri) manually parses monero:// query parameters and forwards raw params["tx_amount"] into middlePanel.transferView.sendTo(...).

  2. Inspect pages/Transfer.qml:95-105. fillPaymentDetails(...) inserts the attacker-controlled amount into recipientModel via: recipientModel.newRecipient(address, Utils.removeTrailingZeros(amount || "")) There is no numeric validation here.

  3. Inspect pages/Transfer.qml:203-212. recipientModel.getAmountTotal() treats the exact literal "(all)" as a special internal value and returns the wallet's unlocked balance.

  4. Inspect main.qml:964-980. handlePayment() checks whether any recipient amount equals "(all)". If so, it routes the request into: currentWallet.createTransactionAllAsync(...) instead of the normal numeric transaction creation path.

  5. Compare this with src/libwalletqt/WalletManager.cpp:406-423. The canonical URI parser uses a typed uint64_t amount and converts it back to display text, which is materially stricter and does not expose the UI-only "(all)" sentinel as an external amount value.

  6. Using those code paths, a deeplink like the following is sufficient to reach the send-all branch condition: monero://<attacker_address>?tx_amount=(all)&tx_description=test

Observed Result

Attacker-controlled external deeplink input can reach the transaction flow as the reserved internal sentinel "(all)", causing Monero GUI to prepare a send-all transfer to the attacker-controlled address.

Expected Secure Behavior

External payment-request URIs should only accept canonical numeric amounts parsed by the backend URI parser. Reserved UI control values such as "(all)" should never be reachable from untrusted external input.

Supporting Material / References

Relevant code references:

  • main.qml:447-478
  • pages/Transfer.qml:95-105
  • pages/Transfer.qml:203-212
  • main.qml:964-980
  • src/libwalletqt/WalletManager.cpp:406-423

Validation scope:

  • authorized local source review
  • safe local reachability validation only
  • no real wallets
  • no real funds
  • no public infrastructure

AI Assistance Disclosure

AI was used to help organize and polish the report wording. The technical content is based on authorized local source review and safe local validation in my environment.

Impact

A malicious payment link can be transformed from a normal payment request into a send-all request to an attacker-controlled address.

This is a transaction-integrity issue in a financial wallet. The victim still has to interact with the UI and confirm the transaction, so this is not zero-click theft, but the bug lets an attacker control whether the request is interpreted as a normal amount or as "send all unlocked balance." That materially increases the effectiveness of phishing and payment-request social engineering.

This report was disclosed publicly on HackerOne and belongs to its author and the program. See the original