Platform: Code4rena
Start Date: 30/10/2023
Pot Size: $49,250 USDC
Total HM: 14
Participants: 243
Period: 14 days
Judge: 0xsomeone
Id: 302
League: ETH
Rank: 99/243
Findings: 1
Award: $25.24
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: bird-flu
Also found by: 00decree, 0xAadi, AS, Audinarey, DeFiHackLabs, Eigenvectors, Fitro, Hama, Kaysoft, Krace, REKCAH, SovaSlava, The_Kakers, Viktor_Cortess, cartlex_, degensec, devival, evmboi32, funkornaut, jacopod, openwide, peanuts, rotcivegaf, smiling_heretic, xAriextz, xiao
25.2356 USDC - $25.24
At the end of every auction, the winning bidder's bid is not transferred to the token owners address. This is a direct loss of fund for the token owner.
(See Coded POC below)
At the end of the auction when claimAuction(...)
function is called, the token is transferred from the ownerOfToken
to the highestBidder
as shown below, however the the winnig bid is not transferred to the ownerOfToken
, but instead it is transferred to the owner/deployer of the AuctionDemo.sol
contract.
function claimAuction(uint256 _tokenid) public WinnerOrAdminRequired(_tokenid,this.claimAuction.selector){ ... for (uint256 i=0; i< auctionInfoData[_tokenid].length; i ++) { if (auctionInfoData[_tokenid][i].bidder == highestBidder && auctionInfoData[_tokenid][i].bid == highestBid && auctionInfoData[_tokenid][i].status == true) { IERC721(gencore).safeTransferFrom(ownerOfToken, highestBidder, _tokenid); // transfer token to bidder // @audit highestBid is transferred to the owner() of AuctionDemo > (bool success, ) = payable(owner()).call{value: highestBid}(""); ... } else if (auctionInfoData[_tokenid][i].status == true) { ... } } }
This vulnerabiliity is borne from the fact that the AuctionDemo.sol
contracts inherits only the ownable
contract and its owner()
function returns the owner of the contract instead of the owner of the token
Add the test case below to the nextGen.test.js
file and run npx hardhat test
context("Funds are sent to wrong owner", () => { it("funds are sent to wrong address", async function () { const mintedTokenId = parseInt(await contracts.hhCore.viewTokensIndexMin(2) + await contracts.hhCore.viewCirSupply(2)); // 1) mint to auctioner for him to auction await contracts.hhMinter.mintAndAuction( signers.auctioner, // _recipient "mint and auction test token", // _tokenData 2, // _saltfun_o 2, // _collectionID 1799646112 // _auctionEndTime ) // 2) confirm that token recipient is not the address returned by owner() in the Auctiion contract expect(await contracts.hhAuction.owner()).to.not.be.equal(signers.auctioner.address) }) })
Manual Foundry
Modify the claimAuction(...)
function by removing
(bool success, ) = payable(owner()).call{value: highestBid}("");
and adding
(bool success, ) = payable(ownerOfToken).call{value: highestBid}("");
as shown below
function claimAuction(uint256 _tokenid) public WinnerOrAdminRequired(_tokenid,this.claimAuction.selector){ ... for (uint256 i=0; i< auctionInfoData[_tokenid].length; i ++) { if (auctionInfoData[_tokenid][i].bidder == highestBidder && auctionInfoData[_tokenid][i].bid == highestBid && auctionInfoData[_tokenid][i].status == true) { IERC721(gencore).safeTransferFrom(ownerOfToken, highestBidder, _tokenid); // transfer token to bidder // @audit highestBid is transferred to the owner() of AuctionDemo > (bool success, ) = payable(ownerOfToken).call{value: highestBid}(""); ... } else if (auctionInfoData[_tokenid][i].status == true) { ... } } }
Token-Transfer
#0 - c4-pre-sort
2023-11-20T07:50:26Z
141345 marked the issue as duplicate of #245
#1 - c4-judge
2023-12-08T22:26:13Z
alex-ppg marked the issue as satisfactory
#2 - c4-judge
2023-12-09T00:22:20Z
alex-ppg changed the severity to 2 (Med Risk)