A Ledger Clear Signing bug let an affected device display one action while its signature authorized 257. A signer could approve what looked like a routine transaction without seeing the other actions covered by the signature.

We tested Safe, Aave, and Morpho Clear Signing flows. This essay examines the finding and its implications for hardware-wallet trust. The security advisory documents affected versions, mitigations, and coordinated disclosure.

Remediation status: Ledger Ethereum app 1.22.3 rejects the demonstrated flows. Users should update the Ethereum app separately from their device firmware. See the advisory for the affected-version matrix and verification guidance.

Signature overflow attacks on Ledger: one call displayed, 257 calls signed

1. The Threat Model

Ledger’s security premise is sound: the computer and transaction interface may be compromised. Its secure-display threat model therefore requires the device to preserve What You See Is What You Sign even when the host cannot be trusted.

For institutional Safe users, Ledger applies the same principle directly. Its Beyond the Quorum article identifies a compromised frontend as the threat and presents Clear Signing as an independent view of a transaction’s true intent.

Ledger Enterprise Multisig presents Clear Signing as the point where blind signing stops

Ledger Enterprise Multisig at app.multisig.ledger.com, captured on 2026-08-26. The product presents Clear Signing as the point where “blind signing stops” for Safe transactions.

Clear Signing therefore sits at the final security boundary. When browser JavaScript is compromised, the device must independently describe the authorization it is about to sign.

The February 2025 Bybit theft made the compromised-frontend threat concrete. Sygnia’s public findings describe malicious JavaScript injected into resources served from Safe’s AWS S3 bucket. The code manipulated the transaction initiated through Safe. Ledger’s response presented trusted hardware display and Clear Signing as the layer that should contain that failure mode.

LEDGER_CLEAR_SIGNING_BYPASS reaches the same threat model through a different failure mode: a compromised frontend supplies the payload, and device-side Clear Signing produces a human-readable description that omits part of the signed authorization.

2. The Parser Bug

The vulnerability was in the shared array machinery of Ledger’s Ethereum Generic Transaction Parser. The parser calculated an attacker-controlled array pass count as a 16-bit integer, then stored it in an 8-bit countdown:

uint16_t passes;
uint8_t passes_remaining[MAX_ARRAYS];

passes_remaining[index] = passes;

For a 257-element array, the narrowing conversion changes 257 to 1:

passes = 257
(uint8_t)257 = 1

The parser then combines the full-width count with the truncated countdown to select the first element it will review:

idx = start + (passes - passes_remaining)
idx = 0 + (257 - 1)
idx = 256

The same truncated value controls iteration. Instead of starting at element 0 and reviewing all 257 elements, the parser starts at element 256 and reviews one. Elements 0 through 255 remain in the signed calldata but never reach the trusted screen.

The behavior generalizes for any non-zero remainder:

array length = N = 256q + k, where 1 <= k <= 255
displayed    = final k elements = N mod 256
omitted      = first 256q elements = N - (N mod 256)

The hidden prefix can contain different actions, mostly no-op calls surrounding one consequential operation, or any other valid execution sequence that fits the device’s constraints. The demonstrated Safe payload uses q = 1 and k = 1: the screen reviews one call while the signature authorizes 257.

The original fix is commit 2c3d93cc — “Fix array iteration truncation”.

3. Signed Metadata

Clear Signing does not interpret every contract call generically. A transaction enters this parser path only when a host-side Context Module supplies matching, contract-specific metadata. That metadata tells the Ethereum app which intent, fields, labels, paths, and formatting to render.

These Ledger-authorized descriptors make supported protocols legible on the device. They also define the attack surface wherever they direct the vulnerable parser through an attacker-controlled array.

In our August 25 snapshot, Ledger’s production dapp descriptor service exposed array-backed CALLDATA fields for these transaction shapes:

Scroll horizontally to view all columns →

Descriptor creator Selector Array field Observed chains
Safe 0x1a833ee3 (batchExecute) Transaction (Call[]) Ethereum, Base, Optimism, Arbitrum, Polygon
Aave DAO 0xac9650d8 (multicall) Call (bytes[]) Ethereum, Base, Optimism, Arbitrum, Polygon, Linea
Morpho DAO 0x374f435d (multicall) Action (bytes[]) Ethereum, Base
Morpho DAO 0x803a7fba (reallocate) Action (bytes[]) Ethereum, Base

We demonstrated three paths through this surface:

Scroll horizontally to view all columns →

Flow Device displayed Signature also authorized Fork result
Safe Send 10 USDC Maximum USDC allowance for the attacker Safe accepted the authorization; the allowance was used to empty its remaining USDC balance
Aave Supply 10 USDC Withdraw the signer’s existing USDC supply position to the attacker The exact affected-app transaction executed
Morpho Supply 10 USDC Withdraw the signer’s existing USDC position to the attacker The exact affected-app transaction executed

Safe is the main worked example because its array elements carry arbitrary nested calls and its multi-owner treasury workflow makes the organizational impact especially clear. Aave and Morpho extend the result beyond Safe and EIP-712 authorization.

4. Safe

Safe’s BatchExecutor provided a path from parser failure to treasury impact. Its batchExecute(Call[]) entry point accepts an array whose elements contain a target, value, and arbitrary nested calldata. Ledger’s production Clear Signing infrastructure supplied the archived batchExecute(Call[]) context for that transaction shape.

The operational threat is malicious JavaScript running inside an authorized Safe frontend. The frontend constructs one attacker-chosen SafeTx, obtains the accepted display context, and asks each required owner to sign the same hash.

The attack path is:

  1. Malicious JavaScript runs inside the transaction frontend trusted by the organization.
  2. The browser presents a routine payment while sending an attacker-crafted EIP-712 SafeTx to each required owner.
  3. Each affected Ledger displays only the final batch action and returns a valid signature over the complete hash.
  4. Once the configured threshold is met, any relayer can submit the signed SafeTx to Safe.
  5. The hidden maximum allowance survives the displayed payment and lets the approved spender transfer the remaining token balance.

Safe still enforces its configured threshold and any independent guards or policy controls. The exploitable failure is correlated semantic review: if the owners rely on the same affected firmware and metadata path, every device can repeat the same incomplete interpretation while contributing a valid signature to the quorum.

The execution-critical fields in our proof were:

Scroll horizontally to view all columns →

SafeTx field Signed value
to Safe BatchExecutor
operation DELEGATECALL (1)
data Pointer-aliased batchExecute(Call[257])

The affected application identified a Safe transaction and its nested batch, displayed only the 10 USDC transfer, showed operation type 1, and asked the user to sign. It never displayed the maximum allowance in SafeTx.data.

Actual Safe EIP-712 review sequence from the executable LEDGER_CLEAR_SIGNING_BYPASS proof of concept

Actual Ragger frames from the affected Apex P review. Surrounding labels and background are editorial.

Safe’s own getTransactionHash matched the hash signed by the Ledger. An unrelated relayer submitted those fields and the recorded signature to a real Safe 1.4.1 proxy on a pinned, disposable mainnet fork. The fork installed the recovered Ledger address as the effective owner and set threshold 1.

Execution emitted 256 undisplayed USDC Approval events, the displayed 10 USDC Transfer, and ExecutionSuccess. The Safe nonce advanced and the attacker’s allowance became uint256.max. A second local transaction then acted as the approved spender and transferred the remaining USDC from the Safe.

That run proves a signature produced after an incomplete device review was blockchain-usable.

5. Aave

Aave V3’s archived multicall(bytes[]) context provides a direct-transaction path through the same shared array parser.

A user opens a compromised frontend to supply 10 USDC to Aave. The browser sends a 257-entry multicall to the Ledger. Entry 0 withdraws the user’s existing USDC supply position to the attacker, entries 1 through 255 reference aliased filler calls, and entry 256 performs the expected 10 USDC supply.

With Ethereum app 1.22.2, the parser starts at entry 256. The device displays the 10 USDC supply while its signature covers the complete multicall, including the withdrawal. We submitted that exact signed transaction to a pinned mainnet fork: the existing position moved to the attacker, and the displayed supply completed.

Aave Clear Signing bypass: a 10 USDC supply displayed while an existing position is withdrawn

The affected app reviewed the final supply entry. The signed multicall also contained the preceding withdrawal.

6. Morpho

Morpho’s archived Bundler3 multicall context supplied a second direct-transaction path through the vulnerable parser.

A user opens a compromised frontend to supply 10 USDC to a Morpho market. The browser sends a 257-entry Bundler3 multicall to the Ledger. Entry 0 moves the expected 10 USDC into Morpho’s adapter, entry 1 withdraws the user’s complete existing USDC position to the attacker, entries 2 through 255 reference one aliased no-op call, and entry 256 performs the expected 10 USDC supply for the visible recipient.

With Ethereum app 1.22.2, the parser starts at entry 256. The device displays the 10 USDC supply while its signature covers the complete multicall, including the withdrawal. We submitted that exact signed transaction to the pinned mainnet fork: the existing 100 USDC position moved to the attacker, the displayed supply completed, and no residual USDC remained in the adapter.

Morpho Clear Signing bypass: a 10 USDC supply displayed while an existing position is withdrawn

The affected app reviewed the final supply entry. The signed multicall also contained the preceding funding call, withdrawal, and aliased no-op calls.

Aave and Morpho establish the same result through different transaction shapes: the screen reviewed a suffix while the signature authorized the whole array.

7. Making 257 Calls Fit

Practical exploitation also had to satisfy the device’s memory constraints.

A conventional batchExecute(Call[257]) encoding repeats every dynamic tuple. For the demonstrated ERC-20 calls, that produces 65,860 bytes, far beyond the tested flow’s practical limit.

Same-scale comparison of the naive 65,860-byte ABI encoding, the 16 KiB heap limit, and the 8,740-byte aliased encoding

The conventional encoding does not fit. Reusing tuple bodies brings the same 257 logical entries below the parser’s 16 KiB allocation.

ABI dynamic offsets allow multiple array entries to point to the same tuple. We built an offset table in which the first 256 entries all point to one shared approval tuple while the final entry points to one transfer tuple:

ABI offset table showing 256 entries referencing hidden CALL_A and the final entry referencing displayed CALL_B

The offset table contains 257 entries, but the encoded payload stores only two tuple bodies.

Aliasing reduced the calldata from 65,860 bytes to 8,740 bytes, allowing it to fit the original 16 KiB parser allocator. The signature still commits to all 257 logical array entries.

At execution, Safe invokes tuple A 256 times. Those calls repeatedly set the same allowance, leaving one maximum approval. Tuple B performs the small payment shown by the device. The approved spender can then call transferFrom directly.

Comparison of the truncated device review with the complete Safe and EVM interpretation committed to by the signature

The device starts at entry 256 and reviews the transfer. Safe interprets the complete array, and the signature commits to both tuple bodies.

The signed descriptor remains authentic. The calldata remains valid ABI. The signature remains cryptographically correct. The trusted interpretation omits signed actions.

8. Proof of Concept

Before testing: Treat the PoC page and its outputs as untrusted. Use a separate test device with a new, disposable seed phrase and no real funds. Never use your everyday wallet or reuse its recovery phrase. Never enter a seed phrase into the PoC page or any website.

The hardware signing artifact wizard presents the Safe, Aave, and Morpho proofs as three paths through one page. For each protocol it reconstructs the canonical 257-element payload, requests a fresh signature from a connected Ledger through WebHID, and verifies the signer against the complete payload.

The page ships a frozen copy of the Ledger-signed production descriptors, token metadata, and device certificates used by the demonstrations. It does not query Ledger’s current metadata service and does not require a CAL origin token. This preserves the historical Clear Signing inputs if the hosted metadata changes or disappears.

The page keeps the evidence chain explicit: Select, Capture, Inspect, and Export. Safe exports an EIP-712 owner authorization with every SafeTx field and the corresponding Safe.execTransaction calldata. Aave and Morpho export complete raw signed EIP-1559 transactions.

The page generates commands that consume the exported artifact through independent Safe or DeFi runners on a pinned, disposable Anvil fork.

The final result compares the device display, signed authorization, and local execution:

  • Device display
    • Send 10 USDC
  • Signed authorization
    • Transfer 10 USDC
    • Approve attacker: MAX
  • Anvil result
    • Transfer completed
    • Allowance created
    • Safe balance: 0

A physical-device capture requires Chrome or Edge, an affected test device, and a disposable seed. The fixed Ethereum app rejects the malformed array. The page does not offer an archived-signature fallback.

9. Fix and Release

Remediation timeline showing the March report, May source correction, vulnerable 1.22.2 tag, and fixed 1.22.3 release

Ledger merged the root-cause correction into public develop on May 5. The security-relevant change was a three-line bounds check:

@@ -261,6 +261,9 @@ static bool path_array(const s_array_args *array,
     *offset += 1;
     if (arrays_info->index == arrays_info->depth) {
         // new depth
+        if (passes > UINT8_MAX) {
+            return false;
+        }
         arrays_info->passes_remaining[arrays_info->index] = passes;
         arrays_info->depth += 1;
     }

The first installable stable release containing the correction became available on August 25.

On August 26, we gave only that patch hunk to a fresh, isolated Codex session running gpt-5.6-sol. We omitted the commit title, repository, report, surrounding declarations, and exploit details. Its first conclusion was:

An unchecked narrowing conversion when storing passes in passes_remaining[].

The model selected 256 as the first boundary test, identified 255 as the valid maximum, and warned that the old code could select the wrong element or stop traversal early. Reconstructing the complete exploit chain still required surrounding source and human analysis. This single triage run illustrates how the patch hunk alone enabled an agent to identify the bug class and critical input boundary.

Source correction and user protection are separate milestones. GitHub’s repository security advisory workflow supports private fix development, allowing a root-cause diff to remain private until an installable release is ready.

The correction missed stable versions 1.22.1 and 1.22.2. Version 1.22.2 fixed a separate issue reported by TestMachine, but left this bug unfixed. For most of the agreed disclosure period, the correction was visible in public Git history while users on the stable release remained affected.

We notified Ledger that the original PoC still reproduced on the exact 1.22.2 tag. The fixed Ethereum app 1.22.3 became installable two days later, on August 25. We retested it immediately and confirmed that the same PoC was rejected. The full disclosure and remediation record is in the appendix.

We are publishing with fixed Ethereum app 1.22.3 available through Ledger’s normal application catalog.

10. Conclusion: One Thing Well

Version 1.22.3 removes this truncation. The harder question remains: what other bugs could emerge from the same firmware complexity?

Ledger’s default flow gives trusted firmware two security-critical jobs:

  1. protect and use the private key; and
  2. interpret evolving smart-contract semantics for the user.

The second job is substantial. A stateful C parser combines attacker-controlled calldata with evolving metadata, walks nested structures, manages parser state, formats values, and decides which facts deserve space on a small screen. Here, one unchecked narrowing conversion changed 257 reviews into one. Each protocol and display feature adds another opportunity for interpretation to diverge from authorization.

Metadata signatures establish the provenance of rendering instructions. Completeness, correct firmware interpretation, and correspondence between the screen and every signed action remain separate security properties.

For multisig custody, identical devices create a shared semantic control. A quorum can be cryptographically valid while every signer receives the same wrong interpretation from the same firmware and metadata path.

Blind signing makes the absence of semantic verification visible. Incomplete Clear Signing can instead present a readable interpretation that omits actions covered by the signature.

A narrower hardware signer would protect keys, display one stable commitment, and sign exactly that commitment. Decoding, simulation, policy evaluation, and human-readable views could run across independent, replaceable systems that bind their conclusions to the same hash.

11. Appendix: Disclosure Record

Dates use YYYY-MM-DD. Times in the underlying correspondence are retained in America/Argentina/Cordoba (ART, UTC-3). The table records confirmed email exchanges and public GitHub or release activity.

Scroll horizontally to view all columns →

Date Record Event
2026-03-28 Email Bitfinding submitted the encrypted report and PoC.
2026-03-31 Email Ledger acknowledged receipt.
2026-04-22 Email Bitfinding reported reproduction on Ethereum app 1.22.0.
2026-04-23 Email Ledger validated exploitability, classified the report High, and approved the maximum reward for the category. Bitfinding asked to publish the analysis and PoC after remediation and release.
2026-05-04 Email Ledger agreed to post-remediation publication and requested a wait until early September. Bitfinding accepted.
2026-05-05 GitHub Commit 2c3d93cc merged the correction into public develop through PR #1017.
2026-05-27 GitHub Version 1.22.1 did not include the fix.
2026-08-10 to 2026-08-13 GitHub / production catalog Ledger prepared Ethereum app 1.22.2 to fix a separate issue reported by TestMachine.
2026-08-23 Email Bitfinding reported that the correction was absent from 1.22.2 and master, requested the first installable fixed version, and reiterated first-week-of-September publication.
2026-08-24 Email / GitHub Ledger confirmed that the correction had missed the release branch and treated the release-line feedback as a new bounty. A public release/1.22.3 branch appeared with an equivalent backport.
2026-08-25 Email / GitHub / production catalog Ledger described the original issue as partially fixed and requested Communications review. Fixed app 1.22.3 became available for Nano S Plus, Nano X, Stax, Flex, and Apex P. Bitfinding confirmed that the same PoC no longer reproduced.
2026-08-27 Public disclosure Ledger published LSB-024, crediting Bitfinding and noting that Florian Pradines independently reported a variant. The bulletin covers the bug, affected versions, impact, fix, and coordinated-disclosure timeline.
2026-09-07 Publication Bitfinding published this document, Signature overflow attacks on Ledger.