DeFiFoundry
20,000 USDC
View results
Submission Details
Severity: medium
Invalid

Invariant breakage in FjordStaking::addAuthorizedSablierSender allows to set zero address as authorizedSablierSenders while FjordStaking::constructor doesnt allow it

Summary

FjordStaking::addAuthorizedSablierSender allows to break an invariant/restriction imposed in FjordStaking's contructor making possible to set address(0) as an AuthorizedSablierSender.

Vulnerability Details

FjordStaking constructor doesnt allow setting address(0) as an AuthorizedSablierSender

constructor(
address _fjordToken,
address _rewardAdmin,
address _sablier,
address _authorizedSablierSender,
address _fjordPoints
) {
//... snippet
@> if (_authorizedSablierSender != address(0)) {
authorizedSablierSenders[_authorizedSablierSender] = true;
}

However, FjordStaking::addAuthorizedSablierSender allows to bypass this restriction:

function addAuthorizedSablierSender(address _address) external onlyOwner {
authorizedSablierSenders[_address] = true;
}

The following proof of concept shows the previous described bug, using addAuthorizedSablierSender to set address zero as an AuthorizedSablierSender
Create this test file as FjordStakingInvariant.t.sol in test directory

// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity =0.8.21;
import "../src/FjordStaking.sol";
import { FjordPoints } from "../src/FjordPoints.sol";
import { Test } from "forge-std/Test.sol";
import { MockERC20 } from "solmate/test/utils/mocks/MockERC20.sol";
import { FjordPointsMock } from "./mocks/FjordPointsMock.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "forge-std/console.sol";
contract FjordStakingInvariant is Test {
uint256 constant addRewardPerEpoch = 1 ether;
FjordStaking fjordStaking;
MockERC20 token;
address minter = makeAddr("minter");
address newMinter = makeAddr("new_minter");
address alice = makeAddr("alice");
address bob = makeAddr("bob");
address internal constant SABLIER_ADDRESS = address(0xB10daee1FCF62243aE27776D7a92D39dC8740f95);
address points;
bool isMock = true;
address authorizedSender = address(this);
bool isFuzzOrInvariant = false;
function setUp() public {
if (!isFuzzOrInvariant) {
vm.createSelectFork({ urlOrAlias: "mainnet", blockNumber: 19_595_905 });
}
if (isMock) {
points = address(new FjordPointsMock());
} else {
points = address(new FjordPoints());
}
token = new MockERC20("Fjord", "FJO", 18);
}
function testInvariantBreakageAddAuthorizedSablierSender() public {
address zero_addr = address(0);
console.log("[1] Trying to create FjordStaking with zero addr as auth sablier sender");
fjordStaking =
new FjordStaking(address(token), minter, SABLIER_ADDRESS, zero_addr, points);
console.log("[2] After create, fjordStaking.authorizedSablierSenders(zero_addr): ",fjordStaking.authorizedSablierSenders(zero_addr));
console.log("[3] Call fjordStaking.addAuthorizedSablierSender(zero_addr)");
fjordStaking.addAuthorizedSablierSender(zero_addr);
console.log("[4] After call addAuthorizedSablierSender, fjordStaking.authorizedSablierSenders(zero_addr): ",fjordStaking.authorizedSablierSenders(zero_addr));
}
}

Execute test file with

forge test --mt testInvariantBreakageAddAuthorizedSablierSender

Observe address zero is now a AuthorizedSablierSender

Impact

Invariant breaking about authorized sablier senders
Restriction Bypassing

Tools Used

Manual Review

Recommendations

Modify addAuthorizedSablierSender to not be able to break invariant about addAuthorizedSablierSender:

function addAuthorizedSablierSender(address _address) external onlyOwner {
- authorizedSablierSenders[_address] = true;
+ if (_authorizedSablierSender != address(0)) {
+ authorizedSablierSenders[_address] = true;
+ }
}
Updates

Lead Judging Commences

inallhonesty Lead Judge about 1 year ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

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