LunaFI Hack

Reported loss $36K
Polygon
Other

What happened

On May 22, 2023, an attacker exploited the reward logic of LunaFi, a betting platform on Polygon. Verichains put the loss at about $36,000. LunaFi's vLFI staking contract let claimRewards() pay out with no time lock on the staked balance. Rewards were not tied to how long a stake had been held, so the same vLFI could be moved to a new address and claim again.

The attack ran as a campaign of transactions rather than a single one. Between about 21:05 and 22:04 UTC the attacker's wallet sent dozens of run() calls to its attack contract, broken up by restake() and swap() calls. In one of them, 0xa0480d0f...6243, the contract created 50 new contracts and passed a single balance of about 5,740 vLFI from one to the next, pulling 30,137.5 LFI of rewards out of the vLFI contract at every hop (about 1.5 million LFI in that transaction alone). Minutes later the attacker re-staked 6,027,460 LFI and then 12,233,090 LFI into the vLFI contract, growing the balance that could claim, and at 21:47 UTC swapped 80,696,700 LFI for about 18.9 WETH on QuickSwap. The live figure of about 1,500,000 LFI matches only the first mapped transaction; the campaign's full LFI total was not tallied. According to Verichains' weekly digest, LunaFi acknowledged the hack, took full responsibility, and was working with Hacken on next steps.

Attacker: 0x11576cb3d8d6328cf319e85b10e09a228e84a8de

Attack contract: 0x43623b96936e854f8d85f893011f22ac91e58164

Example claim transaction: 0xa0480d0f7c8d8bf898cadde954e773ddc3740f1ffa31cdd98fe4c5f5d8266243

Re-stake transactions: 0xae1905de5eaa9603ca526d83d16f3397b90fe5b21990cff92d936106690f882a, 0x8e4b4be1c93071dd5c37f69ee5a285efc2abdbce779b8895413102c82b8ebf8a

Swap transaction: 0x76045837b25e3c5707b88f3df0660b91c31795c7b3f8dee8b67db26e7193a685

How it happened

  1. At 20:36 UTC the attacker bought LFI through QuickSwap, deployed attack contract 0x4362...8164, and called its init function at 21:03 UTC.
  2. From 21:05 UTC the attacker called the contract's run() function again and again. In 0xa0480d0f...6243 (21:40:17 UTC) the contract created 50 new contracts and moved one balance of 5,739.99 vLFI from each to the next. At every hop a reward claim ran against the vLFI contract and 30,137.5 LFI went to the attack contract, about 1,506,876 LFI in that transaction, because the contract never checked how long that balance had been staked.
  3. The attacker then called restake() to put the harvested LFI back into the vLFI contract: 6,027,460 LFI at 21:40:35 UTC (0xae1905de...882a) and 12,233,090 LFI at 21:43:27 UTC (0x8e4b4be1...bf8a). A larger vLFI balance meant larger claims in the following run() calls.
  4. At 21:47:07 UTC (0x76045837...a685) the attack contract sold 80,696,700 LFI into the QuickSwap LFI/WETH pair for about 18.9 WETH. More run(), swap() and token-withdrawal calls followed until 22:14 UTC, with two more withdrawals the next morning.

Protocol details

Classification Other / Protocol Logic
Protocol Type Prediction Market
Affected asset / contract LFI
Implementation language Solidity
Protocol links Website @LunaFi_Project

Security review history

Build your security review skills

Work through hands-on labs covering real exploit mechanics, review techniques, and defensive patterns.