SMBLorisAttack.py 11 KB

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