Summary
According to the project documentation, producers should not be able to join the special event. However, it was found that producers are currently able to join the special event, violating the intended access control.
Vulnerability Details
The vulnerability stems from improper access control checks within the special event joining mechanism. The current implementation fails to properly validate and restrict producers from participating in the special event. This allows producers to bypass the intended restrictions and gain unauthorized access to the event. The MartenitsaEvent contract extends the MartenitsaToken contract, enabling the MartenitsaEvent contract to possess its own producers array and isProducer mapping. Consequently, it operates independently of the storage variables within the deployed MartenitsaToken contract, where all producers are typically stored and mapped.
@> contract MartenitsaEvent is MartenitsaToken {
HealthToken private _healthToken;
function joinEvent() external {
require(block.timestamp < eventEndTime, "Event has ended");
require(!_participants[msg.sender], "You have already joined the event");
@> require(!isProducer[msg.sender], "Producers are not allowed to participate");
require(_healthToken.balanceOf(msg.sender) >= healthTokenRequirement, "Insufficient HealthToken balance");
_participants[msg.sender] = true;
participants.push(msg.sender);
emit ParticipantJoined(msg.sender);
(bool success) = _healthToken.transferFrom(msg.sender, address(this), healthTokenRequirement);
require(success, "The transfer is not successful");
_addProducer(msg.sender);
}
}
Impact
MartenitsaToken producers are able to access the special event which is not the intended system design.
POC
Below you can see a POC that shows how this can happen:
function testProducerJoinEvent() public activeEvent eligibleForReward {
vm.startPrank(bob);
marketplace.collectReward();
healthToken.transfer(address(jack), 1 ether);
vm.stopPrank();
vm.startPrank(jack);
assert(martenitsaEvent.isProducer(jack) == true);
healthToken.approve(address(martenitsaEvent), 10 ** 18);
martenitsaEvent.joinEvent();
vm.stopPrank();
}
Tools Used
VS Code, Foundry, Manual Review
Recommendations
The inheritance should be removed and the main MartenitsaToken
contract should be used for access control
- contract MartenitsaEvent is MartenitsaToken {
+ contract MartenitsaEvent {
HealthToken private _healthToken;
+ MartenitsaToken public martenitsaToken;
.......
constructor(address healthToken) onlyOwner {
_healthToken = HealthToken(healthToken);
+ martenitsaToken = MartenitsaToken(_martenitsaToken);
}
..........
function joinEvent() external {
require(block.timestamp < eventEndTime, "Event has ended");
require(!_participants[msg.sender], "You have already joined the event");
- require(!isProducer[msg.sender], "Producers are not allowed to participate");
+ require(!martenitsaToken.isProducer(msg.sender), "Producers are not allowed to participate");
require(_healthToken.balanceOf(msg.sender) >= healthTokenRequirement, "Insufficient HealthToken balance");
_participants[msg.sender] = true;
participants.push(msg.sender);
emit ParticipantJoined(msg.sender);
(bool success) = _healthToken.transferFrom(msg.sender, address(this), healthTokenRequirement);
require(success, "The transfer is not successful");
_addProducer(msg.sender);
}
}