DDoSAttack.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. import logging
  2. from random import randint, choice
  3. from lea import Lea
  4. from collections import deque
  5. from scapy.layers.inet import IP, Ether, TCP, RandShort
  6. from Attack import BaseAttack
  7. from Attack.AttackParameters import Parameter as Param
  8. from Attack.AttackParameters import ParameterTypes
  9. from ID2TLib.Utility import update_timestamp, get_interval_pps, get_nth_random_element, get_attacker_config
  10. logging.getLogger("scapy.runtime").setLevel(logging.ERROR)
  11. # noinspection PyPep8
  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.NUMBER_ATTACKERS: ParameterTypes.TYPE_INTEGER_POSITIVE,
  32. Param.ATTACK_DURATION: ParameterTypes.TYPE_INTEGER_POSITIVE,
  33. Param.VICTIM_BUFFER: ParameterTypes.TYPE_INTEGER_POSITIVE
  34. }
  35. def init_params(self):
  36. """
  37. Initialize the parameters of this attack using the user supplied command line parameters.
  38. Use the provided statistics to calculate default parameters and to process user
  39. supplied queries.
  40. :param statistics: Reference to a statistics object.
  41. """
  42. # PARAMETERS: initialize with default values
  43. # (values are overwritten if user specifies them)
  44. self.add_param_value(Param.INJECT_AFTER_PACKET, randint(0, self.statistics.get_packet_count()))
  45. # attacker configuration
  46. num_attackers = randint(1, 16)
  47. # The most used IP class in background traffic
  48. most_used_ip_class = self.statistics.process_db_query("most_used(ipClass)")
  49. self.add_param_value(Param.IP_SOURCE, self.generate_random_ipv4_address(most_used_ip_class, num_attackers))
  50. self.add_param_value(Param.MAC_SOURCE, self.generate_random_mac_address(num_attackers))
  51. self.add_param_value(Param.PORT_SOURCE, str(RandShort()))
  52. self.add_param_value(Param.PACKETS_PER_SECOND, 0)
  53. self.add_param_value(Param.ATTACK_DURATION, randint(5,30))
  54. # victim configuration
  55. random_ip_address = self.statistics.get_random_ip_address()
  56. self.add_param_value(Param.IP_DESTINATION, random_ip_address)
  57. destination_mac = self.statistics.get_mac_address(random_ip_address)
  58. if isinstance(destination_mac, list) and len(destination_mac) == 0:
  59. destination_mac = self.generate_random_mac_address()
  60. self.add_param_value(Param.MAC_DESTINATION, destination_mac)
  61. self.add_param_value(Param.VICTIM_BUFFER, randint(1000,10000))
  62. def generate_attack_pcap(self):
  63. BUFFER_SIZE = 1000
  64. # Determine source IP and MAC address
  65. num_attackers = self.get_param_value(Param.NUMBER_ATTACKERS)
  66. if num_attackers is not None: # user supplied Param.NUMBER_ATTACKERS
  67. # The most used IP class in background traffic
  68. most_used_ip_class = self.statistics.process_db_query("most_used(ipClass)")
  69. # Create random attackers based on user input Param.NUMBER_ATTACKERS
  70. ip_source_list = self.generate_random_ipv4_address(most_used_ip_class, num_attackers)
  71. mac_source_list = self.generate_random_mac_address(num_attackers)
  72. else: # user did not supply Param.NUMBER_ATTACKS
  73. # use default values for IP_SOURCE/MAC_SOURCE or overwritten values
  74. # if user supplied any values for those params
  75. ip_source_list = self.get_param_value(Param.IP_SOURCE)
  76. mac_source_list = self.get_param_value(Param.MAC_SOURCE)
  77. num_attackers = len(ip_source_list)
  78. # Initialize parameters
  79. packets = deque(maxlen=BUFFER_SIZE)
  80. port_source_list = self.get_param_value(Param.PORT_SOURCE)
  81. mac_destination = self.get_param_value(Param.MAC_DESTINATION)
  82. ip_destination = self.get_param_value(Param.IP_DESTINATION)
  83. most_used_ip_address = self.statistics.get_most_used_ip_address()
  84. pps = self.get_param_value(Param.PACKETS_PER_SECOND)
  85. if pps == 0:
  86. result = self.statistics.process_db_query("SELECT MAX(maxPktRate) FROM ip_statistics WHERE ipAddress='"+ip_destination+"';")
  87. if result is not None and not 0:
  88. pps = num_attackers * result
  89. else:
  90. result = self.statistics.process_db_query("SELECT MAX(maxPktRate) FROM ip_statistics WHERE ipAddress='"+most_used_ip_address+"';")
  91. pps = num_attackers * result
  92. # Calculate complement packet rates of the background traffic for each interval
  93. attacker_pps = pps / num_attackers
  94. complement_interval_attacker_pps = self.statistics.calculate_complement_packet_rates(attacker_pps)
  95. # Check ip.src == ip.dst
  96. self.ip_src_dst_equal_check(ip_source_list, ip_destination)
  97. port_destination = self.get_param_value(Param.PORT_DESTINATION)
  98. if not port_destination: # user did not define port_dest
  99. port_destination = self.statistics.process_db_query(
  100. "SELECT portNumber FROM ip_ports WHERE portDirection='in' AND ipAddress='" + ip_destination + "' ORDER BY portCount DESC LIMIT 1;")
  101. if not port_destination: # no port was retrieved
  102. port_destination = self.statistics.process_db_query(
  103. "SELECT portNumber FROM ip_ports WHERE portDirection='in' GROUP BY portNumber ORDER BY SUM(portCount) DESC LIMIT 1;")
  104. if not port_destination:
  105. port_destination = max(1, str(RandShort()))
  106. path_attack_pcap = None
  107. timestamp_prv_reply, timestamp_confirm = 0, 0
  108. minDelay, maxDelay = self.get_reply_delay(ip_destination)
  109. victim_buffer = self.get_param_value(Param.VICTIM_BUFFER)
  110. attack_duration = self.get_param_value(Param.ATTACK_DURATION)
  111. pkts_num = int(pps * attack_duration)
  112. source_win_sizes = self.statistics.get_rnd_win_size(pkts_num)
  113. destination_win_dist = self.statistics.get_win_distribution(ip_destination)
  114. if len(destination_win_dist) > 0:
  115. destination_win_prob_dict = Lea.fromValFreqsDict(destination_win_dist)
  116. destination_win_value = destination_win_prob_dict.random()
  117. else:
  118. destination_win_value = self.statistics.process_db_query("most_used(winSize)")
  119. # MSS that was used by IP destination in background traffic
  120. mss_dst = self.statistics.get_most_used_mss(ip_destination)
  121. if mss_dst is None:
  122. mss_dst = self.statistics.process_db_query("most_used(mssValue)")
  123. replies_count = 0
  124. total_pkt_num = 0
  125. # For each attacker, generate his own packets, then merge all packets
  126. for attacker in range(num_attackers):
  127. # Timestamp
  128. timestamp_next_pkt = self.get_param_value(Param.INJECT_AT_TIMESTAMP)
  129. attack_ends_time = timestamp_next_pkt + attack_duration
  130. timestamp_next_pkt = update_timestamp(timestamp_next_pkt, attacker_pps)
  131. attacker_pkts_num = int(pkts_num / num_attackers) + randint(0,100)
  132. for pkt_num in range(attacker_pkts_num):
  133. # Stop the attack when it exceeds the duration
  134. if timestamp_next_pkt > attack_ends_time:
  135. break
  136. # Build request package
  137. # Select one IP address and its corresponding MAC address
  138. (ip_source, mac_source) = get_nth_random_element(ip_source_list, mac_source_list)
  139. # Determine source port
  140. (port_source, ttl_value) = get_attacker_config(ip_source_list ,ip_source)
  141. request_ether = Ether(dst=mac_destination, src=mac_source)
  142. request_ip = IP(src=ip_source, dst=ip_destination, ttl=ttl_value)
  143. # Random win size for each packet
  144. source_win_size = choice(source_win_sizes)
  145. request_tcp = TCP(sport=port_source, dport=port_destination, flags='S', ack=0, window=source_win_size)
  146. request = (request_ether / request_ip / request_tcp)
  147. request.time = timestamp_next_pkt
  148. # Append request
  149. packets.append(request)
  150. total_pkt_num +=1
  151. # Build reply package
  152. if replies_count <= victim_buffer:
  153. reply_ether = Ether(src=mac_destination, dst=mac_source)
  154. reply_ip = IP(src=ip_destination, dst=ip_source, flags='DF')
  155. reply_tcp = TCP(sport=port_destination, dport=port_source, seq=0, ack=1, flags='SA', window=destination_win_value,options=[('MSS', mss_dst)])
  156. reply = (reply_ether / reply_ip / reply_tcp)
  157. timestamp_reply = update_timestamp(timestamp_next_pkt, attacker_pps, minDelay)
  158. while (timestamp_reply <= timestamp_prv_reply):
  159. timestamp_reply = update_timestamp(timestamp_prv_reply, attacker_pps, minDelay)
  160. timestamp_prv_reply = timestamp_reply
  161. reply.time = timestamp_reply
  162. packets.append(reply)
  163. replies_count+=1
  164. total_pkt_num += 1
  165. attacker_pps = max(get_interval_pps(complement_interval_attacker_pps, timestamp_next_pkt), (pps / num_attackers) / 2)
  166. timestamp_next_pkt = update_timestamp(timestamp_next_pkt, attacker_pps)
  167. # Store timestamp of first packet (for attack label)
  168. if total_pkt_num <= 2 :
  169. self.attack_start_utime = packets[0].time
  170. elif pkt_num % BUFFER_SIZE == 0: # every 1000 packets write them to the pcap file (append)
  171. last_packet = packets[-1]
  172. packets = sorted(packets, key=lambda pkt: pkt.time)
  173. path_attack_pcap = self.write_attack_pcap(packets, True, path_attack_pcap)
  174. packets = []
  175. if len(packets) > 0:
  176. packets = sorted(packets, key=lambda pkt: pkt.time)
  177. path_attack_pcap = self.write_attack_pcap(packets, True, path_attack_pcap)
  178. # Store timestamp of last packet
  179. self.attack_end_utime = last_packet.time
  180. # Return packets sorted by packet time_sec_start
  181. # pkt_num+1: because pkt_num starts at 0
  182. return total_pkt_num , path_attack_pcap