SMBLorisAttack.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. import logging
  2. import csv
  3. from random import shuffle, randint, choice, uniform
  4. from lea import Lea
  5. from Attack import BaseAttack
  6. from Attack.AttackParameters import Parameter as Param
  7. from Attack.AttackParameters import ParameterTypes
  8. from ID2TLib.Utility import *
  9. from ID2TLib.SMBLib import *
  10. logging.getLogger("scapy.runtime").setLevel(logging.ERROR)
  11. # noinspection PyPep8
  12. from scapy.layers.inet import IP, Ether, TCP
  13. from scapy.layers.netbios import NBTSession
  14. # Resources:
  15. # https://github.com/rapid7/metasploit-framework/blob/master/modules/auxiliary/dos/smb/smb_loris.rb
  16. # https://samsclass.info/124/proj14/smbl.htm
  17. # https://gist.githubusercontent.com/marcan/6a2d14b0e3eaa5de1795a763fb58641e/raw/565befecf4d9a4a27248d027a90b6e3e5994b5b6/smbloris.c
  18. # http://smbloris.com/
  19. class SMBLorisAttack(BaseAttack.BaseAttack):
  20. def __init__(self):
  21. """
  22. Creates a new instance of the SMBLorisAttack.
  23. """
  24. # Initialize attack
  25. super(SMBLorisAttack, self).__init__("SMBLoris Attack", "Injects an SMBLoris DoS Attack",
  26. "Resource Exhaustion")
  27. # Define allowed parameters and their type
  28. self.supported_params = {
  29. Param.IP_SOURCE: ParameterTypes.TYPE_IP_ADDRESS,
  30. Param.IP_DESTINATION: ParameterTypes.TYPE_IP_ADDRESS,
  31. Param.MAC_SOURCE: ParameterTypes.TYPE_MAC_ADDRESS,
  32. Param.MAC_DESTINATION: ParameterTypes.TYPE_MAC_ADDRESS,
  33. Param.INJECT_AT_TIMESTAMP: ParameterTypes.TYPE_FLOAT,
  34. Param.INJECT_AFTER_PACKET: ParameterTypes.TYPE_PACKET_POSITION,
  35. Param.PACKETS_PER_SECOND: ParameterTypes.TYPE_FLOAT,
  36. Param.ATTACK_DURATION: ParameterTypes.TYPE_INTEGER_POSITIVE,
  37. Param.NUMBER_ATTACKERS: ParameterTypes.TYPE_INTEGER_POSITIVE
  38. }
  39. def init_params(self):
  40. """
  41. Initialize the parameters of this attack using the user supplied command line parameters.
  42. Use the provided statistics to calculate default parameters and to process user
  43. supplied queries.
  44. :param statistics: Reference to a statistics object.
  45. """
  46. # PARAMETERS: initialize with default values
  47. # (values are overwritten if user specifies them)
  48. most_used_ip_address = self.statistics.get_most_used_ip_address()
  49. if isinstance(most_used_ip_address, list):
  50. most_used_ip_address = most_used_ip_address[0]
  51. # The most used IP class in background traffic
  52. most_used_ip_class = self.statistics.process_db_query("most_used(ipClass)")
  53. num_attackers = randint(1, 16)
  54. source_ip = self.generate_random_ipv4_address(most_used_ip_class, num_attackers)
  55. self.add_param_value(Param.IP_SOURCE, source_ip)
  56. self.add_param_value(Param.MAC_SOURCE, self.generate_random_mac_address(num_attackers))
  57. random_ip_address = self.statistics.get_random_ip_address()
  58. # ip-dst should be valid and not equal to ip.src
  59. while not self.is_valid_ip_address(random_ip_address) or random_ip_address == source_ip:
  60. random_ip_address = self.statistics.get_random_ip_address()
  61. self.add_param_value(Param.IP_DESTINATION, random_ip_address)
  62. destination_mac = self.statistics.get_mac_address(random_ip_address)
  63. if isinstance(destination_mac, list) and len(destination_mac) == 0:
  64. destination_mac = self.generate_random_mac_address()
  65. self.add_param_value(Param.MAC_DESTINATION, destination_mac)
  66. self.add_param_value(Param.PACKETS_PER_SECOND,
  67. (self.statistics.get_pps_sent(most_used_ip_address) +
  68. self.statistics.get_pps_received(most_used_ip_address)) / 2)
  69. self.add_param_value(Param.INJECT_AFTER_PACKET, randint(0, self.statistics.get_packet_count()))
  70. self.add_param_value(Param.ATTACK_DURATION, 30)
  71. def generate_attack_pcap(self):
  72. def getIpData(ip_address: str):
  73. """
  74. :param ip_address: the ip of which (packet-)data shall be returned
  75. :return: MSS, TTL and Window Size values of the given IP
  76. """
  77. # Set MSS (Maximum Segment Size) based on MSS distribution of IP address
  78. mss_dist = self.statistics.get_mss_distribution(ip_address)
  79. if len(mss_dist) > 0:
  80. mss_prob_dict = Lea.fromValFreqsDict(mss_dist)
  81. mss_value = mss_prob_dict.random()
  82. else:
  83. mss_value = self.statistics.process_db_query("most_used(mssValue)")
  84. # Set TTL based on TTL distribution of IP address
  85. ttl_dist = self.statistics.get_ttl_distribution(ip_address)
  86. if len(ttl_dist) > 0:
  87. ttl_prob_dict = Lea.fromValFreqsDict(ttl_dist)
  88. ttl_value = ttl_prob_dict.random()
  89. else:
  90. ttl_value = self.statistics.process_db_query("most_used(ttlValue)")
  91. # Set Window Size based on Window Size distribution of IP address
  92. win_dist = self.statistics.get_win_distribution(ip_address)
  93. if len(win_dist) > 0:
  94. win_prob_dict = Lea.fromValFreqsDict(win_dist)
  95. win_value = win_prob_dict.random()
  96. else:
  97. win_value = self.statistics.process_db_query("most_used(winSize)")
  98. return mss_value, ttl_value, win_value
  99. pps = self.get_param_value(Param.PACKETS_PER_SECOND)
  100. # Timestamp
  101. first_timestamp = self.get_param_value(Param.INJECT_AT_TIMESTAMP)
  102. # store start time of attack
  103. self.attack_start_utime = first_timestamp
  104. # Initialize parameters
  105. packets = []
  106. ip_destination = self.get_param_value(Param.IP_DESTINATION)
  107. mac_destination = self.get_param_value(Param.MAC_DESTINATION)
  108. # Determine source IP and MAC address
  109. num_attackers = self.get_param_value(Param.NUMBER_ATTACKERS)
  110. if (num_attackers is not None) and (num_attackers is not 0): # user supplied Param.NUMBER_ATTACKERS
  111. # The most used IP class in background traffic
  112. most_used_ip_class = self.statistics.process_db_query("most_used(ipClass)")
  113. # Create random attackers based on user input Param.NUMBER_ATTACKERS
  114. ip_source = self.generate_random_ipv4_address(most_used_ip_class, num_attackers)
  115. mac_source = self.generate_random_mac_address(num_attackers)
  116. else: # user did not supply Param.NUMBER_ATTACKS
  117. # use default values for IP_SOURCE/MAC_SOURCE or overwritten values
  118. # if user supplied any values for those params
  119. ip_source = self.get_param_value(Param.IP_SOURCE)
  120. mac_source = self.get_param_value(Param.MAC_SOURCE)
  121. ip_source_list = []
  122. mac_source_list = []
  123. if isinstance(ip_source, list):
  124. ip_source_list = ip_source
  125. else:
  126. ip_source_list.append(ip_source)
  127. if isinstance(mac_source, list):
  128. mac_source_list = mac_source
  129. else:
  130. mac_source_list.append(mac_source)
  131. if (num_attackers is None) or (num_attackers is 0):
  132. num_attackers = min(len(ip_source_list), len(mac_source_list))
  133. # Check ip.src == ip.dst
  134. self.ip_src_dst_equal_check(ip_source_list, ip_destination)
  135. # Get MSS, TTL and Window size value for destination IP
  136. destination_mss_value, destination_ttl_value, destination_win_value = getIpData(ip_destination)
  137. minDelay,maxDelay = self.get_reply_delay(ip_destination)
  138. attack_duration = self.get_param_value(Param.ATTACK_DURATION)
  139. attack_ends_time = first_timestamp + attack_duration
  140. victim_pps = pps*num_attackers
  141. for attacker in range(num_attackers):
  142. # Get MSS, TTL and Window size value for source IP(attacker)
  143. source_mss_value, source_ttl_value, source_win_value = getIpData(ip_source_list[attacker])
  144. attacker_seq = randint(1000, 50000)
  145. victim_seq = randint(1000, 50000)
  146. sport = 1025
  147. # Timestamps of first packets shouldn't be exactly the same to look more realistic
  148. timestamp_next_pkt = uniform(first_timestamp, first_timestamp+0.010)
  149. while timestamp_next_pkt <= attack_ends_time:
  150. # Establish TCP connection
  151. if sport > 65535:
  152. sport = 1025
  153. # prepare reusable Ethernet- and IP-headers
  154. attacker_ether = Ether(src=mac_source_list[attacker], dst=mac_destination)
  155. attacker_ip = IP(src=ip_source_list[attacker], dst=ip_destination, ttl=source_ttl_value, flags='DF')
  156. victim_ether = Ether(src=mac_destination, dst=mac_source_list[attacker])
  157. victim_ip = IP(src=ip_destination, dst=ip_source_list[attacker], ttl=destination_ttl_value, flags='DF')
  158. # connection request from attacker (client)
  159. syn_tcp = TCP(sport=sport, dport=smb_port, window=source_win_value, flags='S',
  160. seq=attacker_seq, options=[('MSS', source_mss_value)])
  161. attacker_seq += 1
  162. syn = (attacker_ether / attacker_ip / syn_tcp)
  163. syn.time = timestamp_next_pkt
  164. timestamp_next_pkt = update_timestamp(timestamp_next_pkt, victim_pps, minDelay)
  165. packets.append(syn)
  166. # response from victim (server)
  167. synack_tcp = TCP(sport=smb_port, dport=sport, seq=victim_seq, ack=attacker_seq, flags='SA',
  168. window=destination_win_value, options=[('MSS', destination_mss_value)])
  169. victim_seq += 1
  170. synack = (victim_ether / victim_ip / synack_tcp)
  171. synack.time = timestamp_next_pkt
  172. timestamp_next_pkt = update_timestamp(timestamp_next_pkt, pps, minDelay)
  173. packets.append(synack)
  174. # acknowledgement from attacker (client)
  175. ack_tcp = TCP(sport=sport, dport=smb_port, seq=attacker_seq, ack=victim_seq, flags='A',
  176. window=source_win_value, options=[('MSS', source_mss_value)])
  177. ack = (attacker_ether / attacker_ip / ack_tcp)
  178. ack.time = timestamp_next_pkt
  179. timestamp_next_pkt = update_timestamp(timestamp_next_pkt, pps)
  180. packets.append(ack)
  181. # send NBT session header paket with maximum LENGTH-field
  182. req_tcp = TCP(sport=sport, dport=smb_port, seq=attacker_seq, ack=victim_seq, flags='AP',
  183. window=source_win_value, options=[('MSS', source_mss_value)])
  184. req_payload = NBTSession(TYPE=0x00, LENGTH=0x1FFFF)
  185. attacker_seq += len(req_payload)
  186. req = (attacker_ether / attacker_ip / req_tcp / req_payload)
  187. req.time = timestamp_next_pkt
  188. timestamp_next_pkt = update_timestamp(timestamp_next_pkt, victim_pps, minDelay)
  189. packets.append(req)
  190. # final ack from victim (server)
  191. last_ack_tcp = TCP(sport=smb_port, dport=sport, seq=victim_seq, ack=attacker_seq, flags='A',
  192. window=destination_win_value, options=[('MSS', destination_mss_value)])
  193. last_ack = (victim_ether / victim_ip / last_ack_tcp)
  194. last_ack.time = timestamp_next_pkt
  195. timestamp_next_pkt = update_timestamp(timestamp_next_pkt, pps, minDelay)
  196. packets.append(last_ack)
  197. sport += 1
  198. # store end time of attack
  199. self.attack_end_utime = packets[-1].time
  200. # write attack packets to pcap
  201. pcap_path = self.write_attack_pcap(sorted(packets, key=lambda pkt: pkt.time))
  202. # return packets sorted by packet time_sec_start
  203. return len(packets), pcap_path