Platform: Code4rena
Start Date: 14/06/2022
Pot Size: $50,000 USDC
Total HM: 19
Participants: 99
Period: 5 days
Judge: HardlyDifficult
Total Solo HM: 4
Id: 136
League: ETH
Rank: 85/99
Findings: 1
Award: $48.98
π Selected for report: 0
π Solo Findings: 0
π Selected for report: joestakey
Also found by: 0x1f8b, 0x29A, 0x52, 0xDjango, 0xNazgul, 0xNineDec, 0xf15ers, 0xkowloon, 0xmint, 8olidity, BowTiedWardens, Chom, Cityscape, Czar102, ElKu, FSchmoede, Funen, GimelSec, GreyArt, IllIllI, KIntern, Kaiziron, Kenshin, Lambda, MadWookie, MiloTruck, PPrieditis, Picodes, Ruhum, Sm4rty, StErMi, TerrierLover, TomJ, Treasure-Seeker, VAD37, WatchPug, Wayne, _Adam, a12jmx, abhinavmir, antonttc, apostle0x01, asutorufos, berndartmueller, cccz, cloudjunky, codexploder, cryptphi, csanuragjain, defsec, delfin454000, fatherOfBlocks, georgypetrov, hake, hansfriese, horsefacts, hyh, k, kenta, nxrblsrpr, oyc_109, peritoflores, rajatbeladiya, reassor, rfa, robee, sach1r0, saian, samruna, shenwilly, simon135, sorrynotsorry, sseefried, throttle, unforgiven, wagmi, zzzitron
48.9813 USDC - $48.98
https://github.com/code-423n4/2022-06-infinity/blob/main/contracts/token/InfinityToken.sol#L60
The Admin/Owner of the Infinity Token contract can control a number of administrative variables that can ensure they can advanceEpoch()
and mint themselves (as the admin/owner) new tokens. This also doesn't require the timelock to be set to 30 days when the attack is performed.
requestChange()
setting the configId
to keccak256('Timelock')
and the value to 1
.confirmChange()
specfying the configId
of keccak256('Timelock')
and the timelock will now be set to 1 second.requestChange()
setting the configId
to keccak256('Cliff')
and the value to 1
.confirmChange()
specfying the configId
of keccak256('Cliff')
and the cliff will now be set to 1.requestChange()
setting the configId
to keccak256('EpochDuration')
and the value to 1
.confirmChange()
specfying the configId
of keccak256('EpochDuration')
and the epoch duration will now be set to 1.Note: I haven't chosen the optimal numbers here. All I am demonstrating is that when you control all these variables essential to governance you can change the timelock, wait for it to thaw, modify other governance variables quickly, mint new supply and then rug pull.
A foundry
test case demonstrates this;
function testOwnerZero() public { // Set the ADMIN variable similar to the TimeLockConfig.sol code bytes32 ADMIN = keccak256('Admin'); // As the current admin let's set the new admin to address(0) vm.prank(address(1337)); infToken.requestChange(ADMIN, uint256(uint160(address(0)))); // Let's fast forward past the timelock and confirm the change. vm.warp(block.timestamp + 2592000); infToken.confirmChange(ADMIN); assertEq(infToken.getAdmin(), address(0)); } function testRugPull() public { // Set the ADMIN variable similar to the TimeLockConfig.sol code bytes32 C_TIMELOCK = keccak256('Timelock'); bytes32 C_EPOCH_CLIFF = keccak256('Cliff'); bytes32 C_EPOCH_DURATION = keccak256('EpochDuration'); uint timestamp = block.timestamp; // Change the timelock window so that once it expires we can inflate supply // mint new token supply and then sell them as fast as possible. // Check we have the right admin address. assertEq(address(1337), infToken.getAdmin()); // As the current admin request a change to the timelock vm.prank(address(1337)); infToken.requestChange(C_TIMELOCK, uint256(1)); // Let's fast forward 30 days and apply the change then immediately rug pull. vm.warp(timestamp += 2592000); // Confirm the change from any address. infToken.confirmChange(C_TIMELOCK); // As the current admin let's set the new admin to address(0) vm.startPrank(address(1337)); infToken.requestChange(C_EPOCH_CLIFF, 1); infToken.requestChange(C_EPOCH_DURATION, 1); vm.stopPrank(); // Let's fast forward past the timelock and confirm the change. vm.warp(timestamp += 1); infToken.confirmChange(C_EPOCH_CLIFF); infToken.confirmChange(C_EPOCH_DURATION); assertEq(infToken.getCliff(), 1); assertEq(infToken.getEpochDuration(), 1); // advanceEpoch will mint the admin/owner new tokens. infToken.advanceEpoch(); }
Vim / Foundy
The governance model should be restructured to ensure there is either a two key system where governance changes are proposed by one address and then accepted by another or some of the key governance variables can only be modified by one entity and others by another.
Having all variables configurable by an administrator and not defined as immutable and passed to the constructor at contract creation creates a risky governance model that can be rug pulled.
#0 - nneverlander
2022-06-22T13:23:07Z
While the assessment is true, this assumes mal-intent on the part of the admin or admin losing private keys.
A more governance friendly token implementation is out of scope for this audit.
Can be classified as low.
#1 - HardlyDifficult
2022-07-10T15:44:59Z
Agree with the sponsor here. Lowering risk and converting this into a QA report for the warden.
#2 - HardlyDifficult
2022-07-12T05:05:35Z