receiving with Lightning: partial MPP might be accepted

Impact

When using Lightning in Electrum to receive money, an attacker can get the client confused and accept a partial payment, getting the client to reveal the proof-of-payment preimage.

There are two related but independently exploitable bugs, each with a corresponding attack.

First bug: received MPP HTLCs are grouped by payment_secret

The attack requires the attacker to obtain two valid invoices from the victim. Each invoice contains a payment_hash and a payment_secret (as standard). The attacker can send multiple HTLCs to the victim, with criss-crossed payment_hashes and payment_secrets, confusing the victim into accepting one small HTLC, revealing the preimage, and failing the other HTLC(s).

Example attack:

  • Bob(victim) creates invoice1: amount 1 BTC, payment_hash1, payment_secret1; given to attacker

  • Bob(victim) creates invoice2: amount 1 BTC, payment_hash2, payment_secret2; given to attacker

  • Alice sends htlc1: 0.1 BTC, payment_hash1, payment_secret1 (total_msat=1 BTC)

  • Alice sends htlc2: 0.9 BTC, payment_hash2, payment_secret1 (total_msat=1 BTC)

  • Bob reveals preimage for payment_hash1 and fulfills htlc1 (fails other)

The root cause of the bug is that Bob groups the HTLCs for MPP based on payment_secret alone. (Normally you would want to group based on payment_hash, but that does not work if forwarding trampoline payments) After htlc2 arrives, Bob sees that enough money has been received (in htlc1+htlc2) and marks the HTLC MPP group as accepted. It is only after this, that Bob validates that the HTLCs have the expected payment_secret. Bob fulfills htlc1, revealing the preimage. When considering htlc2, it is noticed that the payment_secret does not correspond to the payment_hash, and htlc2 is failed.

Second bug: received MPP HTLCs might have non-uniform total_msat values

The attacker can send two small HTLCs to the victim, set the total_msat field inside the onion of the htlc for one of them maliciously, and get the victim to fulfill the other HTLC, revealing the preimage.

Example attack:

  • Bob(victim) creates invoice: amount 1 BTC

  • Alice sends htlc1: 0.1 BTC (total_msat=0.2 BTC)

  • Alice sends htlc2: 0.1 BTC (total_msat=1 BTC)

  • Bob reveals preimage and fulfills htlc2 (fails other)

The root cause of the bug is that when Bob groups the HTLCs for MPP, he does not validate that all HTLCs have the same total_msat value set (which is attacker-controlled). The logic that checks whether the group is complete uses the total_msat value from the first received HTLC. After receiving htlc2, the group gets marked as accepted. Then, before fulfilling an HTLC, Bob checks that their total_msat value matches the amount in the corresponding invoice. This check fails for htlc1, but passes for htlc2. Hence, Bob fails htlc1 and fulfills htlc2, revealing the preimage.

Affected versions

4.1.0 <= x < 4.4.6

Patches

The issue was fixed in commit [0], backported to the 4.4.x branch in commit [1], released in version 4.4.6.

The first bug was fixed by grouping the HTLCs based on payment_hash+payment_secret. The second bug was fixed by validating that all received HTLCs that are grouped together for MPP have the same total_msat value.

Have these vulnerabilities been exploited?

We have no knowledge of in-the-wild exploitation of these vulnerabilities.

How to know if I was affected?

In the History tab, for the incoming payment, the balance delta reflects the actual amount of money received. Normally the balance delta should match the amount in the invoice (payment request). If the bug was exploited, we would expect the balance delta (actual received money) to be significantly lower than the invoice amount.

References

[0]: https://github.com/spesmilo/electrum/commit/44bdd20ccc40bb307cc3510d8741af7058e2c6e8
[1]: https://github.com/spesmilo/electrum/commit/c5300c9f1c45002e13631251fc3a5575d3e43629
[2]: https://github.com/spesmilo/electrum/commit/e23c6c70501a1d4bc7bba34438ce7b4e84894191

Timeline

  • 2023-08-03: we discovered the vulnerabilities during review of related code.

  • 2023-08-08: an obfuscated fix was pushed to master [0]

  • 2023-08-17: the backported fix was pushed to the 4.4.x branch [1], and version 4.4.6 was tagged

  • 2023-08-22: version 4.4.6 released, containing the fix, and a notice was added in the release notes[2] about upcoming disclosure.

  • 2023-09-12: Public disclosure of the issue.

Known as