def __init__(self, ip, port, urlOpener=ExtendedUrllib(), proxy_cert=Proxy.SSL_CERT): """ :param ip: IP address to bind :param port: Port to bind :param urlOpener: The urlOpener that will be used to open the requests that arrive from the browser :param proxyHandler: A class that will know how to handle requests from the browser :param proxy_cert: Proxy certificate to use, this is needed for proxying SSL connections. """ Proxy.__init__(self, ip, port, urlOpener, w3afLocalProxyHandler, proxy_cert) self.daemon = True self.name = 'LocalProxyThread' # Internal vars self._request_queue = Queue.Queue() self._edited_requests = {} self._edited_responses = {} # User configured parameters self._methods_to_trap = set() self._what_to_trap = re.compile('.*') self._what_not_to_trap = re.compile('.*\.(gif|jpg|png|css|js|ico|swf|axd|tif)$') self._trap = False self._fix_content_length = True
def __init__(self, ip, port, urlOpener=ExtendedUrllib(), proxy_cert=Proxy.SSL_CERT): """ :param ip: IP address to bind :param port: Port to bind :param urlOpener: The urlOpener that will be used to open the requests that arrive from the browser :param proxyHandler: A class that will know how to handle requests from the browser :param proxy_cert: Proxy certificate to use, this is needed for proxying SSL connections. """ Proxy.__init__(self, ip, port, urlOpener, w3afLocalProxyHandler, proxy_cert) self.daemon = True self.name = 'LocalProxyThread' # Internal vars self._request_queue = Queue.Queue() self._edited_requests = {} self._edited_responses = {} # User configured parameters self._methods_to_trap = set() self._what_to_trap = re.compile('.*') self._what_not_to_trap = re.compile( '.*\.(gif|jpg|png|css|js|ico|swf|axd|tif)$') self._trap = False self._fix_content_length = True
def __init__(self, ip, port, url_opener): """ :param ip: IP address to bind :param port: Port to bind :param url_opener: The urlOpener that will be used to open the requests that arrive from the browser """ Proxy.__init__(self, ip, port, url_opener, handler_klass=InterceptProxyHandler, name='LocalProxyThread') # Internal vars self.requests_pending_modification = Queue.Queue() self.requests_already_modified = Queue.Queue() # User configured parameters self.methods_to_trap = set() self.what_to_trap = re.compile(self.DEFAULT_TRAP) self.what_not_to_trap = re.compile(self.DEFAULT_NO_TRAP) self.trap = False # Forward to handler # pylint: disable=E1103 self.on_request_edit_finished = self._master.on_request_edit_finished
class TestExtendedUrllibProxy(unittest.TestCase): MOTH_MESSAGE = '<title>moth: vulnerable web application</title>' def setUp(self): self.uri_opener = ExtendedUrllib() # Start the proxy daemon self._proxy = Proxy('127.0.0.1', 0, ExtendedUrllib(), w3afProxyHandler) self._proxy.start() self._proxy.wait_for_start() port = self._proxy.get_port() # Configure the proxy settings = OpenerSettings() options = settings.get_options() proxy_address_opt = options['proxy_address'] proxy_port_opt = options['proxy_port'] proxy_address_opt.set_value('127.0.0.1') proxy_port_opt.set_value(port) settings.set_options(options) self.uri_opener.settings = settings def tearDown(self): self.uri_opener.end() def test_http_default_port_via_proxy(self): url = URL(get_moth_http()) http_response = self.uri_opener.GET(url, cache=False) self.assertIn(self.MOTH_MESSAGE, http_response.body) def test_http_port_specification_via_proxy(self): url = URL(get_moth_http()) http_response = self.uri_opener.GET(url, cache=False) self.assertIn(self.MOTH_MESSAGE, http_response.body) def test_https_via_proxy(self): TODO = 'Skip this test because of a strange bug with the extended'\ ' url library and w3af\'s local proxy daemon. More info here:'\ ' https://github.com/andresriancho/w3af/issues/183' raise SkipTest(TODO) url = URL(get_moth_https()) http_response = self.uri_opener.GET(url, cache=False) self.assertIn(self.MOTH_MESSAGE, http_response.body) def test_offline_port_via_proxy(self): url = URL('http://127.0.0.1:8181/') http_response = self.uri_opener.GET(url, cache=False) self.assertEqual(http_response.get_code(), 400) def test_POST_via_proxy(self): url = URL(get_moth_http('/audit/xss/simple_xss_form.py')) http_response = self.uri_opener.POST(url, data='text=123456abc', cache=False) self.assertIn('123456abc', http_response.body)
def _start_proxy(self, uri_opener): """ Saves the proxy configuration to self.local_proxy_url in order for the wrapper to use it in the calls to sqlmap.py and have the traffic go through our proxy (which has the user configuration, logging, etc). :return: None, an exception is raised if something fails. """ host = '127.0.0.1' self.proxy = Proxy(host, 0, uri_opener) self.proxy.start() self.local_proxy_url = 'http://%s:%s/' % (host, self.proxy.get_bind_port())
def setUp(self): # Start the proxy server create_temp_dir() self._proxy = Proxy(self.IP, 0, ExtendedUrllib(), w3afProxyHandler) self._proxy.start() self._proxy.wait_for_start() port = self._proxy.get_port() # Build the proxy opener proxy_handler = urllib2.ProxyHandler( {"http": "http://%s:%s" % (self.IP, port)}) self.proxy_opener = urllib2.build_opener(proxy_handler, urllib2.HTTPHandler)
def crawl(self, freq): # Create the proxy server try: self._proxy = Proxy(self._listen_address, self._listen_port, self._uri_opener, self.create_p_h()) except ProxyException, proxy_exc: om.out.error('%s' % proxy_exc)
def setUp(self): self.uri_opener = ExtendedUrllib() # Start the proxy daemon self._proxy = Proxy('127.0.0.2', 0, ExtendedUrllib(), ProxyHandler) self._proxy.start() self._proxy.wait_for_start() port = self._proxy.get_port() # Configure the proxy settings = OpenerSettings() options = settings.get_options() proxy_address_opt = options['proxy_address'] proxy_port_opt = options['proxy_port'] proxy_address_opt.set_value('127.0.0.2') proxy_port_opt.set_value(port) settings.set_options(options) self.uri_opener.settings = settings
def setUp(self): # Start the proxy server create_temp_dir() self._proxy = Proxy(self.IP, 0, ExtendedUrllib(), w3afProxyHandler) self._proxy.start() self._proxy.wait_for_start() port = self._proxy.get_port() # Build the proxy opener proxy_handler = urllib2.ProxyHandler({"http": "http://%s:%s" % (self.IP, port)}) self.proxy_opener = urllib2.build_opener(proxy_handler, urllib2.HTTPHandler)
class SQLMapWrapper(object): DEBUG_ARGS = ['-v6'] DEFAULT_ARGS = [sys.executable, 'sqlmap.py', '--output-dir=%s' % tempfile.gettempdir()] SQLMAP_LOCATION = os.path.join(ROOT_PATH, 'plugins', 'attack', 'db', 'sqlmap') VULN_STR = 'sqlmap identified the following injection points' NOT_VULN_STR = 'all tested parameters appear to be not injectable' SQLMAP_ERRORS = ('connection timed out to the target', 'infinite redirect loop detected', 'it is not recommended to continue in this kind of cases', 'unable to connect to the target url or proxy', "[INFO] skipping '", '[CRITICAL] unable to retrieve page content') def __init__(self, target, uri_opener, coloring=False, debug=False): if not isinstance(target, Target): fmt = 'Invalid type %s for target parameter in SQLMapWrapper ctor.' raise TypeError(fmt % type(target)) self._start_proxy(uri_opener) self.debug = debug self.target = target self.coloring = coloring self.last_command = None self.verified_vulnerable = False def _start_proxy(self, uri_opener): """ Saves the proxy configuration to self.local_proxy_url in order for the wrapper to use it in the calls to sqlmap.py and have the traffic go through our proxy (which has the user configuration, logging, etc). :return: None, an exception is raised if something fails. """ host = '127.0.0.1' self.proxy = Proxy(host, 0, uri_opener) self.proxy.start() self.local_proxy_url = 'http://%s:%s/' % (host, self.proxy.get_bind_port()) def cleanup(self): self.proxy.stop() def is_vulnerable(self): """ :return: True if the URL is vulnerable to SQL injection. """ if self.verified_vulnerable: return self.verified_vulnerable params = ['--batch'] full_command, stdout, stderr = self.run_sqlmap(params) if self.VULN_STR in stdout and self.NOT_VULN_STR not in stdout: self.verified_vulnerable = True return True if self.NOT_VULN_STR in stdout and self.VULN_STR not in stdout: return False for error_string in self.SQLMAP_ERRORS: if error_string in stdout: # We found an unknown sqlmap error, such as a timeout return False fmt = 'Unexpected answer found in sqlmap output for command "%s": "%s"' raise NotImplementedError(fmt % (full_command, stdout)) def _run(self, custom_params): """ Internal function used by run_sqlmap and run_sqlmap_with_pipes to call subprocess. :return: A Popen object. """ final_params = self.get_wrapper_params(custom_params) target_params = self.target.to_params() all_params = self.DEFAULT_ARGS + final_params + target_params if self.debug: all_params += self.DEBUG_ARGS process = subprocess.Popen(args=all_params, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=False, universal_newlines=True, cwd=self.SQLMAP_LOCATION) full_command = ' '.join(all_params) self.last_command = full_command return process def run_sqlmap(self, custom_params): """ Run sqlmap and wait for it to finish before getting its output. :param custom_params: A list with the extra parameters that we want to send to sqlmap. :return: Runs sqlmap and returns a tuple containing: (last command run, stdout, sterr) """ process = self._run(custom_params) self.last_stdout, self.last_stderr = process.communicate() om.out.debug('[sqlmap_wrapper] %s' % self.last_command) for line in self.last_stdout.split('\n'): om.out.debug('[sqlmap_wrapper] %s' % line) return self.last_command, self.last_stdout, self.last_stderr def run_sqlmap_with_pipes(self, custom_params): """ Run sqlmap and immediately return handlers to stdout, stderr and stdin so the code using this can interact directly with the process. :param custom_params: A list with the extra parameters that we want to send to sqlmap. :return: Runs sqlmap and returns a tuple with: (last command run, Popen object so that everyone can read .stdout) This is very useful for using with w3af's output manager. """ process = self._run(custom_params) return self.last_command, process def direct(self, params): if isinstance(params, basestring): extra_params = shlex.split(params) return self.run_sqlmap_with_pipes(extra_params) def get_wrapper_params(self, extra_params=[]): params = [] # TODO: This one will dissapear the day I add stdin handling support # for the wrapper. Please remember that this support will have to # take care of stdin and all other inputs from other UIs params.append('--batch') if not self.coloring: params.append('--disable-coloring') if self.local_proxy_url is not None: params.append('--proxy=%s' % self.local_proxy_url) params.extend(extra_params) return params def _wrap_param(self, custom_params): """ Utility function to allow me to easily wrap params. :return: Runs sqlmap with --dbs and returns a tuple with: (last command run, Popen object so that everyone can read .stdout, .stderr, .stdin attributes) """ process = self._run(custom_params) return self.last_command, process def dbs(self): return self._wrap_param(['--dbs',]) def tables(self): return self._wrap_param(['--tables',]) def users(self): return self._wrap_param(['--users',]) def dump(self): return self._wrap_param(['--dump',]) def read(self, filename): """ :param filename: The file to be read :return: The contents of the file that was passed as parameter """ cmd, process = self._wrap_param(['--file-read=%s' % filename,]) local_file_re = re.compile("the local file (.*?) and") # pylint: disable=E1101 stdout = process.stdout.read() try: local_file = local_file_re.search(stdout).group(1) except: # FIXME: I'll have to fix this at some point... files that do not # exist should raise an exception (or something similar), instead # of just returning an empty string. This is a big FAIL from my # initial design of the payloads/shell API. return '' else: if os.path.exists(local_file): return file(local_file).read() return
class SQLMapWrapper(object): OUTPUT_DIR = '%s/%s' % (tempfile.gettempdir(), os.getpid()) DEBUG_ARGS = ['-v6'] DEFAULT_ARGS = [ sys.executable, 'sqlmap.py', '--output-dir=%s' % OUTPUT_DIR ] SQLMAP_LOCATION = os.path.join(ROOT_PATH, 'plugins', 'attack', 'db', 'sqlmap') VULN_STR = 'sqlmap identified the following injection points' NOT_VULN_STR = 'all tested parameters appear to be not injectable' SQLMAP_ERRORS = ('connection timed out to the target', 'infinite redirect loop detected', 'it is not recommended to continue in this kind of cases', 'unable to connect to the target url or proxy', "[INFO] skipping '", '[CRITICAL] unable to retrieve page content') def __init__(self, target, uri_opener, coloring=False, debug=False): if not isinstance(target, Target): fmt = 'Invalid type %s for target parameter in SQLMapWrapper ctor.' raise TypeError(fmt % type(target)) self.debug = debug self.target = target self.coloring = coloring self.last_command = None self.verified_vulnerable = False self.proxy = None self.local_proxy_url = None self.last_stdout = None self.last_stderr = None if uri_opener is not None: self.start_proxy(uri_opener) def start_proxy(self, uri_opener): """ Saves the proxy configuration to self.local_proxy_url in order for the wrapper to use it in the calls to sqlmap.py and have the traffic go through our proxy (which has the user configuration, logging, etc). :return: None, an exception is raised if something fails. """ host = '127.0.0.1' self.proxy = Proxy(host, 0, uri_opener, name='SQLMapWrapperProxy') self.proxy.start() self.local_proxy_url = 'http://%s:%s/' % (host, self.proxy.get_bind_port()) def __reduce__(self): """ Need to define this method in order to remove the uri_opener from the pickled string. This will make sure that when the object is un-pickled we get the real uri_opener from w3af's core. The object being un-pickled is the SQLMapShell, which when un-pickled from the kb we call "shell.set_url_opener(w3af_core.uri_opener)", which then calls start_proxy(uri_opener) in order to restore the opener """ return self.__class__, (self.target, None, self.coloring, self.debug) def cleanup(self): self.proxy.stop() def is_vulnerable(self): """ :return: True if the URL is vulnerable to SQL injection. """ if self.verified_vulnerable: return self.verified_vulnerable params = ['--batch'] full_command, stdout, stderr = self.run_sqlmap(params) if self.VULN_STR in stdout and self.NOT_VULN_STR not in stdout: self.verified_vulnerable = True return True if self.NOT_VULN_STR in stdout and self.VULN_STR not in stdout: return False for error_string in self.SQLMAP_ERRORS: if error_string in stdout: # We found an unknown sqlmap error, such as a timeout return False fmt = 'Unexpected answer found in sqlmap output for command "%s": "%s"' raise NotImplementedError(fmt % (full_command, stdout)) def _run(self, custom_params): """ Internal function used by run_sqlmap and run_sqlmap_with_pipes to call subprocess. :return: A Popen object. """ if not os.path.exists(self.OUTPUT_DIR): os.mkdir(self.OUTPUT_DIR) final_params = self.get_wrapper_params(custom_params) target_params = self.target.to_params() all_params = self.DEFAULT_ARGS + final_params + target_params if self.debug: all_params += self.DEBUG_ARGS process = subprocess.Popen(args=all_params, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=False, universal_newlines=True, cwd=self.SQLMAP_LOCATION) full_command = ' '.join(all_params) self.last_command = full_command return process def run_sqlmap(self, custom_params): """ Run sqlmap and wait for it to finish before getting its output. :param custom_params: A list with the extra parameters that we want to send to sqlmap. :return: Runs sqlmap and returns a tuple containing: (last command run, stdout, sterr) """ process = self._run(custom_params) self.last_stdout, self.last_stderr = process.communicate() om.out.debug('[sqlmap_wrapper] %s' % self.last_command) for line in self.last_stdout.split('\n'): om.out.debug('[sqlmap_wrapper] %s' % line) return self.last_command, self.last_stdout, self.last_stderr def run_sqlmap_with_pipes(self, custom_params): """ Run sqlmap and immediately return handlers to stdout, stderr and stdin so the code using this can interact directly with the process. :param custom_params: A list with the extra parameters that we want to send to sqlmap. :return: Runs sqlmap and returns a tuple with: (last command run, Popen object so that everyone can read .stdout) This is very useful for using with w3af's output manager. """ process = self._run(custom_params) return self.last_command, process def direct(self, params): if isinstance(params, basestring): extra_params = shlex.split(params) return self.run_sqlmap_with_pipes(extra_params) def get_wrapper_params(self, extra_params=[]): # TODO: This one will disappear the day I add stdin handling support # for the wrapper. Please remember that this support will have to # take care of stdin and all other inputs from other UIs params = ['--batch'] if not self.coloring: params.append('--disable-coloring') if self.local_proxy_url is not None: params.append('--proxy=%s' % self.local_proxy_url) params.extend(extra_params) return params def _wrap_param(self, custom_params): """ Utility function to allow me to easily wrap params. :return: Runs sqlmap with --dbs and returns a tuple with: (last command run, Popen object so that everyone can read .stdout, .stderr, .stdin attributes) """ process = self._run(custom_params) return self.last_command, process def dbs(self): return self._wrap_param(['--dbs']) def tables(self): return self._wrap_param(['--tables']) def users(self): return self._wrap_param(['--users']) def dump(self): return self._wrap_param(['--dump']) def read(self, filename): """ :param filename: The file to be read :return: The contents of the file that was passed as parameter """ cmd, process = self._wrap_param(['--file-read=%s' % filename]) local_file_re = re.compile("the local file (.*?) and") # pylint: disable=E1101 stdout = process.stdout.read() try: local_file = local_file_re.search(stdout).group(1) except: # FIXME: I'll have to fix this at some point... files that do not # exist should raise an exception (or something similar), instead # of just returning an empty string. This is a big FAIL from my # initial design of the payloads/shell API. return '' else: if os.path.exists(local_file): return file(local_file).read() return
class TestExtendedUrllibProxy(unittest.TestCase): MOTH_MESSAGE = '<title>moth: vulnerable web application</title>' def setUp(self): self.uri_opener = ExtendedUrllib() # Start the proxy daemon self._proxy = Proxy('127.0.0.2', 0, ExtendedUrllib(), ProxyHandler) self._proxy.start() self._proxy.wait_for_start() port = self._proxy.get_port() # Configure the proxy settings = OpenerSettings() options = settings.get_options() proxy_address_opt = options['proxy_address'] proxy_port_opt = options['proxy_port'] proxy_address_opt.set_value('127.0.0.2') proxy_port_opt.set_value(port) settings.set_options(options) self.uri_opener.settings = settings def tearDown(self): self.uri_opener.end() def test_http_default_port_via_proxy(self): # TODO: Write this test pass def test_http_port_specification_via_proxy(self): self.assertEqual(self._proxy.total_handled_requests, 0) url = URL(get_moth_http()) http_response = self.uri_opener.GET(url, cache=False) self.assertIn(self.MOTH_MESSAGE, http_response.body) self.assertEqual(self._proxy.total_handled_requests, 1) def test_https_via_proxy(self): self.assertEqual(self._proxy.total_handled_requests, 0) url = URL(get_moth_https()) http_response = self.uri_opener.GET(url, cache=False) self.assertIn(self.MOTH_MESSAGE, http_response.body) self.assertEqual(self._proxy.total_handled_requests, 1) def test_offline_port_via_proxy(self): url = URL('http://127.0.0.1:8181/') http_response = self.uri_opener.GET(url, cache=False) self.assertEqual(http_response.get_code(), 500) self.assertIn('Connection refused', http_response.body) def test_POST_via_proxy(self): url = URL(get_moth_http('/audit/xss/simple_xss_form.py')) http_response = self.uri_opener.POST(url, data='text=123456abc', cache=False) self.assertIn('123456abc', http_response.body)
class SQLMapWrapper(object): OUTPUT_DIR = '%s/%s' % (tempfile.gettempdir(), os.getpid()) DEBUG_ARGS = ['-v6'] # This was added for Debian (and most likely useful for other distributions) # because we don't want to have a sqlmap.deb and duplicate the same files # in w3af.deb # # https://github.com/andresriancho/w3af/issues/10538 # INSTALLED_DEFAULT_ARGS = ['sqlmap', '--output-dir=%s' % OUTPUT_DIR] # The embedded sqlmap (the whole directory) is removed in Debian EMBEDDED_DEFAULT_ARGS = [ sys.executable, 'sqlmap.py', '--output-dir=%s' % OUTPUT_DIR ] SQLMAP_LOCATION = os.path.join(ROOT_PATH, 'plugins', 'attack', 'db', 'sqlmap') VULN_STR = '[INFO] the back-end DBMS is' NOT_VULN_STR = 'all tested parameters appear to be not injectable' SQLMAP_ERRORS = ('connection timed out to the target', 'infinite redirect loop detected', 'it is not recommended to continue in this kind of cases', 'unable to connect to the target url or proxy', "[INFO] skipping '", '[CRITICAL] unable to retrieve page content', 'establish SSL connection') def __init__(self, target, uri_opener, coloring=False, debug=False): if not isinstance(target, Target): fmt = 'Invalid type %s for target parameter in SQLMapWrapper ctor.' raise TypeError(fmt % type(target)) self.debug = debug self.target = target self.coloring = coloring self.last_command = None self.verified_vulnerable = False self.proxy = None self.local_proxy_url = None self.last_stdout = None self.last_stderr = None if uri_opener is not None: self.start_proxy(uri_opener) def start_proxy(self, uri_opener): """ Saves the proxy configuration to self.local_proxy_url in order for the wrapper to use it in the calls to sqlmap.py and have the traffic go through our proxy (which has the user configuration, logging, etc). :return: None, an exception is raised if something fails. """ host = '127.0.0.1' self.proxy = Proxy(host, 0, uri_opener, name='SQLMapWrapperProxy') self.proxy.start() self.proxy.wait_for_start() self.local_proxy_url = 'http://%s:%s/' % (host, self.proxy.get_bind_port()) def __reduce__(self): """ Need to define this method in order to remove the uri_opener from the pickled string. This will make sure that when the object is un-pickled we get the real uri_opener from w3af's core. The object being un-pickled is the SQLMapShell, which when un-pickled from the kb we call "shell.set_url_opener(w3af_core.uri_opener)", which then calls start_proxy(uri_opener) in order to restore the opener """ return self.__class__, (self.target, None, self.coloring, self.debug) def cleanup(self): self.proxy.stop() def is_vulnerable(self): """ :return: True if the URL is vulnerable to SQL injection. """ if self.verified_vulnerable: return self.verified_vulnerable params = ['--batch'] full_command, stdout, stderr = self.run_sqlmap(params) if full_command is None: # Something really bad happen with sqlmap return False if self.VULN_STR in stdout and self.NOT_VULN_STR not in stdout: self.verified_vulnerable = True return True if self.NOT_VULN_STR in stdout and self.VULN_STR not in stdout: return False for error_string in self.SQLMAP_ERRORS: if error_string in stdout: # We found an unknown sqlmap error, such as a timeout return False fmt = 'Unexpected answer found in sqlmap output for command "%s": "%s"' raise NotImplementedError(fmt % (full_command, stdout)) def _get_base_args(self): """ Simple logic to get the base args in different environments where: * sqlmap is in PATH * The embedded sqlmap is not available :see: https://github.com/andresriancho/w3af/issues/10538 :return: The base args to execute sqlmap in this environment, or raise an exception if something is wrong. """ if os.path.exists(self.SQLMAP_LOCATION): # This is the most common scenario where the user installs w3af # from source and wants to use the embedded sqlmap return self.SQLMAP_LOCATION, self.EMBEDDED_DEFAULT_ARGS # sqlmap is not embedded, most likely because the packager removed # it and sqlmap executable is in path, make sure it's there before # we return the base args if not which('sqlmap'): raise RuntimeError('The "sqlmap" command is not in PATH') return os.getcwd(), self.INSTALLED_DEFAULT_ARGS def _run(self, custom_params): """ Internal function used by run_sqlmap and run_sqlmap_with_pipes to call subprocess. :return: A Popen object. """ if not os.path.exists(self.OUTPUT_DIR): os.mkdir(self.OUTPUT_DIR) cwd, base_args = self._get_base_args() final_params = self.get_wrapper_params(custom_params) target_params = self.target.to_params() all_params = base_args + final_params + target_params if self.debug: all_params += self.DEBUG_ARGS try: process = subprocess.Popen(args=all_params, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=False, universal_newlines=True, cwd=cwd) except OSError, os_err: # https://github.com/andresriancho/w3af/issues/10186 # OSError: [Errno 12] Cannot allocate memory if os_err.errno == errno.ENOMEM: msg = ('The operating system is running low on memory and' ' failed to start the sqlmap process.') om.out.error(msg) # This tells the rest of the world that the command failed self.last_command = None return None else: # Let me discover/handle other errors raise else:
class SQLMapWrapper(object): OUTPUT_DIR = '%s/%s' % (tempfile.gettempdir(), os.getpid()) DEBUG_ARGS = ['-v6'] # This was added for Debian (and most likely useful for other distributions) # because we don't want to have a sqlmap.deb and duplicate the same files # in w3af.deb # # https://github.com/andresriancho/w3af/issues/10538 # INSTALLED_DEFAULT_ARGS = ['sqlmap', '--output-dir=%s' % OUTPUT_DIR] # The embedded sqlmap (the whole directory) is removed in Debian EMBEDDED_DEFAULT_ARGS = [sys.executable, 'sqlmap.py', '--output-dir=%s' % OUTPUT_DIR] SQLMAP_LOCATION = os.path.join(ROOT_PATH, 'plugins', 'attack', 'db', 'sqlmap') VULN_STR = '[INFO] the back-end DBMS is' NOT_VULN_STR = 'all tested parameters do not appear to be injectable' SQLMAP_ERRORS = ('connection timed out to the target', 'infinite redirect loop detected', 'it is not recommended to continue in this kind of cases', 'unable to connect to the target url or proxy', "[INFO] skipping '", '[CRITICAL] unable to retrieve page content', 'establish SSL connection') def __init__(self, target, uri_opener, coloring=False, debug=False): if not isinstance(target, Target): fmt = 'Invalid type %s for target parameter in SQLMapWrapper ctor.' raise TypeError(fmt % type(target)) self.debug = debug self.target = target self.coloring = coloring self.last_command = None self.verified_vulnerable = False self.proxy = None self.local_proxy_url = None self.last_stdout = None self.last_stderr = None if uri_opener is not None: self.start_proxy(uri_opener) def start_proxy(self, uri_opener): """ Saves the proxy configuration to self.local_proxy_url in order for the wrapper to use it in the calls to sqlmap.py and have the traffic go through our proxy (which has the user configuration, logging, etc). :return: None, an exception is raised if something fails. """ host = '127.0.0.1' self.proxy = Proxy(host, 0, uri_opener, name='SQLMapWrapperProxy') self.proxy.start() self.proxy.wait_for_start() self.local_proxy_url = 'http://%s:%s/' % (host, self.proxy.get_bind_port()) def __reduce__(self): """ Need to define this method in order to remove the uri_opener from the pickled string. This will make sure that when the object is un-pickled we get the real uri_opener from w3af's core. The object being un-pickled is the SQLMapShell, which when un-pickled from the kb we call "shell.set_url_opener(w3af_core.uri_opener)", which then calls start_proxy(uri_opener) in order to restore the opener """ return self.__class__, (self.target, None, self.coloring, self.debug) def cleanup(self): self.proxy.stop() def is_vulnerable(self): """ :return: True if the URL is vulnerable to SQL injection. """ if self.verified_vulnerable: return self.verified_vulnerable params = ['--batch'] full_command, stdout, stderr = self.run_sqlmap(params) if full_command is None: # Something really bad happen with sqlmap return False if self.VULN_STR in stdout and self.NOT_VULN_STR not in stdout: self.verified_vulnerable = True return True if self.NOT_VULN_STR in stdout and self.VULN_STR not in stdout: return False for error_string in self.SQLMAP_ERRORS: if error_string in stdout: # We found an unknown sqlmap error, such as a timeout return False fmt = 'Unexpected answer found in sqlmap output for command "%s": "%s"' raise NotImplementedError(fmt % (full_command, stdout)) def _get_base_args(self): """ Simple logic to get the base args in different environments where: * sqlmap is in PATH * The embedded sqlmap is not available :see: https://github.com/andresriancho/w3af/issues/10538 :return: The base args to execute sqlmap in this environment, or raise an exception if something is wrong. """ if os.path.exists(self.SQLMAP_LOCATION): # This is the most common scenario where the user installs w3af # from source and wants to use the embedded sqlmap return self.SQLMAP_LOCATION, self.EMBEDDED_DEFAULT_ARGS # sqlmap is not embedded, most likely because the packager removed # it and sqlmap executable is in path, make sure it's there before # we return the base args if not which('sqlmap'): raise RuntimeError('The "sqlmap" command is not in PATH') return os.getcwd(), self.INSTALLED_DEFAULT_ARGS def _run(self, custom_params): """ Internal function used by run_sqlmap and run_sqlmap_with_pipes to call subprocess. :return: A Popen object. """ if not os.path.exists(self.OUTPUT_DIR): os.mkdir(self.OUTPUT_DIR) cwd, base_args = self._get_base_args() final_params = self.get_wrapper_params(custom_params) target_params = self.target.to_params() all_params = base_args + final_params + target_params if self.debug: all_params += self.DEBUG_ARGS try: process = subprocess.Popen(args=all_params, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=False, universal_newlines=True, cwd=cwd) except OSError, os_err: # https://github.com/andresriancho/w3af/issues/10186 # OSError: [Errno 12] Cannot allocate memory if os_err.errno == errno.ENOMEM: msg = ('The operating system is running low on memory and' ' failed to start the sqlmap process.') om.out.error(msg) # This tells the rest of the world that the command failed self.last_command = None return None else: # Let me discover/handle other errors raise else:
class TestProxy(unittest.TestCase): IP = '127.0.0.1' def setUp(self): # Start the proxy server create_temp_dir() self._proxy = Proxy(self.IP, 0, ExtendedUrllib(), w3afProxyHandler) self._proxy.start() self._proxy.wait_for_start() port = self._proxy.get_port() # Build the proxy opener proxy_handler = urllib2.ProxyHandler({"http": "http://%s:%s" % (self.IP, port)}) self.proxy_opener = urllib2.build_opener(proxy_handler, urllib2.HTTPHandler) def test_do_req_through_proxy(self): resp_body = self.proxy_opener.open(get_moth_http()).read() # Basic check self.assertTrue(len(resp_body) > 0) # Get response using the proxy proxy_resp = self.proxy_opener.open(get_moth_http()) # Get it without any proxy direct_resp = urllib2.urlopen(get_moth_http()) # Must be equal self.assertEqual(direct_resp.read(), proxy_resp.read()) # Have to remove the Date header because in some cases they differ # because one request was sent in second X and the other in X+1, which # makes the test fail direct_resp_headers = dict(direct_resp.info()) proxy_resp_headers = dict(proxy_resp.info()) # Make sure that a change in the seconds returned in date doesn't break # the test del direct_resp_headers['date'] del proxy_resp_headers['date'] del direct_resp_headers['transfer-encoding'] del proxy_resp_headers['content-length'] self.assertEqual(direct_resp_headers, proxy_resp_headers) def test_do_ssl_req_through_proxy(self): resp_body = self.proxy_opener.open(get_moth_https()).read() # Basic check self.assertTrue(len(resp_body) > 0) # Get response using the proxy proxy_resp = self.proxy_opener.open(get_moth_https()) # Get it without any proxy direct_resp = urllib2.urlopen(get_moth_https()) # Must be equal self.assertEqual(direct_resp.read(), proxy_resp.read()) # Have to remove the Date header because in some cases they differ # because one request was sent in second X and the other in X+1, which # makes the test fail direct_resp_headers = dict(direct_resp.info()) proxy_resp_headers = dict(proxy_resp.info()) del direct_resp_headers['date'] del proxy_resp_headers['date'] self.assertEqual(direct_resp_headers, proxy_resp_headers) def test_proxy_req_ok(self): """Test if self._proxy.stop() works as expected. Note that the check content is the same as the previous check, but it might be that this check fails because of some error in start() or stop() which is run during setUp and tearDown.""" # Get response using the proxy proxy_resp = self.proxy_opener.open(get_moth_http()).read() # Get it the other way resp = urllib2.urlopen(get_moth_http()).read() # They must be very similar self.assertEqual(resp, proxy_resp) def test_stop_no_requests(self): """Test what happens if I stop the proxy without sending any requests through it""" # Note that the test is completed by self._proxy.stop() in tearDown pass def test_stop_stop(self): """Test what happens if I stop the proxy twice.""" # Note that the test is completed by self._proxy.stop() in tearDown self._proxy.stop() def tearDown(self): # Shutdown the proxy server self._proxy.stop()
class TestProxy(unittest.TestCase): IP = '127.0.0.1' def setUp(self): # Start the proxy server create_temp_dir() self._proxy = Proxy(self.IP, 0, ExtendedUrllib(), w3afProxyHandler) self._proxy.start() self._proxy.wait_for_start() port = self._proxy.get_port() # Build the proxy opener proxy_handler = urllib2.ProxyHandler( {"http": "http://%s:%s" % (self.IP, port)}) self.proxy_opener = urllib2.build_opener(proxy_handler, urllib2.HTTPHandler) def test_do_req_through_proxy(self): resp_body = self.proxy_opener.open(get_moth_http()).read() # Basic check self.assertTrue(len(resp_body) > 0) # Get response using the proxy proxy_resp = self.proxy_opener.open(get_moth_http()) # Get it without any proxy direct_resp = urllib2.urlopen(get_moth_http()) # Must be equal self.assertEqual(direct_resp.read(), proxy_resp.read()) # Have to remove the Date header because in some cases they differ # because one request was sent in second X and the other in X+1, which # makes the test fail direct_resp_headers = dict(direct_resp.info()) proxy_resp_headers = dict(proxy_resp.info()) # Make sure that a change in the seconds returned in date doesn't break # the test del direct_resp_headers['date'] del proxy_resp_headers['date'] del direct_resp_headers['transfer-encoding'] del proxy_resp_headers['content-length'] self.assertEqual(direct_resp_headers, proxy_resp_headers) def test_do_ssl_req_through_proxy(self): resp_body = self.proxy_opener.open(get_moth_https()).read() # Basic check self.assertTrue(len(resp_body) > 0) # Get response using the proxy proxy_resp = self.proxy_opener.open(get_moth_https()) # Get it without any proxy direct_resp = urllib2.urlopen(get_moth_https()) # Must be equal self.assertEqual(direct_resp.read(), proxy_resp.read()) # Have to remove the Date header because in some cases they differ # because one request was sent in second X and the other in X+1, which # makes the test fail direct_resp_headers = dict(direct_resp.info()) proxy_resp_headers = dict(proxy_resp.info()) del direct_resp_headers['date'] del proxy_resp_headers['date'] self.assertEqual(direct_resp_headers, proxy_resp_headers) def test_proxy_req_ok(self): """Test if self._proxy.stop() works as expected. Note that the check content is the same as the previous check, but it might be that this check fails because of some error in start() or stop() which is run during setUp and tearDown.""" # Get response using the proxy proxy_resp = self.proxy_opener.open(get_moth_http()).read() # Get it the other way resp = urllib2.urlopen(get_moth_http()).read() # They must be very similar self.assertEqual(resp, proxy_resp) def test_stop_no_requests(self): """Test what happens if I stop the proxy without sending any requests through it""" # Note that the test is completed by self._proxy.stop() in tearDown pass def test_stop_stop(self): """Test what happens if I stop the proxy twice.""" # Note that the test is completed by self._proxy.stop() in tearDown self._proxy.stop() def tearDown(self): # Shutdown the proxy server self._proxy.stop()