By now you might have heard of a few different bugs (,) with LND that has left the Lightning Network temporarily crippled and most nodes in a state where funds can be stolen from them. This has occurred twice within a month and is due to LND's reliance on a library that also pretends to be a bitcoin node and is seldom looked at or maintained, called BTCD (,).
Luckily the fix was released within a day, both times. So most people would not lose funds if they updated promptly, though there are still some HTLC edge cases that exist that would allow an attacker to take off with funds within even a few hours. If you've been in a coma and you're just now hearing about this, I'm sorry. The responsible thing to do is to close your channels before you go into a coma.
About a month ago, a few friends and I released a tool called LNsploit. LNsploit's sole purpose is testing out and finding vulnerabilities that exist on Lightning. There's a lot of work that's planned to go into it, but the initial proof of concept was demoed during a TABConf workshop that I believe was a great success.
Initially, there was not enough time to build any actual exploits into LNsploit because most of the time was spent making the Text User Interface and building in Lightning Network functionality by building multiple nodes into it with LDK. It was the Sunday before TABConf and I get a text from benthecarman that LND is broken. One of the first things I ask is if it's reproducible to put into LNsploit and then we get to work. I spent about 24 hours with the help of Ben trying to figure out how taproot works, how to create a large 998/999 multisig transaction, etc.
I admit, I have sat through entire lectures at Austin Bitdevs about Taproot but I just didn't get more than the surface level. Implementing Taproot script was a thing of its own. After almost giving up, Ben spends about an hour taking a last stab at it and gets it in there, we now have our first exploit!
Not only that, but instead of partying all TABConf, a few devs including Ben and I burn the midnight oil in the hotel lobby to get in another attack - broadcasting an old channel state transaction. When you combine this with the LND bug, you can successfully steal money from an LND node that does not update in time. I was able to demonstrate that during the TABConf workshop and many attendees were able to follow along to completion (minus a few code bugs that prevented a few of them, this is still a work in progress).
Fast forward to now, Ben was able to get in the new LND bug into LNsploit much easier. Many thanks to him, LNsploit would just be called
Now on to the actual attack! Before I demonstrate how to steal sats from an unpatched LND node, let me go over a few things.
- The damage was already done on testnet/mainnet and broadcasting another transaction similar to the one that caused the LND bug has almost no additional gain. This demonstration is only useful for regtest. The new <v0.15.3 bug in LNsploit will only work if you're connected to a regtest bitcoind node that can mine transactions.
- There's a risk in broadcasting an old channel state. You may lose all funds in that channel if the other node is capable of broadcasting the justice transaction at any time before the timelock. Be aware of what you're doing.
- Please get permission from affected nodes before you attack them on a live network.
- If you find a new exploit with LNsploit, please consider responsible disclosure to the affected software vendors or node runners.
Let's demonstrate how to combine the LND bug attack with a previous commitment transaction to effectively steal funds from an LND node!
There are a few prerequisites for running the software and regtest network.
- Go to https://www.nakamoto.codes/BitcoinDevShop/LNsploit and clone the repo.
- Install Docker for your computer if you don't have it already.
- Install https://lightningpolar.com if you don't have it already.
- Install Rust https://www.rust-lang.org/tools/install if you don't have it already.
Polar Network Setup
- Open Polar and click on "Create Network".
- 0 LND, 0 CLN, 0 eclair, 1 bitcoind - click create
- In "Network Designer", scroll down to check for new versions
- Drag 1 "LND v0.15.2-beta" node onto the screen
- Drag 1 "LND v0.15.1-beta" node onto the screen
- Click Start
- When the nodes have a green dot, drag a line from Alice to bob (open channel)
- In the lnsploit directory, run
cp config.yaml local.config.yamlto copy a version of the config
- Use a text editor to change the values to match what your polar instance has
- Click on the bitcoind node and go to the "connect" tab.
- Copy / double-check the "RPC Host" / "Username" / "Password" matches
- Run LNsploit with
cargo run local.config.yaml
- (Optional) in another terminal window, run
tail -f .logs.txtto see more detailed info on what's going on.
- Create a new node by clicking the capital
shift+nat the same time) on your keyboard.
- You can click this many times if you want, it creates a new lightning node each time.
- Go to the nodes screen by pressing capital
enterto select the new node you created.
- You may press the arrow key up or down to cycle through nodes/actions.
Connect to Peers
- With the node view screen selected, press
enterwhile selecting the "Connect Peer" action.
- In Polar, click on the "Alice" node, go to the "Connect" tab, and copy the "P2P External" string.
- In LNsploit, click
ctrl+vat the same time to paste in the string.
- Click the
enterkey to connect to the peer.
ESCto go back to the node view screen.
- Do this again for the "Bob" node.
- With the node view screen selected, press
enterwhile selecting the "Open Channel" action.
enteron the first node to open a 100k sat channel with the node.
- Press the
downarrow key to go to the next node and press
enteron them too.
- In Polar, mine 6 blocks to confirm the channels.
- Click on the bitcoind node.
- Go to the Actions tab.
- Make sure 6 is typed into the first input box and then click
Pay Invoice from each Alice and Bob
Spend down our channel some so we have an outdated channel state that we can broadcast later.
- In Polar, create an invoice with the "Alice" node by right-clicking Alice and clicking "Create Invoice" again when the pop-up comes up. Copy the invoice and close the box.
- In LNsploit, in the node view screen, press
enteron the Pay action.
ctrl+vto paste the invoice and then hit
enterto pay the invoice.
- Do the same thing for the "Bob" node.
Broadcast large transaction
Let's break the v0.15.1 LND node by broadcasting one of the large transactions that broke them on mainnet. For this test, we can demonstrate that the v0.15.1 node breaks but the v0.15.2 node does not.
- (Optional) In Polar, Open the Alice and Bob node terminals (right-click on the nodes and click launch terminal).
tail -f home/lnd/.lnd/logs/bitcoin/regtest/lnd.logto monitor LND logs.
- In LNsploit, press the
ESCkey until you are back at the home menu screen.
- Go down to the "Exploit" action and press
enteron the "Break LND <= v0.15.1" action.
- This broadcasts a transaction but you should not mine a block yet!
- (Optional) check the logs of the two LND nodes. Bob has an error processing a mempool tx, and Alice does not have an error.
Broadcast previous channel state
Let's broadcast the previous channel states that reflect a better balance for us from before we paid the invoice.
- In LNsploit, Press
ESCuntil we are back at the menu screen.
shift+Lto go to the node screen.
enteron your node.
enteron the "Broadcast revoked commitment transaction".
enteron both of the channels.
- (Optionally) Check the lnsploit logs to see the transaction that we broadcast, reflecting a ~100k sat amount.
- In Polar, mine 1 block. This block should have the large script tx and the 2 revocation txs.
- With Alice's logs, check that she thinks "someone is doing something sketchy".
- With Bob's logs, check that he does not think "someone is doing something sketchy". He should be unable to process the block, reproducing the LND bug. In Polar, Bob's info shows that he is not in sync.
- In Polar, mine 1 block.
- With Alice's logs, check that she thinks "justice has been served".
- With Bob's logs, check that he is still having a problem processing the block.
Congratulations! After enough confirmations, your LDK node should be able to process the revocation transaction and redeem the time-locked funds. Unless Bob updates to the latest LND version, he will not be able to broadcast a justice transaction given that he doesn't even know the commitment transaction was in the bad block.
Break LND v0.15.2
To demonstrate that LNsploit can now break v0.15.3 and under, now let's break the Alice node. If you want to go through the effort of creating a new channel with Alice and doing this all over again, you can go ahead. But for simplicity, I'm just going to mine a block with a transaction that recently broke LND again, to demonstrate that it works.
- In LNsploit, Go back to the exploit screen
- Press the arrow down key to highlight "Break LND <=0.15.3" and hit enter
- Go view the Alice logs to see that she is now unable to process a block, just like Bob was unable to before.
Now you just broke Alice too! If Polar had the recent version of LND v0.15.4, you could play around with that to verify that it is not affected by this latest bug.
Keep playing around
Some Ideas of what you can test while you are here:
- Can you keep paying invoices between Alice and Bob?
- Can you spin up a new node and open a channel to Bob? Can Bob open a channel to them?
- What happens when you restart Bob? Can you create or pay invoices?
- What happens when you mine thousands of blocks? Does LNsploit eventually redeem the time-locked transaction?
- Could you have successfully broadcasted a revocation transaction without it being in the same block as the large script tx? Would bob have processed the proceeding blocks and broadcast the justice transaction?
- How does this LND bug behave differently when running without the bitcoind zeromq connections? Does it still process consecutive blocks?
At this point, you've successfully broken a few different versions of LND with LNsploit and you've even been able to steal funds! It is too late to utilize this now on testnet / mainnet (actually, I believe the latest bug hasn't been executed on testnet yet!). If you would have used this tool to coordinate with Burak while he broadcasted his breaking transactions, you could have possibly stolen funds on mainnet too. However, you would have run into the risk that the node updated in time before the channel timelock expires. LNsploit does not yet broadcast transactions with a held HTLC payment still in flight, which would allow you to steal funds in hours rather than the ~2-week channel timelocks.
I applaud the efforts of all devs, breakers, and lightning community members throughout the last month. Lightning is NOT operating in an adversarial environment and if we're going to be serious about this $100M+ network that this entire industry is building upon, then we NEED things like this to happen to grow, learn, adapt, and be resilient. I appreciate the way Burak has gone about this and, to my knowledge, without trying to steal funds, especially knowing that LNsploit existed since we chatted about this at TABConf. Hopefully, I can continue to work on LNsploit during my free time and push the limits of Lightning myself. I feel the moral obligation to. If we don't do it, and lightning developers don't consider it, then someone with malicious intent will.
Thanks again to Ben Carman, Paul Miller, and w3irdrobot for thier help in LNsploit. If you would like to contribute, please find the code repo here: https://www.nakamoto.codes/BitcoinDevShop/LNsploit