Beginner FriendlySolidity
100 EXP
View results
Submission Details
Severity: high
Valid

Flawed Loop Logic in InheritanceManager::buyOutEstateNFT Skips Payments to Most Beneficiaries, Enabling Caller to Retain Funds

Summary

Flawed Loop Logic in buyOutEstateNFT Skips Payments to Most Beneficiaries, Enabling Caller to Retain Funds

Vulnerability Details

The buyOutEstateNFT function contains an early return when encountering the caller’s address in the beneficiaries array. This skips payments to all beneficiaries listed after the caller that acquiring the NFT.

Impact

High Severity

Theft of Beneficiary Funds: Only beneficiaries positioned before the caller in the array receive payments; others are ignored.

Tools Used

Manual code review
Foundry test case (provided)

PoC

function test_OnlyUserinFrontIsPayed() public {
address owner = makeAddr("owner");
address user1 = makeAddr("user1");
address user2 = makeAddr("user2");
address user3 = makeAddr("user3");
address user3 = makeAddr("user4");
vm.startPrank(owner);
im.addBeneficiery(user1);
im.addBeneficiery(user2);
im.addBeneficiery(user3);
im.addBeneficiery(user4);
im.createEstateNFT("our beach-house", 23, address(usdc));
vm.stopPrank();
usdc.mint(user2, 15);
vm.warp(1 + 90 days);
vm.startPrank(user2);
usdc.approve(address(im), 20);
im.inherit();
im.buyOutEstateNFT(1);
vm.stopPrank();
console.log("User1 will have:", usdc.balanceOf(user1));
console.log("User2 will have:", usdc.balanceOf(user2));
console.log("User3 will have:", usdc.balanceOf(user3));
console.log("User4 will have:", usdc.balanceOf(user4));
}

Log

Ran 1 test for test/Mytest.t.sol:Testcontract
[PASS] test_OnlyUserinFrontIsPayed() (gas: 438575)
Logs:
User1 will have: 3
User2 will have: 0
User3 will have: 0
User4 will have: 0

Recommendations

Remove Early Return and Track Caller

function buyOutEstateNFT(uint256 _nftID) external onlyBeneficiaryWithIsInherited {
uint256 value = nftValue[_nftID];
uint256 divisor = beneficiaries.length;
uint256 multiplier = beneficiaries.length - 1;
uint256 finalAmount = (value / divisor) * multiplier;
IERC20(assetToPay).safeTransferFrom(msg.sender, address(this), finalAmount);
for (uint256 i = 0; i < beneficiaries.length; i++) {
- if (msg.sender == beneficiaries[i]) {
- return;
- } else {
+ if (beneficiaries[i] != msg.sender) {
IERC20(assetToPay).safeTransfer(beneficiaries[i], finalAmount / divisor);
}
}
nft.burnEstate(_nftID);
}
Updates

Lead Judging Commences

0xtimefliez Lead Judge 9 months ago
Submission Judgement Published
Validated
Assigned finding tags:

buyOutNFT has return instead of continue

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.

Give us feedback!