PortscanAttack.py 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. import logging
  2. from random import shuffle, randint, choice, uniform
  3. from lea import Lea
  4. from Attack import BaseAttack
  5. from Attack.AttackParameters import Parameter as Param
  6. from Attack.AttackParameters import ParameterTypes
  7. logging.getLogger("scapy.runtime").setLevel(logging.ERROR)
  8. # noinspection PyPep8
  9. from scapy.layers.inet import IP, Ether, TCP
  10. class PortscanAttack(BaseAttack.BaseAttack):
  11. def __init__(self, statistics, pcap_file_path):
  12. """
  13. Creates a new instance of the PortscanAttack.
  14. :param statistics: A reference to the statistics class.
  15. """
  16. # Initialize attack
  17. super(PortscanAttack, self).__init__(statistics, "Portscan Attack", "Injects a nmap 'regular scan'",
  18. "Scanning/Probing")
  19. # Define allowed parameters and their type
  20. self.supported_params = {
  21. Param.IP_SOURCE: ParameterTypes.TYPE_IP_ADDRESS,
  22. Param.IP_DESTINATION: ParameterTypes.TYPE_IP_ADDRESS,
  23. Param.PORT_SOURCE: ParameterTypes.TYPE_PORT,
  24. Param.PORT_DESTINATION: ParameterTypes.TYPE_PORT,
  25. Param.PORT_OPEN: ParameterTypes.TYPE_PORT,
  26. Param.MAC_SOURCE: ParameterTypes.TYPE_MAC_ADDRESS,
  27. Param.MAC_DESTINATION: ParameterTypes.TYPE_MAC_ADDRESS,
  28. Param.INJECT_AT_TIMESTAMP: ParameterTypes.TYPE_FLOAT,
  29. Param.INJECT_AFTER_PACKET: ParameterTypes.TYPE_PACKET_POSITION,
  30. Param.PORT_DEST_SHUFFLE: ParameterTypes.TYPE_BOOLEAN,
  31. Param.PORT_DEST_ORDER_DESC: ParameterTypes.TYPE_BOOLEAN,
  32. Param.IP_SOURCE_RANDOMIZE: ParameterTypes.TYPE_BOOLEAN,
  33. Param.PACKETS_PER_SECOND: ParameterTypes.TYPE_FLOAT,
  34. Param.PORT_SOURCE_RANDOMIZE: ParameterTypes.TYPE_BOOLEAN
  35. }
  36. # PARAMETERS: initialize with default values
  37. # (values are overwritten if user specifies them)
  38. most_used_ip_address = self.statistics.get_most_used_ip_address()
  39. if isinstance(most_used_ip_address, list):
  40. most_used_ip_address = most_used_ip_address[0]
  41. self.add_param_value(Param.IP_SOURCE, most_used_ip_address)
  42. self.add_param_value(Param.IP_SOURCE_RANDOMIZE, 'False')
  43. self.add_param_value(Param.MAC_SOURCE, self.statistics.get_mac_address(most_used_ip_address))
  44. random_ip_address = self.statistics.get_random_ip_address()
  45. self.add_param_value(Param.IP_DESTINATION, random_ip_address)
  46. destination_mac = self.statistics.get_mac_address(random_ip_address)
  47. if isinstance(destination_mac, list) and len(destination_mac) == 0:
  48. destination_mac = self.generate_random_mac_address()
  49. self.add_param_value(Param.MAC_DESTINATION, destination_mac)
  50. self.add_param_value(Param.PORT_DESTINATION, '1-1023,1720,1900,8080,56652')
  51. self.add_param_value(Param.PORT_OPEN, '8080,9232,9233')
  52. self.add_param_value(Param.PORT_DEST_SHUFFLE, 'False')
  53. self.add_param_value(Param.PORT_DEST_ORDER_DESC, 'False')
  54. self.add_param_value(Param.PORT_SOURCE, randint(1024, 65535))
  55. self.add_param_value(Param.PORT_SOURCE_RANDOMIZE, 'False')
  56. self.add_param_value(Param.PACKETS_PER_SECOND,
  57. (self.statistics.get_pps_sent(most_used_ip_address) +
  58. self.statistics.get_pps_received(most_used_ip_address)) / 2)
  59. self.add_param_value(Param.INJECT_AFTER_PACKET, randint(0, self.statistics.get_packet_count()))
  60. def generate_attack_pcap(self):
  61. def update_timestamp(timestamp, pps, maxdelay):
  62. """
  63. Calculates the next timestamp to be used based on the packet per second rate (pps) and the maximum delay.
  64. :return: Timestamp to be used for the next packet.
  65. """
  66. return timestamp + uniform(0.1 / pps, maxdelay)
  67. # Determine ports
  68. dest_ports = self.get_param_value(Param.PORT_DESTINATION)
  69. if self.get_param_value(Param.PORT_DEST_ORDER_DESC):
  70. dest_ports.reverse()
  71. elif self.get_param_value(Param.PORT_DEST_SHUFFLE):
  72. shuffle(dest_ports)
  73. if self.get_param_value(Param.PORT_SOURCE_RANDOMIZE):
  74. # Aidmar
  75. sport = randint(1, 65535)
  76. #sport = randint(0, 65535)
  77. else:
  78. sport = self.get_param_value(Param.PORT_SOURCE)
  79. # Timestamp
  80. timestamp_next_pkt = self.get_param_value(Param.INJECT_AT_TIMESTAMP)
  81. # store start time of attack
  82. self.attack_start_utime = timestamp_next_pkt
  83. # Initialize parameters
  84. packets = []
  85. ip_source = self.get_param_value(Param.IP_SOURCE)
  86. ip_destination = self.get_param_value(Param.IP_DESTINATION)
  87. mac_source = self.get_param_value(Param.MAC_SOURCE)
  88. mac_destination = self.get_param_value(Param.MAC_DESTINATION)
  89. pps = self.get_param_value(Param.PACKETS_PER_SECOND)
  90. randomdelay = Lea.fromValFreqsDict({1 / pps: 70, 2 / pps: 30, 5 / pps: 15, 10 / pps: 3})
  91. maxdelay = randomdelay.random()
  92. # open ports
  93. # Aidmar - the ports that were already used by ip.dst (direction in) in the background traffic are open ports
  94. ports_open = self.get_param_value(Param.PORT_OPEN)
  95. if ports_open == [8080,9232,9233]: # user did not define open ports
  96. ports_used_by_ip_dst = self.statistics.process_db_query(
  97. "SELECT portNumber FROM ip_ports WHERE portDirection='in' AND ipAddress='" + ip_destination + "';")
  98. if ports_used_by_ip_dst: # if no ports were retrieved from database
  99. ports_open = ports_used_by_ip_dst
  100. else:
  101. ports_open = [80, 443] # TO-DO: take random ports from nmap-services from the most frequent
  102. print("\nPorts used by %s: %s" % (ip_destination, ports_open))
  103. # convert ports_open to array
  104. if not isinstance(ports_open, list):
  105. ports_open = [ports_open]
  106. # =========================================================================================================
  107. # MSS (Maximum Segment Size) for Ethernet. Allowed values [536,1500]
  108. # Aidmar
  109. # mss = self.statistics.get_mss(ip_destination)
  110. mss_dst = self.statistics.get_mss(ip_destination)
  111. mss_src = self.statistics.get_mss(ip_source)
  112. # =========================================================================================================
  113. # Set TTL based on TTL distribution of IP address
  114. ttl_dist = self.statistics.get_ttl_distribution(ip_source)
  115. if len(ttl_dist) > 0:
  116. ttl_prob_dict = Lea.fromValFreqsDict(ttl_dist)
  117. ttl_value = ttl_prob_dict.random()
  118. else:
  119. ttl_value = self.statistics.process_db_query("most_used(ttlValue)")
  120. for dport in dest_ports:
  121. # Parameters changing each iteration
  122. if self.get_param_value(Param.IP_SOURCE_RANDOMIZE) and isinstance(ip_source, list):
  123. ip_source = choice(ip_source)
  124. # 1) Build request package
  125. request_ether = Ether(src=mac_source, dst=mac_destination)
  126. request_ip = IP(src=ip_source, dst=ip_destination, ttl=ttl_value)
  127. # Aidmar - random src port for each packet
  128. sport = randint(1, 65535)
  129. if mss_src is None:
  130. request_tcp = TCP(sport=sport, dport=dport, flags='S')
  131. else:
  132. request_tcp = TCP(sport=sport, dport=dport, flags='S', options=[('MSS', mss_src)])
  133. # =========================================================================================================
  134. request = (request_ether / request_ip / request_tcp)
  135. # first packet uses timestamp provided by attack parameter Param.INJECT_AT_TIMESTAMP
  136. if len(packets) > 0:
  137. timestamp_next_pkt = update_timestamp(timestamp_next_pkt, pps, maxdelay)
  138. request.time = timestamp_next_pkt
  139. packets.append(request)
  140. # 2) Build reply package
  141. if dport in ports_open: # destination port is OPEN
  142. reply_ether = Ether(src=mac_destination, dst=mac_source)
  143. reply_ip = IP(src=ip_destination, dst=ip_source, flags='DF')
  144. if mss_dst is None:
  145. reply_tcp = TCP(sport=dport, dport=sport, seq=0, ack=1, flags='SA', window=29200)
  146. else:
  147. reply_tcp = TCP(sport=dport, dport=sport, seq=0, ack=1, flags='SA', window=29200,
  148. options=[('MSS', mss_dst)])
  149. reply = (reply_ether / reply_ip / reply_tcp)
  150. timestamp_next_pkt = update_timestamp(timestamp_next_pkt, pps, maxdelay)
  151. reply.time = timestamp_next_pkt
  152. packets.append(reply)
  153. # requester confirms
  154. confirm_ether = request_ether
  155. confirm_ip = request_ip
  156. confirm_tcp = TCP(sport=sport, dport=dport, seq=1, window=0, flags='R')
  157. reply = (confirm_ether / confirm_ip / confirm_tcp)
  158. timestamp_next_pkt = update_timestamp(timestamp_next_pkt, pps, maxdelay)
  159. reply.time = timestamp_next_pkt
  160. packets.append(reply)
  161. # else: destination port is NOT OPEN -> no reply is sent by target
  162. # store end time of attack
  163. self.attack_end_utime = packets[-1].time
  164. # write attack packets to pcap
  165. pcap_path = self.write_attack_pcap(sorted(packets, key=lambda pkt: pkt.time))
  166. # return packets sorted by packet time_sec_start
  167. return len(packets), pcap_path