Pods Finance Bug Fix Postmortem

Summary

Whitehat Csanuragjain submitted a vulnerability to Immunefi regarding Pods Finance on June 25. The vulnerability was given a severity rating of “high”, as it is a logic error that allows for theft of yield or abuse of the rewards system on the protocol. The contract was not deployed on mainnet, so there were no funds at risk. Pods Finance received the report, evaluated it, and paid out $4,000 USDC to the whitehat in just 13 minutes, winning the award by far for the fastest ever bug bounty response and pay-out on Immunefi. We congratulate Pods Finance for their excellent work and response.

Vulnerability Analysis

Pods Finance has a rewards system that mints rewards for users who issue options, but the reward calculation logic itself, present in both AavePodPut.sol and AavePodCall.sol, allows a malicious attacker to claim rewards owed to other users and drain the entire contract. This vulnerability was rated as “high” because it is tantamount to a theft of yield. Although the vulnerable contracts also hold original user funds, this bug does not allow an attacker to steal them. The way it works is that, if a malicious attacker had minted at least one option in the past, they could repeatedly call unmintWithRewards() with a single share forever — as long as the attacker has the required gas.

function

     function unmintWithRewards(uint256 amountOfOptions) external unmintWindow {         _claimRewards(_getClaimableAssets());
uint256 rewardsToSend = shares[msg.sender].mul(_rewardBalance()).div(totalShares);
(uint256 strikeToSend, uint256 underlyingToSend) = _unmintOptions(amountOfOptions, msg.sender); require(strikeToSend > 0, "AavePodPut: amount of options is too low"); // Sends strike asset IERC20(strikeAsset()).safeTransfer(msg.sender, strikeToSend); emit Unmint(msg.sender, amountOfOptions, strikeToSend, underlyingToSend); if (rewardsToSend > 0) { IERC20(rewardAsset).safeTransfer(msg.sender, rewardsToSend); emit RewardsClaimed(msg.sender, rewardsToSend); }}

The logic above distributes rewards based only on the user shares, instead of also considering the amountOfOptions. As opposed to the withdraw function, where the caller always withdraws 100% of their shares, the unmint function allows the caller to do a partial unmint. The error was that the rewards function was paying as if the user was always unminting 100% of their shares. This led to a vulnerability where the user could remove more rewards than what was due.

Vulnerability Fix

The reward calculation should be based on options unminted, rather than only the shares a user possesses. Pods Finance implemented a fix here.

uint256 rewardsToSend = (shares[msg.sender].mul(amountOfOptions).div(mintedOptions[msg.sender])).mul(_rewardBalance()).div(totalShares);

Acknowledgements

We’d like to thank the Pods Finance team for their record-breaking rapid and effective response to the bug report. Pods Finance paid out a bounty of $4,000 USDC to the whitehat. To report additional vulnerabilities, please see Pods Finance’s bug bounty program with Immunefi. If you’re interested in protecting your project with a bug bounty like Pods Finance, visit the Immunefi services page and fill out the form.

Immunefi is the premier bug bounty platform for smart contracts, where hackers review code, disclose vulnerabilities, get paid, and make crypto safer.