The buyOutEstateNFT function has two major flaws: (1) it stops paying beneficiaries after encountering the caller, leaving later beneficiaries unpaid, and (2) it miscalculates payouts, leaving funds stuck in the contract. This allows a beneficiary to buy out an NFT while underpaying others and trapping assets.
How the Exploit Works
When a beneficiary buys out an NFT, the function calculates a total payout (finalAmount) but distributes it unevenly due to flawed logic. It stops processing beneficiaries after the caller and uses an incorrect per-beneficiary amount, leaving some unpaid and funds unspent.
Step-by-Step Exploit Execution
Setup:
The contract has three beneficiaries: beneficiary1, beneficiary2, beneficiary3.
An NFT is created with a value of 300 ether (in an ERC20 token).
After 90 days, beneficiary1 inherits the contract.
Exploit:
beneficiary2 calls buyOutEstateNFT(1) to buy the NFT.
The function calculates:
finalAmount = (300 / 3) * 2 = 200 ether (for two other beneficiaries).
Per-beneficiary payout = 200 / 3 ≈ 66.67 ether.
It pays beneficiary1 (~66.67 ether) but stops at beneficiary2 (the caller), leaving beneficiary3 unpaid.
Result:
beneficiary1 gets ~66.67 ether.
beneficiary3 gets 0 ether.
beneficiary2 pays 200 ether, but only ~66.67 ether is distributed, leaving ~133.33 ether stuck in the contract.
Impact: Beneficiaries are underpaid or unpaid, and funds remain locked in the contract.
Root Cause:
The loop exits early when msg.sender is found.
The payout calculation divides the total amount incorrectly among beneficiaries.
PoC
Slither ,ai , personal scanner, foundry
Issue
The buyOutEstateNFT function has two problems:
It stops paying beneficiaries after encountering the caller, leaving remaining beneficiaries unpaid.
It miscalculates payouts, underpaying beneficiaries and trapping funds in the contract.
Fix
Revise buyOutEstateNFT to pay all non-caller beneficiaries correctly and calculate payouts accurately:
Explanation
Change 1: Replaced early loop termination with a condition to skip the caller, ensuring all other beneficiaries are paid.
Change 2: Fixed payout calculation to value / divisor per beneficiary, with totalPayout as (divisor - 1) * amountPerBeneficiary.
Why It Works: The loop now processes all beneficiaries, and the corrected math ensures funds are fully distributed without leftovers.
Best Practice: In payout logic, process all recipients and verify calculations to prevent fund misdistribution or trapping.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.