Platform: Code4rena
Start Date: 23/06/2023
Pot Size: $60,500 USDC
Total HM: 31
Participants: 132
Period: 10 days
Judge: 0xean
Total Solo HM: 10
Id: 254
League: ETH
Rank: 54/132
Findings: 2
Award: $144.81
🌟 Selected for report: 0
🚀 Solo Findings: 0
143.4901 USDC - $143.49
The executeFlashloan() in PeUSDMainnetStableVision.sol allows users to execute flash loans but the problem is that the receiver doesnt have to be the msg.sender so an attacker can do 2 things:
Execute other users flash loans
If a user is a smart contract that has a fallback function(for example a multisig wallet), then the attacker can call executeFlashloan() with the user contract address as the receiver and receiver.onFlashLoan() is not going to revert because the contract has a fallback function which is a special function that is executed when a function that does not exist is called
Because there is a fee for executing a flash loan, the attacker will burn shares of other users(either the flash loan contract or any contract that has a fallback function)
The attacker can burn shares of other users(either the flash loan contract or any contract that has a fallback function) by continously calling executeFlashloan() or he can just make the shareAmount so big that the fee will be huge and it will burn all the users shares.
function executeFlashloan(FlashBorrower receiver, uint256 eusdAmount, bytes calldata data) public payable { uint256 shareAmount = EUSD.getSharesByMintedEUSD(eusdAmount); EUSD.transferShares(address(receiver), shareAmount); receiver.onFlashLoan(shareAmount, data); bool success = EUSD.transferFrom(address(receiver), address(this), EUSD.getMintedEUSDByShares(shareAmount)); require(success, "TF"); uint256 burnShare = getFee(shareAmount); EUSD.burnShares(address(receiver), burnShare); emit Flashloaned(receiver, eusdAmount, burnShare); }
As you can see above, there is no check if msg.sender == receiver so an attacker abuse this
Example 1
Example 2
Manual review
Use msg.sender instead of receiver so that flash loans that want to execute this function execute it themselves.
Or the second solution might be burning shares from msg.sender instead of the receiver so that flash loan contracts dont have to hold any shares.
function executeFlashloan(uint256 eusdAmount, bytes calldata data) public payable { uint256 shareAmount = EUSD.getSharesByMintedEUSD(eusdAmount); EUSD.transferShares(msg.sender, shareAmount); FlashBorrower(msg.sender).onFlashLoan(shareAmount, data); bool success = EUSD.transferFrom(msg.sender, address(this), EUSD.getMintedEUSDByShares(shareAmount)); require(success, "TF"); uint256 burnShare = getFee(shareAmount); EUSD.burnShares(msg.sender, burnShare); emit Flashloaned(FlashBorrower(msg.sender), eusdAmount, burnShare); }
Other
#0 - c4-pre-sort
2023-07-04T14:01:05Z
JeffCX marked the issue as duplicate of #280
#1 - c4-judge
2023-07-28T15:29:33Z
0xean marked the issue as satisfactory
#2 - c4-judge
2023-07-28T19:53:20Z
0xean changed the severity to 3 (High Risk)
🌟 Selected for report: bytes032
Also found by: 0xMAKEOUTHILL, 0xgrbr, 0xkazim, 0xnacho, Arz, Co0nan, CrypticShepherd, Cryptor, HE1M, Iurii3, LaScaloneta, LokiThe5th, LuchoLeonel1, MrPotatoMagic, Musaka, Qeew, RedTiger, SovaSlava, Toshii, Vagner, a3yip6, azhar, bart1e, devival, hl_, jnrlouis, kutugu, peanuts, pep7siup, qpzm, smaul
1.3247 USDC - $1.32
Judge has assessed an item in Issue #899 as 2 risk. The relevant finding follows:
issue #!
#0 - 0xean
2023-07-27T23:53:08Z
Issue #1
#1 - c4-judge
2023-07-28T00:23:33Z
0xean marked the issue as duplicate of #27
#2 - c4-judge
2023-07-28T17:15:05Z
0xean marked the issue as satisfactory