PortscanAttack.py 13 KB

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