DDoSAttack.py 13 KB

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