Reproduced Exploit
LEND H-18: incorrect `srcEid` check in `borrowWithInterest` (cross-chain debt reads 0 → 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: 58387-h-18-incorrect-srceid-check-in-borrowwithinterest-sherlock-l. Standalone Foundry PoC and full write-up: 58387-h-18-incorrect-srceid-check-in-borrowwithinterest-sherlock_exp in the
evm-hack-registrymirror.
Vulnerability classes: cross-chain · wrong-endpoint-id-field · liquidation-evasion · accounting-under-report
Reproduction: a faithful minimal reproduction of
LendStorage.borrowWithInterest(Sherlock2025-05-lend-audit-contest, commit713372a1). The vulnerable function is reproduced verbatim (marked@>); the lToken interest index and the liquidation gate are faithful minimal doubles. Local deploy, no fork.
Root cause#
LendStorage.borrowWithInterest sums a borrower's cross-chain debt on the current
chain. When a cross-chain borrow is stored on the source chain (Chain A), the
record's fields are set to srcEid = ChainB (the destination) and
destEid = ChainA (the current chain). But the filter uses the wrong field:
Borrow[] memory borrows = crossChainBorrows[borrower][_token];
...
for (uint256 i = 0; i < borrows.length; i++) {
if (borrows[i].srcEid == currentEid) { // @> wrong field
borrowedAmount +=
(borrows[i].principle * LTokenInterface(_lToken).borrowIndex()) / borrows[i].borrowIndex;
}
}
return borrowedAmount;
borrows[i].srcEid is ChainB, and currentEid is ChainA, so the condition is
never true on the source chain. Every real cross-chain borrow is skipped and
borrowWithInterest returns 0.
Impact#
- Cross-chain debt is invisible. An account with a real cross-chain borrow reads
as having zero cross-chain debt, so
getHypotheticalAccountLiquiditytreats it as over-collateralized. - Liquidation evasion → bad debt. An underwater cross-chain borrower cannot be liquidated (the liquidation path sees no debt), so the protocol eats the loss. The same under-report also lets the borrower draw further borrows against phantom capacity.
- In the PoC, a 1,000-token underwater borrow reads as 0; the liquidation attempt no-ops and the borrower retains the full 1,000-token principal.
Attack walkthrough#
PoC#
Registry (Foundry, local deploy — exploit path + a fixed-filter control):
cd 58387-h-18-incorrect-srceid-check-in-borrowwithinterest-sherlock_exp
forge test -vv
Expected: test_attacker_evadesLiquidation PASS (reported debt 0 vs a real
1_000e18 borrow; principal retained) and test_control_fixedReportsDebtAndLiquidates
PASS (fixed destEid == currentEid filter reports the real 1_000e18). The browser
EVM Playground is served at
/hacks/58387-h-18-incorrect-srceid-check-in-borrowwithinterest-sherlock/.
Remediation#
Filter cross-chain borrows on the source chain by destEid == currentEid, matching
how the records are actually stored:
- if (borrows[i].srcEid == currentEid) {
+ if (borrows[i].destEid == currentEid) {
References#
- Sherlock 2025-05-lend-audit-contest, issue #831: https://github.com/sherlock-audit/2025-05-lend-audit-contest-judging/issues/831
- Vulnerable code: https://github.com/sherlock-audit/2025-05-lend-audit-contest/blob/713372a1ccd8090ead836ca6b1acf92e97de4679/Lend-V2/src/LayerZero/LendStorage.sol#L478-L503
Sources & further analysis#
Reproductions & code
- Standalone PoC + full trace: 58387-h-18-incorrect-srceid-check-in-borrowwithinterest-sherlock_exp (evm-hack-registry mirror).
- AuditVault finding: 58387-h-18-incorrect-srceid-check-in-borrowwithinterest-sherlock-l.
- Upstream DeFiHackLabs PoC directory: src/test.
Alerts & third-party analyses
- DeFiHackLabs incident explorer: search "LEND H-18: incorrect
srcEidcheck inborrowWithInterest(cross-chain debt reads 0 → 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.