Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

REENTRANCY ATTACK POSSIBLE IF THE _feeTo IS A MALICIOUS CONTRACT IN FeeWrapper._chargeFeePayable() FUNCTION #1166

Open
code423n4 opened this issue Apr 13, 2023 · 2 comments
Labels
2 (Med Risk) Assets not at direct risk, but function/availability of the protocol could be impacted or leak value bug Something isn't working M-08 primary issue Highest quality submission among a set of duplicates selected for report This submission will be included/highlighted in the audit report

Comments

@code423n4
Copy link
Contributor

Lines of code

https://github.com/code-423n4/2023-04-rubicon/blob/main/contracts/utilities/FeeWrapper.sol#L76-L89
https://github.com/code-423n4/2023-04-rubicon/blob/main/contracts/utilities/FeeWrapper.sol#L108-L121

Vulnerability details

Impact

Reentrancy attack could happen if _feeTo is a malicious contract and has recieve() function implemented in _feeTo contract. FeeWrapper._chargeFeePayable() function is used to send the _feeAmount of Eth to the _feeTo address.

Consider that FeeWrapper._chargeFeePayable() is called by the FeeWrapper._rubicallPayable() function with the necessary _feeParams input parameter, and the following scenario can occur:

1 . The call to (bool OK, ) = payable(_feeTo).call{value: _feeAmount}("") is made. Lets assume the _totalAmount = 1Eth and _feeAmount = 0.1Eth initially.
2 . _feeTo contract has recieve() function implemented in it.
3 . It reenters the FeeWrapper contract by calling the rubicallPayable() function by sending 0.01Eth as msg.value.
4 . Sets the _feeParams.totalAmount = 0.01Eth and _feeParams.feeAmount = 0.1Eth in the call to rubicallPayable()
5 . FeeWrapper.chargeFeePayable() will be called again by the rubicallPayable() function.
6 . (bool OK, ) = payable(_feeTo).call{value: _feeAmount}(""); will be called and Tx will pass since there is enouth Eth in the contract from the previous deposit.
7 . Again _feeParams.feeAmount = 0.1Eth will be transferred to the _feeTo address.
8 . This can be continued till there is enough Eth in the contract.
9 . This happens due to there is no check to make sure msg.value > _feeAmount before the low level call to the _feeTo address.

Proof of Concept

function _chargeFeePayable(
    FeeParams memory _feeParams
) internal returns (uint256 _msgValue) {
    // _feeToken is ETH
    uint256 _totalAmount = _feeParams.totalAmount;
    uint256 _feeAmount = _feeParams.feeAmount;
    address _feeTo = _feeParams.feeTo;
    require(msg.value == _totalAmount, "FeeWrapper: not enough ETH sent");

    // transfer fee to the 3rd party protocol
    (bool OK, ) = payable(_feeTo).call{value: _feeAmount}("");
	require(OK, "ETH transfer failed");
    _msgValue = msg.value - _feeAmount;
}

https://github.com/code-423n4/2023-04-rubicon/blob/main/contracts/utilities/FeeWrapper.sol#L108-L121

function _rubicallPayable(
    CallParams memory _params
) internal returns (bytes memory) {
    // charge fee from feeParams
    uint256 _msgValue = _chargeFeePayable(_params.feeParams);

    (bool _OK, bytes memory _data) = _params.target.call{value: _msgValue}(
        bytes.concat(_params.selector, _params.args)
    );

    require(_OK, "low-level call to the router failed");

    return _data;
}

https://github.com/code-423n4/2023-04-rubicon/blob/main/contracts/utilities/FeeWrapper.sol#L76-L89

Tools Used

Manual Review and VSCode

Recommended Mitigation Steps

Two options can be recommended to mitigate this issue:

  1. Add a reentrancy guard. Openzeppelin ReentrancyGuard.sol can be used, and nonReentrant modifier can be added to both FeeWrapper._chargeFeePayable() and FeeWrapper._rubicallPayable() functions.

  2. Check that the ``msg.value > _feeAmountbefore the low levelcall` to the `_feeTo` address is made. In that case there is no gain to the `_feeTo` malicious contract, since the value it sending via `msg.value` is any way greater than the `_feeAmount` which he can steal via `reentrancy attack`.

@code423n4 code423n4 added 2 (Med Risk) Assets not at direct risk, but function/availability of the protocol could be impacted or leak value bug Something isn't working labels Apr 13, 2023
code423n4 added a commit that referenced this issue Apr 13, 2023
@c4-pre-sort
Copy link

0xSorryNotSorry marked the issue as duplicate of #495

@c4-judge
Copy link
Contributor

HickupHH3 marked the issue as selected for report

@c4-judge c4-judge reopened this May 20, 2023
@c4-judge c4-judge added primary issue Highest quality submission among a set of duplicates selected for report This submission will be included/highlighted in the audit report labels May 20, 2023
@C4-Staff C4-Staff added the M-08 label Jun 16, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
2 (Med Risk) Assets not at direct risk, but function/availability of the protocol could be impacted or leak value bug Something isn't working M-08 primary issue Highest quality submission among a set of duplicates selected for report This submission will be included/highlighted in the audit report
Projects
None yet
Development

No branches or pull requests

4 participants