{"author":{"address":"0x1f2479ee1b4aFE789e19D257D2D50810ac90fa59","user":"https://learnblockchain.cn/people/3877"},"content":{"body":"# 警惕科学家钓鱼合约\r\n\r\n## 备注\r\n\r\n时间：2024 年 6 月 30 日\r\n\r\n作者：[33357](https://github.com/33357)\r\n\r\n## 正文\r\n\r\n有个朋友给我发了一个合约 https://etherscan.io/address/0x8876a8cf6e142a0aeb834b824e97870111bb7da1 说有套利可能，可以来研究。我发现这的确是一个有明显漏洞的合约，里面还有 15 ETH 的资金。但仔细研究过后发现这是专门用来钓鱼的合约，想要去攻击的智能合约新手很容易上当。\r\n\r\n### 代码分析\r\n\r\n``` javascript\r\n/**\r\n *Submitted for verification at Etherscan.io on 2024-06-18\r\n*/\r\n\r\n// SPDX-License-Identifier: MIT\r\npragma solidity 0.7.6;\r\n\r\ncontract smart_bank {\r\n    function Deposit(uint _unlockTime) public payable {\r\n        Holder storage acc = Accounts[msg.sender];\r\n        acc.balance += msg.value;\r\n        acc.unlockTime = _unlockTime \u003e block.timestamp ? _unlockTime : block.timestamp;\r\n        LogFile.AddMessage(msg.sender, msg.value, \"Put\");\r\n    }\r\n\r\n    function Collect(uint _am) public payable {\r\n        Holder storage acc = Accounts[msg.sender];\r\n        if (acc.balance \u003e MinSum \u0026\u0026 acc.balance \u003e= _am \u0026\u0026 block.timestamp \u003e acc.unlockTime) {\r\n            (bool success, ) = msg.sender.call{value: _am}(\"\");\r\n            if (success) {\r\n                acc.balance -= _am;\r\n                LogFile.AddMessage(msg.sender, _am, \"Collect\");\r\n            }\r\n        }\r\n    }\r\n\r\n    struct Holder {\r\n        uint unlockTime;\r\n        uint balance;\r\n    }\r\n\r\n    mapping(address =\u003e Holder) public Accounts;\r\n\r\n    Log LogFile;\r\n\r\n    uint public MinSum = 1 ether;\r\n\r\n    constructor(address log) {\r\n        LogFile = Log(log);\r\n    }\r\n\r\n    fallback() external payable {\r\n        Deposit(0);\r\n    }\r\n\r\n    receive() external payable {\r\n        Deposit(0);\r\n    }\r\n}\r\n\r\ncontract Log {\r\n    event Message(address indexed Sender, string Data, uint Val, uint Time);\r\n\r\n    function AddMessage(address _adr, uint _val, string memory _data) external {\r\n        emit Message(_adr, _data, _val, block.timestamp);\r\n    }\r\n}\r\n```\r\n这个合约有两个明显的漏洞：\r\n1. 在 `msg.sender.call` 之后调用 `acc.balance -= _am`, 而且没有重入锁，可以使用重入攻击。\r\n2. 在 0.7.6 的 solidity 版本之下使用 `acc.balance -= _am`，可以使用整数溢出攻击。\r\n\r\n但如果你想当然的去用上面的手段攻击合约，就上合约部署者的当了。\r\n\r\n这个合约有一个奇怪的方法 `LogFile.AddMessage`，虽然在下面给出了 `contract Log` 的源代码，看起来没有问题，但在合约初始化是没有使用 `LogFile = new Log()` 而是使用了 `LogFile = Log(log)`，这导致了 `LogFile.AddMessage` 函数调用的代码其实在其他地址。\r\n\r\n### 代码溯源\r\n\r\n通过分析合约构造交易的 https://etherscan.io/tx/0x9cb838f7b2eb28951fc2d0b560f8bb98ce32b1789b00735c89385fd9740c96e2 的 `data`，可以找到 logFile 的真实合约地址是 https://etherscan.io/address/0x441f6fb6e9506082625fe0b973025ef65badf584 ，而这个合约并没有开源。使用反编译工具可以一窥究竟 https://etherscan.io/bytecode-decompiler?a=0x441f6fb6e9506082625fe0b973025ef65badf584\r\n\r\n``` python\r\n# Palkeoramix decompiler. \r\n\r\ndef storage:\r\n  stor0 is addr at storage 0\r\n  stor1 is addr at storage 1\r\n  stor2 is addr at storage 2\r\n\r\ndef _fallback() payable: # default function\r\n  revert\r\n\r\ndef unknown4b906714(addr _param1, uint256 _param2, array _param3) payable: \r\n  if stor1 != tx.origin:\r\n      require tx.origin == stor2\r\n  call _param1 with:\r\n     value _param2 wei\r\n       gas gas_remaining wei\r\n      args _param3[all]\r\n  require ext_call.success\r\n\r\ndef AddMessage(address _adr, uint256 _val, string _data): # not payable\r\n  log 0xb7206ff2: Array(len=_data.length, data=_data[all]), _val, block.timestamp, _adr\r\n  if caller == stor0:\r\n      if stor1 != tx.origin:\r\n          if stor2 != tx.origin:\r\n              if _val \u003e 0:\r\n                  require 0 \u003c _data.length\r\n                  require Mask(8, 248, cd[(_data + 36)]) != 'C'\r\n```\r\n\r\n可以看到 `AddMessage` 函数的真实逻辑是：\r\n1. 检查 caller 是否为 stor0，如果是则进入下一步\r\n2. 检查 tx.origin 是否是 stor1 或者 stor2，如果不是则进入下一步\r\n3. 检查 _val 是否大于 0，如果是则进入下一步\r\n4. _data 长度必须大于 0，并且首字母不能是 `C`\r\n\r\n所以当别人调用 `Deposit` 往合约里存 ETH 的时候可以正常执行，但调用 `Collect` 向合约取 ETH 的时候会触发首字母不能是 `C` 的检查，从而无法执行。\r\n\r\n### 总结\r\n\r\n科学家的收入虽然吸引人，但经验不足的新手很容易掉坑里。最近也出现了一些专门钓鱼科学家的合约，大家要谨慎检查。\r\n\r\n原文发布在 [https://33357.xyz](https://33357.xyz/)","title":"警惕科学家钓鱼合约"},"history":null,"timestamp":1719720152,"version":1}