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

The Same address List of Owners with the same nonce will deploy different coinbase wallet #68

Open
c4-bot-5 opened this issue Mar 20, 2024 · 15 comments
Labels
bug Something isn't working disagree with severity Sponsor confirms validity, but disagrees with warden’s risk assessment (sponsor explain in comments) downgraded by judge Judge downgraded the risk level of this issue grade-a high quality report This report is of especially high quality primary issue Highest quality submission among a set of duplicates Q-17 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

@c4-bot-5
Copy link
Contributor

Lines of code

https://github.com/code-423n4/2024-03-coinbase/blob/e0573369b865d47fed778de00a7b6df65ab1744e/src/SmartWallet/CoinbaseSmartWalletFactory.sol#L28-L56

Vulnerability details

Impact

The same address list should give the same address based on the assumption that if the parameters passed are the same but the create account in the factory will deploy 2 different address if the same address are passed when the position of the address are switched with the same nonce. This error will break the already established fact that the same owners list and nonce should give the same address and revert without creating another account. But will not follow this assertion hence invalidating it. The impact of this issue is critical as it undermines the expected behavior of the system regarding account creation. Specifically, it disrupts the deterministic nature of account address generation, potentially leading to confusion, data inconsistencies, and security concerns. If left unaddressed, this issue could compromise the integrity and reliability of the system.

Proof of Concept

The vulnerability can be demonstrated through the following test scenario:

  1. Alice creates an owner list containing two addresses: Address 1 and Address 2, passing Address 1 before Address 2, and creates an account.
  2. Alice removes the list and passes the same addresses, but this time with Address 2 before Address 1, resulting in the creation of another account with a different address for Alice.

The provided test scenario confirms that the system behaves inconsistently when the order of addresses in the owner list is changed, leading to the creation of multiple accounts for the same owner.

function setUp() public {
    account = new CoinbaseSmartWallet();
    factory = new CoinbaseSmartWalletFactory(address(account));
    owners.push(abi.encode(address(1)));
    owners.push(abi.encode(address(2)));
}
  function test_createAccountDeploysToPredeterminedAddress() public {
        address p = factory.getAddress(owners, 0);
        CoinbaseSmartWallet a = factory.createAccount{value: 1e18}(owners, 0);
        assertEq(address(a), p);

        owners.pop();
  owners.pop();
  owners.push(abi.encode(address(2)));
  owners.push(abi.encode(address(1)));
  

 address pp = factory.getAddress(owners, 0);
 CoinbaseSmartWallet aa = factory.createAccount{value: 1e18}(owners,0);
 assertEq(address(aa), pp);

 assertEq(address(aa), address(a));

1 gives 0x761f8B0050e1C64663cdf67b163e139e74f2DDAd::isOwnerAddress;

2 gives0x00b70D9C493558d6F201966236B643EE44a43f30::isOwnerAddress;

Ran 1 test for test/SmartWallet/CoinbaseSmartWalletFactory.t.sol:CoinbaseSmartWalletFactoryTest
[FAIL. Reason: assertion failed] test_createAccountDeploysToPredeterminedAddress() (gas: 545531)
Logs:
  Error: a == b not satisfied [address]
        Left: 0x00b70D9C493558d6F201966236B643EE44a43f30
       Right: 0x761f8B0050e1C64663cdf67b163e139e74f2DDAd

Traces:
  [545531] CoinbaseSmartWalletFactoryTest::test_createAccountDeploysToPredeterminedAddress()
    ├─ [1979] CoinbaseSmartWalletFactory::getAddress([0x0000000000000000000000000000000000000000000000000000000000000001, 0x0000000000000000000000000000000000000000000000000000000000000002], 115792089237316195423570985008687907853269984665640564039457584007913129639935 [1.157e77]) [staticcall]
    │   └─ ← 0x761f8B0050e1C64663cdf67b163e139e74f2DDAd
    ├─ [241054] CoinbaseSmartWalletFactory::createAccount{value: 1000000000000000000}([0x0000000000000000000000000000000000000000000000000000000000000001, 0x0000000000000000000000000000000000000000000000000000000000000002], 115792089237316195423570985008687907853269984665640564039457584007913129639935 [1.157e77])    
    │   ├─ [34337] → new <unknown>@0x761f8B0050e1C64663cdf67b163e139e74f2DDAd
    │   │   └─ ← 95 bytes of code
    │   ├─ [168692] 0x761f8B0050e1C64663cdf67b163e139e74f2DDAd::initialize([0x0000000000000000000000000000000000000000000000000000000000000001, 0x0000000000000000000000000000000000000000000000000000000000000002])
    │   │   ├─ [165886] CoinbaseSmartWallet::initialize([0x0000000000000000000000000000000000000000000000000000000000000001, 0x0000000000000000000000000000000000000000000000000000000000000002]) [delegatecall]
    │   │   │   ├─ emit AddOwner(index: 0, owner: 0x0000000000000000000000000000000000000000000000000000000000000001)
    │   │   │   ├─ emit AddOwner(index: 1, owner: 0x0000000000000000000000000000000000000000000000000000000000000002)
    │   │   │   └─ ← ()
    │   │   └─ ← ()
    │   └─ ← 0x761f8B0050e1C64663cdf67b163e139e74f2DDAd
    ├─ [1979] CoinbaseSmartWalletFactory::getAddress([0x0000000000000000000000000000000000000000000000000000000000000002, 0x0000000000000000000000000000000000000000000000000000000000000001], 115792089237316195423570985008687907853269984665640564039457584007913129639935 [1.157e77]) [staticcall]
    │   └─ ← 0x00b70D9C493558d6F201966236B643EE44a43f30
    ├─ [238554] CoinbaseSmartWalletFactory::createAccount{value: 1000000000000000000}([0x0000000000000000000000000000000000000000000000000000000000000002, 0x0000000000000000000000000000000000000000000000000000000000000001], 115792089237316195423570985008687907853269984665640564039457584007913129639935 [1.157e77])    
    │   ├─ [34337] → new <unknown>@0x00b70D9C493558d6F201966236B643EE44a43f30
    │   │   └─ ← 95 bytes of code
    │   ├─ [166192] 0x00b70D9C493558d6F201966236B643EE44a43f30::initialize([0x0000000000000000000000000000000000000000000000000000000000000002, 0x0000000000000000000000000000000000000000000000000000000000000001])
    │   │   ├─ [165886] CoinbaseSmartWallet::initialize([0x0000000000000000000000000000000000000000000000000000000000000002, 0x0000000000000000000000000000000000000000000000000000000000000001]) [delegatecall]
    │   │   │   ├─ emit AddOwner(index: 0, owner: 0x0000000000000000000000000000000000000000000000000000000000000002)
    │   │   │   ├─ emit AddOwner(index: 1, owner: 0x0000000000000000000000000000000000000000000000000000000000000001)
    │   │   │   └─ ← ()
    │   │   └─ ← ()
    │   └─ ← 0x00b70D9C493558d6F201966236B643EE44a43f30
    ├─ emit log(val: "Error: a == b not satisfied [address]")
    ├─ emit log_named_address(key: "      Left", val: 0x00b70D9C493558d6F201966236B643EE44a43f30)
    ├─ emit log_named_address(key: "     Right", val: 0x761f8B0050e1C64663cdf67b163e139e74f2DDAd)
    ├─ [0] VM::store(VM: [0x7109709ECfa91a80626fF3989D68f67F5b1DD12D], 0x6661696c65640000000000000000000000000000000000000000000000000000, 0x0000000000000000000000000000000000000000000000000000000000000001)
    │   └─ ← ()
    └─ ← ()

Test result: FAILED. 0 passed; 1 failed; 0 skipped; finished in 2.07ms

Ran 1 test suite in 2.07ms: 0 tests passed, 1 failed, 0 skipped (1 total tests)

Failing tests:
Encountered 1 failing test in test/SmartWallet/CoinbaseSmartWalletFactory.t.sol:CoinbaseSmartWalletFactoryTest
[FAIL. Reason: assertion failed] test_createAccountDeploysToPredeterminedAddress() (gas: 545531)

when the list of owner was enlogated the system upholds this assertion

function setUp() public {
       account = new CoinbaseSmartWallet();
       factory = new CoinbaseSmartWalletFactory(address(account));
       owners.push(abi.encode(address(1)));
       owners.push(abi.encode(address(2)));
        owners.push(abi.encode(address(3)));
        owners.push(abi.encode(address(4)));
   }
  function test_createAccountDeploysToPredeterminedAddress() public {
       address p = factory.getAddress(owners, 0);
       CoinbaseSmartWallet a = factory.createAccount{value: 1e18}(owners, 0);
       assertEq(address(a), p);

       owners.pop();
 owners.pop();
 owners.push(abi.encode(address(2)));
 owners.push(abi.encode(address(1)));
 owners.push(abi.encode(address(3)));
 owners.push(abi.encode(address(4)));
 

address pp = factory.getAddress(owners, 0);
CoinbaseSmartWallet aa = factory.createAccount{value: 1e18}(owners,0);
assertEq(address(aa), pp);

assertEq(address(aa), address(a));

Ran 1 test for test/SmartWallet/CoinbaseSmartWalletFactory.t.sol:CoinbaseSmartWalletFactoryTest
[FAIL. Reason: AlreadyOwner(0x0000000000000000000000000000000000000000000000000000000000000002)] test_createAccountDeploysToPredeterminedAddress() (gas: 801156)
Traces:
 [792756] CoinbaseSmartWalletFactoryTest::test_createAccountDeploysToPredeterminedAddress()
   ├─ [2755] CoinbaseSmartWalletFactory::getAddress([0x0000000000000000000000000000000000000000000000000000000000000001, 0x0000000000000000000000000000000000000000000000000000000000000002, 0x0000000000000000000000000000000000000000000000000000000000000003, 0x0000000000000000000000000000000000000000000000000000000000000004], 0) [staticcall]
   │   └─ ← 0xcd6C85B8890C999EE10eF30e9ce0aC831A7B6E58
   ├─ [385457] CoinbaseSmartWalletFactory::createAccount{value: 1000000000000000000}([0x0000000000000000000000000000000000000000000000000000000000000001, 0x0000000000000000000000000000000000000000000000000000000000000002, 0x0000000000000000000000000000000000000000000000000000000000000003, 0x0000000000000000000000000000000000000000000000000000000000000004], 0)
   │   ├─ [34337] → new <unknown>@0xcd6C85B8890C999EE10eF30e9ce0aC831A7B6E58
   │   │   └─ ← 95 bytes of code
   │   ├─ [311578] 0xcd6C85B8890C999EE10eF30e9ce0aC831A7B6E58::initialize([0x0000000000000000000000000000000000000000000000000000000000000001, 0x0000000000000000000000000000000000000000000000000000000000000002, 0x0000000000000000000000000000000000000000000000000000000000000003, 0x0000000000000000000000000000000000000000000000000000000000000004])
   │   │   ├─ [308736] CoinbaseSmartWallet::initialize([0x0000000000000000000000000000000000000000000000000000000000000001, 0x0000000000000000000000000000000000000000000000000000000000000002, 0x0000000000000000000000000000000000000000000000000000000000000003, 0x0000000000000000000000000000000000000000000000000000000000000004]) [delegatecall]
   │   │   │   ├─ emit AddOwner(index: 0, owner: 0x0000000000000000000000000000000000000000000000000000000000000001)
   │   │   │   ├─ emit AddOwner(index: 1, owner: 0x0000000000000000000000000000000000000000000000000000000000000002)
   │   │   │   ├─ emit AddOwner(index: 2, owner: 0x0000000000000000000000000000000000000000000000000000000000000003)
   │   │   │   ├─ emit AddOwner(index: 3, owner: 0x0000000000000000000000000000000000000000000000000000000000000004)
   │   │   │   └─ ← ()
   │   │   └─ ← ()
   │   └─ ← 0xcd6C85B8890C999EE10eF30e9ce0aC831A7B6E58
   ├─ [3532] CoinbaseSmartWalletFactory::getAddress([0x0000000000000000000000000000000000000000000000000000000000000001, 0x0000000000000000000000000000000000000000000000000000000000000002, 0x0000000000000000000000000000000000000000000000000000000000000002, 0x0000000000000000000000000000000000000000000000000000000000000001, 0x0000000000000000000000000000000000000000000000000000000000000003, 0x0000000000000000000000000000000000000000000000000000000000000004], 0) [staticcall]
   │   └─ ← 0x5f01e364aA7bca426802DD1ac2275371Dc84eaE9
   ├─ [245225] CoinbaseSmartWalletFactory::createAccount{value: 1000000000000000000}([0x0000000000000000000000000000000000000000000000000000000000000001, 0x0000000000000000000000000000000000000000000000000000000000000002, 0x0000000000000000000000000000000000000000000000000000000000000002, 0x0000000000000000000000000000000000000000000000000000000000000001, 0x0000000000000000000000000000000000000000000000000000000000000003, 0x0000000000000000000000000000000000000000000000000000000000000004], 0)
   │   ├─ [34337] → new <unknown>@0x5f01e364aA7bca426802DD1ac2275371Dc84eaE9
   │   │   └─ ← 95 bytes of code
   │   ├─ [169896] 0x5f01e364aA7bca426802DD1ac2275371Dc84eaE9::initialize([0x0000000000000000000000000000000000000000000000000000000000000001, 0x0000000000000000000000000000000000000000000000000000000000000002, 0x0000000000000000000000000000000000000000000000000000000000000002, 0x0000000000000000000000000000000000000000000000000000000000000001, 0x0000000000000000000000000000000000000000000000000000000000000003, 0x0000000000000000000000000000000000000000000000000000000000000004])
   │   │   ├─ [169507] CoinbaseSmartWallet::initialize([0x0000000000000000000000000000000000000000000000000000000000000001, 0x0000000000000000000000000000000000000000000000000000000000000002, 0x0000000000000000000000000000000000000000000000000000000000000002, 0x0000000000000000000000000000000000000000000000000000000000000001, 0x0000000000000000000000000000000000000000000000000000000000000003, 0x0000000000000000000000000000000000000000000000000000000000000004]) [delegatecall]
   │   │   │   ├─ emit AddOwner(index: 0, owner: 0x0000000000000000000000000000000000000000000000000000000000000001)
   │   │   │   ├─ emit AddOwner(index: 1, owner: 0x0000000000000000000000000000000000000000000000000000000000000002)
   │   │   │   └─ ← AlreadyOwner(0x0000000000000000000000000000000000000000000000000000000000000002)
   │   │   └─ ← AlreadyOwner(0x0000000000000000000000000000000000000000000000000000000000000002)
   │   └─ ← AlreadyOwner(0x0000000000000000000000000000000000000000000000000000000000000002)
   └─ ← AlreadyOwner(0x0000000000000000000000000000000000000000000000000000000000000002)

Test result: FAILED. 0 passed; 1 failed; 0 skipped; finished in 5.00ms

Ran 1 test suite in 5.00ms: 0 tests passed, 1 failed, 0 skipped (1 total tests)

Failing tests:
Encountered 1 failing test in test/SmartWallet/CoinbaseSmartWalletFactory.t.sol:CoinbaseSmartWalletFactoryTest
[FAIL. Reason: AlreadyOwner(0x0000000000000000000000000000000000000000000000000000000000000002)] test_createAccountDeploysToPredeterminedAddress() (gas: 801156)

Encountered a total of 1 failing tests, 0 tests succeeded

Tools Used

The vulnerability was identified through manual testing and analysis, utilizing custom test scripts and the Foundry testing framework.

Recommended Mitigation Steps

The protocol should consider reverting the create wallet function when the number of owners is 2 or should review this function to ensure that the same addresses will not deploy different account with the same nonce

Assessed type

Error

@c4-bot-5 c4-bot-5 added 3 (High Risk) Assets can be stolen/lost/compromised directly bug Something isn't working labels Mar 20, 2024
c4-bot-7 added a commit that referenced this issue Mar 20, 2024
@c4-pre-sort
Copy link

raymondfam marked the issue as sufficient quality report

@c4-pre-sort c4-pre-sort added the sufficient quality report This report is of sufficient quality label Mar 22, 2024
@c4-pre-sort
Copy link

raymondfam marked the issue as high quality report

@c4-pre-sort c4-pre-sort added high quality report This report is of especially high quality and removed sufficient quality report This report is of sufficient quality labels Mar 22, 2024
@raymondfam
Copy link

raymondfam commented Mar 22, 2024

This would indeed be an issue and extend beyond securing SCW deployment on various chains for the same address.

@c4-pre-sort
Copy link

raymondfam marked the issue as duplicate of #46

@c4-pre-sort
Copy link

raymondfam marked the issue as not a duplicate

@c4-pre-sort
Copy link

raymondfam marked the issue as primary issue

@wilsoncusack
Copy link

This is working as intended. Users need to pass original owners in the same order to get the same address.

@c4-sponsor
Copy link

wilsoncusack marked the issue as disagree with severity

@c4-sponsor c4-sponsor added disagree with severity Sponsor confirms validity, but disagrees with warden’s risk assessment (sponsor explain in comments) sponsor acknowledged Technically the issue is correct, but we're not going to resolve it for XYZ reasons labels Mar 26, 2024
@c4-sponsor
Copy link

wilsoncusack (sponsor) acknowledged

@3docSec
Copy link

3docSec commented Mar 27, 2024

User mistake with no impact other than purely UX - having to recreate the wallet with a different input order.

@c4-judge
Copy link
Contributor

3docSec changed the severity to QA (Quality Assurance)

@c4-judge c4-judge added downgraded by judge Judge downgraded the risk level of this issue QA (Quality Assurance) Assets are not at risk. State handling, function incorrect as to spec, issues with clarity, syntax and removed 3 (High Risk) Assets can be stolen/lost/compromised directly labels Mar 27, 2024
@c4-judge
Copy link
Contributor

3docSec marked the issue as grade-a

@c4-judge c4-judge added grade-a 2 (Med Risk) Assets not at direct risk, but function/availability of the protocol could be impacted or leak value and removed downgraded by judge Judge downgraded the risk level of this issue QA (Quality Assurance) Assets are not at risk. State handling, function incorrect as to spec, issues with clarity, syntax labels Mar 27, 2024
@c4-judge
Copy link
Contributor

This previously downgraded issue has been upgraded by 3docSec

@3docSec
Copy link

3docSec commented Mar 27, 2024

(temporarily upgrading to to dedupe #114)

@c4-judge c4-judge added downgraded by judge Judge downgraded the risk level of this issue 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 27, 2024
@c4-judge
Copy link
Contributor

3docSec changed the severity to QA (Quality Assurance)

@C4-Staff C4-Staff added the Q-17 label Apr 1, 2024
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) downgraded by judge Judge downgraded the risk level of this issue grade-a high quality report This report is of especially high quality primary issue Highest quality submission among a set of duplicates Q-17 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

8 participants