Solidity 合约安全,常见漏洞(第三篇)

这篇具有很好参考价值的文章主要介绍了Solidity 合约安全,常见漏洞(第三篇)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

Solidity 合约安全,常见漏洞(第三篇)

ERC20 代币问题

如果你只处理受信任的 ERC20 代币,这些问题大多不适用。然而,当与任意的或部分不受信任的 ERC20 代币交互时,就有一些需要注意的地方。

ERC20:转账扣费

当与不信任的代币打交道时,你不应该认为你的余额一定会增加那么多。一个 ERC20 代币有可能这样实现它的转账函数,如下所示:

contract ERC20 {

  // internally called by transfer() and transferFrom()
  // balance and approval checks happen in the caller
  function _transfer(address from, address to, uint256 amount) internal returns (bool) {
    fee = amount * 100 / 99;

    balanceOf[from] -= to;
    balanceOf[to] += (amount - fee);

    balanceOf[TREASURY] += fee;

    emit Transfer(msg.sender, to, (amount - fee));
    return true;
  }
}

这种代币对每笔交易都会征收 1%的税。因此,如果一个智能合约与该代币进行如下交互,我们将得到意想不到的回退或资产被盗。

contract Stake {

  mapping(address => uint256) public balancesInContract;

  function stake(uint256 amount) public {
    token.transferFrom(msg.sender, address(this), amount);
    balancesInContract[msg.sender] += amount; //  这是错误的
  }

  function unstake() public {
    uint256 toSend = balancesInContract[msg.sender];
    delete balancesInContract[msg.sender];

    // this could revert because toSend is 1% greater than
    // the amount in the contract. Otherwise, 1% will be "stolen"// from other depositors.
    token.transfer(msg.sender, toSend);
  }
}

ERC20: rebase 的代币

Rebasing 代币由 Olympus DAO 的 sOhm 代币 和 Ampleforth 的 AMPL 代币所推广。Coingecko 维护了一个 Rebasing ERC20 代币的列表。
当一个代币回溯时,总发行量会发生变化,每个人的余额会根据回溯的方向而增加或减少。
在处理 rebase 代币时,以下代码可能会被破坏:

contract WillBreak {
  mapping(address => uint256) public balanceHeld;
  IERC20 private rebasingToken

  function deposit(uint256 amount) external {
    balanceHeld[msg.sender] = amount;
    rebasingToken.transferFrom(msg.sender, address(this), amount);
  }

  function withdraw() external {
    amount = balanceHeld[msg.sender];
    delete balanceHeld[msg.sender];

    // 错误, amount 也许会超出转出范围
    rebasingToken.transfer(msg.sender, amount);
  }
}

许多合约的解决方案是简单地不允许 rebase 代币。然而,我们可以修改上面的代码,在将账户余额转给接受者之前检查 balanceOf(address(this))。那么,即使余额发生变化,它仍然可以工作。

ERC20: ERC777 在 ERC20 上的包裹

ERC20,如果按照标准实现,ERC20 代币没有转账钩子(hook),因此 transfer 和 transferFrom 不会有重入问题。
带有转账钩子的代币有应用优势,这就是为什么所有的 NFT 标准都实现了它们,以及为什么 ERC777 被最终确定。然而,这已经引起了足够的混乱,以至于 Openzeppelin 废止了 ERC777 库。
如果你只想让你的协议与那些行为像 ERC20 代币但有转账 hook 的代币兼容,那么这只是一个简单的问题,把 transfer 和 transferFrom 函数当作它们会向接收者进行一个函数调用即可。
这种 ERC777 的重入发生在 Uniswap 身上(如果你好奇,Openzeppelin 在这里记录了这个漏洞)。

ERC20: 不是所有的 ERC20 代币转账都会返回 true

ERC20 规范规定,ERC20 代币在转账成功时必须返回 true。因为大多数 ERC20 的实现不可能失败,除非授权不足或转账的金额太多,大多数开发者已经习惯于忽略 ERC20 代币的返回值,并假设一个失败的 trasfer 将被回退。
坦率地说,如果你只与一个你知道其行为的受信任的 ERC20 代币打交道,这并不重要。但在处理任意的 ERC20 代币时,必须考虑到这种行为上的差异。
在许多合约中都有一个隐含的期望,即失败的转账应该总是回退,而不是返回错误,因为大多数 ERC20 代币没有返回错误的机制,所以这导致了很多混乱。
使这个问题更加复杂的是,一些 ERC20 代币并不遵循返回 true 的协议,特别是 Tether。一些代币在转账失败后会回退,这将导致回退的结果冒泡到调用者。因此,一些库包裹了 ERC20 代币的转账调用,以回退恢复并返回一个布尔值。下面是一些实现方法:
参考:Openzeppelin SafeTransfer 及 Solady SafeTransfer (大大地提高了 Gas 效率)

ERC20: 地址投毒

这不是一个智能合约的漏洞,但为了完整起见,我们在这里提到它。
转账零代币是 ERC20 规范所允许的。这可能会导致前端应用程序的混乱,并可能欺骗用户,让他们错误的以为他们最近将代币发送给了某地址。Metamask在这个线程中有更多关于这个问题的内容。

ERC20: 查看代码,规避跑路

(在 web3 术语中,“rugged"意味着’'跑路”, 直译是"从你脚下拉出地毯" 。)
没有什么能阻止有人在 ERC20 代币上添加函数,让他们随意创建、转账和销毁代币–或自毁或升级。所以从根本上说,ERC20 代币的 “无需信任” 程度是有限制的。

借贷协议中的逻辑错误

当考虑到基于 DeFi 协议的借贷如何被破坏时,考虑在软件层面传播的 bug 并影响商业逻辑层面是很有帮助的。形成和完成一个债券合约有很多步骤。这里有一些需要考虑的攻击向量。

贷款人损失的方式

  • 使到期本金减少(可能为零)而不进行任何支付的漏洞。
  • 当贷款没有偿还或抵押物降到阈值以下时,买方的抵押物不能被清算。
  • 如果协议有一个转移债务所有权的机制,这可能是一个从贷款人那里偷取债券的方式。
  • 贷款本金或付款的到期日被不适当地移到以后的日期。

借款人损失的方式

  • 偿还本金时没有减少本金债务的 bug。
  • 一个 bug 或 gas 攻击使用户无法进行支付。
  • 本金或利率被非法提高。
  • 预言机的操纵导致抵押物贬值。
  • 贷款本金或付款的到期日被不适当地移到一个较早的日期。

如果抵押品从协议中被抽走,那么贷款人和借款人都会损失,因为借款人没有动力去偿还贷款,而借款人则会损失本金。
正如上面所看到的,DeFi 协议被 "黑 "的范围比从协议中抽走一堆钱(通常成为新闻的那类事件)要多得多。

抵押(staking)协议中的漏洞

成为新闻的那种黑客是抵押协议被黑掉数百万美元,但这并不是唯一要面对的问题,抵押协议可能面临的问题有:

  • 奖励能否延迟支付,或过早地被索取?
  • 奖励能否被不适当地减少或增加?在更糟糕的情况下,能否阻止用户获得任何奖励?
  • 人们能否索取不属于他们的本金或奖励,在最坏的情况下,会耗尽协议所有资金?
  • 存放的资产会不会被卡在协议中(部分或全部),或被不适当地延迟提取?
  • 相反,如果质押需要时间承诺,用户是否可以在承诺时间之前提取?
  • 如果支付的是不同的资产或货币,其价值是否可以在相关的智能合约范围内被操纵?如果协议 mint 自己的代币来奖励流动性提供者或质押者,这一点是相关的。
  • 如果存在预期和披露出的本金损失的风险因素,这种风险是否可以被不适当地操纵?
  • 协议的关键参数是否有管理、中心化或治理风险?

需要关注的关键是代码中涉及 "资金退出 "部分的代码。
还有一个 "资金入口 "的漏洞也要寻找。

  • 有权参与协议中的资产抵押的用户能否被不适当地阻止?

用户收到的奖励有一个隐含的风险回报和一个预期的资金时间价值。明确这些假设是什么,以及协议会怎样偏离预期是很有帮助的。

未检查的返回值

有两种方法来调用外部智能合约:1)用接口定义调用函数;2)使用.call 方法。如下图所示:

contract A {
  uint256 public x;

  function setx(uint256 _x) external {
    require(_x > 10, "x must be bigger than 10");
    x = _x;
  }
}

interface IA {
  function setx(uint256 _x) external;
}

contract B {
  function setXV1(IA a, uint256 _x) external {
    a.setx(_x);
  }

  function setXV2(address a, uint256 _x) external {
    (bool success, ) =
    a.call(abi.encodeWithSignature("setx(uint256)", _x));
    // success is not checked!
  }
}

在合约 B 中,如果 _x 小于 10,setXV2 会默默地失败。当一个函数通过.call 方法被调用时,被调用者可以回退,但父函数不会回退。必须检查返回成功的值,并且代码行为必须相应地分支。

msg.value 在一个循环中

在循环中使用 msg.value 是很危险的,因为这可能会让发起者 重复使用 msg.value。
这种情况可能会出现在 payable 的 multicalls 中。Multicalls 使用户能够提交一个交易列表,以避免重复支付 21,000 的 Gas 交易费。然而,msg.value 在通过函数循环执行时被 “重复使用”,有可能使用户双花。
这就是Opyn Hack的根本原因。

私有变量

私有变量在区块链上仍然是可见的,所以敏感信息不应该被存储在那里。如果它们不能被访问,验证者如何能够处理取决于其值的交易?私有变量不能从外部的 Solidity 合约中读取,但它们可以使用以太坊客户端在链外读取。
要读取一个变量,你需要知道它的存储槽。在下面的例子中,myPrivateVar 的存储槽是 0。

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract PrivateVarExample {
  uint256 private myPrivateVar;

  constructor(uint256 _initialValue) {
    myPrivateVar = _initialValue;
  }
}

下面是读取已部署的智能合约的私有变量的 javascript 代码

const Web3 = require("web3");
const PRIVATE_VAR_EXAMPLE_ADDRESS = "0x123..."; // Replace with your contract address

async function readPrivateVar() {
  const web3 = new Web3("http://localhost:8545"); // Replace with your provider's URL

  // Read storage slot 0 (where 'myPrivateVar' is stored)
  const storageSlot = 0;
  const privateVarValue = await web3.eth.getStorageAt(
    PRIVATE_VAR_EXAMPLE_ADDRESS,
    storageSlot
  );

  console.log("Value of private variable 'myPrivateVar':",
              web3.utils.hexToNumberString(privateVarValue));
}

readPrivateVar();

不安全的代理调用

委托调用(Delegatecall)不应该被用于不受信任的合约,因为它把所有的控制权都交给了委托接受者。在这个例子中,不受信任的合约偷走了合约中所有的以太币。

contract UntrustedDelegateCall {
  constructor() payable {
    require(msg.value == 1 ether);
  }

  function doDelegateCall(address _delegate, bytes calldata data) public {
    (bool ok, ) = _delegate.delegatecall(data);
    require(ok, "delegatecall failed");
  }

}

contract StealEther {
  function steal() public {
    // you could also selfdestruct here
    // if you really wanted to be mean
    (bool ok,) =
    tx.origin.call{value: address(this).balance}("");
    require(ok);
  }

  function attack(address victim) public {
    UntrustedDelegateCall(victim).doDelegateCall(
      address(this),
      abi.encodeWithSignature("steal()"));
  }
}

升级与代理有关的 bug

我们无法在一个章节中对这个话题进行公正的解释。大多数升级错误通常可以通过使用 Openzeppelin 的hardhat 插件和阅读它所保护的问题来避免出错。
作为一个快速的总结,以下是与智能合约升级有关的问题:文章来源地址https://www.toymoban.com/news/detail-702304.html

  • 自毁(self-destruct)和委托调用(delegatecall)不应该在执行合约中使用。
  • 必须注意在升级过程中,存储变量不能相互覆盖
  • 在执行合约中应避免调用外部库,因为不可能预测它们会如何影响存储访问。
  • 部署者决不能忽视调用初始化函数
  • 在基类合约中没有包括间隙(gap)变量,以防止在基类合约中加入新的变量时发生存储碰撞(这由 hardhat 插件自动处理)。
  • 不可变(immutable)变量中的值在升级时不会被保留
  • 非常不鼓励在构造函数中做任何事情,因为未来的升级必须执行相同的构造函数逻辑以保持兼容性。

到了这里,关于Solidity 合约安全,常见漏洞(第三篇)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【Solidity】智能合约案例——③版权保护合约

    目录 一、合约源码分析: 二、合约整体流程:         1.部署合约:         2.添加实体:          3.查询实体         4.审核版权:         5.版权转让         Copyright.sol:主合约,定义了版权局的实体,功能为:审核版权         Opus.sol:定义两个实体:作者和作

    2024年02月04日
    浏览(40)
  • 【Solidity】智能合约案例——②供应链金融合约

    目录 一、合约源码分析: 二、合约整体流程:         1.部署合约:         2.添加实体         3.发送交易存证            ①.银行向公司交易(公司向银行提供交易存证)            ②.公司向银行交易(银行向公司提供交易存证)            ③.公司向公司交易

    2024年02月06日
    浏览(32)
  • Solidity智能合约安全指南:预防已知攻击的关键.

    账户类型 创建成本 交易发起 使用场景 作用 外部账户(私钥的所有者控制) 创建账户是免费的 可以自主发起交易 外部所有的账户之间只能进行ETH和代币交易 1、接受、持有和发送ETH 和 token 2、与已部署的智能合约进行交互 合约账户(由代码控制,部署在网络上的智能合约

    2024年02月12日
    浏览(46)
  • 智能合约安全漏洞与解决方案

    使用OpenZeppelin安全库,防止了数字溢出漏洞攻击,报出了SafeMath错误: 不安全写法:lockTime[msg.sender] += _secondsToIncrease; 安全写法:    lockTime[msg.sender] = lockTime[msg.sender].add(_secondsToIncrease); 整数溢出真实案例: 2018年4月22日,黑客利用以太坊ERC-20智能合约中数据溢出的漏洞攻击蔡

    2024年02月03日
    浏览(44)
  • 智能合约安全,著名的区块链漏洞:双花攻击

    区块链技术通过提供去中心化和透明的系统彻底改变了各个行业。 但是,与任何技术一样,它也不能免受漏洞的影响。一个值得注意的漏洞是双花攻击。 在本文中,我们将深入研究双花攻击的复杂性,探讨其工作原理、开发方法、预防措施及其对区块链生态系统的影响。 区

    2024年02月04日
    浏览(37)
  • 【论文阅读】 智能合约安全漏洞检测技术研究综述

    2016 年 6 月,黑客利用 DAO(decentralized autonomous organization)合约的 可重入漏洞 , 窃取了价值约 6000 万美元的以太币(即以太坊数字货币); 2017 年 7 月, 由于 Parity 多签名钱包合约的 Delegatecall 漏洞 (parity multi-sig wallet delegatecall), 价值近 3 亿美元的以太币被冻结; 2018 年 4 月, 恶意攻击者

    2024年03月14日
    浏览(50)
  • Solidity 合约漏洞,价值 38BNB 漏洞分析

    https://twitter.com/NumenAlert/status/1626447469361102850 https://twitter.com/bbbb/status/1626392605264351235 攻击交易: https://bscscan.com/tx/0x146586f05a4513136deab3557ad15df8f77ffbcdbd0dd0724bc66dbeab98a962 攻击账号:0x187473cf30e2186f8fb0feda1fd21bad9aa177ca 攻击合约:0xd1b5473ffbadd80ff274f672b295ba8811b32538 被攻击合约:Starlink 0

    2024年02月04日
    浏览(35)
  • 智能合约安全分析,Vyper 重入锁漏洞全路径分析

    7 月 30 日 21:10 至 7 月 31 日 06:00 链上发生大规模攻击事件,导致多个 Curve 池的资金损失。漏洞的根源都是由于特定版本的 Vyper 中出现的重入锁故障。 通过对链上交易数据初步分析,我们对其攻击的交易进行整理归纳,并对攻击流程进一步的分析,由于攻击涉及多个交易池。

    2024年02月09日
    浏览(41)
  • solidity案例详解(六)服务评价合约

     有服务提供商和用户两类实体,其中服务提供商部署合约,默认诚信为true,用户负责使用智能合约接受服务及评价,服务提供商的评价信息存储在一个映射中,可以根据服务提 供商的地址来查找评价信息。用户评价信息, 服务提供商的评价信息会随之更新。服务提供商查

    2024年02月03日
    浏览(32)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包