Impact
A malicious or compromised NWC client with a valid NWC connection can spend more than the configured daily spending limit due to:
- non-atomic budget check/update under concurrent request handling, and
- satoshi truncation of millisatoshi values during budget accounting
Context
- Electrum has support for external and internal (built-in) plugins.
- Nostr Wallet Connect is a built-in plugin. Plugins are default disabled.
- The NWC plugin runs an NWC server that can initiate lightning payments on behalf of the user.
- The plugin can be configured with a maximum daily spending limit (
daily_limit_sat).
Note that depending on the usecase, the NWC client can be conceptually either trusted or untrusted.
Example usecases:
- running an open-source nostr client such as Amethyst, and using NWC to tip social media posts
- Amethyst is ~trusted here, and is unlikely to attack the NWC server
- using a paid service such as ppq.ai that bills the NWC server on-demand
- ppq.ai is considered an untrusted adversarial entity in this context, so an attack is in-scope
Affected version
- the NWC plugin was added in version 4.6.0
- Electrum 4.7.2 contains the fix, all prior versions are affected
First bug (race bypass)
- User configures
daily_limit_sat=1000.
- Malicious NWC client prepares two 1000-sat invoices.
- Malicious NWC client sends two
pay_invoice requests concurrently on the same connection.
- Both may pass
budget_allows_spend() before either call to add_to_budget(), resulting in 2000 sat total spend.
Second bug (msat truncation bypass)
- User creates an NWC connection with a small limit, e.g.
daily_limit_sat=1.
- Pay a zero-amount invoice via NWC using
amount=1999 (msat).
- Budget logic accounts
invoice.get_amount_sat() as 1 sat, while spend is 1.999 sat.
- Repeating this exceeds intended sat-limit policy.
Relevant code snippets
- Concurrent request task spawning: https://github.com/spesmilo/electrum/blob/ff44d4b4d16d420efe6202634eb1a42292905270/electrum/plugins/nwc/nwcserver.py#L350-L370
- Budget check then payment then budget update (non-atomic): https://github.com/spesmilo/electrum/blob/ff44d4b4d16d420efe6202634eb1a42292905270/electrum/plugins/nwc/nwcserver.py#L835-L853
- Budget allow logic: https://github.com/spesmilo/electrum/blob/ff44d4b4d16d420efe6202634eb1a42292905270/electrum/plugins/nwc/nwcserver.py#L892-L899
- Amount conversion truncates msat->sat (floor): https://github.com/spesmilo/electrum/blob/ff44d4b4d16d420efe6202634eb1a42292905270/electrum/invoices.py#L175-L183
Patches
Electrum 4.7.2 fully fixes these.
The race bypass bug is fixed in: 5222374
The msat truncation is fixed in: 3956bff
Timeline
- 2026-03-02: private disclosure received
- 2026-03-03: fixes implemented, reviewed, merged
- 2026-04-02: version 4.7.2 released that includes the fixes
- 2026-04-28: public disclosure
Credits
We thank maru1009 for finding and responsibly disclosing these issues to us, nicely written up in an easy to understand way.
Impact
A malicious or compromised NWC client with a valid NWC connection can spend more than the configured daily spending limit due to:
Context
daily_limit_sat).Note that depending on the usecase, the NWC client can be conceptually either trusted or untrusted.
Example usecases:
Affected version
First bug (race bypass)
daily_limit_sat=1000.pay_invoicerequests concurrently on the same connection.budget_allows_spend()before either call toadd_to_budget(), resulting in 2000 sat total spend.Second bug (msat truncation bypass)
daily_limit_sat=1.amount=1999(msat).invoice.get_amount_sat()as 1 sat, while spend is 1.999 sat.Relevant code snippets
Patches
Electrum 4.7.2 fully fixes these.
The race bypass bug is fixed in: 5222374
The msat truncation is fixed in: 3956bff
Timeline
Credits
We thank maru1009 for finding and responsibly disclosing these issues to us, nicely written up in an easy to understand way.