DDoSAttack.py 12 KB

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