def test_injection_from_dict_with_template(self, n=100): for i in range(2, n, 2): config = ''' X:_data_: Y:_sxor_(_data_, '\xaa'): data1="""44444444%s41414141%s""" ''' % ("X" * i, "Y" * i) inj_dict = {'X': 'a' * (i // 2), 'Y': 'b' * (i // 2)} psi = StegoInjector(config) templ_pkt = psi.inject('\x00' * i, 'data1') print templ_pkt, "<----" stego_pkt = psi.injectByTag(inj_dict, template='data1', pkt=templ_pkt) testable = 'DDDD%sAAAA%s' % ('a' * (i // 2), 'b' * (i // 2)) # print( stego_pkt, testable ) extr_dict = psi.extractByTag(stego_pkt, 'data1') # print( extr_dict, inj_dict ) self.assertTrue(extr_dict == inj_dict)
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
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 test_injection_from_text( self, n = 4) : config = ''' X:_data_: Y:_data_: data1="""44444444%s41414141%s""" ''' % ("X"*n, "Y"*n) data = 'aabb' psi = StegoInjector( config ) stego_pkt = psi.inject(data, template = 'data1') self.assertTrue( stego_pkt == 'DDDDaaAAAAbb')
def test_syntax( self ) : conf1 = "la:_data_:" # 2 letter tag try : p = StegoInjector( conf1 ) except Exception as e : self.assertTrue ( type(e) == StegoSchemeParseException ) conf2 = "C:_data_:" # Hex Letter try : p = StegoInjector( conf2 ) except Exception as e : self.assertTrue ( type(e) == StegoSchemeParseException )
def test_injection_scramble( self, ) : config = ''' X:_data_: Y:_data_: data1="""44X44X4141Y4141Y44X43X""" ''' data = 'fff' # 0x660x660x66 psi = StegoInjector( config ) stego_pkt = psi.inject(data, template = 'data1') self.assertTrue( stego_pkt.encode('hex').count('6') == 6 )
def test_extraction_from_dict(self, n=4): config = ''' X:_data_: Y:_data_: data1="""44444444%s41414141%s""" ''' % ("X" * n, "Y" * n) inj_dict = {'X': 'a' * (n // 2), 'Y': 'b' * (n // 2)} psi = StegoInjector(config) pkt = 'DDDDaaAAAAbb' extr_dict = psi.extractByTag(pkt, template='data1') self.assertTrue(inj_dict == extr_dict)
def test_injection_scramble2( self, ) : config = ''' X:_data_: Y:_data_: data1="""44X44X4141Y4141Y44X43X""" ''' pkt = codecs.decode('4464464141641416446436', 'hex') psi = StegoInjector( config ) extr_pkt = psi.extract(pkt, template = 'data1') data = 'fff' self.assertTrue( extr_pkt == data )
def test_syntax_multiple_packets( self ) : config = ''' X:_data_: Y:_data_: data1="""44X44X4141Y4141Y44X43X""" data2="""44X44X4141Y4141Y44X43X""" ''' psi = StegoInjector( config ) cap1 = psi.getCapacity('data1') cap2 = psi.getCapacity('data2')
def test_injection_from_text_to_hex( self, n = 4) : config = ''' X:_data_: Y:_data_: data1="""44444444%s41414141%s""" ''' % ("X"*n, "Y"*n) data = 'aa' psi = StegoInjector( config, hex_inject = True ) stego_pkt = psi.inject(data, template = 'data1') extracted = psi.extract( stego_pkt, 'data1' ) self.assertTrue( extracted == data )
def test_group_str_similarity( self ) : config = """ X:_data_: data1 = '''4444444445454545'''X[2:6] data2 = "4444XXXXXXXX4545" """ psi = StegoInjector( config ) res1 = psi.inject("A"*4, 'data1') res2 = psi.inject("A"*4, 'data2') self.assertTrue( res1 == res2 )
def test_ascii_to_hex_template( self, n = 1000 ) : pkt = urandom( n ) pkt = pkt.replace(pkt[-1], '~') pkt = pkt.replace(pkt[-2], '~') pkt = pkt.replace(pkt[-3], '~') templ = asciiToHexTemplate( pkt ) config = ''' X:_data_: data1="""%s""" ''' % templ psi = StegoInjector( config ) cap = psi.getCapacity( 'data1' ) inj = psi.inject(cap*'~', 'data1' ) print( "Changed %d bytes" % cap ) self.assertTrue( inj == pkt )
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' )
def test_injection_with_pkt( self ) : config = ''' X:_data_: Y:_data_: data1="""44X44X4141Y4141Y44X43X""" data2="""44X44X4141Y4141Y44X43X""" ''' psi = StegoInjector( config ) pkt = "\xFF"*11 inj_pkt = psi.inject( "\x00"*3, 'data1', pkt ) # print( inj_pkt.encode('hex') ) testable = "\xFF\x0F\xF0\xFF\xFF\x0F\xFF\xF0\xFF\x0F\xF0" # print( testable.encode('hex') ) self.assertTrue( inj_pkt == testable)
def test_injection_from_dict(self, n=100): for i in range(2, n, 2): config = ''' X:_data_: Y:_sxor_(_data_, '\xaa'): data1="""44444444%s41414141%s""" ''' % ("X" * i, "Y" * i) inj_dict = {'X': 'a' * (i / 2), 'Y': 'b' * (i / 2)} psi = StegoInjector(config) stego_pkt = psi.injectByTag(inj_dict, template='data1') testable = 'DDDD%sAAAA%s' % ('a' * (i / 2), 'b' * (i / 2)) # print stego_pkt, testable extr_dict = psi.extractByTag(stego_pkt, 'data1') # print extr_dict, inj_dict self.failUnless(extr_dict == inj_dict)
def test_injection_equivalence( self ) : config = ''' X:_data_: Y:_sxor_(_data_, chr(_index_) ): data1 = """XXXXYYYY""" ''' psi = StegoInjector( config ) data = "\x00"*4 data_dict = { 'X' : "\x00" * 2, 'Y' : '\x00' * 2} pkt1 = psi.inject(data, 'data1') pkt2 = psi.injectByTag( data_dict, 'data1' ) # print( pkt1.encode('hex'), pkt2.encode('hex') ) self.assertTrue( pkt1 == pkt2 ) extr1 = psi.extract( pkt1, 'data1' ) extr_dict = psi.extractByTag( pkt2, 'data1' ) self.assertTrue( data == extr1 ) self.assertTrue( data_dict == extr_dict )
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
), ( # Tranformation #4 - Destination Port -> Source Port ( 'ip_tcp_syn:N', 'ip_tcp_rst_ack:M' ), ('!H','!H'), '_data_' ), ( # Tranformation #5 - SYN -> RST+ACK ( 'ip_tcp_syn:W', 'ip_tcp_rst_ack:W' ), ('!B','!B'), '_data_ + 18' # Make '02' (SYN) to '14' (RST+ACK) ), # No other transformations ] dt1 = DataTransformer(stego_config, transformation_list) steg_inj = StegoInjector(stego_config) orchestrator = SimpleOrchestrator( passphrase, tag_length = 3, in_length = 8, out_length = 6, ) packets = Queue.Queue() def check_syn( pkt ) : pkt = pkt.encode('hex') tcp_flags = pkt[94:96] return tcp_flags == '02'
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