DDoSAttack.py 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. import logging
  2. from random import randint, uniform
  3. from lea import Lea
  4. from scipy.stats import gamma
  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, RandShort
  11. from collections import deque
  12. class DDoSAttack(BaseAttack.BaseAttack):
  13. def __init__(self):
  14. """
  15. Creates a new instance of the DDoS attack.
  16. """
  17. # Initialize attack
  18. super(DDoSAttack, self).__init__("DDoS Attack", "Injects a DDoS attack'",
  19. "Resource Exhaustion")
  20. # Define allowed parameters and their type
  21. self.supported_params = {
  22. Param.IP_SOURCE: ParameterTypes.TYPE_IP_ADDRESS,
  23. Param.MAC_SOURCE: ParameterTypes.TYPE_MAC_ADDRESS,
  24. Param.PORT_SOURCE: ParameterTypes.TYPE_PORT,
  25. Param.IP_DESTINATION: ParameterTypes.TYPE_IP_ADDRESS,
  26. Param.MAC_DESTINATION: ParameterTypes.TYPE_MAC_ADDRESS,
  27. Param.PORT_DESTINATION: ParameterTypes.TYPE_PORT,
  28. Param.INJECT_AT_TIMESTAMP: ParameterTypes.TYPE_FLOAT,
  29. Param.INJECT_AFTER_PACKET: ParameterTypes.TYPE_PACKET_POSITION,
  30. Param.PACKETS_PER_SECOND: ParameterTypes.TYPE_FLOAT,
  31. Param.PACKETS_LIMIT: ParameterTypes.TYPE_INTEGER_POSITIVE,
  32. Param.NUMBER_ATTACKERS: ParameterTypes.TYPE_INTEGER_POSITIVE
  33. }
  34. def init_params(self):
  35. """
  36. Initialize the parameters of this attack using the user supplied command line parameters.
  37. Use the provided statistics to calculate default parameters and to process user
  38. supplied queries.
  39. :param statistics: Reference to a statistics object.
  40. """
  41. # PARAMETERS: initialize with default values
  42. # (values are overwritten if user specifies them)
  43. self.add_param_value(Param.INJECT_AFTER_PACKET, randint(0, self.statistics.get_packet_count()))
  44. # attacker configuration
  45. num_attackers = randint(1, 16)
  46. self.add_param_value(Param.IP_SOURCE, self.generate_random_ipv4_address(num_attackers))
  47. self.add_param_value(Param.MAC_SOURCE, self.generate_random_mac_address(num_attackers))
  48. self.add_param_value(Param.PORT_SOURCE, str(RandShort()))
  49. self.add_param_value(Param.PACKETS_PER_SECOND, randint(1, 64))
  50. # victim configuration
  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. port_destination = self.statistics.process_db_query(
  58. "SELECT portNumber FROM ip_ports WHERE portDirection='in' ORDER BY RANDOM() LIMIT 1;")
  59. if port_destination is None:
  60. port_destination = str(RandShort())
  61. self.add_param_value(Param.PORT_DESTINATION, port_destination)
  62. self.add_param_value(Param.PACKETS_LIMIT, randint(1000, 5000))
  63. def generate_attack_pcap(self):
  64. def update_timestamp(timestamp, pps, maxdelay):
  65. """
  66. Calculates the next timestamp to be used based on the packet per second rate (pps) and the maximum delay.
  67. :return: Timestamp to be used for the next packet.
  68. """
  69. return timestamp + uniform(0.1 / pps, maxdelay)
  70. def get_nth_random_element(*element_list):
  71. """
  72. Returns the n-th element of every list from an arbitrary number of given lists.
  73. For example, list1 contains IP addresses, list 2 contains MAC addresses. Use of this function ensures that
  74. the n-th IP address uses always the n-th MAC address.
  75. :param element_list: An arbitrary number of lists.
  76. :return: A tuple of the n-th element of every list.
  77. """
  78. range_max = min([len(x) for x in element_list])
  79. if range_max > 0: range_max -= 1
  80. n = randint(0, range_max)
  81. return tuple(x[n] for x in element_list)
  82. def index_increment(number: int, max: int):
  83. if number + 1 < max:
  84. return number + 1
  85. else:
  86. return 0
  87. def get_attacker_config(ipAddress: str):
  88. """
  89. Returns the attacker configuration depending on the IP address, this includes the port for the next
  90. attacking packet and the previously used (fixed) TTL value.
  91. :param ipAddress: The IP address of the attacker
  92. :return: A tuple consisting of (port, ttlValue)
  93. """
  94. # Determine port
  95. port = attacker_port_mapping.get(ipAddress)
  96. if port is not None: # use next port
  97. next_port = attacker_port_mapping.get(ipAddress) + 1
  98. if next_port > (2 ** 16 - 1):
  99. next_port = 1
  100. else: # generate starting port
  101. next_port = RandShort()
  102. attacker_port_mapping[ipAddress] = next_port
  103. # Determine TTL value
  104. ttl = attacker_ttl_mapping.get(ipAddress)
  105. if ttl is None: # determine TTL value
  106. is_invalid = True
  107. pos = ip_source_list.index(ipAddress)
  108. pos_max = len(gd)
  109. while is_invalid:
  110. ttl = int(round(gd[pos]))
  111. if 0 < ttl < 256: # validity check
  112. is_invalid = False
  113. else:
  114. pos = index_increment(pos, pos_max)
  115. attacker_ttl_mapping[ipAddress] = ttl
  116. # return port and TTL
  117. return next_port, ttl
  118. BUFFER_SIZE = 1000
  119. # Determine source IP and MAC address
  120. num_attackers = self.get_param_value(Param.NUMBER_ATTACKERS)
  121. if num_attackers is not None: # user supplied Param.NUMBER_ATTACKERS
  122. # Create random attackers based on user input Param.NUMBER_ATTACKERS
  123. ip_source_list = self.generate_random_ipv4_address(num_attackers)
  124. mac_source_list = self.generate_random_mac_address(num_attackers)
  125. else: # user did not supply Param.NUMBER_ATTACKS
  126. # use default values for IP_SOURCE/MAC_SOURCE or overwritten values
  127. # if user supplied any values for those params
  128. ip_source_list = self.get_param_value(Param.IP_SOURCE)
  129. mac_source_list = self.get_param_value(Param.MAC_SOURCE)
  130. # Timestamp
  131. timestamp_next_pkt = self.get_param_value(Param.INJECT_AT_TIMESTAMP)
  132. pps = self.get_param_value(Param.PACKETS_PER_SECOND)
  133. randomdelay = Lea.fromValFreqsDict({1 / pps: 70, 2 / pps: 30, 5 / pps: 15, 10 / pps: 3})
  134. # Initialize parameters
  135. packets = deque(maxlen=BUFFER_SIZE)
  136. port_source_list = self.get_param_value(Param.PORT_SOURCE)
  137. mac_destination = self.get_param_value(Param.MAC_DESTINATION)
  138. ip_destination = self.get_param_value(Param.IP_DESTINATION)
  139. port_destination = self.get_param_value(Param.PORT_DESTINATION)
  140. attacker_port_mapping = {}
  141. attacker_ttl_mapping = {}
  142. # Gamma distribution parameters derived from MAWI 13.8G dataset
  143. alpha, loc, beta = (2.3261710235, -0.188306914406, 44.4853123884)
  144. gd = gamma.rvs(alpha, loc=loc, scale=beta, size=len(ip_source_list))
  145. path_attack_pcap = None
  146. for pkt_num in range(self.get_param_value(Param.PACKETS_LIMIT)):
  147. # Select one IP address and its corresponding MAC address
  148. (ip_source, mac_source) = get_nth_random_element(ip_source_list, mac_source_list)
  149. # Determine source port
  150. (port_source, ttl_value) = get_attacker_config(ip_source)
  151. maxdelay = randomdelay.random()
  152. request_ether = Ether(dst=mac_destination, src=mac_source)
  153. request_ip = IP(src=ip_source, dst=ip_destination, ttl=ttl_value)
  154. request_tcp = TCP(sport=port_source, dport=port_destination, flags='S', ack=0)
  155. request = (request_ether / request_ip / request_tcp)
  156. request.time = timestamp_next_pkt
  157. packets.append(request)
  158. timestamp_next_pkt = update_timestamp(timestamp_next_pkt, pps, maxdelay)
  159. # Store timestamp of first packet (for attack label)
  160. if pkt_num == 1:
  161. self.attack_start_utime = packets[0].time
  162. elif pkt_num % BUFFER_SIZE == 0:
  163. last_packet = packets[-1]
  164. packets = sorted(packets, key=lambda pkt: pkt.time)
  165. path_attack_pcap = self.write_attack_pcap(packets, True, path_attack_pcap)
  166. packets = []
  167. if len(packets) > 0:
  168. packets = sorted(packets, key=lambda pkt: pkt.time)
  169. path_attack_pcap = self.write_attack_pcap(packets, True, path_attack_pcap)
  170. # Store timestamp of last packet
  171. self.attack_end_utime = last_packet.time
  172. # return packets sorted by packet time_sec_start
  173. # pkt_num+1: because pkt_num starts at 0
  174. return pkt_num + 1, path_attack_pcap