PortscanAttack.py 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  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):
  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__("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. 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. :param statistics: Reference to a statistics object.
  42. """
  43. # PARAMETERS: initialize with default values
  44. # (values are overwritten if user specifies them)
  45. most_used_ip_address = self.statistics.get_most_used_ip_address()
  46. if isinstance(most_used_ip_address, list):
  47. most_used_ip_address = most_used_ip_address[0]
  48. self.add_param_value(Param.IP_SOURCE, most_used_ip_address)
  49. self.add_param_value(Param.IP_SOURCE_RANDOMIZE, 'False')
  50. self.add_param_value(Param.MAC_SOURCE, self.statistics.get_mac_address(most_used_ip_address))
  51. random_ip_address = self.statistics.get_random_ip_address()
  52. self.add_param_value(Param.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(Param.MAC_DESTINATION, destination_mac)
  57. self.add_param_value(Param.PORT_DESTINATION, '1-1023,1720,1900,8080')
  58. self.add_param_value(Param.PORT_OPEN, '8080,9232,9233')
  59. self.add_param_value(Param.PORT_DEST_SHUFFLE, 'False')
  60. self.add_param_value(Param.PORT_DEST_ORDER_DESC, 'False')
  61. self.add_param_value(Param.PORT_SOURCE, randint(1024, 65535))
  62. self.add_param_value(Param.PORT_SOURCE_RANDOMIZE, 'False')
  63. self.add_param_value(Param.PACKETS_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(Param.INJECT_AFTER_PACKET, randint(0, self.statistics.get_packet_count()))
  67. def generate_attack_pcap(self):
  68. def update_timestamp(timestamp, pps, maxdelay):
  69. """
  70. Calculates the next timestamp to be used based on the packet per second rate (pps) and the maximum delay.
  71. :return: Timestamp to be used for the next packet.
  72. """
  73. return timestamp + uniform(0.1 / pps, maxdelay)
  74. # Determine ports
  75. dest_ports = self.get_param_value(Param.PORT_DESTINATION)
  76. if self.get_param_value(Param.PORT_DEST_ORDER_DESC):
  77. dest_ports.reverse()
  78. elif self.get_param_value(Param.PORT_DEST_SHUFFLE):
  79. shuffle(dest_ports)
  80. if self.get_param_value(Param.PORT_SOURCE_RANDOMIZE):
  81. sport = randint(0, 65535)
  82. else:
  83. sport = self.get_param_value(Param.PORT_SOURCE)
  84. # Timestamp
  85. timestamp_next_pkt = self.get_param_value(Param.INJECT_AT_TIMESTAMP)
  86. # store start time of attack
  87. self.attack_start_utime = timestamp_next_pkt
  88. # Initialize parameters
  89. packets = []
  90. ip_source = self.get_param_value(Param.IP_SOURCE)
  91. ip_destination = self.get_param_value(Param.IP_DESTINATION)
  92. mac_source = self.get_param_value(Param.MAC_SOURCE)
  93. mac_destination = self.get_param_value(Param.MAC_DESTINATION)
  94. pps = self.get_param_value(Param.PACKETS_PER_SECOND)
  95. randomdelay = Lea.fromValFreqsDict({1 / pps: 70, 2 / pps: 30, 5 / pps: 15, 10 / pps: 3})
  96. maxdelay = randomdelay.random()
  97. # MSS (Maximum Segment Size) for Ethernet. Allowed values [536,1500]
  98. mss = self.statistics.get_mss(ip_destination)
  99. # Set TTL based on TTL distribution of IP address
  100. ttl_dist = self.statistics.get_ttl_distribution(ip_source)
  101. if len(ttl_dist) > 0:
  102. ttl_prob_dict = Lea.fromValFreqsDict(ttl_dist)
  103. ttl_value = ttl_prob_dict.random()
  104. else:
  105. ttl_value = self.statistics.process_db_query("most_used(ttlValue)")
  106. for dport in dest_ports:
  107. # Parameters changing each iteration
  108. if self.get_param_value(Param.IP_SOURCE_RANDOMIZE) and isinstance(ip_source, list):
  109. ip_source = choice(ip_source)
  110. # 1) Build request package
  111. request_ether = Ether(src=mac_source, dst=mac_destination)
  112. request_ip = IP(src=ip_source, dst=ip_destination, ttl=ttl_value)
  113. request_tcp = TCP(sport=sport, dport=dport, flags='S')
  114. request = (request_ether / request_ip / request_tcp)
  115. # first packet uses timestamp provided by attack parameter Param.INJECT_AT_TIMESTAMP
  116. if len(packets) > 0:
  117. timestamp_next_pkt = update_timestamp(timestamp_next_pkt, pps, maxdelay)
  118. request.time = timestamp_next_pkt
  119. packets.append(request)
  120. # 2) Build reply package
  121. if dport in self.get_param_value(Param.PORT_OPEN): # destination port is OPEN
  122. reply_ether = Ether(src=mac_destination, dst=mac_source)
  123. reply_ip = IP(src=ip_destination, dst=ip_source, flags='DF')
  124. if mss is None:
  125. reply_tcp = TCP(sport=dport, dport=sport, seq=0, ack=1, flags='SA', window=29200)
  126. else:
  127. reply_tcp = TCP(sport=dport, dport=sport, seq=0, ack=1, flags='SA', window=29200,
  128. options=[('MSS', mss)])
  129. reply = (reply_ether / reply_ip / reply_tcp)
  130. timestamp_next_pkt = update_timestamp(timestamp_next_pkt, pps, maxdelay)
  131. reply.time = timestamp_next_pkt
  132. packets.append(reply)
  133. # requester confirms
  134. confirm_ether = request_ether
  135. confirm_ip = request_ip
  136. confirm_tcp = TCP(sport=sport, dport=dport, seq=1, window=0, flags='R')
  137. reply = (confirm_ether / confirm_ip / confirm_tcp)
  138. timestamp_next_pkt = update_timestamp(timestamp_next_pkt, pps, maxdelay)
  139. reply.time = timestamp_next_pkt
  140. packets.append(reply)
  141. # else: destination port is NOT OPEN -> no reply is sent by target
  142. # store end time of attack
  143. self.attack_end_utime = packets[-1].time
  144. # write attack packets to pcap
  145. pcap_path = self.write_attack_pcap(sorted(packets, key=lambda pkt: pkt.time))
  146. # return packets sorted by packet time_sec_start
  147. return len(packets), pcap_path