DDoSAttack.py 8.0 KB

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