蜜罐中隐藏的陷阱
蜜罐智能合约是欺诈性智能合约的一种,它们看起来对用户很有吸引力,并承诺带来经济利益。
要保护自己免受蜜罐智能合约的侵害,您需要在使用合约代码之前对其进行彻底研究和验证。智能合约和区块链的世界在不断发展,新的漏洞和欺骗方法经常出现,因此开发人员、审计人员和用户必须了解该领域的最新趋势和发展。本文将介绍其中的一些示例。
1. 回退功能蜜罐
在这种类型的蜜罐中,合约具有脆弱的 "回退 "功能,看似允许任何人拥有合约并删除其余额。
示例代码:
pragma solidity 0.4.18
contract FallbackHoneypot {
address public owner;
function() public payable {
if (owner == 0) {
owner = msg.sender;
} else {
revert();
}
}
function withdraw() public {
require(msg.sender == owner);
msg.sender.transfer(this.balance);
}
}
在此示例中,回退函数允许任何用户成为所有者。
2.隐藏条件蜜罐
此类合同包含一个隐藏条件或要求,必须满足该条件或要求,看似脆弱的函数才能成功。用户可能认为他们可以使用该合约,但隐藏条件确保他们无法使用。
示例代码:
pragma solidity 0.4.18
contract HiddenConditionHoneypot {
uint256 public counter = 0;
function deposit() public payable {
require(msg.value == 1 ether);
counter++;
}
function withdraw() public {
require(counter > 100);
msg.sender.transfer(this.balance);
}
}
在此合约中,用户可以存入 1 个以太币,似乎存入 100 个以太币后,用户就可以提取整个合约的余额。
3.重入式蜜罐
在这种类型的蜜罐中,合约很容易受到重入式攻击,即攻击者可以在取款过程中重复调用合约函数。
示例代码:
pragma solidity 0.4.18
contract ReentrancyHoneypot {
mapping(address => uint256) public balances;
function deposit() public payable {
balances[msg.sender] += msg.value;
}
function withdraw(uint256 _amount) public {
require(balances[msg.sender] >= _amount);
if (msg.sender.call.value(_amount)()) {
balances[msg.sender] -= _amount;
}
}
}
在本示例中,由于使用了 msg.sender.call.value(_amount)(),该合约很容易受到重入攻击。
4. 气体限制蜜罐
在这种类型的蜜罐中,合约似乎允许用户提取资金,但执行提取功能所需的气体量超过了气体块限制,导致交易无法进行:
示例代码:
pragma solidity 0.4.18
contract GasLimitHoneypot {
uint256 constant public numArrayElements = 1000;
uint256[numArrayElements] public someArray;
function deposit() public payable {
for (uint256 i = 0; i < numArrayElements; i++) {
someArray[i] = 0;
}
}
function withdraw() public {
uint256 balance = address(this).balance;
for (uint256 i = 0; i < numArrayElements; i++) {
someArray[i] = 1;
}
msg.sender.transfer(balance);
}
}
在此示例中,存款函数初始化了一个包含 1000 个元素的数组。withdraw 函数将合同余额转移给发送方,但它修改了数组元素。执行循环所需的气体超过了气体块限制,从而阻止了取款,并在合约中延迟了取款。
5. 时间戳操纵蜜罐
在这种类型的蜜罐中,合约使用以太坊区块链时间戳作为执行条件。
示例代码:
pragma solidity 0.4.18
contract TimestampHoneypot {
uint256 public lastInteraction;
uint256 public prize;
function deposit() public payable {
lastInteraction = now;
prize += msg.value;
}
function withdraw() public {
require(now >= lastInteraction + 1 hours);
msg.sender.transfer(prize);
prize = 0;
}
}
在此合约中,用户可以在最后一次交互 1 小时后存入资金并提取整个合约余额。然而,由于矿工可以操纵区块时间戳,他们可以提前触发取款功能,从而实际盗取资金。
6. 隐藏转账蜜罐
在这种类型的蜜罐中,合约包含向攻击者地址的隐藏转账,这给人一种合法合约的感觉,但实际上会导致资金损失。
示例代码:
pragma solidity 0.4.18
contract HiddenTransferHoneypot {
address public owner;
constructor() public {
owner = msg.sender;
}
function deposit() public payable {
if (msg.value >= 1 ether) {
owner.transfer(msg.value);
}
}
function withdraw() public {
require(msg.sender == owner);
owner.transfer(address(this).balance);
}
}
在此示例中,合约似乎允许存款和取款。但是,如果存款金额等于或大于 1 以太坊,存款函数就会包含向所有者(攻击者)转移资金的隐藏功能。毫无戒心的用户在存入资金后会丢失以太币,因为他们的以太币将被转移到攻击者的地址。
7. 重入巢穴
重入巢穴是基于利用合约中的重入漏洞,即合约函数在状态更新前可被递归调用:
示例代码:
pragma solidity 0.4.18
contract ReentrancyHoneypot {
mapping(address => uint256) public balances;
function deposit() public payable {
balances[msg.sender] += msg.value;
}
function withdraw() public {
uint256 amount = balances[msg.sender];
(bool success,) = msg.sender.call.value(amount)("");
require(success, "Withdrawal failed.");
balances[msg.sender] = 0;
}
}
<
在此示例中,合约授权存款和取款。但是,它很容易受到重入攻击。攻击者可以创建一个恶意合约,在余额设置为零之前递归调用 withdraw 函数,从而耗尽整个合约的余额。
8. 类型转换和溢出蜜罐
在这种类型的蜜罐中,攻击者使用类型转换和整数溢出来欺骗用户,使其相信合约是安全的,但实际上它包含隐藏的陷阱:
示例代码:
pragma solidity 0.4.18
contract TypeCastingHoneypot {
uint8 public count = 0;
uint256 public reward = 1 ether;
function increment() public payable {
require(msg.value >= reward);
uint8 prevCount = count;
count++;
if (count < prevCount) {
msg.sender.transfer(address(this).balance);
}
}
}
在此示例中,当用户发送的金额等于或大于奖励金额时,合同将奖励递增计数变量的用户。但是,由于计数变量使用的是 uint8,当数值达到 255 时,会发生整数溢出。溢出后,计数将重置为 0,攻击者可获得合约的全部余额。
9. 委托调用蜜罐
在这种类型的蜜罐中,攻击者使用委托调用函数代表看似安全的合约执行恶意代码。
示例代码:
pragma solidity 0.4.18
contract DelegateCallHoneypot {
address public owner;
constructor() public {
owner = msg.sender;
}
function() external payable {}
function withdraw() public {
require(msg.sender == owner);
owner.transfer(address(this).balance);
}
function execute(address _target, bytes memory _data) public {
require(msg.sender == owner);
(bool success,) = _target.delegatecall(_data);
require(success, "Execution failed.");
}
}
在此示例中,合约允许存款、取款和通过 execute 函数执行代码。但是,攻击者可以创建恶意代码,通过执行 delegatecall 函数来操纵合约的状态,包括所有者变量。通过更改所有者,攻击者可以获得对合约的控制权并清空其余额
10. 隐藏存储操纵蜜罐
在这种类型的蜜罐中,攻击者暗中操纵存储变量,诱骗用户控制合约:
示例代码:
pragma solidity 0.4.18 pragma solidity 0.4.18
contract HiddenStorageManipulation {
mapping(address => uint256) public balances;
uint256 public totalSupply;
address public owner;
constructor() public {
owner = msg.sender;
balances[owner] = 1000;
totalSupply = 1000;
}
function transfer(address _to, uint256 _value) public {
require(balances[msg.sender] >= _value);
balances[msg.sender] -= _value;
balances[_to] += _value;
if (totalSupply - balances[owner] >= 1000) {
owner = _to;
}
}
}
在此示例中,合约看起来像一个简单的代币合约。用户可以互相传递代币,所有者最初被设置为合约的创建者。但是,攻击者可以暗中操纵 totalSupply 和余额变量来重新获得对合约的控制权。当 totalSupply 与余额[owner]之差达到 1000 时,owner 变量就会更新,从而使攻击者重新获得对合约的控制权。
11. 函数名碰撞蜜罐
在这种类型的蜜罐中,攻击者利用函数名碰撞诱使用户调用错误的函数。
示例代码:
pragma solidity 0.4.18
contract FunctionNameCollision {
address public owner;
constructor() public {
owner = msg.sender;
}
function withdraw() external {
require(msg.sender == owner);
msg.sender.transfer(address(this).balance);
}
function withdraw(uint256 amount) external {
require(msg.sender == owner);
owner.transfer(amount);
}
function() external payable {}
}
在此示例中,合同有两个提取功能。其中一个允许所有者提取全部余额,另一个允许所有者提取一定金额。但是,如果用户试图通过调用 withdraw(uint256) 提取一定金额,由于函数名称碰撞,用户将无意中调用不带参数的 withdraw(),这将导致提取整个合约余额
12. 委托调用漏洞蜜罐
在这种类型的蜜罐中,攻击者使用委托调用在另一个合约中执行恶意函数,从而导致用户的资金被盗:
示例代码:
pragma solidity 0.4.18
contract DelegatecallVulnerability {
address public owner;
constructor() public {
owner = msg.sender;
}
function withdraw() external {
require(msg.sender == owner);
msg.sender.transfer(address(this).balance);
}
function () external payable {
if (msg.value > 1 ether) {
this.delegatecall(msg.data);
}
}
}
contract Malicious {
function withdraw() external {
msg.sender.transfer(address(this).balance);
}
}
在此示例中,合约看似无害,只有一个简单的 withdraw() 函数,允许所有者提取合约的余额。然而,该合约还包含一个后备函数,当收到超过 1 个以太币时,该函数会使用 msg.data 执行另一个合约的委托调用。
13. 伪整数溢出蜜罐
这类蜜罐基于 Solidity 没有内置浮点数支持这一事实。攻击者可以创建表面上使用十进制数但实际上欺诈性地操纵整数的合约。
示例代码:
pragma solidity 0.4.18
contract PseudoIntegersOverflow {
mapping(address => uint256) public balances;
function deposit() external payable {
require(msg.value > 0);
uint256 pseudoValue = msg.value * 100;
balances[msg.sender] += pseudoValue;
}
function withdraw(uint256 amount) external {
uint256 pseudoAmount = amount * 100;
require(balances[msg.sender] >= pseudoAmount);
balances[msg.sender] -= pseudoAmount;
msg.sender.transfer(amount);
}
}
此示例给人的印象是,合约通过在保存值之前将其乘以 100 来精确管理用户的余额。然而,这种方法具有误导性,因为 Solidity 不支持浮点数。当用户尝试提取资金时,由于整数截断,合约会错误地计算伪和,用户可能无法提取其全部余额。
要防范此类蜜罐,您需要了解 Solidity 语言的局限性及其处理数字操作的方式。在使用声称支持十进制精度的合约时要小心谨慎,并始终检查合约代码是否存在潜在问题。
14. 智能合约中的隐藏费用
某些恶意智能合约可能会在用户不知情的情况下向用户收取隐藏费用。此类支付可能会伪装在合约代码中,或仅在特定条件下触发。
示例代码:
pragma solidity 0.4.18
contract HiddenFees {
address public owner;
uint256 public feePercentage;
constructor(uint256 _feePercentage) public {
owner = msg.sender;
feePercentage = _feePercentage;
}
function withdraw(uint256 amount) external {
uint256 fee = amount * feePercentage / 100;
uint256 netAmount = amount - fee;
require(address(this).balance >= netAmount);
msg.sender.transfer(netAmount);
owner.transfer(fee);
}
}
在本示例中,合约创建者在部署合约时设置了费用 (feePercentage)。在与合约交互时,用户可能不会意识到这一费用。当用户提取资金时,合约会计算手续费,从提取金额中减去手续费,然后将剩余部分发送给用户。
为避免落入这一陷阱,请仔细查看合同代码,确保您了解所有相关费用。查找隐藏或伪装的结算。
15. 隐藏的状态操纵
在某些情况下,恶意合同可能允许合同所有者或攻击者操纵合同的内部状态,从而给其他用户带来意想不到的后果。
示例代码:
pragma solidity 0.4.18
contract HiddenStateManipulation {
address public owner;
mapping(address => uint256) public balances;
constructor() public {
owner = msg.sender;
}
function deposit() external payable {
balances[msg.sender] += msg.value;
}
function withdraw(uint256 amount) external {
require(balances[msg.sender] >= amount);
msg.sender.transfer(amount);
balances[msg.sender] -= amount;
}
function manipulateBalance(address user, uint256 newBalance) external {
require(msg.sender == owner);
balances[user] = newBalance;
}
}
在此示例中,合约允许用户存入和提取资金。但是,manipulateBalance 函数允许合约所有者任意更改任何用户的余额。
应仔细检查合约代码中是否存在允许状态操纵的功能,尤其是当只有合约所有者或某些地址可以访问这些功能时。
16. 隐藏的令牌窃取
在某些情况下,恶意合约可能包含隐藏功能,允许合约所有者或攻击者从毫无戒心的用户那里窃取令牌:
示例代码:
pragma solidity 0.4.18
contract HiddenTokenStealing {
address public owner;
mapping(address => uint256) public balances;
constructor() public {
owner = msg.sender;
}
function deposit(IERC20 token, uint256 amount) external {
require(token.transferFrom(msg.sender, address(this), amount));
balances[msg.sender] += amount;
}
function withdraw(IERC20 token, uint256 amount) external {
require(balances[msg.sender] >= amount);
require(token.transfer(msg.sender, amount));
balances[msg.sender] -= amount;
}
function stealTokens(IERC20 token, address user, uint256 amount) external {
require(msg.sender == owner);
require(token.transfer(owner, amount));
balances[user] -= amount;
}
}
在此示例中,合约允许用户存入和提取 ERC20 代币。
结论:
Honeypot 只是 DeFi 众多欺诈计划中的一个,它不仅利用了提款限制,还利用了上述各种隐藏的陷阱。
为了不上当受骗,您需要了解欺诈计划的特殊性、Solidity 编程语言的精妙之处,并在与智能合约交互之前对其进行深入研究。
通过了解情况并进行彻底的尽职调查,您可以最大限度地降低与使用智能合约相关的风险。
如果您对智能合约的安全性或功能有任何疑问,请使用我们的 Lotus Market 平台。
Lotus Market 平台由经验丰富的开发人员和专业审计人员团队创建。该项目的目标是将投资欺诈性代币的风险降至最低,并帮助安全、舒适地交易加密货币。
Regards, Lotus Market team.
All posts