def __send_unencrypted_ack(self): chlo = ACKPacket() conf.L3socket = L3RawSocket chlo.setfieldval( 'CID', string_to_ascii(SessionInstance.get_instance().connection_id)) chlo.setfieldval( "Packet Number", PacketNumberInstance.get_instance().get_next_packet_number()) # print("First Ack Packet Number {}".format(int(str(PacketNumberInstance.get_instance().highest_received_packet_number), 16))) chlo.setfieldval( 'Largest Acked', int( str(PacketNumberInstance.get_instance(). highest_received_packet_number), 16)) chlo.setfieldval( 'First Ack Block Length', int( str(PacketNumberInstance.get_instance(). highest_received_packet_number), 16)) associated_data = extract_from_packet(chlo, end=15) body = extract_from_packet(chlo, start=27) message_authentication_hash = FNV128A().generate_hash( associated_data, body, True) chlo.setfieldval('Message Authentication Hash', string_to_ascii(message_authentication_hash)) p = IP(dst=SessionInstance.get_instance().destination_ip) / UDP( dport=6121, sport=61250) / chlo send(p)
def handle_received_encrypted_packet(self, packet): a = AEADPacketDynamic(packet[0][1][1].payload.load) a.parse() # print(">>>>>>>> Received packet with MAH: {}".format(a.get_field(AEADFieldNames.MESSAGE_AUTHENTICATION_HASH))) # Start key derixvation SessionInstance.get_instance().div_nonce = a.get_field( AEADFieldNames.DIVERSIFICATION_NONCE) SessionInstance.get_instance( ).message_authentication_hash = a.get_field( AEADFieldNames.MESSAGE_AUTHENTICATION_HASH) packet_number = a.get_field(AEADFieldNames.PACKET_NUMBER) SessionInstance.get_instance().packet_number = packet_number # print("Packet Number {}".format(packet_number)) SessionInstance.get_instance( ).largest_observed_packet_number = packet_number SessionInstance.get_instance().associated_data = a.get_associated_data( ) # print("Associated Data {}".format(SessionInstance.get_instance().associated_data)) ciphertext = split_at_nth_char( a.get_field(AEADFieldNames.ENCRYPTED_FRAMES)) # print("Received peer public value {}".format(SessionInstance.get_instance().peer_public_value)) dhke.generate_keys(SessionInstance.get_instance().peer_public_value, SessionInstance.get_instance().shlo_received) # SessionInstance.get_instance().packet_number = packet_number # Process the streams processor = FramesProcessor(ciphertext) processor.process()
def reset(self, reset_server, reset_run=True): # also reset the server if reset_server: # remove the previous session CacheInstance.get_instance().remove_session_model() filename = str(time.time()) open('resets/{}'.format(filename), 'a') time.sleep(8) if reset_run: # For the three times a command we do not want to remove the run events, only when there is a complete reset # which occurs after an iteration or after an explicit RESET command. self.run_events = [] self.run = "" self.previous_result = "" PacketNumberInstance.get_instance().reset() conn_id = random.getrandbits(64) SessionInstance.get_instance().shlo_received = False SessionInstance.get_instance().scfg = "" SessionInstance.get_instance().zero_rtt = False self.logger.info("Changing CID from {}".format( SessionInstance.get_instance().connection_id)) SessionInstance.get_instance().connection_id_as_number = conn_id SessionInstance.get_instance().connection_id = str( format(conn_id, 'x').zfill(16)) # Pad to 16 chars self.logger.info("To {}".format( SessionInstance.get_instance().connection_id))
def send_chlo(self, only_reset): # print("Only reset? {}".format(only_reset)) self.reset(only_reset) if only_reset: self.learner.respond("RESET") return # print(SessionInstance.get_instance().connection_id) # print("Sending CHLO") chlo = QUICHeader() conf.L3socket = L3RawSocket chlo.setfieldval( 'CID', string_to_ascii(SessionInstance.get_instance().connection_id)) chlo.setfieldval( "Packet Number", PacketNumberInstance.get_instance().get_next_packet_number()) associated_data = extract_from_packet(chlo, end=15) body = extract_from_packet(chlo, start=27) message_authentication_hash = FNV128A().generate_hash( associated_data, body) chlo.setfieldval('Message Authentication Hash', string_to_ascii(message_authentication_hash)) # Store chlo for the key derivation SessionInstance.get_instance( ).chlo = extract_from_packet_as_bytestring(chlo) self.sniffer.add_observer(self) p = IP(dst=SessionInstance.get_instance().destination_ip) / UDP( dport=6121, sport=61250) / chlo send(p) self.wait_for_signal_or_expiration() self.processed = False self.sniffer.remove_observer(self)
def close_connection(self): """ We do this the unfriendly way, since GoAway does not work. friendly way by means of a Go Away :return: """ frame_data = "02" # frame type frame_data += "00000000" # error code, no error # frame_data += "00000000" # latest responded stream Id frame_data += "0000" # No reason therefore length of 0 # encrypt it packet_number = PacketNumberInstance.get_instance( ).get_next_packet_number() ciphertext = CryptoManager.encrypt(bytes.fromhex(frame_data), packet_number, SessionInstance.get_instance(), self.logger) a = AEADRequestPacket() a.setfieldval("Public Flags", 0x18) a.setfieldval('Packet Number', packet_number) a.setfieldval("Message Authentication Hash", string_to_ascii(ciphertext[0:24])) a.setfieldval( 'CID', string_to_ascii(SessionInstance.get_instance().connection_id)) self.logger.info("Closing connection {}".format( SessionInstance.get_instance().connection_id)) self.logger.info("With ciphertext {}".format(ciphertext)) p = IP(dst=SessionInstance.get_instance().destination_ip) / UDP( dport=6121, sport=61250) / a / Raw(load=string_to_ascii(ciphertext[24:])) # ans, _ = sr(p, count=3) send(p) self.wait_for_signal_or_expiration() self.processed = False self.sniffer.remove_observer(self) time.sleep(1)
def send_ping(self): print("Sending ping message...") ping = PingPacket() ping.setfieldval( 'CID', string_to_ascii(SessionInstance.get_instance().connection_id)) packet_number = PacketNumberInstance.get_instance( ).get_next_packet_number() ciphertext = CryptoManager.encrypt(bytes.fromhex("07"), packet_number, SessionInstance.get_instance()) ping.setfieldval('Packet Number', packet_number) ping.setfieldval("Message Authentication Hash", string_to_ascii(ciphertext[:24])) conf.L3socket = L3RawSocket p = IP(dst=SessionInstance.get_instance().destination_ip) / UDP( dport=6121, sport=61250) / ping / Raw(load=string_to_ascii(ciphertext[24:])) # Maybe we cannot assume that is just a version negotiation packet? send(p)
def make_get_request(self): get_request = "800300002501250000000500000000FF418FF1E3C2E5F23A6BA0AB9EC9AE38110782848750839BD9AB7A85ED6988B4C7" packet_number = PacketNumberInstance.get_instance( ).get_next_packet_number() ciphertext = CryptoManager.encrypt(bytes.fromhex(get_request), packet_number, self.__instance) # Send it to the server a = AEADRequestPacket() a.setfieldval( 'CID', string_to_ascii(SessionInstance.get_instance().connection_id)) a.setfieldval("Public Flags", 0x18) a.setfieldval('Packet Number', packet_number) a.setfieldval("Message Authentication Hash", string_to_ascii(ciphertext[0:24])) p = IP(dst=SessionInstance.get_instance().destination_ip) / UDP( dport=6121, sport=61250) / a / Raw(load=string_to_ascii(ciphertext[24:])) # self.__sniffer.add_observer(self) send(p)
def send_second_ack(self): chlo = SecondACKPacket() conf.L3socket = L3RawSocket chlo.setfieldval( 'CID', string_to_ascii(SessionInstance.get_instance().connection_id)) chlo.setfieldval( "Packet Number", PacketNumberInstance.get_instance().get_next_packet_number()) associated_data = extract_from_packet(chlo, end=15) body = extract_from_packet(chlo, start=27) message_authentication_hash = FNV128A().generate_hash( associated_data, body) chlo.setfieldval('Message Authentication Hash', string_to_ascii(message_authentication_hash)) p = IP(dst=SessionInstance.get_instance().destination_ip) / UDP( dport=6121, sport=61250) / chlo send(p)
def parse(self): """ :return: """ tags = [] self.parse_type() tag = self.read_byte(4) if tag == "53484c4f": tag_number = self.read_byte(2) tag_number = struct.unpack("<h", bytes.fromhex(tag_number))[ 0] # Number of tags that need to be processed self.read_byte(2) offset = 0 for i in range(0, tag_number): tag = bytes.fromhex(self.read_byte(4)) length = struct.unpack("<i", bytes.fromhex(self.read_byte(4)))[0] length = length - offset offset += length tags.append({'tag': tag, 'length': length}) for tag in tags: tag['value'] = self.read_byte(tag['length']) if "PUBS".encode('utf-8') in tag['tag']: SessionInstance.get_instance( ).peer_public_value = bytes.fromhex(tag['value']) elif "SNO".encode('utf-8') in tag['tag']: SessionInstance.get_instance().server_nonce = tag['value'] print(tags) SessionInstance.get_instance( ).last_received_shlo = SessionInstance.get_instance( ).message_authentication_hash return True return False
def send_message(endpoint, msg: bytes, expect_answer=False): if endpoint == ConnectionEndpoint.CRYPTO_ORACLE or True: print("Sending message ...") crypto_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) crypto_socket.connect( (SessionInstance.get_instance().destination_ip, 3030)) crypto_socket.send(msg) if expect_answer: # Arbitrary big sized buffer print("Waiting for response...") data = crypto_socket.recv(555555) decoded_data = data.decode('utf-8') return json.loads(decoded_data) crypto_socket.close() else: raise NotImplementedError("Currently only Crypto Oracle")
def send_full_chlo(self): chlo = FullCHLOPacket() chlo.setfieldval( 'CID', string_to_ascii(SessionInstance.get_instance().connection_id)) chlo.setfieldval('SCID_Value', SessionInstance.get_instance().server_config_id) chlo.setfieldval('STK_Value', SessionInstance.get_instance().source_address_token) # Lets just create the public key for DHKE dhke.set_up_my_keys() chlo.setfieldval( "Packet Number", PacketNumberInstance.get_instance().get_next_packet_number()) chlo.setfieldval( 'PUBS_Value', string_to_ascii( SessionInstance.get_instance().public_values_bytes)) associated_data = extract_from_packet(chlo, end=15) body = extract_from_packet(chlo, start=27) message_authentication_hash = FNV128A().generate_hash( associated_data, body) chlo.setfieldval('Message Authentication Hash', string_to_ascii(message_authentication_hash)) conf.L3socket = L3RawSocket SessionInstance.get_instance( ).chlo = extract_from_packet_as_bytestring( chlo, start=31 ) # CHLO from the CHLO tag, which starts at offset 26 (22 header + frame type + stream id + offset) # print("Send full CHLO") p = IP(dst=SessionInstance.get_instance().destination_ip) / UDP( dport=6121, sport=61250) / chlo # Maybe we cannot assume that is just a version negotiation packet? # ans, _ = sr(p) self.sniffer.add_observer(self) send(p) self.wait_for_signal_or_expiration() self.processed = False self.sniffer.remove_observer(self)
def packet_update(self, packet): # print("Received update from the Sniffer thread") a = AEADPacketDynamic(packet[0][1][1].payload.load) a.parse() print(">>>>>>>> Received packet with MAH: {}".format( a.get_field(AEADFieldNames.MESSAGE_AUTHENTICATION_HASH))) # Start key derixvation SessionInstance.get_instance().div_nonce = a.get_field( AEADFieldNames.DIVERSIFICATION_NONCE) SessionInstance.get_instance( ).message_authentication_hash = a.get_field( AEADFieldNames.MESSAGE_AUTHENTICATION_HASH) packet_number = a.get_field(AEADFieldNames.PACKET_NUMBER) SessionInstance.get_instance().packet_number = packet_number SessionInstance.get_instance( ).largest_observed_packet_number = packet_number # print(">>>><<<!!!! Updating highest received packet number to {}".format(int(packet_number, 16))) # PacketNumberInstance.get_instance().update_highest_received_packet_number(int(packet_number, 16)) dhke.generate_keys(SessionInstance.get_instance().peer_public_value, SessionInstance.get_instance().shlo_received) SessionInstance.get_instance().associated_data = a.get_associated_data( ) SessionInstance.get_instance().packet_number = packet_number # Process the streams processor = FramesProcessor( split_at_nth_char(a.get_field(AEADFieldNames.ENCRYPTED_FRAMES))) processor.process(self) # print("GETTER received packets {}".format(self.__received_packets)) if self.__received_packets < 3: self.__received_packets += 1 else: self.__sniffer.remove_observer(self) self.send_ack()
def process(self): """ Assumption, the stream frame is last. FIXME :return: """ # Add stream Id == 1 check # Set the stream Id. It starts after the header (byte 27), after the byte frame type (28). try: was_shlo = SHLOPacketProcessor(self.packet_body).parse() SessionInstance.get_instance().shlo_received = was_shlo if was_shlo: self.status = "shlo" else: self.status = "unknown" except NotSHLOButHtmlException as err: # If we catch the exception, then it is not a SHLO (Stream ID != 1) self.status = "http" except NotHtmlNorSHLOException as err: # We don't know what it is. self.status = "unknown" except NotSHLOButCloseException as err: self.status = "close"
def send_full_chlo_to_existing_connection(self): """ Is it sent encrypted? :return: """ try: previous_session = SessionModel.get(SessionModel.id == 1) self.logger.info(previous_session) self.logger.info("Server config Id {}".format( previous_session.server_config_id)) self.logger.info(SessionInstance.get_instance().app_keys) SessionInstance.get_instance( ).last_received_rej = "-1" # I want to force the sniffer to generate a new set of keys. SessionInstance.get_instance().zero_rtt = True # The order is important! tags = [ { 'name': 'PAD', 'value': '00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' }, { 'name': 'SNI', 'value': '7777772e6578616d706c652e6f7267' }, { 'name': 'STK', 'value': previous_session.source_address_token }, { 'name': 'SNO', 'value': previous_session.server_nonce }, { 'name': 'VER', 'value': '00000000' }, { 'name': 'CCS', 'value': '01e8816092921ae87eed8086a2158291' }, { 'name': 'NONC', 'value': '5ac349e90091b5556f1a3c52eb57f92c12640e876e26ab2601c02b2a32f54830' }, { 'name': 'AEAD', 'value': '41455347' # AESGCM12 }, { 'name': 'SCID', 'value': previous_session.server_config_id }, { 'name': 'PDMD', 'value': '58353039' }, { 'name': 'ICSL', 'value': '1e000000' }, { 'name': 'PUBS', 'value': '96D49F2CE98F31F053DCB6DFE729669385E5FD99D5AA36615E1A9AD57C1B090C' }, { 'name': 'MIDS', 'value': '64000000' }, { 'name': 'KEXS', 'value': '43323535' # C25519 }, { 'name': 'XLCT', 'value': '8d884a6c79a0e6de' }, { 'name': 'CFCW', 'value': '00c00000' }, { 'name': 'SFCW', 'value': '00800000' }, ] d = DynamicCHLOPacket(tags) body = d.build_body() PacketNumberInstance.get_instance().reset() conn_id = random.getrandbits(64) SessionInstance.get_instance( ).server_nonce = previous_session.server_nonce SessionInstance.get_instance().connection_id_as_number = conn_id SessionInstance.get_instance().connection_id = str( format(conn_id, 'x').zfill(8)) SessionInstance.get_instance().peer_public_value = bytes.fromhex( previous_session.public_value) self.logger.info("Using connection Id {}".format( SessionInstance.get_instance().connection_id)) SessionInstance.get_instance().shlo_received = False # SessionInstance.get_instance().zero_rtt = True # This one should only be set if the Zero RTT CHLO does not result in a REJ. # a = FullCHLOPacketNoPadding() a.setfieldval( 'Packet Number', PacketNumberInstance.get_instance().get_next_packet_number()) a.setfieldval( 'CID', string_to_ascii(SessionInstance.get_instance().connection_id)) # # Lets just create the public key for DHKE dhke.set_up_my_keys() associated_data = extract_from_packet(a, end=15) body_mah = [body[i:i + 2] for i in range(0, len(body), 2)] message_authentication_hash = FNV128A().generate_hash( associated_data, body_mah) conf.L3socket = L3RawSocket SessionInstance.get_instance( ).chlo = extract_from_packet_as_bytestring( a, start=27 ) # CHLO from the CHLO tag, which starts at offset 26 (22 header + frame type + stream id + offset) SessionInstance.get_instance().chlo += body[4:] # dhke.generate_keys(bytes.fromhex(previous_session.public_value), False) # ciphertext = CryptoManager.encrypt(bytes.fromhex(SessionInstance.get_instance().chlo), 1) # a.setfieldval('Message Authentication Hash', string_to_ascii(message_authentication_hash)) # # print("Send full CHLO from existing connection") # p = IP(dst=SessionInstance.get_instance().destination_ip) / UDP( dport=6121, sport=61250) / a / Raw(load=string_to_ascii(body)) # # Maybe we cannot assume that is just a version negotiation packet? self.sniffer.add_observer(self) send(p) self.wait_for_signal_or_expiration() self.processed = False self.sniffer.remove_observer(self) except Exception: self.send_chlo(False)
def inform_observer(self, packet): parsed_packet = AEADPacketDynamic(packet[0][1][1].payload.load) parsed_packet.parse() if parsed_packet.get_field( AEADFieldNames.CID) == SessionInstance.get_instance( ).connection_id: self.__logger.info("Received packet with MAH {}".format( parsed_packet.get_field( AEADFieldNames.MESSAGE_AUTHENTICATION_HASH))) print(">>>>>>>> Received packet with MAH: {}".format( parsed_packet.get_field( AEADFieldNames.MESSAGE_AUTHENTICATION_HASH))) self.__packet_instance.highest_received_packet_number = parsed_packet.get_field( AEADFieldNames.PACKET_NUMBER) # Catch the Public Reset packet, flags == 0x0e && TAG == PRST (50525354) # print("Parsed packet Public Flags {}".format(parsed_packet.get_field(AEADFieldNames.PUBLIC_FLAGS))) if parsed_packet.get_field(AEADFieldNames.PUBLIC_FLAGS) == "0e": # check if the tag is equal to the PRST tag = parsed_packet.packet_body[9:13] if tag == b'PRST': self.__logger.info("Parsed as PRST") # Public Reset Packet for observer in self.__observers: observer.update("", "PRST") return if SessionInstance.get_instance( ).shlo_received or parsed_packet.has_field( AEADFieldNames.DIVERSIFICATION_NONCE): threading.Thread(target=self.__send_encrypted_ack, args=()).start() result = self.__handle_encrypted_packet(parsed_packet) self.__logger.info("Parsed as encrypted packet") for observer in self.__observers: # print("Result {}".format(result)) observer.update("", result) else: # Add a check if it is really a REJ or just garbage try: threading.Thread(target=self.__send_unencrypted_ack, args=()).start() rej_packet = RejectionPacket(packet[0][1][1].payload.load) rej_tag = rej_packet.getfieldval('Tag_1') if rej_tag == b'REJ\x00': # print("REJ Received") SessionInstance.get_instance( ).last_received_rej = parsed_packet.get_field( AEADFieldNames.MESSAGE_AUTHENTICATION_HASH) self.__logger.info("Parsed as REJ packet") # if not SessionInstance.get_instance().currently_sending_zero_rtt: self.__handle_rej_packet(rej_packet) for observer in self.__observers: observer.update("", "REJ") else: self.__logger.info("Parsed as Garbage") threading.Thread(target=self.__send_unencrypted_ack, args=()).start() # print("Garbage received, ack has been sent.") except Exception: self.__logger.info("Parsed as closed. Not a REJ packet.") # Considered as garbage. # Maybe its not a REJ packet # for observer in self.__observers: # observer.update("", "closed") else: self.__logger.info("Discarding old connection id message received")
def send_ack_for_encrypted_message(self): ack = AckNotificationPacket() conf.L3socket = L3RawSocket ack.setfieldval( 'CID', string_to_ascii(SessionInstance.get_instance().connection_id)) next_packet_number_int = PacketNumberInstance.get_instance( ).get_next_packet_number() next_packet_number_byte = int(next_packet_number_int).to_bytes( 8, byteorder='little') next_packet_number_nonce = int(next_packet_number_int).to_bytes( 2, byteorder='big') # print("Sending encrypted ack for packet number {}".format(next_packet_number_int)) ack.setfieldval("Packet Number", next_packet_number_int) highest_received_packet_number = format( int( PacketNumberInstance.get_instance(). get_highest_received_packet_number(), 16), 'x') ack_body = "40" ack_body += str(highest_received_packet_number).zfill(2) ack_body += "0062" ack_body += str(highest_received_packet_number).zfill(2) ack_body += "00" # not sure yet if we can remove this? keys = SessionInstance.get_instance().keys request = { 'mode': 'encryption', 'input': ack_body, 'key': keys['key1'].hex(), # For encryption, we use my key 'additionalData': "18" + SessionInstance.get_instance().connection_id + next_packet_number_byte.hex() [:4], # Fixed public flags 18 || fixed connection Id || packet number 'nonce': keys['iv1'].hex() + next_packet_number_nonce.hex().ljust(16, '0') } # print("Ack request for encryption {}".format(request)) ciphertext = CryptoConnectionManager.send_message( ConnectionEndpoint.CRYPTO_ORACLE, json.dumps(request).encode('utf-8'), True) ciphertext = ciphertext['data'] # print("Ciphertext in ack {}".format(ciphertext)) ack.setfieldval("Message Authentication Hash", string_to_ascii(ciphertext[:24])) SessionInstance.get_instance().nr_ack_send += 1 p = IP(dst=SessionInstance.get_instance().destination_ip) / UDP( dport=6121, sport=61250) / ack / Raw(load=string_to_ascii(ciphertext[24:])) send(p)
def wait_for_signal_or_expiration(self): # wait for a specific time otherwise start = time.time() expired = False print(self.run_results) while not self.processed and not expired: if time.time() - start >= self.TIMEOUT: expired = True if expired: # print("General expired") if len(self.run_results) == 3 or True: # Get the majority element c = Counter(self.run_results) value, count = c.most_common()[0] self.learner.respond(value) self.logger.info("General expired") self.run += str(self.current_event) # self.ndc.add_run(self.run, value) self.run_results = [] self.previous_result = value # self.reset(True, False) # Reset the server else: self.run_results.append("EXP") self.logger.info( "Received first time {} launching again".format("EXP")) time.sleep(2) self.logger.info("Run events {}".format(self.run_events)) # if not isinstance(self.current_event, SendGETRequestEvent): self.send(self.current_event, True) else: # print("General response {}".format(self.result)) self.logger.info("Currently at run results {}".format( self.run_results)) self.logger.info("Current running event {}".format( self.current_event)) self.logger.info("Previous result {}".format(self.previous_result)) if isinstance(self.current_event, CloseConnectionEvent): if self.previous_result == "EXP": self.learner.respond("closed") self.run_results = [] return if isinstance(self.current_event, SendGETRequestEvent): # Does not need multiple times, as only the first time we get an HTTP response if self.previous_result == "EXP": self.learner.respond("EXP") self.run_results = [] return elif self.result == "REJ": self.learner.respond("EXP") self.run_results = [] self.result = "" return elif self.previous_result == "shlo": self.learner.respond("http") self.previous_result = "http" self.result = "" self.run_results = [] return else: self.learner.respond(self.result) self.previous_result = self.result self.result = "" self.run_results = [] return if isinstance(self.current_event, SendInitialCHLOEvent): # Does not really need to send multiple times if self.previous_result == "": self.learner.respond("REJ") self.previous_result = "REJ" self.result = "" self.run_results = [] return if isinstance(self.current_event, SendFullCHLOEvent): # If it is a full CHLO and we receive a SHLO. Do not send it again if self.result == "http": self.learner.respond("EXP") self.result = "" self.run_results = [] return elif self.previous_result == "EXP": self.learner.respond("EXP") self.previous_result = "EXP" self.run_results = [] return elif self.previous_result == "shlo": self.learner.respond("EXP") self.previous_result = "EXP" self.run_results = [] return elif self.previous_result == "" or self.previous_result == "PRST": self.learner.respond("PRST") self.result = "" self.run_results = [] return if self.result == "shlo": self.learner.respond("shlo") self.run_results = [] self.previous_result = "shlo" return if isinstance(self.current_event, ZeroRTTCHLOEvent): # We can only send this once, otherwise the second time it will automatically send it as a full message SessionInstance.get_instance( ).currently_sending_zero_rtt = False if self.result == "REJ" or self.result == "shlo": self.learner.respond(self.result) self.previous_result = self.result self.run_results = [] return if len(self.run_results) == 2 and isinstance( self.current_event, ZeroRTTCHLOEvent): # We actually need it, otherwise a subsequent Full CHLO will not result in a SHLO. SessionInstance.get_instance( ).currently_sending_zero_rtt = False if len(self.run_results) == 3 or True: # Get the majority element if self.run_results.count("EXP") < 3: self.run_results = [ x for x in self.run_results if 'EXP' != x ] c = Counter(self.run_results) value, count = c.most_common()[0] if isinstance(self.current_event, SendGETRequestEvent): # If there is atleast one HTTP response, then it is a HTTP response. # Because the server didn't respond to three subsequent GET requests. if "http" in self.run_results: value = "http" if "REJ" in self.run_results: if self.run_results.count("REJ") == len( self.run_results): value = "EXP" else: # remove all the REJs value = list( filter(lambda a: a != 'REJ', self.run_results))[0] if self.previous_result == "shlo" and "http" not in self.run_results: value = "http" if isinstance(self.current_event, ZeroRTTCHLOEvent): SessionInstance.get_instance( ).currently_sending_zero_rtt = False if value == "REJ": SessionInstance.get_instance().zero_rtt = False elif value == "shlo": SessionInstance.get_instance().zero_rtt = True if self.previous_result == "REJ" and value != "shlo": value = "shlo" elif self.previous_result == "shlo": value = "shlo" elif value == "EXP": value = "REJ" self.learner.respond(value) self.logger.info("Responding to learner {}".format(value)) # self.run += str(self.current_event) # self.ndc.add_run(self.run, value) self.previous_result = value self.run_results = [] # self.reset(True, False) else: self.run_results.append(self.result) self.logger.info( "Received first time {} launching again".format( self.result)) time.sleep(2) self.logger.info("Run events {}".format(self.run_events)) # if not isinstance(self.current_event, SendGETRequestEvent): self.send(self.current_event, True) self.logger.info("=========== Request Finished ===========") self.result = ""
def __handle_rej_packet(self, a): self.__logger.info("Storing REJ information from packet {}".format( a.getfieldval('Message Authentication Hash'))) for key, value in a.fields.items(): if "Server_Config_ID" in key: SessionInstance.get_instance().server_config_id = value self.__logger.info("STORING: ServerConfigId {}".format(value)) if "Source_Address_Token" in key: SessionInstance.get_instance().source_address_token = value self.__logger.info("STORING: SATKOK {}".format(value)) if "Server_Nonce" in key: SessionInstance.get_instance().server_nonce = value.hex() self.__logger.info("STORING: SNONCE {}".format(value)) if "Public_Value" in key: # Has length 35, remove the first 4 bytes which only indicate the length of 32 bytes. SessionInstance.get_instance( ).peer_public_value = bytes.fromhex(value[3:].hex()) self.__logger.info("Public value used for DHKE {}".format( value[3:].hex())) # Store it locally, such that in a next CHLO we can directly use it. CacheInstance.get_instance().add_session_model( SessionModel( source_address_token=SessionInstance.get_instance( ).source_address_token.hex(), server_nonce=SessionInstance.get_instance().server_nonce, server_config_id=SessionInstance.get_instance( ).server_config_id.hex(), public_value=SessionInstance.get_instance( ).peer_public_value.hex(), connection_id=SessionInstance.get_instance().connection_id)) if SessionInstance.get_instance().scfg == "": SessionInstance.get_instance( ).scfg = extract_from_packet_as_bytestring(a, start=452, end=452 + 135)
def send_encrypted_request(self): """ Make an AEAD GET Request to example.org :return: """ self.logger.info("Making GET Request") # Generate forward secure keys if it hasn't already been done. current_app_key = SessionInstance.get_instance().app_keys if current_app_key['type'] != "FORWARD" or current_app_key[ 'mah'] != SessionInstance.get_instance().last_received_shlo: if len(SessionInstance.get_instance().peer_public_value) == 0: pass else: key = dhke.generate_keys( SessionInstance.get_instance().peer_public_value, True, self.logger) SessionInstance.get_instance().app_keys['type'] = "FORWARD" SessionInstance.get_instance().app_keys[ 'mah'] = SessionInstance.get_instance().last_received_shlo SessionInstance.get_instance().app_keys['key'] = key get_request = "800300002501250000000500000000FF418FF1E3C2E5F23A6BA0AB9EC9AE38110782848750839BD9AB7A85ED6988B4C7" packet_number = PacketNumberInstance.get_instance( ).get_next_packet_number() ciphertext = CryptoManager.encrypt(bytes.fromhex(get_request), packet_number, SessionInstance.get_instance(), self.logger) # Send it to the server a = AEADRequestPacket() a.setfieldval( 'CID', string_to_ascii(SessionInstance.get_instance().connection_id)) a.setfieldval("Public Flags", 0x18) a.setfieldval('Packet Number', packet_number) a.setfieldval("Message Authentication Hash", string_to_ascii(ciphertext[0:24])) p = IP(dst=SessionInstance.get_instance().destination_ip) / UDP( dport=6121, sport=61250) / a / Raw(load=string_to_ascii(ciphertext[24:])) self.sniffer.add_observer(self) send(p) self.wait_for_signal_or_expiration() self.processed = False self.sniffer.remove_observer(self)
def process(self, is_encrypted=True, logger=None): self.processedFramesInstance = ProcessedFramesInstance.get_instance() self.processedFramesInstance.reset_processed_bytes() if is_encrypted: # Response is encrypted so we need to decrypt it associated_data = SessionInstance.get_instance().associated_data packet_number = int(SessionInstance.get_instance().packet_number, 16).to_bytes(8, byteorder='little') nonce = SessionInstance.get_instance().keys['iv2'][0:4] + packet_number # The ciphertext starts from the Message Authentication Hash and continues until the end of this stream # Containing everything it meets along the way. message_authentication_hash = SessionInstance.get_instance().message_authentication_hash ciphertext = "".join(self.packet_body) complete_ciphertext = message_authentication_hash complete_ciphertext += self.processedFramesInstance.get_processed_bytes().hex() complete_ciphertext += ciphertext # print("ProcessedFrames thus far {}".format(self.processedFramesInstance.get_processed_bytes())) request_data = { 'mode': 'decryption', 'input': complete_ciphertext, 'additionalData': associated_data, 'nonce': nonce.hex(), 'key': SessionInstance.get_instance().keys['key2'].hex() # other key, used for decryption,. } logger.info("Requesting decryption for {}".format(request_data)) try: response = CryptoConnectionManager.send_message(ConnectionEndpoint.CRYPTO_ORACLE, json.dumps(request_data).encode('utf-8'), True) # print("Response after decryption {}".format(response['data'])) logger.info("Decrypted {}".format(response['data'])) self.packet_body = split_at_nth_char(response['data']) except JSONDecodeError as err: self.packet_body = [] processors = [ { 'processor': StreamProcessor(), 'result': '', 'processes': 0 }, { 'processor': BlockedProcessor(), 'result': '', 'processes': 0 }, { 'processor': CongestionFeedbackProcessor(), 'result': '', 'processes': 0 }, { 'processor': ConnectionCloseProcessor(), 'result': 'closed', 'processes': 0 }, { 'processor': GoAwayProcessor(), 'result': '', 'processes': 0 }, { 'processor': PaddingProcessor(), 'result': '', 'processes': 0 }, { 'processor': PingProcessor(), 'result': '', 'processes': 0 }, { 'processor': RSTStreamProcessor(), 'result': '', 'processes': 0 }, { 'processor': StopWaitingProcessor(), 'result': '', 'processes': 0 }, { 'processor': WindowUpdateProcessor(), 'result': '', 'processes': 0 }, { 'processor': AckProcessor(), 'result': '', 'processes': 0 }, { 'processor': UnknownProcessor(), 'result': '', 'processes': -999999 } ] processed_by = [] # while len(self.packet_body) > 0: # # As long as we need to process the packet body # for processor in processors: # processor['processor'].receive(self.packet_body) # if processor['processor'].my_frame(): # processor['processor'].process() # logger.info("Processing started by {}".format(processor['processor'])) # self.packet_body = processor['processor'].result() # print("Processed by {} with result {}".format(processor['processor'], self.packet_body)) # processor['processes'] += 1 # # if isinstance(processor['processor'], StreamProcessor): # print("Stream processor has status {}".format(processor['processor'].status)) # processor['result'] = processor['processor'].status # logger.info("Result of stream processor {}".format(processor['result'])) # # if not processor['result'] == "": # processed_by.append(processor['result']) # break packet_as_string = "".join(self.packet_body) # regexes for the SHLO part indexes = find_all_indexes_of_substring_in_string(packet_as_string, "a001") indexes += find_all_indexes_of_substring_in_string(packet_as_string, "a401") # regex for the HTTP part indexes += find_all_indexes_of_substring_in_string(packet_as_string, "c0053c") for index in indexes: processor = StreamProcessor() processor.receive(split_at_nth_char(packet_as_string[index:], 2)) processor.process() processed_by.append(processor.status) # print("Finished processing ? {} ".format(processed_by)) # logger.info("After processing {}".format(processed_by)) if len(processed_by) > 0: # print("Process result {}".format(processed_by[0])) if processed_by.count('unknown') == len(processed_by): return 'unknown' else: logger.info("Complete processed by in FP {}".format(processed_by)) p = list(filter(lambda a: a != 'unknown', processed_by))[0] logger.info("Returning in FramesProcessor {}".format(p)) return p
def __handle_encrypted_packet(self, a): SessionInstance.get_instance().div_nonce = a.get_field( AEADFieldNames.DIVERSIFICATION_NONCE) SessionInstance.get_instance( ).message_authentication_hash = a.get_field( AEADFieldNames.MESSAGE_AUTHENTICATION_HASH) packet_number = a.get_field(AEADFieldNames.PACKET_NUMBER) SessionInstance.get_instance().packet_number = packet_number # print("Packet Number {}".format(packet_number)) SessionInstance.get_instance( ).largest_observed_packet_number = packet_number PacketNumberInstance.get_instance( ).highest_received_packet_number = packet_number SessionInstance.get_instance().associated_data = a.get_associated_data( ) # print("Associated Data {}".format(SessionInstance.get_instance().associated_data)) ciphertext = split_at_nth_char( a.get_field(AEADFieldNames.ENCRYPTED_FRAMES)) print("Received peer public value {}".format( SessionInstance.get_instance().peer_public_value)) # Only generate a new set of initial keys when we receive a new REJ current_app_key = SessionInstance.get_instance().app_keys self.__logger.info( "Currently stored app key {}".format(current_app_key)) self.__logger.info("Last received REJ") if current_app_key['type'] != "REJ" \ or current_app_key['mah'] != SessionInstance.get_instance().last_received_rej\ or SessionInstance.get_instance().zero_rtt: # If the generated key does not comply with the previously received REJ, then create a new set of keys # Or if it is the first set of keys. Otherwise we just use the previous set of keys. key = dhke.generate_keys( SessionInstance.get_instance().peer_public_value, SessionInstance.get_instance().shlo_received, self.__logger) SessionInstance.get_instance().app_keys['type'] = "REJ" SessionInstance.get_instance().app_keys[ 'mah'] = SessionInstance.get_instance().last_received_rej SessionInstance.get_instance().app_keys['key'] = key # SessionInstance.get_instance().packet_number = packet_number # Process the streams processor = FramesProcessor(ciphertext) return processor.process(logger=self.__logger)