def are_variants( url_a , url_b ): ''' This function analyzes if two URLs are variants. Two requests are variants if: - They have the same URL - They have the same method - They have the same parameters - The values for each parameter have the same type (int / string) @parameter url_a: The URL we want to analyze @parameter url_b: The other URL we want to analyze @return: True if the URLs are variants. ''' qs_a = urlParser.getQueryString( url_a ) qsr_a = httpQsRequest.httpQsRequest() qsr_a.setURL( urlParser.uri2url(url_a) ) qsr_a.setDc( qs_a ) qs_b = urlParser.getQueryString( url_b ) qsr_b = httpQsRequest.httpQsRequest() qsr_b.setURL( urlParser.uri2url(url_b) ) qsr_b.setDc( qs_b ) return qsr_a.is_variant_of( qsr_b )
def _analyze_urls(self, references): ''' Analyze what references are cached by archive.org @return: A list of query string objects for the URLs that are in the cache AND are in the target web site. ''' # Init some internal variables res = [] # Translate archive.org URL's to normal URL's real_URLs = [] for url in references: try: url = url.url_string[url.url_string.url.index('http', 1):] except Exception: pass else: real_URLs.append( url ) real_URLs = list(set(real_URLs)) if len( real_URLs ): om.out.debug('Archive.org cached the following pages:') for i in real_URLs: om.out.debug('- ' + i ) else: om.out.debug('Archive.org did not find any pages.') # Verify if they exist in the target site and add them to the result if they do. for real_url in real_URLs: if self._exists_in_target( real_url ): QSObject = real_url.getQueryString() qsr = httpQsRequest() qsr.setURI( real_url ) qsr.setDc( QSObject ) res.append( qsr ) if len( res ): msg = 'The following pages are in Archive.org cache and also in' msg += ' the target site:' om.out.debug(msg) for i in res: om.out.debug('- ' + i.getURI() ) else: om.out.debug('All pages found in archive.org cache are missing in the target site.') return res
def fastExploit(self): """ Exploits a web app with [blind] sql injections vulns. The options are configured using the plugin options and setOptions() method. """ om.out.debug("Starting sqlmap fastExploit.") om.out.console(SQLMAPCREATORS) if self._url is None or self._method is None or self._data is None or self._injvar is None: raise w3afException("You have to configure the plugin parameters") else: freq = None if self._method == "POST": freq = httpPostDataRequest.httpPostDataRequest() elif self._method == "GET": freq = httpQsRequest.httpQsRequest() else: raise w3afException("Method not supported.") freq.setURL(self._url) freq.setDc(urlParser.getQueryString("http://a/a.txt?" + self._data)) freq.setHeaders({}) bsql = blind_sqli_response_diff() bsql.setUrlOpener(self._urlOpener) bsql.setEqualLimit(self._equalLimit) bsql.setEquAlgorithm(self._equAlgorithm) vuln_obj = bsql.is_injectable(freq, self._injvar) if not vuln_obj: raise w3afException("Could not verify SQL injection " + str(vuln)) else: om.out.console("SQL injection could be verified, trying to create the DB driver.") # Try to get a shell using all vuln msg = "Trying to exploit using vulnerability with id: " + str(vuln_obj.getId()) msg += ". Please wait..." om.out.console(msg) shell_obj = self._generateShell(vuln_obj) if shell_obj is not None: kb.kb.append(self, "shell", shell_obj) return [shell_obj] raise w3afException("No exploitable vulnerabilities found.")
def fastExploit( self ): ''' Exploits a web app with [blind] sql injections vulns. The options are configured using the plugin options and setOptions() method. ''' om.out.debug( 'Starting sqlmap fastExploit.' ) om.out.console( SQLMAPCREATORS ) if self._url is None or self._method is None or self._data is None or self._injvar is None: raise w3afException('You have to configure the plugin parameters') else: freq = None if self._method == 'POST': freq = httpPostDataRequest.httpPostDataRequest() elif self._method == 'GET': freq = httpQsRequest.httpQsRequest() else: raise w3afException('Method not supported.') freq.setURL( self._url ) freq.setDc( parse_qs( self._data ) ) freq.setHeaders( {} ) bsql = blind_sqli_response_diff() bsql.setUrlOpener( self._urlOpener ) bsql.setEqualLimit( self._equalLimit ) bsql.setEquAlgorithm( self._equAlgorithm ) vuln_obj = bsql.is_injectable( freq, self._injvar ) if not vuln_obj: raise w3afException('Could not verify SQL injection ' + str(vuln) ) else: om.out.console('SQL injection could be verified, trying to create the DB driver.') # Try to get a shell using all vuln msg = 'Trying to exploit using vulnerability with id: ' + str( vuln_obj.getId() ) msg += '. Please wait...' om.out.console( msg ) shell_obj = self._generateShell( vuln_obj ) if shell_obj is not None: kb.kb.append( self, 'shell', shell_obj ) return [shell_obj, ] raise w3afException('No exploitable vulnerabilities found.')
def isExchangable( self, fuzzableRequest ): ''' @parameter mutant: The mutant you want to test if sending using querystring or postdata is the same. @return: [True|False] ''' if not ( isinstance( fuzzableRequest, httpQsRequest ) or isinstance( fuzzableRequest, httpPostDataRequest ) ) : return False # I get the mutant as it is response = self._sendMutant( fuzzableRequest, analyze=False ) if fuzzableRequest.getMethod() == 'GET': # I have to create a httpPostDataRequest and set all the parameters to it. pdr = httpPostDataRequest() pdr.setURL( fuzzableRequest.getURL() ) pdr.setDc( fuzzableRequest.getDc() ) pdr.setHeaders( fuzzableRequest.getHeaders() ) pdr.setCookie( fuzzableRequest.getCookie() ) response2 = self._sendMutant( pdr, analyze=False ) if response2.getBody() == response.getBody(): return True elif fuzzableRequest.getMethod() == 'POST': # I have to create a httpQsRequest and set all the parameters to it. qsr = httpQsRequest() qsr.setURL( fuzzableRequest.getURL() ) qsr.setDc( fuzzableRequest.getDc() ) qsr.setHeaders( fuzzableRequest.getHeaders() ) qsr.setCookie( fuzzableRequest.getCookie() ) response2 = self._sendMutant( qsr, analyze=False ) if response2.getBody() == response.getBody(): return True else: return False
def createFuzzableRequests( httpResponse, request=None, add_self=True ): ''' Generates the fuzzable requests based on an http response instance. @parameter httpResponse: An httpResponse instance. @parameter request: The HTTP request that generated the httpResponse @parameter add_self: If I should add the current HTTP request (@parameter request) to the result on not. @return: A list of fuzzable requests. ''' res = [] # query string url = httpResponse.getURL() QSObject = httpResponse.getURI().getQueryString() # Headers for all fuzzable requests created here: # And add the fuzzable headers to the dict headers = {} for header_name in cf.cf.getData('fuzzableHeaders' ): if header_name not in headers: headers[ header_name ] = '' # Get the cookie! cookieObj = _createCookie( httpResponse ) # # create the fuzzable request that represents the request object passed as parameter # if add_self: self_headers = {} if request: self_headers = request.getHeaders() for header_name in cf.cf.getData('fuzzableHeaders' ): if header_name not in headers: self_headers[ header_name ] = '' qsr = httpQsRequest.httpQsRequest() qsr.setURL( url ) qsr.setDc( QSObject ) qsr.setHeaders( self_headers ) qsr.setCookie( cookieObj ) res.append( qsr ) # Try to find forms in the document form_list = [] try: dp = dpCache.dpc.getDocumentParserFor( httpResponse ) except w3afException: # Failed to find a suitable parser for the document pass else: form_list = dp.getForms() if not form_list: # Check if its a wsdl file wsdlp = wsdlParser.wsdlParser() try: wsdlp.setWsdl( httpResponse.getBody() ) except w3afException: pass else: webServiceList = wsdlp.getMethods() if len( webServiceList ) != 0: for remoteMethod in webServiceList: wspdr = wsPostDataRequest.wsPostDataRequest() wspdr.setURL( remoteMethod.getLocation() ) wspdr.setAction( remoteMethod.getAction() ) wspdr.setParameters( remoteMethod.getParameters() ) wspdr.setNS( remoteMethod.getNamespace() ) wspdr.setMethodName( remoteMethod.getMethodName() ) wspdr.setHeaders( headers ) res.append( wspdr ) else: # create one httpPostDataRequest for each form variant mode = cf.cf.getData('fuzzFormComboValues') for form in form_list: for variant in form.getVariants(mode): if form.getMethod().upper() == 'POST': r = httpPostDataRequest.httpPostDataRequest() r.setMethod(variant.getMethod()) r.setFileVariables(form.getFileVariables()) else: # The default is a GET request r = httpQsRequest.httpQsRequest() r.setURL(variant.getAction()) r.setDc(variant) r.setHeaders(headers) r.setCookie(cookieObj) res.append(r) return res
def createFuzzableRequestRaw(method, url, postData, headers): ''' Creates a fuzzable request based on a query sent FROM the browser. This is used in plugins like spiderMan. @parameter method: A string that represents the method ('GET', 'POST', etc) @parameter url: An url_object that represents the URL @parameter postData: A string that represents the postdata, if its a GET request, set to None. @parameter headers: A dict that holds the headers ''' if not isinstance(url, url_object): msg = 'The "url" parameter of createFuzzableRequestRaw @ frFactory' msg += ' must be of urlParser.url_object type.' raise ValueError( msg ) # # Just a query string request ! no postdata # if not postData: qsr = httpQsRequest.httpQsRequest() qsr.setURL(url) qsr.setMethod(method) qsr.setHeaders(headers) dc = url.getQueryString() qsr.setDc(dc) return qsr # # Seems to be something that has post data # pdr = httpPostDataRequest.httpPostDataRequest() pdr.setURL(url) pdr.setMethod(method) for header_name in headers.keys(): if header_name.lower() == 'content-length': del headers[header_name] pdr.setHeaders(headers) # # Parse the content # Case #1, multipart form data # conttype = headers.get('content-type', '') if conttype and conttype.startswith('multipart/form-data'): tmp, pdict = cgi.parse_header(conttype) try: dc = cgi.parse_multipart(StringIO(postData), pdict) except: om.out.debug('Multipart form data is invalid, the browser sent something weird.') else: resultDc = queryString() for i in dc.keys(): resultDc[i] = dc[i] # We process multipart requests as x-www-form-urlencoded # TODO We need native support of multipart requests! headers['content-type'] = 'application/x-www-form-urlencoded' pdr.setDc(resultDc) pdr.setHeaders(headers) return pdr # # Case #2, JSON request # try: dc = json.loads(postData) except: pass else: # It's json! welcome to the party dude! pdr = jsonPostDataRequest.jsonPostDataRequest() pdr.setURL(url) pdr.setMethod(method) pdr.setHeaders(headers) pdr.setDc(dc) return pdr # # Case #3, XMLRPC request # postDataLower = postData.lower() stopWords = [ '<methodcall>', '<methodname>', '<params>', '</methodcall>', '</methodname>', '</params>' ] allStopWords = True for word in stopWords: if word not in postDataLower: allStopWords = False if allStopWords: xmlrpc_request = xmlrpcRequest.xmlrpcRequest(postData) xmlrpc_request.setURL( url ) xmlrpc_request.setMethod( method ) xmlrpc_request.setHeaders( headers ) return xmlrpc_request # # Case #4, the "default". # # NOT a JSON or XMLRPC request!, let's try the simple url encoded post data... # try: dc = parse_qs(postData) pdr.setDc( dc ) except: om.out.debug('Failed to create a data container that can store this data: "' + postData + '".') else: return pdr
def are_variants( url_a , url_b ): ''' This function analyzes if two URLs are variants. Two requests are variants if: - They have the same URL - They have the same method - They have the same parameters - The values for each parameter have the same type (int / string) @parameter url_a: The URL we want to analyze @parameter url_b: The other URL we want to analyze @return: True if the URLs are variants. >>> from core.data.parsers.urlParser import url_object >>> a = url_object('http://www.w3af.com/foo.php') >>> b = url_object('http://www.w3af.com/foo.php') >>> are_variants( a, b ) True >>> a = url_object('http://www.w3af.com/foo.php?id=1') >>> b = url_object('http://www.w3af.com/foo.php?foo=1') >>> are_variants( a, b ) False >>> a = url_object('http://www.w3af.com/bar.php?id=1') >>> b = url_object('http://www.w3af.com/foo.php?foo=1') >>> are_variants( a, b ) False >>> a = url_object('http://www.w3af.com/foo.php?id=1') >>> b = url_object('http://www.rapid7.com/foo.php?id=1') >>> are_variants( a, b ) False >>> a = url_object('http://www.w3af.com/foo.php?id=1&foo=bar') >>> b = url_object('http://www.rapid7.com/foo.php?id=1') >>> are_variants( a, b ) False >>> a = 'http://www.w3af.com/foo.php?id=1' >>> b = 'http://www.rapid7.com/foo.php?id=1' >>> are_variants( a, b ) Traceback (most recent call last): File "<stdin>", line 1, in ? ValueError: The "url_a" parameter in "are_variants" must be of urlParser.url_object type. ''' if not isinstance(url_a, url_object): msg = 'The "url_a" parameter in "are_variants" ' msg += ' must be of urlParser.url_object type.' raise ValueError( msg ) if not isinstance(url_b, url_object): msg = 'The "url_b" parameter in "are_variants" ' msg += ' must be of urlParser.url_object type.' raise ValueError( msg ) qs_a = url_a.getQueryString() qsr_a = httpQsRequest.httpQsRequest() qsr_a.setURL( url_a.uri2url() ) qsr_a.setDc( qs_a ) qs_b = url_b.getQueryString() qsr_b = httpQsRequest.httpQsRequest() qsr_b.setURL( url_b.uri2url() ) qsr_b.setDc( qs_b ) return qsr_a.is_variant_of( qsr_b )
def createFuzzableRequestRaw(method, url, postData, headers): """ Creates a fuzzable request based on a query sent FROM the browser. This is used in plugins like spiderMan. @parameter method: A string that represents the method ('GET', 'POST', etc) @parameter url: A string that represents the URL @parameter postData: A string that represents the postdata, if its a GET request, set to None. @parameter headers: A dict that holds the headers """ # # Just a query string request ! no postdata # if not postData: qsr = httpQsRequest.httpQsRequest() qsr.setURL(url) qsr.setMethod(method) qsr.setHeaders(headers) dc = urlParser.getQueryString(url) qsr.setDc(dc) return qsr # # Seems to be something that has post data # pdr = httpPostDataRequest.httpPostDataRequest() pdr.setURL(url) pdr.setMethod(method) for header_name in headers.keys(): if header_name.lower() == "content-length": del headers[header_name] pdr.setHeaders(headers) # # Parse the content # Case #1, multipart form data # if "content-type" in headers.keys() and headers["content-type"].startswith("multipart/form-data"): tmp, pdict = cgi.parse_header(headers["content-type"]) try: dc = cgi.parse_multipart(StringIO(postData), pdict) except: om.out.debug("Multipart form data is invalid, the browser sent something wierd.") else: resultDc = queryString() for i in dc.keys(): resultDc[i] = dc[i] # We process multipart requests as x-www-form-urlencoded # TODO We need native support of multipart requests! headers["content-type"] = "application/x-www-form-urlencoded" pdr.setDc(resultDc) pdr.setHeaders(headers) return pdr # # Case #2, JSON request # try: dc = json.loads(postData) except: pass else: # It's json! welcome to the party dude! pdr = jsonPostDataRequest.jsonPostDataRequest() pdr.setURL(url) pdr.setMethod(method) pdr.setHeaders(headers) pdr.setDc(dc) return pdr # # Case #3, XMLRPC request # postDataLower = postData.lower() stopWords = ["<methodcall>", "<methodname>", "<params>", "</methodcall>", "</methodname>", "</params>"] allStopWords = True for word in stopWords: if word not in postDataLower: allStopWords = False if allStopWords: xmlrpc_request = xmlrpcRequest.xmlrpcRequest(postData) xmlrpc_request.setURL(url) xmlrpc_request.setMethod(method) xmlrpc_request.setHeaders(headers) return xmlrpc_request # # Case #4, the "default". # # NOT a JSON or XMLRPC request!, let's try the simple url encoded post data... # try: dc = urlParser.getQueryString("http://w3af/?" + postData) pdr.setDc(dc) except: om.out.debug('Failed to create a data container that can store this data: "' + postData + '".') else: return pdr