LNbits Attack - My story
How an LNbits server was used to drain a Lightning node
- Summary
- Setup that was attacked
- Attack chain
- Signs of compromise to look for
- Why it worked
- If you run LNbits, do this now
- If you think you’re compromised
Summary
An attacker went from a free LNbits account to full control of the
Lightning node behind it in about eight hours. They abused LNbits’
own features and one extension bug to install a malicious extension,
which read the node’s LND admin macaroon and tried to close every
channel to the attacker’s address and sweep the on-chain wallet.
Fast force-closes by the operator limited the loss to a single channel.
Setup that was attacked
- LNbits 1.5.4 in Docker, exposed to the internet through a Cloudflare Tunnel
- Funding source: LND REST on the local network
- The container mounted the node’s whole LND folder, including admin.macaroon
- Open sign-up and “login by user ID” were both enabled
Attack chain
- Accounts. The attacker registered dozens of accounts, many with
admin-style usernames (admin, root, lnbitsadmin, operator, the
operator’s own handles) to find real ones, and sent thousands of
password guesses. None got into an admin account, and nothing
rate-limited them. - Probing extensions. They enabled every available extension on
their own accounts and tested each for a way in, including making
the server send requests to its own internal addresses (SSRF). - Reading server files. A flaw in the Paywall extension’s
“file” download let a normal user read files inside the LNbits
container, unlocked with a 1-sat payment. - Super user. With what that file read exposed, they made
authenticated super-user requests, even though LNbits blocks
super-user login by user ID alone. - Hosting the payload on the victim. As a normal user they uploaded
a malicious extension zip (labelled as an image) and a fake extension
catalog as LNbits “assets”. - Installing it. Using super-user settings, they added the asset’s
local URL as a trusted extension catalog and installed the extension
from it. - Taking the node. The extension ran as soon as it was installed:
- read admin.macaroon from the mounted LND folder
- listed channels and requested co-op closes to the attacker’s address
- started a loop that tried every 3 minutes to sweep the whole
on-chain balance - opened a remote shell endpoint, which the attacker used to fix
bugs in their own sweeper and drop helper scripts
LND rejected the attacker’s force-close requests, and their first
sweeper used the wrong API endpoint. The patched sweeper only sent
confirmed funds, and none were confirmed before the container was
stopped.
Signs of compromise to look for
- An unknown extension in data/extensions/ or data/upgrades/
(this one was named pwnNNNN) lnbits_extensions_manifestscontaining a URL pointing at
127.0.0.1 or /api/v1/assets/- Assets with mismatched types (a .zip or .json stored as image/png)
- Bursts of new accounts with admin-like names, or mass failed logins
- Paywalls of type “file” you didn’t create
- Python scripts or JSON logs you don’t recognise in the data folder
- Lots of 1-sat test payments with odd memos
- Forwarded IPs in reserved ranges (198.51.100.x, 203.0.113.x):
the attacker was forging proxy headers
Why it worked
- Public LNbits with open sign-up and login by user ID
- An extension bug that let a normal user read files on the server
- LNbits’ signing secrets stored where that bug could read them
- Super users can install code from any catalog (by design)
- LNbits held a full-permission admin macaroon. That one fact turned
an LNbits break-in into a node takeover. - No login rate limiting
If you run LNbits, do this now
- Turn off open sign-up and “user-id-only” login if you don’t need them
- Turn off extensions you don’t use, especially ones that fetch URLs
(Paywall file downloads, Watchonly) - Never give LNbits admin.macaroon. Bake one with only the permissions
it needs (invoices and Lightning payments; no channel closing, no
on-chain sends) and mount that file alone, not the whole LND folder. - Put /admin behind an access gate (Cloudflare Access, VPN, IP allowlist)
- Rate-limit /api/v1/auth at the proxy
- Check installed extensions and the manifest list for anything you
didn’t add - Keep LNbits and its extensions up to date
If you think you’re compromised
- Stop LNbits and set its restart policy to “no”. Malicious extensions
run as soon as they load, and LNbits may reinstall a deleted one at
startup from its database. - Copy the data folder and container logs before changing anything.
- On LND: delete the macaroon root keys and generate new macaroons, so
stolen ones stop working. - Force-close channels you don’t trust, and move all funds to a new seed.
- Don’t run the old LNbits install again. Start fresh with a new data
folder and new secrets. - Pay back any custodial balances you hold for other users.
Write a comment