Reproduced Exploit
LEND H-3: both cross-chain mappings populated bricks `borrowWithInterest` (liquidation evasion)
Chain
Other
Category
untagged
Date
May 2025
Source
AuditVault
EVM Playground
Source-level debugger — step opcodes and Solidity in sync
The attack is replayed in an in-browser EVM preloaded with the exact dumped fork state. The execution tree shows every call; step by Solidity line or by opcode across all depths — source, Stack, Memory, Storage, Balances (native / ERC-20 / NFT), Transient storage and Return value stay in sync. Click a tree node, opcode, or source line to jump. No backend, no live RPC.
Source & credit. Reproduction of a public audit finding curated by AuditVault — the original finding: 58372-h-3-user-can-evade-liquidation-and-bridge-funds-by-exploitin. Standalone Foundry PoC and full write-up: 58372-h-3-user-can-evade-liquidation-and-bridge-funds-by-exploit_exp in the
evm-hack-registrymirror.
Vulnerability classes: cross-chain · broken-invariant · liquidation-DoS · bad-debt
Reproduction: a faithful minimal reproduction of
LendStorage.borrowWithInterest(Sherlock2025-05-lend-audit-contest, LendStorage.sol L478-486 @713372a1) — the reverting both-populated invariant is reproduced verbatim (marked@>). The lToken index and the liquidation gate are faithful minimal doubles. Local deploy, no fork.
Root cause#
borrowWithInterest assumes that, for a given user and asset on a chain, only ONE of
crossChainBorrows / crossChainCollaterals is ever populated, and enforces it with
a hard require:
Borrow[] memory borrows = crossChainBorrows[borrower][_token];
Borrow[] memory collaterals = crossChainCollaterals[borrower][_token];
require(borrows.length == 0 || collaterals.length == 0, "Invariant violated: both mappings populated"); // @>
That invariant is not actually enforced elsewhere. A user can populate both mappings for the same asset on the same chain by opening cross-chain borrows in both directions:
- Supply (nearly all) collateral on Chain A, borrow on Chain B →
crossChainBorrowspopulated on A. - Supply a dust amount on Chain B, borrow a dust amount on Chain A →
crossChainCollateralspopulated on A.
Now both arrays are non-empty on Chain A, so the require reverts on every call.
Impact#
- Position becomes uncloseable and non-liquidatable. Every function that reads the
borrow balance — crucially the liquidation path — calls
borrowWithInterest, which now reverts. The underwater position can never be liquidated. - Collateral bridged out; protocol eats bad debt. The user withdraws/bridges out nearly all their collateral while the outstanding borrow can never be repaid or seized — a direct solvency threat.
- In the PoC, the liquidation attempt is bricked and the attacker retains the full 1,000-token collateral it should have lost.
Attack walkthrough#
PoC#
Registry (Foundry, local deploy — exploit path + a redesigned-tracking control):
cd 58372-h-3-user-can-evade-liquidation-and-bridge-funds-by-exploit_exp
forge test -vv
Expected: test_attacker_bricksLiquidationViaBothMappings PASS (borrowWithInterest
reverts with "Invariant violated: both mappings populated"; attacker retains the full
1,000-token collateral) and test_control_fixedDoesNotRevert PASS (the redesigned
read returns the real debt without reverting even with both mappings populated). The
browser EVM Playground is served at
/hacks/58372-h-3-user-can-evade-liquidation-and-bridge-funds-by-exploit/.
Remediation#
Redesign the cross-chain borrow/collateral tracking so both mappings cannot be populated for the same user and asset on the same chain (or compute the borrow balance without a reverting invariant that a user can trigger at will).
References#
- Sherlock 2025-05-lend-audit-contest, issue #224: https://github.com/sherlock-audit/2025-05-lend-audit-contest-judging/issues/224
- Vulnerable code: https://github.com/sherlock-audit/2025-05-lend-audit-contest/blob/713372a1ccd8090ead836ca6b1acf92e97de4679/Lend-V2/src/LayerZero/LendStorage.sol#L478-L486
Sources & further analysis#
Reproductions & code
- Standalone PoC + full trace: 58372-h-3-user-can-evade-liquidation-and-bridge-funds-by-exploit_exp (evm-hack-registry mirror).
- AuditVault finding: 58372-h-3-user-can-evade-liquidation-and-bridge-funds-by-exploitin.
- Upstream DeFiHackLabs PoC directory: src/test.
Alerts & third-party analyses
- DeFiHackLabs incident explorer: search "LEND H-3: both cross-chain mappings populated bricks
borrowWithInterest(liquidation evasion)". - Web3Sec X hacked database: search.
- Rekt leaderboard: search.
- Solodit incident search: search.
These dashboards index community alerts tweets, post-mortems, and independent write-ups. Reach them through the protocol name above to cross-check this reproduction against other analyses.