PortscanAttack.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. import logging
  2. import random as rnd
  3. import lea
  4. import scapy.layers.inet as inet
  5. import Attack.AttackParameters as atkParam
  6. import Attack.BaseAttack as BaseAttack
  7. import ID2TLib.Utility as Util
  8. logging.getLogger("scapy.runtime").setLevel(logging.ERROR)
  9. # noinspection PyPep8
  10. class PortscanAttack(BaseAttack.BaseAttack):
  11. def __init__(self):
  12. """
  13. Creates a new instance of the PortscanAttack.
  14. This attack injects TCP Syn-requests and respective responses into the output pcap file.
  15. """
  16. # Initialize attack
  17. super(PortscanAttack, self).__init__("Portscan Attack", "Injects a nmap 'regular scan'",
  18. "Scanning/Probing")
  19. # Define allowed parameters and their type
  20. self.supported_params.update({
  21. atkParam.Parameter.IP_SOURCE: atkParam.ParameterTypes.TYPE_IP_ADDRESS,
  22. atkParam.Parameter.IP_DESTINATION: atkParam.ParameterTypes.TYPE_IP_ADDRESS,
  23. atkParam.Parameter.PORT_SOURCE: atkParam.ParameterTypes.TYPE_PORT,
  24. atkParam.Parameter.PORT_DESTINATION: atkParam.ParameterTypes.TYPE_PORT,
  25. atkParam.Parameter.PORT_OPEN: atkParam.ParameterTypes.TYPE_PORT,
  26. atkParam.Parameter.MAC_SOURCE: atkParam.ParameterTypes.TYPE_MAC_ADDRESS,
  27. atkParam.Parameter.MAC_DESTINATION: atkParam.ParameterTypes.TYPE_MAC_ADDRESS,
  28. atkParam.Parameter.INJECT_AT_TIMESTAMP: atkParam.ParameterTypes.TYPE_FLOAT,
  29. atkParam.Parameter.INJECT_AFTER_PACKET: atkParam.ParameterTypes.TYPE_PACKET_POSITION,
  30. atkParam.Parameter.PORT_DEST_SHUFFLE: atkParam.ParameterTypes.TYPE_BOOLEAN,
  31. atkParam.Parameter.PORT_DEST_ORDER_DESC: atkParam.ParameterTypes.TYPE_BOOLEAN,
  32. atkParam.Parameter.IP_SOURCE_RANDOMIZE: atkParam.ParameterTypes.TYPE_BOOLEAN,
  33. atkParam.Parameter.PACKET_LIMIT_PER_SECOND: atkParam.ParameterTypes.TYPE_FLOAT,
  34. atkParam.Parameter.PORT_SOURCE_RANDOMIZE: atkParam.ParameterTypes.TYPE_BOOLEAN
  35. })
  36. def init_params(self):
  37. """
  38. Initialize the parameters of this attack using the user supplied command line parameters.
  39. Use the provided statistics to calculate default parameters and to process user
  40. supplied queries.
  41. """
  42. # PARAMETERS: initialize with default values
  43. # (values are overwritten if user specifies them)
  44. most_used_ip_address = self.statistics.get_most_used_ip_address()
  45. self.add_param_value(atkParam.Parameter.IP_SOURCE, most_used_ip_address)
  46. self.add_param_value(atkParam.Parameter.IP_SOURCE_RANDOMIZE, 'False')
  47. self.add_param_value(atkParam.Parameter.MAC_SOURCE, self.statistics.get_mac_address(most_used_ip_address))
  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 == most_used_ip_address:
  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.PORT_DESTINATION, self.get_ports_from_nmap_service_dst(1000))
  58. self.add_param_value(atkParam.Parameter.PORT_OPEN, '1')
  59. self.add_param_value(atkParam.Parameter.PORT_DEST_SHUFFLE, 'False')
  60. self.add_param_value(atkParam.Parameter.PORT_DEST_ORDER_DESC, 'False')
  61. self.add_param_value(atkParam.Parameter.PORT_SOURCE, rnd.randint(1024, 65535))
  62. self.add_param_value(atkParam.Parameter.PORT_SOURCE_RANDOMIZE, 'False')
  63. self.add_param_value(atkParam.Parameter.PACKET_LIMIT_PER_SECOND,
  64. (self.statistics.get_pps_sent(most_used_ip_address) +
  65. self.statistics.get_pps_received(most_used_ip_address)) / 2)
  66. self.add_param_value(atkParam.Parameter.INJECT_AFTER_PACKET, rnd.randint(0, self.statistics.get_packet_count()))
  67. def generate_attack_packets(self):
  68. """
  69. Creates the attack packets.
  70. """
  71. mac_source = self.get_param_value(atkParam.Parameter.MAC_SOURCE)
  72. mac_destination = self.get_param_value(atkParam.Parameter.MAC_DESTINATION)
  73. pps = self.get_param_value(atkParam.Parameter.PACKET_LIMIT_PER_SECOND)
  74. # Calculate complement packet rates of the background traffic for each interval
  75. complement_interval_pps = self.statistics.calculate_complement_packet_rates(pps)
  76. # Determine ports
  77. dest_ports = self.get_param_value(atkParam.Parameter.PORT_DESTINATION)
  78. if self.get_param_value(atkParam.Parameter.PORT_DEST_ORDER_DESC):
  79. dest_ports.reverse()
  80. elif self.get_param_value(atkParam.Parameter.PORT_DEST_SHUFFLE):
  81. rnd.shuffle(dest_ports)
  82. if self.get_param_value(atkParam.Parameter.PORT_SOURCE_RANDOMIZE):
  83. # FIXME: why is sport never used?
  84. sport = rnd.randint(1, 65535)
  85. else:
  86. sport = self.get_param_value(atkParam.Parameter.PORT_SOURCE)
  87. # Timestamp
  88. timestamp_next_pkt = self.get_param_value(atkParam.Parameter.INJECT_AT_TIMESTAMP)
  89. # store start time of attack
  90. self.attack_start_utime = timestamp_next_pkt
  91. timestamp_prv_reply, timestamp_confirm = 0, 0
  92. # Initialize parameters
  93. self.packets = []
  94. ip_source = self.get_param_value(atkParam.Parameter.IP_SOURCE)
  95. if isinstance(ip_source, list):
  96. ip_source = ip_source[0]
  97. ip_destination = self.get_param_value(atkParam.Parameter.IP_DESTINATION)
  98. if not isinstance(ip_destination, list):
  99. ip_destination = [ip_destination]
  100. # Check ip.src == ip.dst
  101. self.ip_src_dst_equal_check(ip_source, ip_destination)
  102. for ip in ip_destination:
  103. # Select open ports
  104. ports_open = self.get_param_value(atkParam.Parameter.PORT_OPEN)
  105. if ports_open == 1: # user did not specify open ports
  106. # the ports that were already used by ip.dst (direction in) in the background traffic are open ports
  107. ports_used_by_ip_dst = self.statistics.process_db_query(
  108. "SELECT portNumber FROM ip_ports WHERE portDirection='in' AND ipAddress='" + ip + "'")
  109. if ports_used_by_ip_dst:
  110. ports_open = ports_used_by_ip_dst
  111. else: # if no ports were retrieved from database
  112. # Take open ports from nmap-service file
  113. # ports_temp = self.get_ports_from_nmap_service_dst(100)
  114. # ports_open = ports_temp[0:rnd.randint(1,10)]
  115. # OR take open ports from the most used ports in traffic statistics
  116. ports_open = self.statistics.process_db_query(
  117. "SELECT portNumber FROM ip_ports GROUP BY portNumber ORDER BY SUM(portCount) DESC LIMIT " + str(
  118. rnd.randint(1, 10)))
  119. # in case of one open port, convert ports_open to array
  120. if not isinstance(ports_open, list):
  121. ports_open = [ports_open]
  122. # Set MSS (Maximum Segment Size) based on MSS distribution of IP address
  123. source_mss_dist = self.statistics.get_mss_distribution(ip_source)
  124. if len(source_mss_dist) > 0:
  125. source_mss_prob_dict = lea.Lea.fromValFreqsDict(source_mss_dist)
  126. source_mss_value = source_mss_prob_dict.random()
  127. else:
  128. source_mss_value = Util.handle_most_used_outputs(self.statistics.get_most_used_mss_value())
  129. destination_mss_dist = self.statistics.get_mss_distribution(ip)
  130. if len(destination_mss_dist) > 0:
  131. destination_mss_prob_dict = lea.Lea.fromValFreqsDict(destination_mss_dist)
  132. destination_mss_value = destination_mss_prob_dict.random()
  133. else:
  134. destination_mss_value = Util.handle_most_used_outputs(self.statistics.get_most_used_mss_value())
  135. # Set TTL based on TTL distribution of IP address
  136. source_ttl_dist = self.statistics.get_ttl_distribution(ip_source)
  137. if len(source_ttl_dist) > 0:
  138. source_ttl_prob_dict = lea.Lea.fromValFreqsDict(source_ttl_dist)
  139. source_ttl_value = source_ttl_prob_dict.random()
  140. else:
  141. source_ttl_value = Util.handle_most_used_outputs(self.statistics.get_most_used_ttl_value())
  142. destination_ttl_dist = self.statistics.get_ttl_distribution(ip)
  143. if len(destination_ttl_dist) > 0:
  144. destination_ttl_prob_dict = lea.Lea.fromValFreqsDict(destination_ttl_dist)
  145. destination_ttl_value = destination_ttl_prob_dict.random()
  146. else:
  147. destination_ttl_value = Util.handle_most_used_outputs(self.statistics.get_most_used_ttl_value())
  148. # Set Window Size based on Window Size distribution of IP address
  149. source_win_dist = self.statistics.get_win_distribution(ip_source)
  150. if len(source_win_dist) > 0:
  151. source_win_prob_dict = lea.Lea.fromValFreqsDict(source_win_dist)
  152. source_win_value = source_win_prob_dict.random()
  153. else:
  154. source_win_value = Util.handle_most_used_outputs(self.statistics.get_most_used_win_size())
  155. destination_win_dist = self.statistics.get_win_distribution(ip)
  156. if len(destination_win_dist) > 0:
  157. destination_win_prob_dict = lea.Lea.fromValFreqsDict(destination_win_dist)
  158. destination_win_value = destination_win_prob_dict.random()
  159. else:
  160. destination_win_value = Util.handle_most_used_outputs(self.statistics.get_most_used_win_size())
  161. min_delay, max_delay = self.get_reply_delay(ip)
  162. for dport in dest_ports:
  163. # Parameters changing each iteration
  164. if self.get_param_value(atkParam.Parameter.IP_SOURCE_RANDOMIZE) and isinstance(ip_source, list):
  165. ip_source = rnd.choice(ip_source)
  166. # 1) Build request package
  167. request_ether = inet.Ether(src=mac_source, dst=mac_destination)
  168. request_ip = inet.IP(src=ip_source, dst=ip, ttl=source_ttl_value)
  169. # Random src port for each packet
  170. sport = rnd.randint(1, 65535)
  171. request_tcp = inet.TCP(sport=sport, dport=dport, window=source_win_value, flags='S',
  172. options=[('MSS', source_mss_value)])
  173. request = (request_ether / request_ip / request_tcp)
  174. request.time = timestamp_next_pkt
  175. # Append request
  176. self.packets.append(request)
  177. # 2) Build reply (for open ports) package
  178. if dport in ports_open: # destination port is OPEN
  179. reply_ether = inet.Ether(src=mac_destination, dst=mac_source)
  180. reply_ip = inet.IP(src=ip, dst=ip_source, ttl=destination_ttl_value, flags='DF')
  181. reply_tcp = inet.TCP(sport=dport, dport=sport, seq=0, ack=1, flags='SA', window=destination_win_value,
  182. options=[('MSS', destination_mss_value)])
  183. reply = (reply_ether / reply_ip / reply_tcp)
  184. timestamp_reply = Util.update_timestamp(timestamp_next_pkt, pps, min_delay)
  185. while timestamp_reply <= timestamp_prv_reply:
  186. timestamp_reply = Util.update_timestamp(timestamp_prv_reply, pps, min_delay)
  187. timestamp_prv_reply = timestamp_reply
  188. reply.time = timestamp_reply
  189. self.packets.append(reply)
  190. # requester confirms
  191. confirm_ether = request_ether
  192. confirm_ip = request_ip
  193. confirm_tcp = inet.TCP(sport=sport, dport=dport, seq=1, window=0, flags='R')
  194. confirm = (confirm_ether / confirm_ip / confirm_tcp)
  195. timestamp_confirm = Util.update_timestamp(timestamp_reply, pps, min_delay)
  196. confirm.time = timestamp_confirm
  197. self.packets.append(confirm)
  198. # else: destination port is NOT OPEN -> no reply is sent by target
  199. pps = max(Util.get_interval_pps(complement_interval_pps, timestamp_next_pkt), 10)
  200. timestamp_next_pkt = Util.update_timestamp(timestamp_next_pkt, pps)
  201. def generate_attack_pcap(self):
  202. """
  203. Creates a pcap containing the attack packets.
  204. :return: The location of the generated pcap file.
  205. """
  206. # store end time of attack
  207. self.attack_end_utime = self.packets[-1].time
  208. # write attack packets to pcap
  209. pcap_path = self.write_attack_pcap(sorted(self.packets, key=lambda pkt: pkt.time))
  210. # return packets sorted by packet time_sec_start
  211. return len(self.packets), pcap_path