Platform: Code4rena
Start Date: 22/09/2023
Pot Size: $100,000 USDC
Total HM: 15
Participants: 175
Period: 14 days
Judge: alcueca
Total Solo HM: 4
Id: 287
League: ETH
Rank: 94/175
Findings: 2
Award: $25.79
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: 0xTheC0der
Also found by: 0x180db, 0xDING99YA, 0xRstStn, 0xTiwa, 0xWaitress, 0xblackskull, 0xfuje, 3docSec, Aamir, Black_Box_DD, HChang26, Hama, Inspecktor, John_Femi, Jorgect, Kek, KingNFT, Kow, Limbooo, MIQUINHO, MrPotatoMagic, NoTechBG, Noro, Pessimistic, QiuhaoLi, SovaSlava, SpicyMeatball, T1MOH, TangYuanShen, Vagner, Viktor_Cortess, Yanchuan, _eperezok, alexweb3, alexxander, ast3ros, ayden, bin2chen, blutorque, btk, ciphermarco, ether_sky, gumgumzum, gztttt, hals, imare, its_basu, joaovwfreire, josephdara, klau5, kodyvim, ladboy233, marqymarq10, mert_eren, minhtrng, n1punp, nobody2018, oada, orion, peakbolt, peritoflores, perseverancesuccess, pfapostol, rvierdiiev, stuxy, tank, unsafesol, ustas, windhustler, zambody, zzzitron
0.1127 USDC - $0.11
The function payableCall()
is used to make calls on to other contracts from the virtualAccount which is linked to a userAddress
. This function utilizes the PayableCall struct show below.
struct PayableCall { address target; bytes callData; uint256 value; }
It allows for specially crafted calldata as well as any target address with any value. This allows for the theft of any ERC20, ERC721 or ERC1155 tokens owned by the contract through transfers or approvals.
function payableCall(PayableCall[] calldata calls) public payable returns (bytes[] memory returnData) { uint256 valAccumulator; uint256 length = calls.length; returnData = new bytes[](length); PayableCall calldata _call; for (uint256 i = 0; i < length;) { _call = calls[i]; uint256 val = _call.value; // Humanity will be a Type V Kardashev Civilization before this overflows - andreas // ~ 10^25 Wei in existence << ~ 10^76 size uint fits in a uint256 unchecked { valAccumulator += val; } bool success; if (isContract(_call.target)) (success, returnData[i]) = _call.target.call{value: val}(_call.callData); if (!success) revert CallFailed(); unchecked { ++i; } }
As seen above, the contract does not have any access controls to prevent malicious execution of calls. Passing a call with a Struct like this would easily wipe the contract:
PayableCall({ target: usdt address, value: 0, calldata: bytes(abi.encodeWithSignature(bytes4(keccak256("transfer(address,uint256)")), 5000e6)); })
Manual Review
I recommend implementing the proper access control provided in the contract. The requiresApprovedCaller
modifier.
Access Control
#0 - c4-pre-sort
2023-10-09T07:01:22Z
0xA5DF marked the issue as duplicate of #888
#1 - c4-pre-sort
2023-10-09T07:01:26Z
0xA5DF marked the issue as sufficient quality report
#2 - c4-judge
2023-10-26T11:32:47Z
alcueca marked the issue as satisfactory
🌟 Selected for report: MrPotatoMagic
Also found by: 0xAadi, 0xDING99YA, 0xDemon, 0xRstStn, 0xSmartContract, 0xStriker, 0xWaitress, 0xbrett8571, 0xfuje, 0xsagetony, 0xsurena, 33BYTEZZZ, 3docSec, 7ashraf, ABA, ABAIKUNANBAEV, Aamir, Audinarey, Bauchibred, Black_Box_DD, Daniel526, DanielArmstrong, DanielTan_MetaTrust, Dinesh11G, Eurovickk, Franklin, Inspecktor, John, Jorgect, Joshuajee, K42, Kek, Koolex, LokiThe5th, MIQUINHO, Myd, NoTechBG, QiuhaoLi, SanketKogekar, Sathish9098, Sentry, Soul22, SovaSlava, Stormreckson, Tendency, Topmark, Udsen, V1235816, Viktor_Cortess, Viraz, Yanchuan, ZdravkoHr, Zims, albahaca, albertwh1te, alexweb3, alexxander, ast3ros, audityourcontracts, bareli, bin2chen, bronze_pickaxe, c0pp3rscr3w3r, cartlex_, castle_chain, chaduke, debo, ether_sky, gumgumzum, imare, its_basu, jaraxxus, jasonxiale, josephdara, kodyvim, ladboy233, lanrebayode77, lsaudit, mert_eren, minhtrng, n1punp, nadin, niroh, nmirchev8, orion, peakbolt, perseverancesuccess, pfapostol, ptsanev, rvierdiiev, saneryee, shaflow2, te_aut, terrancrypt, twcctop, unsafesol, ustas, versiyonbir, windhustler, yongskiws, zhaojie, ziyou-
25.6785 USDC - $25.68
https://github.com/code-423n4/2023-09-maia/blob/f5ba4de628836b2a29f9b5fff59499690008c463/src/BranchBridgeAgent.sol#L180-L336 https://github.com/code-423n4/2023-09-maia/blob/f5ba4de628836b2a29f9b5fff59499690008c463/src/RootBridgeAgent.sol#L160-L344
LayerZero requires a fee to be paid to process transactions successfully.
If a user doesn't include enough eth for the gas, the transaction will fail, that's why an estimatefee
function is available on the layerZero Endpoint address.
However no check is done to prevent users from sending insufficient eth with transactions. This causes an issue as transactions will fail on the LayerZero layer since execution will run out of gas
function callOutAndBridge( address payable _refundee, address _recipient, uint16 _dstChainId, bytes calldata _params, SettlementInput calldata _sParams, GasParams calldata _gParams, bool _hasFallbackToggled ) external payable override lock requiresRouter { // Create Settlement and Perform call bytes memory payload = _createSettlement( settlementNonce, _refundee, _recipient, _dstChainId, _params, _sParams.globalAddress, _sParams.amount, _sParams.deposit, _hasFallbackToggled ); //Perform Call. _performCall(_dstChainId, _refundee, payload, _gParams); }
As we can see, estimatefee is not checked at all. It also is not checked in _performCall
Manual Review, LayerZero Docs
Get the estimated native fees for a transaction of a particular payload
using `estimateFees(), and check against the current
msg.value`` to ensure that it is enough.
Error
#0 - c4-pre-sort
2023-10-12T13:21:49Z
0xA5DF marked the issue as duplicate of #785
#1 - c4-pre-sort
2023-10-12T13:21:54Z
0xA5DF marked the issue as low quality report
#2 - 0xA5DF
2023-10-12T13:22:10Z
Identified only part of the issue, partial credit imo
#3 - c4-judge
2023-10-22T05:09:13Z
alcueca marked the issue as not a duplicate
#4 - alcueca
2023-10-22T05:10:09Z
As presented, it only impacts the user calling the function, and the only loss is gas
#5 - c4-judge
2023-10-22T05:10:24Z
alcueca changed the severity to QA (Quality Assurance)
#6 - c4-judge
2023-10-22T05:10:28Z
alcueca marked the issue as grade-a