SMBLorisAttack.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. import logging
  2. import random as rnd
  3. import scapy.layers.inet as inet
  4. from scapy.layers.netbios import NBTSession
  5. import Attack.AttackParameters as atkParam
  6. import Attack.BaseAttack as BaseAttack
  7. import ID2TLib.SMBLib as SMBLib
  8. import ID2TLib.Utility as Util
  9. logging.getLogger("scapy.runtime").setLevel(logging.ERROR)
  10. # noinspection PyPep8
  11. class SMBLorisAttack(BaseAttack.BaseAttack):
  12. def __init__(self):
  13. """
  14. Creates a new instance of the SMBLorisAttack.
  15. This attack injects special SMB-packets, which exploit the SMBLoris DoS vulnerability, into the output pcap
  16. file.
  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.update({
  23. atkParam.Parameter.IP_SOURCE: atkParam.ParameterTypes.TYPE_IP_ADDRESS,
  24. atkParam.Parameter.IP_DESTINATION: atkParam.ParameterTypes.TYPE_IP_ADDRESS,
  25. atkParam.Parameter.MAC_SOURCE: atkParam.ParameterTypes.TYPE_MAC_ADDRESS,
  26. atkParam.Parameter.MAC_DESTINATION: atkParam.ParameterTypes.TYPE_MAC_ADDRESS,
  27. atkParam.Parameter.INJECT_AT_TIMESTAMP: atkParam.ParameterTypes.TYPE_FLOAT,
  28. atkParam.Parameter.INJECT_AFTER_PACKET: atkParam.ParameterTypes.TYPE_PACKET_POSITION,
  29. atkParam.Parameter.PACKET_LIMIT_PER_SECOND: atkParam.ParameterTypes.TYPE_FLOAT,
  30. atkParam.Parameter.ATTACK_DURATION: atkParam.ParameterTypes.TYPE_INTEGER_POSITIVE,
  31. atkParam.Parameter.NUMBER_ATTACKERS: atkParam.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. """
  39. # PARAMETERS: initialize with default values
  40. # (values are overwritten if user specifies them)
  41. most_used_ip_address = self.statistics.get_most_used_ip_address()
  42. # The most used IP class in background traffic
  43. most_used_ip_class = Util.handle_most_used_outputs(self.statistics.get_most_used_ip_class())
  44. num_attackers = rnd.randint(1, 16)
  45. source_ip = self.generate_random_ipv4_address(most_used_ip_class, num_attackers)
  46. self.add_param_value(atkParam.Parameter.IP_SOURCE, source_ip)
  47. self.add_param_value(atkParam.Parameter.MAC_SOURCE, self.generate_random_mac_address(num_attackers))
  48. random_ip_address = self.statistics.get_random_ip_address()
  49. # ip-dst should be valid and not equal to ip.src
  50. while not self.is_valid_ip_address(random_ip_address) or random_ip_address == source_ip:
  51. random_ip_address = self.statistics.get_random_ip_address()
  52. self.add_param_value(atkParam.Parameter.IP_DESTINATION, random_ip_address)
  53. destination_mac = self.statistics.get_mac_address(random_ip_address)
  54. if isinstance(destination_mac, list) and len(destination_mac) == 0:
  55. destination_mac = self.generate_random_mac_address()
  56. self.add_param_value(atkParam.Parameter.MAC_DESTINATION, destination_mac)
  57. self.add_param_value(atkParam.Parameter.PACKET_LIMIT_PER_SECOND,
  58. (self.statistics.get_pps_sent(most_used_ip_address) +
  59. self.statistics.get_pps_received(most_used_ip_address)) / 2)
  60. self.add_param_value(atkParam.Parameter.INJECT_AFTER_PACKET, rnd.randint(0, self.statistics.get_packet_count()))
  61. self.add_param_value(atkParam.Parameter.ATTACK_DURATION, 30)
  62. def generate_attack_packets(self):
  63. """
  64. Creates the attack packets.
  65. """
  66. pps = self.get_param_value(atkParam.Parameter.PACKET_LIMIT_PER_SECOND)
  67. # Timestamp
  68. first_timestamp = self.get_param_value(atkParam.Parameter.INJECT_AT_TIMESTAMP)
  69. # store start time of attack
  70. self.attack_start_utime = first_timestamp
  71. # Initialize parameters
  72. self.packets = []
  73. ip_destination = self.get_param_value(atkParam.Parameter.IP_DESTINATION)
  74. mac_destination = self.get_param_value(atkParam.Parameter.MAC_DESTINATION)
  75. # Determine source IP and MAC address
  76. num_attackers = self.get_param_value(atkParam.Parameter.NUMBER_ATTACKERS)
  77. # user supplied atkParam.Parameter.NUMBER_ATTACKERS
  78. if (num_attackers is not None) and (num_attackers is not 0):
  79. # The most used IP class in background traffic
  80. most_used_ip_class = Util.handle_most_used_outputs(self.statistics.get_most_used_ip_class())
  81. # Create random attackers based on user input atkParam.Parameter.NUMBER_ATTACKERS
  82. ip_source = self.generate_random_ipv4_address(most_used_ip_class, num_attackers)
  83. mac_source = self.generate_random_mac_address(num_attackers)
  84. else: # user did not supply atkParam.Parameter.NUMBER_ATTACKS
  85. # use default values for IP_SOURCE/MAC_SOURCE or overwritten values
  86. # if user supplied any values for those params
  87. ip_source = self.get_param_value(atkParam.Parameter.IP_SOURCE)
  88. mac_source = self.get_param_value(atkParam.Parameter.MAC_SOURCE)
  89. ip_source_list = []
  90. mac_source_list = []
  91. if isinstance(ip_source, list):
  92. ip_source_list = ip_source
  93. else:
  94. ip_source_list.append(ip_source)
  95. if isinstance(mac_source, list):
  96. mac_source_list = mac_source
  97. else:
  98. mac_source_list.append(mac_source)
  99. if (num_attackers is None) or (num_attackers is 0):
  100. num_attackers = min(len(ip_source_list), len(mac_source_list))
  101. # Check ip.src == ip.dst
  102. self.ip_src_dst_equal_check(ip_source_list, ip_destination)
  103. # Get MSS, TTL and Window size value for destination IP
  104. destination_mss_value, destination_ttl_value, destination_win_value = self.get_ip_data(ip_destination)
  105. min_delay, max_delay = self.get_reply_delay(ip_destination)
  106. attack_duration = self.get_param_value(atkParam.Parameter.ATTACK_DURATION)
  107. attack_ends_time = first_timestamp + attack_duration
  108. victim_pps = pps * num_attackers
  109. for attacker in range(num_attackers):
  110. # Get MSS, TTL and Window size value for source IP(attacker)
  111. source_mss_value, source_ttl_value, source_win_value = self.get_ip_data(ip_source_list[attacker])
  112. attacker_seq = rnd.randint(1000, 50000)
  113. victim_seq = rnd.randint(1000, 50000)
  114. sport = 1025
  115. # Timestamps of first self.packets shouldn't be exactly the same to look more realistic
  116. timestamp_next_pkt = rnd.uniform(first_timestamp, Util.update_timestamp(first_timestamp, pps))
  117. while timestamp_next_pkt <= attack_ends_time:
  118. # Establish TCP connection
  119. if sport > 65535:
  120. sport = 1025
  121. # prepare reusable Ethernet- and IP-headers
  122. attacker_ether = inet.Ether(src=mac_source_list[attacker], dst=mac_destination)
  123. attacker_ip = inet.IP(src=ip_source_list[attacker], dst=ip_destination, ttl=source_ttl_value,
  124. flags='DF')
  125. victim_ether = inet.Ether(src=mac_destination, dst=mac_source_list[attacker])
  126. victim_ip = inet.IP(src=ip_destination, dst=ip_source_list[attacker], ttl=destination_ttl_value,
  127. flags='DF')
  128. # connection request from attacker (client)
  129. syn_tcp = inet.TCP(sport=sport, dport=SMBLib.smb_port, window=source_win_value, flags='S',
  130. seq=attacker_seq, options=[('MSS', source_mss_value)])
  131. attacker_seq += 1
  132. syn = (attacker_ether / attacker_ip / syn_tcp)
  133. syn.time = timestamp_next_pkt
  134. timestamp_next_pkt = Util.update_timestamp(timestamp_next_pkt, victim_pps, min_delay)
  135. self.packets.append(syn)
  136. # response from victim (server)
  137. synack_tcp = inet.TCP(sport=SMBLib.smb_port, dport=sport, seq=victim_seq, ack=attacker_seq, flags='SA',
  138. window=destination_win_value, options=[('MSS', destination_mss_value)])
  139. victim_seq += 1
  140. synack = (victim_ether / victim_ip / synack_tcp)
  141. synack.time = timestamp_next_pkt
  142. timestamp_next_pkt = Util.update_timestamp(timestamp_next_pkt, pps, min_delay)
  143. self.packets.append(synack)
  144. # acknowledgement from attacker (client)
  145. ack_tcp = inet.TCP(sport=sport, dport=SMBLib.smb_port, seq=attacker_seq, ack=victim_seq, flags='A',
  146. window=source_win_value, options=[('MSS', source_mss_value)])
  147. ack = (attacker_ether / attacker_ip / ack_tcp)
  148. ack.time = timestamp_next_pkt
  149. timestamp_next_pkt = Util.update_timestamp(timestamp_next_pkt, pps)
  150. self.packets.append(ack)
  151. # send NBT session header packet with maximum LENGTH-field
  152. req_tcp = inet.TCP(sport=sport, dport=SMBLib.smb_port, seq=attacker_seq, ack=victim_seq, flags='AP',
  153. window=source_win_value, options=[('MSS', source_mss_value)])
  154. req_payload = NBTSession(TYPE=0x00, LENGTH=0x1FFFF)
  155. attacker_seq += len(req_payload)
  156. req = (attacker_ether / attacker_ip / req_tcp / req_payload)
  157. req.time = timestamp_next_pkt
  158. timestamp_next_pkt = Util.update_timestamp(timestamp_next_pkt, victim_pps, min_delay)
  159. self.packets.append(req)
  160. # final ack from victim (server)
  161. last_ack_tcp = inet.TCP(sport=SMBLib.smb_port, dport=sport, seq=victim_seq, ack=attacker_seq, flags='A',
  162. window=destination_win_value, options=[('MSS', destination_mss_value)])
  163. last_ack = (victim_ether / victim_ip / last_ack_tcp)
  164. last_ack.time = timestamp_next_pkt
  165. timestamp_next_pkt = Util.update_timestamp(timestamp_next_pkt, pps, min_delay)
  166. self.packets.append(last_ack)
  167. sport += 1
  168. def generate_attack_pcap(self):
  169. """
  170. Creates a pcap containing the attack packets.
  171. :return: The location of the generated pcap file.
  172. """
  173. # store end time of attack
  174. self.attack_end_utime = self.packets[-1].time
  175. # write attack self.packets to pcap
  176. pcap_path = self.write_attack_pcap(sorted(self.packets, key=lambda pkt: pkt.time))
  177. # return packets sorted by packet time_sec_start
  178. return len(self.packets), pcap_path