SMBLorisAttack.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  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
  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. if isinstance(most_used_ip_address, list):
  44. most_used_ip_address = most_used_ip_address[0]
  45. # The most used IP class in background traffic
  46. most_used_ip_class = self.statistics.process_db_query("most_used(ipClass)")
  47. num_attackers = randint(1, 16)
  48. source_ip = self.generate_random_ipv4_address(most_used_ip_class, num_attackers)
  49. self.add_param_value(Param.IP_SOURCE, source_ip)
  50. self.add_param_value(Param.MAC_SOURCE, self.generate_random_mac_address(num_attackers))
  51. random_ip_address = self.statistics.get_random_ip_address()
  52. # ip-dst should be valid and not equal to ip.src
  53. while not self.is_valid_ip_address(random_ip_address) or random_ip_address == source_ip:
  54. random_ip_address = self.statistics.get_random_ip_address()
  55. self.add_param_value(Param.IP_DESTINATION, random_ip_address)
  56. destination_mac = self.statistics.get_mac_address(random_ip_address)
  57. if isinstance(destination_mac, list) and len(destination_mac) == 0:
  58. destination_mac = self.generate_random_mac_address()
  59. self.add_param_value(Param.MAC_DESTINATION, destination_mac)
  60. self.add_param_value(Param.PACKETS_PER_SECOND,
  61. (self.statistics.get_pps_sent(most_used_ip_address) +
  62. self.statistics.get_pps_received(most_used_ip_address)) / 2)
  63. self.add_param_value(Param.INJECT_AFTER_PACKET, randint(0, self.statistics.get_packet_count()))
  64. self.add_param_value(Param.ATTACK_DURATION, 30)
  65. def generate_attack_pcap(self):
  66. def get_ip_data(ip_address: str):
  67. """
  68. :param ip_address: the ip of which (packet-)data shall be returned
  69. :return: MSS, TTL and Window Size values of the given IP
  70. """
  71. # Set MSS (Maximum Segment Size) based on MSS distribution of IP address
  72. mss_dist = self.statistics.get_mss_distribution(ip_address)
  73. if len(mss_dist) > 0:
  74. mss_prob_dict = Lea.fromValFreqsDict(mss_dist)
  75. mss_value = mss_prob_dict.random()
  76. else:
  77. mss_value = self.statistics.process_db_query("most_used(mssValue)")
  78. # Set TTL based on TTL distribution of IP address
  79. ttl_dist = self.statistics.get_ttl_distribution(ip_address)
  80. if len(ttl_dist) > 0:
  81. ttl_prob_dict = Lea.fromValFreqsDict(ttl_dist)
  82. ttl_value = ttl_prob_dict.random()
  83. else:
  84. ttl_value = self.statistics.process_db_query("most_used(ttlValue)")
  85. # Set Window Size based on Window Size distribution of IP address
  86. win_dist = self.statistics.get_win_distribution(ip_address)
  87. if len(win_dist) > 0:
  88. win_prob_dict = Lea.fromValFreqsDict(win_dist)
  89. win_value = win_prob_dict.random()
  90. else:
  91. win_value = self.statistics.process_db_query("most_used(winSize)")
  92. return mss_value, ttl_value, win_value
  93. pps = self.get_param_value(Param.PACKETS_PER_SECOND)
  94. # Timestamp
  95. first_timestamp = self.get_param_value(Param.INJECT_AT_TIMESTAMP)
  96. # store start time of attack
  97. self.attack_start_utime = first_timestamp
  98. # Initialize parameters
  99. packets = []
  100. ip_destination = self.get_param_value(Param.IP_DESTINATION)
  101. mac_destination = self.get_param_value(Param.MAC_DESTINATION)
  102. # Determine source IP and MAC address
  103. num_attackers = self.get_param_value(Param.NUMBER_ATTACKERS)
  104. if (num_attackers is not None) and (num_attackers is not 0): # user supplied Param.NUMBER_ATTACKERS
  105. # The most used IP class in background traffic
  106. most_used_ip_class = self.statistics.process_db_query("most_used(ipClass)")
  107. # Create random attackers based on user input Param.NUMBER_ATTACKERS
  108. ip_source = self.generate_random_ipv4_address(most_used_ip_class, num_attackers)
  109. mac_source = self.generate_random_mac_address(num_attackers)
  110. else: # user did not supply Param.NUMBER_ATTACKS
  111. # use default values for IP_SOURCE/MAC_SOURCE or overwritten values
  112. # if user supplied any values for those params
  113. ip_source = self.get_param_value(Param.IP_SOURCE)
  114. mac_source = self.get_param_value(Param.MAC_SOURCE)
  115. ip_source_list = []
  116. mac_source_list = []
  117. if isinstance(ip_source, list):
  118. ip_source_list = ip_source
  119. else:
  120. ip_source_list.append(ip_source)
  121. if isinstance(mac_source, list):
  122. mac_source_list = mac_source
  123. else:
  124. mac_source_list.append(mac_source)
  125. if (num_attackers is None) or (num_attackers is 0):
  126. num_attackers = min(len(ip_source_list), len(mac_source_list))
  127. # Check ip.src == ip.dst
  128. self.ip_src_dst_equal_check(ip_source_list, ip_destination)
  129. # Get MSS, TTL and Window size value for destination IP
  130. destination_mss_value, destination_ttl_value, destination_win_value = get_ip_data(ip_destination)
  131. minDelay,maxDelay = self.get_reply_delay(ip_destination)
  132. attack_duration = self.get_param_value(Param.ATTACK_DURATION)
  133. attack_ends_time = first_timestamp + attack_duration
  134. victim_pps = pps*num_attackers
  135. for attacker in range(num_attackers):
  136. # Get MSS, TTL and Window size value for source IP(attacker)
  137. source_mss_value, source_ttl_value, source_win_value = get_ip_data(ip_source_list[attacker])
  138. attacker_seq = randint(1000, 50000)
  139. victim_seq = randint(1000, 50000)
  140. sport = 1025
  141. # Timestamps of first packets shouldn't be exactly the same to look more realistic
  142. timestamp_next_pkt = uniform(first_timestamp, update_timestamp(first_timestamp, pps))
  143. while timestamp_next_pkt <= attack_ends_time:
  144. # Establish TCP connection
  145. if sport > 65535:
  146. sport = 1025
  147. # prepare reusable Ethernet- and IP-headers
  148. attacker_ether = Ether(src=mac_source_list[attacker], dst=mac_destination)
  149. attacker_ip = IP(src=ip_source_list[attacker], dst=ip_destination, ttl=source_ttl_value, flags='DF')
  150. victim_ether = Ether(src=mac_destination, dst=mac_source_list[attacker])
  151. victim_ip = IP(src=ip_destination, dst=ip_source_list[attacker], ttl=destination_ttl_value, flags='DF')
  152. # connection request from attacker (client)
  153. syn_tcp = TCP(sport=sport, dport=smb_port, window=source_win_value, flags='S',
  154. seq=attacker_seq, options=[('MSS', source_mss_value)])
  155. attacker_seq += 1
  156. syn = (attacker_ether / attacker_ip / syn_tcp)
  157. syn.time = timestamp_next_pkt
  158. timestamp_next_pkt = update_timestamp(timestamp_next_pkt, victim_pps, minDelay)
  159. packets.append(syn)
  160. # response from victim (server)
  161. synack_tcp = TCP(sport=smb_port, dport=sport, seq=victim_seq, ack=attacker_seq, flags='SA',
  162. window=destination_win_value, options=[('MSS', destination_mss_value)])
  163. victim_seq += 1
  164. synack = (victim_ether / victim_ip / synack_tcp)
  165. synack.time = timestamp_next_pkt
  166. timestamp_next_pkt = update_timestamp(timestamp_next_pkt, pps, minDelay)
  167. packets.append(synack)
  168. # acknowledgement from attacker (client)
  169. ack_tcp = TCP(sport=sport, dport=smb_port, seq=attacker_seq, ack=victim_seq, flags='A',
  170. window=source_win_value, options=[('MSS', source_mss_value)])
  171. ack = (attacker_ether / attacker_ip / ack_tcp)
  172. ack.time = timestamp_next_pkt
  173. timestamp_next_pkt = update_timestamp(timestamp_next_pkt, pps)
  174. packets.append(ack)
  175. # send NBT session header paket with maximum LENGTH-field
  176. req_tcp = TCP(sport=sport, dport=smb_port, seq=attacker_seq, ack=victim_seq, flags='AP',
  177. window=source_win_value, options=[('MSS', source_mss_value)])
  178. req_payload = NBTSession(TYPE=0x00, LENGTH=0x1FFFF)
  179. attacker_seq += len(req_payload)
  180. req = (attacker_ether / attacker_ip / req_tcp / req_payload)
  181. req.time = timestamp_next_pkt
  182. timestamp_next_pkt = update_timestamp(timestamp_next_pkt, victim_pps, minDelay)
  183. packets.append(req)
  184. # final ack from victim (server)
  185. last_ack_tcp = TCP(sport=smb_port, dport=sport, seq=victim_seq, ack=attacker_seq, flags='A',
  186. window=destination_win_value, options=[('MSS', destination_mss_value)])
  187. last_ack = (victim_ether / victim_ip / last_ack_tcp)
  188. last_ack.time = timestamp_next_pkt
  189. timestamp_next_pkt = update_timestamp(timestamp_next_pkt, pps, minDelay)
  190. packets.append(last_ack)
  191. sport += 1
  192. # store end time of attack
  193. self.attack_end_utime = packets[-1].time
  194. # write attack packets to pcap
  195. pcap_path = self.write_attack_pcap(sorted(packets, key=lambda pkt: pkt.time))
  196. # return packets sorted by packet time_sec_start
  197. return len(packets), pcap_path