HACK ANALYSIS 4 min read

zkLend Hack Analysis


zkLend Hack Analysis

Overview:

On February 12, 2025, zkLend (@zkLend), a money-market protocol on Starknet, suffered an exploit that resulted in the loss of approximately $9.5 million (3,600 ETH). The attack was due to a rounding error bug / precision loss in the mint()function of zkLend’s smart contract, which allowed the attacker to manipulate the “lending_accumulator” and repeatedly deposit and withdraw wstETH, effectively inflating their balance.

The attacker subsequently bridged the stolen assets to Ethereum and attempted to launder them via the Railgun mixer.

Smart Contract Hack Overview:

  • Attacker’s Address: 0x645c7
  • zkLend’s message to the hacker/s: 0xe04a7
Fig: Attacker’s Address and Txns

Decoding the Smart Contract Vulnerability:

  • The root cause of the zkLend exploit is a rounding error in the mint() function, caused by improper integer division operations in safe_decimal_math::div(amount, accumulator);. This function, used in multiple contract operations, performs downward rounding during division, which can lead to incorrect balance calculations.
  • This flaw allowed the attacker to repeatedly inflate their balance by manipulating the lending accumulator, exploiting rounding inconsistencies in the ztokenmint() and withdraw() functions.
Fig: Vulnerable div() function in safeMath lib of zkLend
  • The div() function here performs integer division (a / b), which truncates the result, discarding any fractional values.
Fig: div() in safe_decimal_math lib
  • This div()function in safe_decimal_math scales the numerator (a) by SCALE before performing division. However, it still relies on safe_math::div(), which performs integer division and truncates the result (discards decimals).
Fig: Vulnerable mint() Function
  • When amount is small relative to accumulator, the result (scaled_down_amount) can be rounded down to zero due to downward rounding behavior. Here, scaled_down_amount is derived from div(), which performs integer division. If accumulator is significantly larger than amount, the result gets truncated to zero, causing incorrect balance calculations.
  • There is no compensation mechanism to adjust for lost precision, leading to inaccurate internal balance tracking.
  • Also, the total supply update in self.raw_total_supply.write(raw_supply_after); depends on scaled_down_amount. Because scaled_down_amount suffers from precision loss due to truncation, supply calculations become inaccurate, artificially inflating token balances.
  • Additionaly, the contract includes a condition checking whether raw_balance_before == 0. If improperly handled, this check allows attacker to repeatedly mint and withdraw assets while bypassing accounting safeguards.
  • By manipulating the lending_accumulator, the attacker was able to force rounding errors that led to repeated inflation of their token balance without proper cost deductions.

Attack Sequence:

The attacker artificially increased the “lending_accumulator” to an extremely large value of 4.069297906051644020.

Due to improper rounding, they repeatedly deposited 4.069297906051644021 wstETH and received 2 wei in return.

Then, they withdrew (4.069297906051644020 × 1.5) — 1 = 6.103946859077466029 wstETH while only expending 1 wei.

The attacker exploited the flawed rounding logic in a loop to exponentially increase their balance.

By repeating this deposit-withdraw cycle, the attacker exponentially increased their balance, extracting more wstETH each time.

The attacker withdrew the stolen funds from zkLend and bridged them to Ethereum. Then, laundered them through Railgun.

  • The zkLend Team has officially reached out to the attacker, offering a 10% whitehat bounty while requesting the return of the remaining 3,300 ETH to the designated Ethereum address (0xCf31e1b97790afD681723fA1398c5eAd9f69B98C).
  • They have stated that upon receiving the funds, the attacker will be released from any legal liability related to the exploit.
  • More updates on official statements can be found at: https://x.com/zkLend/status/1889515118368829559

Mitigation and Best Practices:

  • Modify safe_decimal_math::div() to ensure rounding errors do not accumulate in a way that can be exploited. Consider using rounding-up mechanisms or precision adjustments.
  • Introduce minimum deposit and withdrawal thresholds to prevent attackers from leveraging small-value transactions to exploit rounding inconsistencies. Also, set upper and lower bounds for lending_accumulator to prevent extreme values from being used to manipulate division results.
  • Implement safe rounding mechanisms in mathematical operations to prevent precision loss from being exploited. Use higher precision arithmetic or rounding compensation techniques where necessary.
  • To prevent such vulnerabilities, the best Smart Contract auditors must examine the Smart Contracts for logical issues. We at CredShields provide smart contract security and end-to-end security of web applications and externally exposed networks. Our public audit reports can be found on https://github.com/Credshields/audit-reports. Schedule a call at https://credshields.com/
  • Scan your Solidity contracts against the latest common security vulnerabilities with 494+ detections at SolidityScan.
Fig: SolidityScan — Smart Contract Vulnerability Scanner

Conclusion:

SolidityScan is an advanced smart contract scanning tool that discovers vulnerabilities and reduces risks in code. Request a security audit with us, and we will help you secure your smart contracts. Signup for a free trial at https://solidityscan.com/signup

Follow us on our Social Media for Web3 security-related updates.

SolidityScan — LinkedIn | Twitter | Telegram | Discord