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

An admin can rug pull by changing contract addresses #40

Closed
code423n4 opened this issue Feb 21, 2022 · 4 comments
Closed

An admin can rug pull by changing contract addresses #40

code423n4 opened this issue Feb 21, 2022 · 4 comments
Labels
bug Something isn't working disagree with severity Sponsor confirms validity, but disagrees with warden’s risk assessment (sponsor explain in comments) duplicate This issue or pull request already exists QA (Quality Assurance) Assets are not at risk. State handling, function incorrect as to spec, issues with clarity, syntax sponsor acknowledged Technically the issue is correct, but we're not going to resolve it for XYZ reasons

Comments

@code423n4
Copy link
Contributor

Lines of code

https://github.com/code-423n4/2022-02-hubble/blob/ed1d885d5dbc2eae24e43c3ecbf291a0f5a52765/contracts/AMM.sol#L730-L734
https://github.com/code-423n4/2022-02-hubble/blob/ed1d885d5dbc2eae24e43c3ecbf291a0f5a52765/contracts/InsuranceFund.sol#L116-L119

Vulnerability details

Impact

By implementing malicious versions of the interfaces required by an IRegistry, the single-sig governor admin address can rug pull funds.

Even if the governor is benevolent the fact that there is a rug vector available may negatively impact the protocol's reputation. In addition the single private key may be taken in a hack. See this example where a similar finding has been flagged as a high-severity issue.

Proof of Concept

By silently changing the oracle of each AMM...

    function syncDeps(address _registry) public onlyGovernance {
        IRegistry registry = IRegistry(_registry);
        clearingHouse = registry.clearingHouse();
        oracle = IOracle(registry.oracle());
    }

https://github.com/code-423n4/2022-02-hubble/blob/ed1d885d5dbc2eae24e43c3ecbf291a0f5a52765/contracts/AMM.sol#L730-L734

...the oracle.getUnderlyingTwapPrice() can be made to return a value favorable to the attacker in all calls to settleFunding(), draining funds

    function getUnderlyingTwapPrice(uint256 _intervalInSeconds) public view returns (int256) {
        return oracle.getUnderlyingTwapPrice(underlyingAsset, _intervalInSeconds);
    }

https://github.com/code-423n4/2022-02-hubble/blob/ed1d885d5dbc2eae24e43c3ecbf291a0f5a52765/contracts/AMM.sol#L387-L389

By silently changing the marginAccount of the InsuranceFund...

    function syncDeps(IRegistry _registry) public onlyGovernance {
        vusd = IERC20(_registry.vusd());
        marginAccount = _registry.marginAccount();
    }

https://github.com/code-423n4/2022-02-hubble/blob/ed1d885d5dbc2eae24e43c3ecbf291a0f5a52765/contracts/InsuranceFund.sol#L116-L119

...the marginAccount can be made to receive all pending obligation funds

    function settlePendingObligation() public {
        if (pendingObligation > 0) {
            uint toTransfer = Math.min(vusd.balanceOf(address(this)), pendingObligation);
            if (toTransfer > 0) {
                pendingObligation -= toTransfer;
                vusd.safeTransfer(marginAccount, toTransfer);
            }
        }
    }

https://github.com/code-423n4/2022-02-hubble/blob/ed1d885d5dbc2eae24e43c3ecbf291a0f5a52765/contracts/InsuranceFund.sol#L77-L85

The provided deployment script only uses a signer rather than a contract as the governance address. Furthermore, the live environment deployed on testnet has a deployed InsuranceFund which uses the onlyGovernance modifier...

    function syncDeps(IRegistry _registry) public onlyGovernance {
        vusd = IERC20(_registry.vusd());
        marginAccount = _registry.marginAccount();
    }

https://github.com/code-423n4/2022-02-hubble/blob/ed1d885d5dbc2eae24e43c3ecbf291a0f5a52765/contracts/InsuranceFund.sol#L116-L119

...and the only transaction interacting with this function appears here and is called by an address, not a contract. There are no other transactions to the insurance fund to change the governance address, so it's clear that the testnet does not use a multisig either.

Tools Used

Code inspection
Hardhat
snowtrace.io

Recommended Mitigation Steps

Use a multisig and disable the ability to change contract addresses

@code423n4 code423n4 added 3 (High Risk) Assets can be stolen/lost/compromised directly bug Something isn't working labels Feb 21, 2022
code423n4 added a commit that referenced this issue Feb 21, 2022
@atvanguard
Copy link
Collaborator

Acknowledging. System heavily relies on the admins to do the right thing, the right way. It's a design choice. Moreover, the governance contract (already voted and created by the DAO) is controlled by reputed members from the ecosystem.

Would classify this as 0 (Informational).

@moose-code
Copy link
Collaborator

@JasoonS lets go through and settle on this one. Further thinking of downgrading to low/informational. Simply the presence of upgradeable smart contracts means everything here plus more is obviously possible by admin.

At the early stage of system development, this is a design choice often taken while many changes still need to likely occur to the system. It is true that timelocks and admin multisigs and other precautions can be put in place. Sounds like the admin is a multisig anyways.

@moose-code moose-code added QA (Quality Assurance) Assets are not at risk. State handling, function incorrect as to spec, issues with clarity, syntax and removed 2 (Med Risk) Assets not at direct risk, but function/availability of the protocol could be impacted or leak value labels Mar 6, 2022
@moose-code
Copy link
Collaborator

Judging all "admin keys can rug or set funny values" as low severity. This is less helpful for the sponsor

@JeeberC4 JeeberC4 added the duplicate This issue or pull request already exists label Mar 24, 2022
@JeeberC4
Copy link

Grouping with warden's QA Report #32

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working disagree with severity Sponsor confirms validity, but disagrees with warden’s risk assessment (sponsor explain in comments) duplicate This issue or pull request already exists QA (Quality Assurance) Assets are not at risk. State handling, function incorrect as to spec, issues with clarity, syntax sponsor acknowledged Technically the issue is correct, but we're not going to resolve it for XYZ reasons
Projects
None yet
Development

No branches or pull requests

4 participants