DDoSAttack.py 11 KB

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