def test_pattern_guesser( self ) : config = ''' X:_data_: Y:_data_: data1="""41414141XXYY""" data2="""41414142XXYY""" ''' psi = StegoInjector( config ) pkt1 = 'AAAAcd' pkt2 = 'AAAdfg' res, score = psi.guessTemplate( pkt1 ) # print( res ) self.assertTrue( res == 'data1' )
class DataTransformer: """ This class provides automated data transformations. It uses the :class:`covertutils.datamanipulation.stegoinjector.StegoInjector` class to create alterations to existing data chunks. **Transformation List** The Transformation List argument is a specially structured list to dictate to the `DataTranformer` which changes should be done to data packet. Specifically, for a SYN - (RST, ACK) sequence to be simulated, the following configuration should be used: .. code:: python X:_data_: L:_data_: K:_data_: ip_tcp_syn = '''45000028LLLL000040067ccd7f0000017f000001XXXX0050KKKKKKKK0000000050022000917c0000''' ip_tcp_rst_ack = '''450000280001000040067ccd7f0000017f0000010014005000000000XXXXXXXXXX50142000916a0000''' The Transformation List that has to be used should dictate the class to: - Unpack Sequence Number from `ip_tcp_syn` template (K tag) - Increment it by 1 - Place it to a `ip_tcp_rst_ack` template (X tag) - All the above while handling **endianess**, **integer overflow checks**, etc The `transformation_list` is declared below: .. code:: python transformation_list = [ ( # Tranformation #1 ( 'ip_tcp_syn:K', 'ip_tcp_rst_ack:X' ), # From template:tag to template:tag ('!I','!I') # Unpack as an 4-byte Integer (reverse Endianess as of network Endianess) and pack it to 4-byte Integer (reverse Endianess again) '_data_ + 1' # Eval this string (with the extracted/unpacked data as '_data_') and pack the result. ), # No other transformations ] """ def __init__(self, stego_configuration, transformation_list): """ :param str stego_configuration: The Stego Configuration to initialize the internal :class:`covertutils.datamanipulation.stegoinjector.StegoInjector` object. :param list transformation_list: The Tranformation List as described above. """ self.injector = StegoInjector(stego_configuration) self.transformation_list = transformation_list def runAll(self, pkt, template=None): """ Runs all Tranformations in the `transformation_list` that relate to the specified template. :param str pkt: The data packet to run the Tranformations on. In `Raw Bytes`. :param str template: The template string that describes the given data packet. If `None` the :func:`covertutils.datamanipulation.stegoinjector.StegoInjector.guessTemplate` function will try to guess the correct template. :rtype: str :return: Returns the `pkt` with all the related tranformations applied. """ if not template: template = self.injector.guessTemplate(pkt) for trans_tuple in self.transformation_list: templates, struct_strs, eval_str = trans_tuple (out_template_tag, in_template_tag) = templates out_template, out_tag = out_template_tag.split(':') in_template, in_tag = in_template_tag.split(':') (out_struct, in_struct) = struct_strs # if template != out_template : # continue out_data = self.injector.extractByTag(pkt, template)[out_tag] structed_data = unpack(out_struct, out_data)[0] # ========================== _data_ = structed_data output_data = eval(eval_str) # print( structed_data, eval_str, output_data ) # ========================== injectable_data = pack(in_struct, output_data) # injectable_dict = {'X' : injectable_data } # print( injectable_data.encode('hex') ) # print( self.injector.getCapacity( template ), len( injectable_data) ) # pkt = self.injector.injectByTag( injectable_dict, template, pkt ) pkt = self.injector.inject(injectable_data, template, pkt) return pkt
class StegoOrchestrator(Orchestrator): """ The `StegoOrchestrator` class combines compression, chunking, encryption, stream tagging and steganography injection, by utilizing the below ``covertutils`` classes: - :class:`covertutils.datamanipulation.AdHocChunker` - :class:`covertutils.datamanipulation.Compressor` - :class:`covertutils.crypto.keys.StandardCyclingKey` - :class:`covertutils.orchestration.StreamIdentifier` - :class:`covertutils.datamanipulation.StegoInjector` - :class:`covertutils.datamanipulation.DataTransformer` The `StegoOrchestrator` packs `(stream, message)` pairs in predefined data templates. """ def __init__(self, passphrase, stego_config, main_template, transformation_list=[], tag_length=2, cycling_algorithm=None, streams=[], hex_inject=False, reverse=False): """ :param str stego_config: The configuration that is passed to :class:`covertutils.datamanipulation.stegoinjector.StegoInjector`. :param str main_template: The default template that will be used in :func:`readyMessage()` `template` argument. :param list transformation_list: The Transformation List that is passed to the :class:`covertutils.datamanipulation.datatransformer.DataTransformer` object. :param class cycling_algorithm: The hashing/cycling function used in all OTP crypto and stream identification. If not specified the :class:`covertutils.crypto.algorithms.StandardCyclingAlgorithm` will be used. The :class:`hashlib.sha256` is a great choice if `hashlib` is available. :param list streams: The list of all streams needed to be recognised by the `SimpleOrchestrator`. A "control" stream is always hardcoded in a `SimpleOrchestrator` object. :param func intermediate_function: A *codec* function with signature `codec( data, encode = False )`. The function is called before and injection of a chunk with *encode = True* and after the extraction of a chunk with *encode = False*. :param bool reverse: If this is set to `True` a `StegoOrchestrator` with reverse streams is created. This parameter is typically used to keep the parameter list the same between 2 `StegoOrchestrator` initializations, yet make them `compatible`. """ self.stego_injector = StegoInjector(stego_config, hex_inject) self.data_tranformer = DataTransformer(stego_config, transformation_list) self.cycling_algorithm = cycling_algorithm if self.cycling_algorithm == None: self.cycling_algorithm = StandardCyclingAlgorithm self.main_template = main_template self.current_template = main_template self.chunk_sizes = {} super(StegoOrchestrator, self).__init__(passphrase, tag_length=tag_length, cycling_algorithm=cycling_algorithm, streams=streams, history=1, reverse=reverse) for index, template in enumerate(self.stego_injector.getTemplates()): stego_capacity = self.stego_injector.getCapacity(template) # # print stego_capacity # # inter_product = self.intermediate_function( "0" * stego_capacity, False ) # Need a valid decodable data string "0000..." is valid hex, base64, etc # intermediate_cap = stego_capacity - self.tag_length # check the capacity of the data length after the intermediate function # intermediate_cap = stego_capacity # check the capacity of the data length after the intermediate function # # intermediate_cap = len( inter_product ) # check the capacity of the data length after the intermediate function # self.chunk_sizes[template] = intermediate_cap @copydoc(Orchestrator.readyMessage) def readyMessage(self, message, stream=None): template = self.current_template if stream == None: stream = self.default_stream # template_capacity = self.stego_injector.getCapacity( template ) template_capacity = self.chunk_sizes[template] # print "Lengths: template_length = %d" % template_capacity self.streams_buckets[stream]['chunker'].setChunkSize(template_capacity) chunks = super(StegoOrchestrator, self).readyMessage(message, stream) # print "Lengths: chunk_length = %d" % len(chunks[0]) ready_chunks = [] for chunk in chunks: modified_chunk = chunk # modified_chunk = self.intermediate_function( chunk, True ) # print "<--" # print chunk.encode('hex') # print modified_chunk.encode('hex') injected = self.stego_injector.inject(modified_chunk, template) alterations = self.__getAlterations( template) # needs to be documented transformed = injected for alteration_templ in alterations: transformed = self.data_tranformer.runAll( transformed, alteration_templ) ready_chunks.append(transformed) return ready_chunks @copydoc(Orchestrator.depositChunk) def depositChunk(self, chunk): templ = self.stego_injector.guessTemplate(chunk) if templ == None: return ( None, None ) # Trigger the notRecognised() method of the underlying Handler self.received_template = templ[0] extr_data = self.stego_injector.extract(chunk, self.received_template) ret = super(StegoOrchestrator, self).depositChunk(extr_data) return ret @copydoc(Orchestrator.addStream) def addStream(self, stream): super(StegoOrchestrator, self).addStream(stream) self.streams_buckets[stream]['chunker'] = AdHocChunker() def useTemplate(self, template): """ :param str template: The template to use for the next Message. Use `None` for random templates. """ self.current_template = template def lastReceivedTemplate(self): """ :rtype: str :return: Returns the last template received. """ return self.received_template def __getAlterations(self, template): # Document the '_alt' suffix templates = self.stego_injector.getTemplates() ret = [] for templ in templates: if templ.startswith(template + "_alt"): ret.append(templ) return ret