From b35ca0cde7f9a868439a2f236d5f259efd1c0735 Mon Sep 17 00:00:00 2001 From: Luna Date: Sat, 26 Oct 2024 12:57:37 +0800 Subject: [PATCH 1/4] perfs: remove redundant parts --- security/1-reentrancy/theDAO/README.md | 197 +++++++++---------------- 1 file changed, 67 insertions(+), 130 deletions(-) diff --git a/security/1-reentrancy/theDAO/README.md b/security/1-reentrancy/theDAO/README.md index 1621329dd..dbbdacff7 100644 --- a/security/1-reentrancy/theDAO/README.md +++ b/security/1-reentrancy/theDAO/README.md @@ -1,23 +1,21 @@ # Reentrancy -Reentrancy attack can lead to great loss. How it works? Basicly, when contract A interact with contract B, contract B can reenter into contract A, exposing vulnerabilities. +Reentrancy 攻击可能导致严重的资产损失。这种攻击是如何实现的?基本上,当合约 A 调用合约 B 时,如果合约 B 再次调用合约 A 的函数,将暴露出潜在的漏洞。 -Here is a bad designed code: +以下是一个设计不当的示例代码: -``` +```solidity import "./IVault.sol"; import "hardhat/console.sol"; - contract BuggyVault is IVault { - - mapping(address=>uint256) public balances; + mapping(address => uint256) public balances; - function deposit() external override payable{ + function deposit() external override payable { balances[msg.sender] += msg.value; } - function withdraw() external override{ + function withdraw() external override { address payable target = payable(msg.sender); (bool success,) = target.call{gas:500000, value:balances[msg.sender]}(""); console.log(success); @@ -26,63 +24,15 @@ contract BuggyVault is IVault { } ``` -if there is a malicious contract with a malicious receive() or fallback(): -``` -pragma solidity ^0.8.7; - - -import "./IVault.sol"; -import "hardhat/console.sol"; -contract Malicious { - - IVault public vault; - - constructor(IVault _vault) { - vault = _vault; - } - - function addDeposit() external payable { - vault.deposit{value: msg.value}(); - } - - function withdrawFromVault() external { - vault.withdraw(); - } - - fallback() external payable{ - vault.withdraw(); - } -}pragma solidity ^0.8.7; +如果有一个恶意合约具备恶意的 `receive()` 或 `fallback()` 函数: +```solidity +pragma solidity ^0.8.7; import "./IVault.sol"; import "hardhat/console.sol"; -contract Malicious { - - IVault public vault; - - constructor(IVault _vault) { - vault = _vault; - } - function addDeposit() external payable { - vault.deposit{value: msg.value}(); - } - - function withdrawFromVault() external { - vault.withdraw(); - } - - fallback() external payable{ - vault.withdraw(); - } -}pragma solidity ^0.8.7; - - -import "./IVault.sol"; -import "hardhat/console.sol"; contract Malicious { - IVault public vault; constructor(IVault _vault) { @@ -93,148 +43,135 @@ contract Malicious { vault.deposit{value: msg.value}(); } - function withdrawFromVault() external { + function withdrawFromVault() external { vault.withdraw(); } - fallback() external payable{ + fallback() external payable { vault.withdraw(); } } ``` -When malicious contract calls withdraw() in BuggyVault, the money transfer reenterer into receive() of malicious receive, causing keeping sending ether to malicious contract. +当恶意合约调用 `BuggyVault` 的 `withdraw()` 函数时,资金传递到恶意合约的 `receive()` 函数中,导致反复调用 `withdraw()` 函数,不断地将以太坊转入恶意合约。 -Though reentrancy attack could potentially cause great, great loss, it is not that easy to happen. If we use "transfer" or "send" instead of "call", it would not happen. +虽然重入攻击可能导致严重损失,但使用 `transfer` 或 `send` 而不是 `call` 可以防止此类攻击。 -- what if we use "transfer": transfer has a limit gas of 2300 only to emit events, and failure of transfer would cause revert. -- what if we use "send": send has a limit gas of 2300 only to emit events, but failure of "send" would not revert, causing the attacker lost all his vault balance and not withdrawing any ether from the vault!Loosing everything :) - -# Analysis +- **使用 `transfer`**:`transfer` 的 gas 限制为 2300,仅足够触发事件,传输失败会触发回滚。 +- **使用 `send`**:`send` 同样有 2300 的 gas 限制,不会回滚传输失败的情况,攻击者将损失其在 Vault 中的余额而无法提取任何以太坊。 -The reentrancy attack is caused by many factors: -- it has reentrancies: When withdraw() in BuggyVault is called, it transfer money to Malicious contract, activating its fallback which enters into "withdraw()" again. -- it does not check balance: Before transfering money, withdraw() function does not check money. -- it doesn't specify gas limit: The default gas of "call" is 63*gas()/64, which is sufficient for malicious contract to do bad stuffs. -- it doen't check transfer result: Transfering ethers using "call" would not panic on failure, so you should alway check whether your call is success. +## 分析 +重入攻击的成因包括以下因素: +- **存在重入路径**:`BuggyVault` 的 `withdraw()` 函数在转账过程中调用恶意合约,激活其 `fallback()` 函数,再次调用 `withdraw()`。 +- **缺乏余额检查**:在转账前没有检查余额。 +- **缺少 gas 限制**:`call` 默认分配大量 gas,为恶意合约执行攻击提供条件。 +- **未检查传输结果**:`call` 的传输失败不会导致错误,因此应检查传输是否成功。 -# Best practices +## 最佳实践 -According to the analysis above, we can do several things: +根据以上分析,可以采用以下措施防范重入攻击: -## Use reentrancy guard(Only use it in risks) -We can mark additional status from reentrancy. Several libraries like openzeppelin can be helpful. Refer to SafeVault1.sol for more details. +### 使用 Reentrancy Guard(仅在必要时使用) -``` -pragma solidity ^0.8.7; +通过状态标记防止重入调用。像 OpenZeppelin 提供的库可用于实现此功能。参考 `SafeVault1.sol`: +```solidity +pragma solidity ^0.8.7; import "./IVault.sol"; import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; contract SafeVault1 is IVault, ReentrancyGuard { - - mapping(address=>uint256) public balances; + mapping(address => uint256) public balances; - function deposit() external override payable{ + function deposit() external override payable { balances[msg.sender] += msg.value; } - function withdraw() external override nonReentrant{ + function withdraw() external override nonReentrant { address payable target = payable(msg.sender); - (bool success,) = target.call{value:balances[msg.sender]}(""); + (bool success,) = target.call{value: balances[msg.sender]}(""); balances[msg.sender] = 0; } } ``` -Note that use this manner only when there is a risk of reentering since it costs more gas to maintain reentering status. +### 遵循“检查-影响-交互”模式(推荐) -You may find that in test "2 attack failed due to reentrancy", the malicious call is not reverted. Could you explain it? :) +“检查-影响-交互”模式能有效避免重入攻击: -## Keep Check-Effect-Interact pattern(Recommended) +1. **检查 (Check)**:严格检查条件。 +2. **影响 (Effect)**:修改内部状态。 +3. **交互 (Interact)**:与外部合约进行交互。 -Always keep Check-Effect-Interact pattern where: -- Check: strictly check your preconditions before your next move. -- Effect: modify your inner states. -- Interact: interact with other accounts. - - Following this style, the attack would not success because the vault balance has been changes. Refer to SafeVault2.sol for more details. +以下是 `SafeVault2.sol` 的示例: -``` +```solidity pragma solidity ^0.8.7; - import "./IVault.sol"; -import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; contract SafeVault2 is IVault { - - mapping(address=>uint256) public balances; + mapping(address => uint256) public balances; - function deposit() external override payable{ + function deposit() external override payable { balances[msg.sender] += msg.value; } - function withdraw() external override nonReentrant{ - //Checks - require(balances[msg.sender] > 0, "Insufficient money"); - //Effects + function withdraw() external override { + // 检查 + require(balances[msg.sender] > 0, "Insufficient balance"); + // 影响 + uint256 balance = balances[msg.sender]; balances[msg.sender] = 0; - //Interact + // 交互 address payable target = payable(msg.sender); - (bool success,) = target.call{value:balances[msg.sender]}(""); + (bool success,) = target.call{value: balance}(""); + require(success, "Transfer failed"); } } ``` -You may find that in test "3 attack failed due to check-effects-interact", the malicious call is not reverted. Could you explain it? :) +### 谨慎使用 `call` 进行转账(推荐) -## Careful use of call when transfering(Recommended) +转账有三种方式: -There are three ways of transfering native token: -- transfer: call "fallback()" or "receive()" if target is contract; only deliver 2300 gas; panic on failure. -- send: call "fallback()" or "receive()" if target is contract; only deliver 2300 gas; No panic on failure. -- call: call whatever function you want and gather return data; By default deliver gas()*63/64, and you can specify it. No panic on failure. +1. **`transfer`**:调用 `fallback()` 或 `receive()`,gas 限制为 2300,传输失败会回滚。 +2. **`send`**:调用 `fallback()` 或 `receive()`,gas 限制为 2300,传输失败不会回滚。 +3. **`call`**:可以指定传输 gas 并检查返回值。确保指定 gas 并检查成功状态。 -The "transfer" manner could fulfill most cases while maintaining safety; The "call" manner is strong, but be sure to specifiy the gas and CHECK THE RESULT!! Refer to GoodVault3.sol for more details. +以下是 `SafeVault3.sol` 的示例: - -``` +```solidity pragma solidity ^0.8.7; - import "./IVault.sol"; -import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; contract SafeVault3 is IVault { - - mapping(address=>uint256) public balances; + mapping(address => uint256) public balances; - function deposit() external override payable{ + function deposit() external override payable { balances[msg.sender] += msg.value; } - function withdraw() external override nonReentrant{ + function withdraw() external override { address payable target = payable(msg.sender); - (bool success,) = target.call{gas:2300, value:balances[msg.sender]}(""); - require(success, "transfer failed!"); + (bool success,) = target.call{gas: 2300, value: balances[msg.sender]}(""); + require(success, "Transfer failed!"); balances[msg.sender] = 0; - //Or simply use transfer: - //target.transfer(balances[msg.sender]); + // 或使用 transfer: + // target.transfer(balances[msg.sender]); } } ``` -# How to use - -``` -npm install -``` +## 使用说明 +安装依赖并运行测试: -``` +```bash +npm install npx hardhat test -``` \ No newline at end of file +``` From 997249429f00c2832d8694017fc47002bba1c2f1 Mon Sep 17 00:00:00 2001 From: Luna Date: Sat, 26 Oct 2024 13:06:00 +0800 Subject: [PATCH 2/4] perfs: optmize the dao contracts --- .../theDAO/contracts/BuggyVault.sol | 16 ++++++------- .../1-reentrancy/theDAO/contracts/IVault.sol | 7 +++--- .../theDAO/contracts/Malicious.sol | 10 ++++---- .../theDAO/contracts/SafeVault1.sol | 14 +++++------ .../theDAO/contracts/SafeVault2.sol | 23 +++++++++++-------- .../theDAO/contracts/SafeVault3.sol | 22 ++++++++++-------- 6 files changed, 49 insertions(+), 43 deletions(-) diff --git a/security/1-reentrancy/theDAO/contracts/BuggyVault.sol b/security/1-reentrancy/theDAO/contracts/BuggyVault.sol index 1de15d2c2..2ee341c6b 100644 --- a/security/1-reentrancy/theDAO/contracts/BuggyVault.sol +++ b/security/1-reentrancy/theDAO/contracts/BuggyVault.sol @@ -1,22 +1,22 @@ +// SPDX-License-Identifier: MIT pragma solidity ^0.8.7; - import "./IVault.sol"; import "hardhat/console.sol"; - contract BuggyVault is IVault { + mapping(address => uint256) public balances; - mapping(address=>uint256) public balances; - - function deposit() external override payable{ + function deposit() external override payable { balances[msg.sender] += msg.value; } - function withdraw() external override{ + function withdraw() external override { address payable target = payable(msg.sender); + (bool success,) = target.call{value:balances[msg.sender]}(""); console.log(success); - balances[msg.sender] = 0; + + balances[msg.sender] = 0; } -} \ No newline at end of file +} diff --git a/security/1-reentrancy/theDAO/contracts/IVault.sol b/security/1-reentrancy/theDAO/contracts/IVault.sol index 4021e1859..59057f7c2 100644 --- a/security/1-reentrancy/theDAO/contracts/IVault.sol +++ b/security/1-reentrancy/theDAO/contracts/IVault.sol @@ -1,8 +1,7 @@ +// SPDX-License-Identifier: MIT pragma solidity ^0.8.7; interface IVault { - - function deposit() external payable ; - + function deposit() external payable; function withdraw() external; -} \ No newline at end of file +} diff --git a/security/1-reentrancy/theDAO/contracts/Malicious.sol b/security/1-reentrancy/theDAO/contracts/Malicious.sol index 420e54d5e..95d048aac 100644 --- a/security/1-reentrancy/theDAO/contracts/Malicious.sol +++ b/security/1-reentrancy/theDAO/contracts/Malicious.sol @@ -1,10 +1,10 @@ +// SPDX-License-Identifier: MIT pragma solidity ^0.8.7; - import "./IVault.sol"; import "hardhat/console.sol"; -contract Malicious { +contract Malicious { IVault public vault; constructor(IVault _vault) { @@ -15,11 +15,11 @@ contract Malicious { vault.deposit{value: msg.value}(); } - function withdrawFromVault() external { + function withdrawFromVault() external { vault.withdraw(); } - fallback() external payable{ + fallback() external payable { vault.withdraw(); } -} \ No newline at end of file +} diff --git a/security/1-reentrancy/theDAO/contracts/SafeVault1.sol b/security/1-reentrancy/theDAO/contracts/SafeVault1.sol index 2c0f48848..156ba7901 100644 --- a/security/1-reentrancy/theDAO/contracts/SafeVault1.sol +++ b/security/1-reentrancy/theDAO/contracts/SafeVault1.sol @@ -1,21 +1,21 @@ +// SPDX-License-Identifier: MIT pragma solidity ^0.8.7; - import "./IVault.sol"; import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; import "hardhat/console.sol"; contract SafeVault1 is IVault, ReentrancyGuard { - - mapping(address=>uint256) public balances; + mapping(address => uint256) public balances; - function deposit() external override payable{ + function deposit() external override payable { balances[msg.sender] += msg.value; } - function withdraw() external override nonReentrant{ + function withdraw() external override nonReentrant { address payable target = payable(msg.sender); - (bool success,) = target.call{value:balances[msg.sender]}(""); + (bool success, ) = target.call{value: balances[msg.sender]}(""); + require(success, "Transfer failed"); balances[msg.sender] = 0; } -} \ No newline at end of file +} diff --git a/security/1-reentrancy/theDAO/contracts/SafeVault2.sol b/security/1-reentrancy/theDAO/contracts/SafeVault2.sol index 598f96853..9a382285b 100644 --- a/security/1-reentrancy/theDAO/contracts/SafeVault2.sol +++ b/security/1-reentrancy/theDAO/contracts/SafeVault2.sol @@ -1,24 +1,27 @@ +// SPDX-License-Identifier: MIT pragma solidity ^0.8.7; - import "./IVault.sol"; import "hardhat/console.sol"; contract SafeVault2 is IVault { - - mapping(address=>uint256) public balances; + mapping(address => uint256) public balances; - function deposit() external override payable{ + function deposit() external override payable { balances[msg.sender] += msg.value; } function withdraw() external override { - //Checks - require(balances[msg.sender] > 0, "Insufficient money"); - //Effects + // Checks + require(balances[msg.sender] > 0, "Insufficient balance"); + + // Effects + uint256 amount = balances[msg.sender]; balances[msg.sender] = 0; - //Interact + + // Interact address payable target = payable(msg.sender); - (bool success,) = target.call{value:balances[msg.sender]}(""); + (bool success, ) = target.call{value: amount}(""); + require(success, "Transfer failed"); } -} \ No newline at end of file +} diff --git a/security/1-reentrancy/theDAO/contracts/SafeVault3.sol b/security/1-reentrancy/theDAO/contracts/SafeVault3.sol index 22e6adab2..56f5103ef 100644 --- a/security/1-reentrancy/theDAO/contracts/SafeVault3.sol +++ b/security/1-reentrancy/theDAO/contracts/SafeVault3.sol @@ -1,23 +1,27 @@ +// SPDX-License-Identifier: MIT pragma solidity ^0.8.7; - import "./IVault.sol"; contract SafeVault3 is IVault { - - mapping(address=>uint256) public balances; + mapping(address => uint256) public balances; - function deposit() external override payable{ + function deposit() external override payable { balances[msg.sender] += msg.value; } function withdraw() external override { address payable target = payable(msg.sender); - (bool success,) = target.call{gas:2300, value:balances[msg.sender]}(""); - require(success, "transfer failed!"); + uint256 amount = balances[msg.sender]; + + // Interact + (bool success, ) = target.call{gas: 2300, value: amount}(""); + require(success, "Transfer failed"); + + // Effects balances[msg.sender] = 0; - //Or use transfer: - //target.transfer(balances[msg.sender]); + // Alternative using transfer: + // target.transfer(amount); } -} \ No newline at end of file +} From 2baf92046188027d64fb24a743e6f43ff324efb1 Mon Sep 17 00:00:00 2001 From: Luna Date: Sat, 26 Oct 2024 13:08:32 +0800 Subject: [PATCH 3/4] feat: add the dao test process --- security/1-reentrancy/theDAO/README.md | 28 ++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/security/1-reentrancy/theDAO/README.md b/security/1-reentrancy/theDAO/README.md index dbbdacff7..f4885d4f4 100644 --- a/security/1-reentrancy/theDAO/README.md +++ b/security/1-reentrancy/theDAO/README.md @@ -175,3 +175,31 @@ contract SafeVault3 is IVault { npm install npx hardhat test ``` + +## 测试流程 + +1. **设置签名人**:在 `before` 钩子中初始化 `vaultOwner`、`maliciousUser`、`user2` 和 `user3` 作为测试参与者的地址。 + +2. **第一部分 - 攻击成功测试**: + - 部署 `BuggyVault` 合约(存在重入攻击漏洞)并部署 `Malicious` 恶意合约,指向 `BuggyVault` 合约地址。 + - `maliciousUser`、`user2` 和 `user3` 分别向 `vault` 合约存款。 + - `maliciousUser` 使用恶意合约进行重入攻击调用 `withdrawFromVault` 函数。 + - 检查恶意合约的余额,确认重入攻击成功,并显示 `user2` 和 `user3` 无法再取回他们的资金。 + +3. **第二部分 - 因重入保护失败的攻击**: + - 部署 `SafeVault1` 合约(带重入保护机制),并将恶意合约连接到该 `vault` 合约。 + - 三个用户(`maliciousUser`、`user2` 和 `user3`)分别进行存款。 + - 尝试使用恶意合约进行重入攻击调用 `withdrawFromVault`。 + - 确认余额未被恶意用户提取,确认重入攻击失败。 + +4. **第三部分 - 因检查-效果-交互模式失败的攻击**: + - 部署 `SafeVault2` 合约(使用“检查-效果-交互”模式)。 + - 设置并进行存款操作。 + - 尝试使用恶意合约进行重入攻击,确认攻击失败并验证合约余额和恶意用户余额。 + +5. **第四部分 - 因使用 `call()` 防范失败的攻击**: + - 部署 `SafeVault3` 合约(使用限制性 `call()` 模式)。 + - 设置并进行存款操作。 + - 尝试使用恶意合约进行重入攻击,预期重入调用将会失败。 + +这些测试通过验证不同的合约防范措施,能够有效检测 `BuggyVault` 中的重入攻击漏洞并确认防御的有效性。 \ No newline at end of file From b378793f52e2ec3d24535d60bbff190b40ff580a Mon Sep 17 00:00:00 2001 From: Luna Date: Sat, 2 Nov 2024 11:06:42 +0800 Subject: [PATCH 4/4] docs: ensure consistency in the README's language --- security/1-reentrancy/theDAO/README.md | 112 ++++++++++++------------- 1 file changed, 56 insertions(+), 56 deletions(-) diff --git a/security/1-reentrancy/theDAO/README.md b/security/1-reentrancy/theDAO/README.md index f4885d4f4..087725ea9 100644 --- a/security/1-reentrancy/theDAO/README.md +++ b/security/1-reentrancy/theDAO/README.md @@ -1,8 +1,8 @@ # Reentrancy -Reentrancy 攻击可能导致严重的资产损失。这种攻击是如何实现的?基本上,当合约 A 调用合约 B 时,如果合约 B 再次调用合约 A 的函数,将暴露出潜在的漏洞。 +A reentrancy attack can lead to significant asset loss. How does this attack occur? Essentially, when Contract A calls Contract B, if Contract B calls back into a function in Contract A, it exposes a potential vulnerability. -以下是一个设计不当的示例代码: +Here’s an example of poorly designed code: ```solidity import "./IVault.sol"; @@ -24,7 +24,7 @@ contract BuggyVault is IVault { } ``` -如果有一个恶意合约具备恶意的 `receive()` 或 `fallback()` 函数: +If a malicious contract includes a harmful `receive()` or `fallback()` function: ```solidity pragma solidity ^0.8.7; @@ -53,28 +53,28 @@ contract Malicious { } ``` -当恶意合约调用 `BuggyVault` 的 `withdraw()` 函数时,资金传递到恶意合约的 `receive()` 函数中,导致反复调用 `withdraw()` 函数,不断地将以太坊转入恶意合约。 +When the malicious contract calls the `withdraw()` function of `BuggyVault`, funds are transferred to the malicious contract’s `receive()` function, triggering repeated `withdraw()` calls and continuously transferring Ether to the malicious contract. -虽然重入攻击可能导致严重损失,但使用 `transfer` 或 `send` 而不是 `call` 可以防止此类攻击。 +While reentrancy attacks can cause significant loss, using `transfer` or `send` instead of `call` can prevent such attacks. -- **使用 `transfer`**:`transfer` 的 gas 限制为 2300,仅足够触发事件,传输失败会触发回滚。 -- **使用 `send`**:`send` 同样有 2300 的 gas 限制,不会回滚传输失败的情况,攻击者将损失其在 Vault 中的余额而无法提取任何以太坊。 +- **Using `transfer`**: `transfer` has a gas limit of 2300, which is enough to trigger an event, and if the transfer fails, it reverts the transaction. +- **Using `send`**: `send` also has a 2300 gas limit, but it does not revert on failure, so attackers cannot withdraw any Ether if they don’t have sufficient balance in the Vault. -## 分析 +## Analysis -重入攻击的成因包括以下因素: -- **存在重入路径**:`BuggyVault` 的 `withdraw()` 函数在转账过程中调用恶意合约,激活其 `fallback()` 函数,再次调用 `withdraw()`。 -- **缺乏余额检查**:在转账前没有检查余额。 -- **缺少 gas 限制**:`call` 默认分配大量 gas,为恶意合约执行攻击提供条件。 -- **未检查传输结果**:`call` 的传输失败不会导致错误,因此应检查传输是否成功。 +The causes of a reentrancy attack include the following factors: +- **Existence of a reentrancy path**: The `withdraw()` function of `BuggyVault` calls into the malicious contract, which activates its `fallback()` function to re-enter `withdraw()`. +- **Lack of balance check**: No balance verification is done before the transfer. +- **Insufficient gas restriction**: `call` provides ample gas by default, enabling the malicious contract to execute the attack. +- **No transfer result check**: `call` doesn’t trigger an error on failed transfers, so it’s essential to verify whether the transfer succeeded. -## 最佳实践 +## Best Practices -根据以上分析,可以采用以下措施防范重入攻击: +Based on the analysis, the following measures can prevent reentrancy attacks: -### 使用 Reentrancy Guard(仅在必要时使用) +### Use a Reentrancy Guard (only when necessary) -通过状态标记防止重入调用。像 OpenZeppelin 提供的库可用于实现此功能。参考 `SafeVault1.sol`: +Prevent reentrant calls by using state flags. Libraries like OpenZeppelin provide this functionality. Refer to `SafeVault1.sol`: ```solidity pragma solidity ^0.8.7; @@ -97,15 +97,15 @@ contract SafeVault1 is IVault, ReentrancyGuard { } ``` -### 遵循“检查-影响-交互”模式(推荐) +### Follow the "Check-Effect-Interaction" Pattern (recommended) -“检查-影响-交互”模式能有效避免重入攻击: +The "Check-Effect-Interaction" pattern effectively prevents reentrancy attacks: -1. **检查 (Check)**:严格检查条件。 -2. **影响 (Effect)**:修改内部状态。 -3. **交互 (Interact)**:与外部合约进行交互。 +1. **Check**: Strictly verify conditions. +2. **Effect**: Update internal state. +3. **Interaction**: Interact with external contracts. -以下是 `SafeVault2.sol` 的示例: +Here’s an example of `SafeVault2.sol`: ```solidity pragma solidity ^0.8.7; @@ -120,12 +120,12 @@ contract SafeVault2 is IVault { } function withdraw() external override { - // 检查 + // Check require(balances[msg.sender] > 0, "Insufficient balance"); - // 影响 + // Effect uint256 balance = balances[msg.sender]; balances[msg.sender] = 0; - // 交互 + // Interaction address payable target = payable(msg.sender); (bool success,) = target.call{value: balance}(""); require(success, "Transfer failed"); @@ -133,15 +133,15 @@ contract SafeVault2 is IVault { } ``` -### 谨慎使用 `call` 进行转账(推荐) +### Carefully Use `call` for Transfers (recommended) -转账有三种方式: +There are three ways to transfer funds: -1. **`transfer`**:调用 `fallback()` 或 `receive()`,gas 限制为 2300,传输失败会回滚。 -2. **`send`**:调用 `fallback()` 或 `receive()`,gas 限制为 2300,传输失败不会回滚。 -3. **`call`**:可以指定传输 gas 并检查返回值。确保指定 gas 并检查成功状态。 +1. **`transfer`**: Calls `fallback()` or `receive()` with a 2300 gas limit and reverts on failure. +2. **`send`**: Calls `fallback()` or `receive()` with a 2300 gas limit but does not revert on failure. +3. **`call`**: Allows specifying gas and checking the return value. Be sure to specify gas and check success. -以下是 `SafeVault3.sol` 的示例: +Here’s an example of `SafeVault3.sol`: ```solidity pragma solidity ^0.8.7; @@ -161,45 +161,45 @@ contract SafeVault3 is IVault { require(success, "Transfer failed!"); balances[msg.sender] = 0; - // 或使用 transfer: + // Alternatively, use transfer: // target.transfer(balances[msg.sender]); } } ``` -## 使用说明 +## Instructions -安装依赖并运行测试: +Install dependencies and run tests: ```bash npm install npx hardhat test ``` -## 测试流程 +## Test Flow -1. **设置签名人**:在 `before` 钩子中初始化 `vaultOwner`、`maliciousUser`、`user2` 和 `user3` 作为测试参与者的地址。 +1. **Setup Signers**: Initialize `vaultOwner`, `maliciousUser`, `user2`, and `user3` as test participants in the `before` hook. -2. **第一部分 - 攻击成功测试**: - - 部署 `BuggyVault` 合约(存在重入攻击漏洞)并部署 `Malicious` 恶意合约,指向 `BuggyVault` 合约地址。 - - `maliciousUser`、`user2` 和 `user3` 分别向 `vault` 合约存款。 - - `maliciousUser` 使用恶意合约进行重入攻击调用 `withdrawFromVault` 函数。 - - 检查恶意合约的余额,确认重入攻击成功,并显示 `user2` 和 `user3` 无法再取回他们的资金。 +2. **Part 1 - Successful Attack Test**: + - Deploy the `BuggyVault` contract (with reentrancy vulnerability) and the `Malicious` contract pointing to the `BuggyVault` address. + - Have `maliciousUser`, `user2`, and `user3` deposit funds to the `vault`. + - `maliciousUser` initiates a reentrancy attack using the `withdrawFromVault` function. + - Verify that `user2` and `user3` are unable to recover their funds due to the attack. -3. **第二部分 - 因重入保护失败的攻击**: - - 部署 `SafeVault1` 合约(带重入保护机制),并将恶意合约连接到该 `vault` 合约。 - - 三个用户(`maliciousUser`、`user2` 和 `user3`)分别进行存款。 - - 尝试使用恶意合约进行重入攻击调用 `withdrawFromVault`。 - - 确认余额未被恶意用户提取,确认重入攻击失败。 +3. **Part 2 - Attack Failure due to Reentrancy Protection**: + - Deploy the `SafeVault1` contract (with reentrancy guard) and connect the malicious contract to this vault. + - Each user (`maliciousUser`, `user2`, and `user3`) makes a deposit. + - Attempt a reentrancy attack through the malicious contract’s `withdrawFromVault` function. + - Verify that the malicious user cannot extract their balance, confirming the attack failed. -4. **第三部分 - 因检查-效果-交互模式失败的攻击**: - - 部署 `SafeVault2` 合约(使用“检查-效果-交互”模式)。 - - 设置并进行存款操作。 - - 尝试使用恶意合约进行重入攻击,确认攻击失败并验证合约余额和恶意用户余额。 +4. **Part 3 - Attack Failure due to Check-Effect-Interaction Pattern**: + - Deploy the `SafeVault2` contract (using "Check-Effect-Interaction" pattern). + - Setup and make deposits. + - Attempt a reentrancy attack using the malicious contract, and verify that the attack failed, confirming contract and malicious user balances. -5. **第四部分 - 因使用 `call()` 防范失败的攻击**: - - 部署 `SafeVault3` 合约(使用限制性 `call()` 模式)。 - - 设置并进行存款操作。 - - 尝试使用恶意合约进行重入攻击,预期重入调用将会失败。 +5. **Part 4 - Attack Failure due to Restricted `call()` Use**: + - Deploy the `SafeVault3` contract (using restrictive `call()`). + - Setup and make deposits. + - Attempt a reentrancy attack, expecting the reentrancy call to fail. -这些测试通过验证不同的合约防范措施,能够有效检测 `BuggyVault` 中的重入攻击漏洞并确认防御的有效性。 \ No newline at end of file +These tests effectively detect the reentrancy vulnerability in `BuggyVault` and confirm the efficacy of the defensive measures. \ No newline at end of file