PortscanAttack.py 10 KB

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