SMBLorisAttack.py 12 KB

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