On January 4, 2025, the Sorra
Staking contract was exploited due to flawed logic in the getPendingRewards() function, which failed to track and deduct previously distributed rewards, enabling repeated withdrawals of the same rewards. The attacker, who had deposited 122,868 SOR tokens on December 21, 2024, exploited this flaw to drain 3,071,721 SOR tokens and profit approximately $41,000.
On January 4, 2025, the Sorra staking contract was exploited due to flawed logic that did not account for whether the user had already withdrawn their rewards.
The attacker prepared for this attack by depositing 122,868 SOR tokens into the staking contract 14 days earlier, on December 21, 2024, and selecting a lockup period of 14 days.
The attacker took advantage of the getPendingRewards()
function, which failed to properly track and deduct previously distributed rewards, allowing repeated withdrawals of the same rewards.
As a result, the attacker repeatedly called the withdraw()
function, ultimately draining 3,071,721 SOR tokens and profiting approximately $41,000.
Attacker Address: 0xdc8076
Attack Transaction: 0x6439d
Vulnerable Contract: 0x5d16b
The attacker prepared for the exploit on December 21, 2024, by depositing 122,868 SOR tokens into the staking contract.
According to the contract logic, the lockup periods vary by tier: tier 0 requires a 14-day wait, tier 1 requires 30 days, and tier 2 requires 60 days. The attacker chose tier 0, resulting in a 14-day lockup period.
On January 4, 2025 (exactly 14 days after the deposit), the attacker initiated the exploit by calling the withdraw()
function.
The withdraw()
function allows users to withdraw a specified _amount
of their staked tokens, calculates and distributes any pending rewards, updates the user's position, and transfers both the staked tokens and rewards (if applicable) to the user.
The getPendingRewards() function calculates the pending rewards for the msg.sender. In this case, it returned 6,143 SOR tokens.
When rewardAmount > 0
, the contract updates userRewardsDistributed[_msgSender()]
with the rewardAmount
. However, due to a flaw in the logic, the calculation of the user's pending rewards does not properly account for the userRewardsDistributed[_msgSender()]
.
This oversight means there is no record or proof that the user has already withdrawn their rewards. As a result, the attacker was able to repeatedly call the withdraw()
function with 1 Wei token.
The attacker ultimately withdrew 3,071,721 SOR tokens through repeated calls and swapped them on UniswapV2, profiting approximately $41,000.
The root cause of the exploit lies in the flawed reward distribution logic in the withdraw()
function. Specifically, the calculation of pending rewards in getPendingRewards()
did not properly account for userRewardsDistributed[_msgSender()]
, allowing the same rewards to be withdrawn repeatedly. This oversight enabled the attacker to exploit the contract by repeatedly calling withdraw()
to claim excessive rewards and profit significantly.
Take a look at the funds flow and track how the stolen tokens were moved across the blockchain.
getPendingRewards()
function should have accounted for the userRewardsDistributed[_msgSender()]
value when calculating pending rewards to ensure rewards are not double-counted.Join 1000+ leaders who secured themselves from losing Billion Dollars.
Get Pure Alpha Straight to Your Inbox. Miss this, and you’re missing out.
Insider Secrets - Delivered Right to You. Subscribe now.