Skip to content
KitploitKITPLOIT
工具漏洞利用博客
Log in
提交
工具漏洞利用博客
提交

黑客、渗透测试和网络安全工具,武装您的安全武器库!

Kitploit 是一个黑客、网络安全和渗透测试工具的目录。发现最新的项目更新,查找漏洞、分析系统、自动化测试并加强你的安全。

订阅源联系隐私© 2026 Kitploit

工具目录

分类

查看所有分类
Loading categories
SoliditySecurity — Solidity 安全 | Kitploit
工具/GitHubGitHub/al1ex/soliditysecurity
漏洞分析代码分析学习与教育精选资源
GitHubal1ex/soliditysecurity

SoliditySecurity

Solidity 安全

查看仓库
35185年前尚未审核

最受欢迎

查看全部 →

发现我们社区最常用的工具。

探索所有工具

浏览我们的工具集合

查看所有工具 →
分享

这是什么

这篇文章旨在作为一篇相对深入且最新的入门文章,详细介绍了 Solidity 开发者过去犯下的错误,以防止未来的开发者重蹈覆辙。

目录

1. 重入

  • 漏洞
  • 预防技术
  • 真实世界示例:The DAO

2. 算术溢出/下溢

  • 漏洞
  • 预防技术
  • 真实世界示例:PoWHC 和批量转账溢出(CVE-2018-10299)

3. 意外 Ether

  • 漏洞
  • 预防技术
  • 真实世界示例:未知

4. Delegatecall

  • 漏洞
  • 预防技术
  • 真实世界示例:Parity Multisig 钱包(第二次攻击)

5. 默认可见性

  • 漏洞
  • 预防技术
  • 真实世界示例:Parity MultiSig 钱包(第一次攻击)

6. 熵错觉

  • 漏洞
  • 预防技术
  • 真实世界示例:PRNG 合约

7. 外部合约引用

  • 漏洞
  • 预防技术
  • 真实世界示例:重入蜜罐

8. 短地址/参数攻击

  • 漏洞
  • 预防技术
  • 真实世界示例:未知

9. 未检查的 CALL 返回值

  • 漏洞
  • 预防技术
  • 真实世界示例:Etherpot 和 King of the Ether

10. 竞争条件 / 抢先交易

  • 漏洞
  • 预防技术
  • 真实世界示例:ERC20 和 Bancor

11. 拒绝服务(DOS)

  • 漏洞
  • 预防技术
  • 真实世界示例:GovernMental

12. 区块时间戳操纵

  • 漏洞
  • 预防技术
  • 真实世界示例:GovernMental

13. 小心构造函数

  • 漏洞
  • 预防技术
  • 真实世界示例:Rubixi

14. 未初始化的存储指针

  • 漏洞
  • 预防技术
  • 真实世界示例:蜜罐:OpenAddressLottery 和 CryptoRoulette

15. 浮点数和数值精度

  • 漏洞
  • 预防技术
  • 真实世界示例:Ethstick

16. tx.origin 认证

  • 漏洞
  • 预防技术
  • 真实世界示例:未知

以太坊怪癖

  • 无密钥 Ether
  • 一次性地址
  • 单交易空投

与加密货币相关的有趣黑客/漏洞列表

参考资料 / 延伸阅读列表

  • Ethereum Wiki - 安全
  • Solidity 文档 - 安全注意事项
  • Consensus - 以太坊智能合约最佳实践
  • 以太坊安全漏洞、攻击及其修复的历史
  • 去中心化应用安全项目(DASP)2018 十大漏洞
  • 以太坊智能合约攻击综述
  • 以太坊智能合约安全
  • 从 Underhanded Solidity 竞赛中学到的教训

1. 重入

以太坊智能合约的特性之一,是能够调用并使用其他外部合约的代码。合约通常也会处理 ether,因此经常会将 ether 发送到各种外部用户地址。调用外部合约或向地址发送 ether 的操作,要求合约提交一次外部调用。这些外部调用可能被攻击者劫持,从而迫使合约执行更多代码(例如通过 fallback 函数),包括对自身的回调。因此,代码执行会“重入”合约。这类攻击被用在了臭名昭著的 DAO 攻击中。

关于重入攻击的进一步阅读,请参阅 智能合约重入攻击 和 Consensus - 以太坊智能合约最佳实践。

漏洞

当合约向未知地址发送 ether 时,就可能发生这种攻击。攻击者可以在外部地址上精心构建一个包含恶意代码的合约,这些恶意代码位于 fallback 函数 中。因此,当合约向该地址发送 ether 时,就会触发恶意代码。通常,恶意代码会在易受攻击的合约上执行某个函数,执行开发者未预期的操作。“重入”这个名字源于这样一个事实:外部恶意合约回调易受攻击合约上的某个函数,并在易受攻击合约的任意位置“重新进入”代码执行。

为了说明这一点,考虑下面这个简单的易受攻击合约,它充当一个以太坊金库,允许存款人每周仅提取 1 ether。

EtherStore.sol:```solidity contract EtherStore {

uint256 public withdrawalLimit = 1 ether;
mapping(address => uint256) public lastWithdrawTime;
mapping(address => uint256) public balances;

function depositFunds() public payable {
    balances[msg.sender] += msg.value;
}

function withdrawFunds (uint256 _weiToWithdraw) public {
    require(balances[msg.sender] >= _weiToWithdraw);
    // limit the withdrawal
    require(_weiToWithdraw <= withdrawalLimit);
    // limit the time allowed to withdraw
    require(now >= lastWithdrawTime[msg.sender] + 1 weeks);
    require(msg.sender.call.value(_weiToWithdraw)());
    balances[msg.sender] -= _weiToWithdraw;
    lastWithdrawTime[msg.sender] = now;
}

}

该合约有两个公共函数:`depositFunds()` 和 `withdrawFunds()`。`depositFunds()` 函数只是增加发送者的余额。`withdrawFunds()` 函数允许发送者指定要提取的 wei 数量。只有当请求提取的金额小于 1 ether 且在过去一周内没有发生过提取时,它才会成功。真的是这样吗?...

漏洞位于第 \[17\] 行,我们在那里向用户发送其请求的 ether 数量。考虑一个恶意攻击者创建如下合约,

Attack.sol:```solidity
import "EtherStore.sol";

contract Attack {
  EtherStore public etherStore;

  // initialise the etherStore variable with the contract address
  constructor(address _etherStoreAddress) {
      etherStore = EtherStore(_etherStoreAddress);
  }

  function pwnEtherStore() public payable {
      // attack to the nearest ether
      require(msg.value >= 1 ether);
      // send eth to the depositFunds() function
      etherStore.depositFunds.value(1 ether)();
      // start the magic
      etherStore.withdrawFunds(1 ether);
  }

  function collectEther() public {
      msg.sender.transfer(this.balance);
  }

  // fallback function - where the magic happens
  function () payable {
      if (etherStore.balance > 1 ether) {
          etherStore.withdrawFunds(1 ether);
      }
  }
}

让我们看看这个恶意合约如何利用我们的EtherStore合约。攻击者会创建上述合约(假设地址为0x0...123),并将EtherStore的合约地址作为构造函数参数传入。这将初始化公共变量etherStore,使其指向我们想要攻击的合约。

然后,攻击者会调用pwnEtherStore()函数,并传入一定数量的以太币(大于或等于1),在此示例中我们假设为1 ether。在此示例中,我们假设许多其他用户已向该合约存入以太币,使其当前余额为10 ether。随后将发生以下情况:

  1. Attack.sol - 第 [15] 行 - 将以msg.value为1 ether(以及大量gas)调用EtherStore合约的depositFunds()函数。发送者(msg.sender)将是我们恶意合约(0x0...123)。因此,balances[0x0..123] = 1 ether。

  2. Attack.sol - 第 [17] 行 - 恶意合约随后将以参数1 ether调用EtherStore合约的withdrawFunds()函数。由于我们之前没有进行过任何提款,这将通过所有要求(EtherStore合约的第 [12]-[16] 行)。

  3. EtherStore.sol - 第 [17] 行 - 合约随后会将1 ether发送回恶意合约。

  4. Attack.sol - 第 [25] 行 - 发送给恶意合约的以太币随后将执行回退函数。

  5. Attack.sol - 第 [26] 行 - EtherStore合约的总余额此前为10 ether,现在为9 ether,因此此if语句通过。

  6. Attack.sol - 第 [27] 行 - 回退函数随后再次调用EtherStore的withdrawFunds()函数,并“重新进入”EtherStore合约。

  7. EtherStore.sol - 第 [11] 行 - 在第二次调用withdrawFunds()时,由于第 [18] 行尚未执行,我们的余额仍然是1 ether。因此,我们仍然有balances[0x0..123] = 1 ether。lastWithdrawTime变量也是如此。同样,我们通过了所有要求。

  8. EtherStore.sol - 第 [17] 行 - 我们再次提取1 ether。

  9. 步骤 4-8 将重复 - 直到EtherStore.balance >= 1不再成立,如Attack.sol中第 [26] 行所规定。

  10. Attack.sol - 第 [26] 行 - 一旦EtherStore合约中剩余的以太币少于1(或等于1),此if语句将失败。这将允许执行EtherStore合约的第 [18] 和 [19] 行(对应于每次调用withdrawFunds()函数)。

  11. EtherStore.sol - 第 [18] 和 [19] 行 - balances和lastWithdrawTime映射将被更新,执行结束。

最终结果是,攻击者通过单笔交易,瞬间从EtherStore合约中提取了所有(除1外)以太币。

预防技术

有多种常见技术有助于避免智能合约中的潜在重入漏洞。第一种是(尽可能)在向外部合约发送以太币时使用内置的 transfer() 函数。transfer函数仅随外部调用发送2300 gas,这不足以让目标地址/合约再调用另一个合约(即重新进入发送合约)。

第二种技术是确保所有更改状态变量的逻辑都在以太币被发送出合约(或任何外部调用)之前完成。在EtherStore示例中,EtherStore.sol的第 [18] 和 [19] 行应放在第 [17] 行之前。将任何对未知地址执行外部调用的代码放在本地化函数或代码执行的最后一步是一种良好实践。这被称为 checks-effects-interactions 模式。

第三种技术是引入互斥锁。即添加一个状态变量,在代码执行期间锁定合约,从而防止重入调用。

将所有这些技术(三种并非全部必需,但出于演示目的均采用)应用于EtherStore.sol,得到以下无重入漏洞的合约:```solidity contract EtherStore {

下载工具