Skip to content

Vulnerability Disclosure: Assertion DoS in Core Lightning

Vulnerability Disclosure: Assertion DoS in Core Lightning

Summary

During my Summer of Bitcoin 2025 internship, I discovered a Denial-of-Service (DoS) vulnerability in Core Lightning (CLN) that allowed a remote peer to crash a node by sending a specifically crafted message. The issue was caused by an overly strict assertion in openingd’s logic.

This vulnerability has been patched in Core Lightning release v26.04. All node operators are advised to upgrade accordingly.

Background

Core Lightning (CLN) uses a multi-daemon architecture. When a peer connects and attempts to open a channel, a specific sub-daemon (openingd) is spawned to handle the channel-opening process. This architecture is designed to isolate faults. If a crash occurs somewhere, it shouldn’t take down the whole node.

However, certain crashes, if they occur in critical paths, can lead to a node-wide crash.

Discovery

This vulnerability was discovered using a new fuzz target I developed, fuzz-open_channel. The target tries to do one of these two things at random: create an open_channel message with the following properties:

  • Structural Validity: Messages should pass the initial wire-protocol checks (length, type).

  • Semantic Randomness: Randomizes the actual content (feature bits, channel flags, TLVs) using fuzzer’s data to test edge-case logic deep within the target’s state machine.

Or create an open_channel message that is completely random (use the fuzzer’s data as the message, straight up). The target then feeds this message to the parser for open_channel messages: fundee_channel(). When hit with a specific message type, the parser raises a SIGABRT signal, which is subsequently caught and flagged by AddressSanitizer.

For more details about the target, see the correspoding Pull Request.

Vulnerability

The HSMD in CLN expects a valid funding_txid within the funding_created message during the channel establishment. handle_setup_channel() within hsmd/libhsmd.c performs an assertion check: !mem_is_zero(&funding_txid, sizeof(funding_txid)). This assertion fails when the funding_txid is all zeros, as provided by the malicious message.

The assertion was intended to guarantee a specific state transition was impossible, but the fuzzer managed to manipulate the message in a way that the original developers assumed was unreachable. The assert() failed, and the daemon called abort(), killing the process.

Verification

An attack program was created to verify the vulnerability. The program acts as a malicious Lightning Network peer that connects to a CLN node and orchestrates a specific message sequence to trigger the crash:

  • Initialization and Connection: The attacker connects to the victim CLN node.

  • Initial Handshake: The attacker completes the init message handshake with the victim.

  • Channel Open and Accept: The attacker completes the exchange of open_channel and accept_channel messages, using standard acceptable parameters.

  • The Malicious Payload:

    • The attacker sends a funding_created message with the funding_txid zeroed out.
    • CLN receives the message and sends the funding_txid to hsmd.
    • hsmd crashes here due to the assertion failure.
    • lightningd detects that hsmd is offline and shuts down.

Criticality

  • Severity: Medium/High (DoS)

  • Attack Vector: Remote (Peer-to-Peer)

  • Consequence: A remote attacker can reliably crash any CLN node that accepts channels initiated by other nodes.

Fix

By the time this vulnerability was discovered, Rusty was working on another issue (a crash when you configure a to-self-delay of 0) that incidentally fixed this issue as well. More or less, the fix was to get rid of the assertion behind the crash. More details can be accessed at the corresponding Pull Request.

Timeline

  • 11/07/2025: Target pushed upstream.

  • 12/07/2025-24/07/2025: Improvements made to the target, dealing with false positives.

  • 24/07/2025: Vulnerability confirmed by Matt using the attack program.

  • 25/07/2025: Ran the attack program locally, draft of the vulnerability report created.

  • 28/07/2025: Vulnerability report sent to CLN’s security mailing list.

  • 04/08/2025: Reply by Rusty confirming the issue and disclosing the supposed fix.

  • 07/08/2025: Fix merged to master.

Lessons Learned

This bug highlights the power of fuzzing for finding “Unknown Unknowns.” No human developer wrote a test case for this specific state combination because “it shouldn’t happen.” The fuzzer, having no knowledge of what should happen, simply tried it and proved that it could happen.

Special thanks to my mentor Matt Morehouse for guiding the entire process, especially the triage and disclosure.