def modifyRequest(self, request ): ''' Mangles the request @parameter request: urllib2.Request instance that is going to be modified by the evasion plugin ''' # First we mangle the URL path = urlParser.getPathQs( request.get_full_url() ) path = self._mutate( path ) # Now we mangle the postdata data = request.get_data() if data: # Only mangle the postdata if it is a url encoded string try: urlParser.getQueryString('http://w3af/?' + data ) except: pass else: data = self._mutate( data ) # Finally, we set all the mutants to the request in order to return it url = urlParser.getProtocol( request.get_full_url() ) url += '://' + urlParser.getNetLocation( request.get_full_url() ) + path new_req = urllib2.Request( url , data, request.headers, request.get_origin_req_host() ) return new_req
def modifyRequest(self, request ): ''' Mangles the request @parameter request: urllib2.Request instance that is going to be modified by the evasion plugin ''' # This is a test URL # http://172.16.1.132/index.asp?q=%uFF1Cscript%3Ealert(%22Hello%22)%3C/script%3E # This is the content of index.asp : # <%=Request.QueryString("q")%> # First we mangle the URL path = urlParser.getPathQs( request.get_full_url() ) path = self._mutate( path ) # Now we mangle the postdata data = request.get_data() if data: # Only mangle the postdata if it is a url encoded string try: urlParser.getQueryString('http://w3af/?' + data ) except: pass else: data = self._mutate( data ) # Finally, we set all the mutants to the request in order to return it url = urlParser.getProtocol( request.get_full_url() ) url += '://' + urlParser.getNetLocation( request.get_full_url() ) + path new_req = urllib2.Request( url , data, request.headers, request.get_origin_req_host() ) return new_req
def GET(self, uri, data='', headers={}, useCache=False, grepResult=True): ''' Gets a uri using a proxy, user agents, and other settings that where set previously. @param uri: This is the url to GET @param data: Only used if the uri parameter is really a URL. @return: An httpResponse object. ''' self._init() if self._isBlacklisted(uri): return self._new_no_content_resp(uri, log_it=True) qs = urlParser.getQueryString( uri ) if qs: req = HTTPRequest( uri ) else: if data: req = HTTPRequest( uri + '?' + data ) else: # It's really an url... req = HTTPRequest( uri ) req = self._addHeaders( req, headers ) return self._send( req , useCache=useCache, grepResult=grepResult)
def _return_without_eval( self, parameters, uri ): if urlParser.getDomainPath( uri ) == uri: return False (server, query , expected_response, method , desc) = parameters function_reference = getattr( self._urlOpener , method ) url = urlParser.uri2url( uri ) url += createRandAlNum( 7 ) if urlParser.getQueryString( query ): url = url + '?' + str( urlParser.getQueryString( query ) ) try: response = function_reference( url ) except KeyboardInterrupt,e: raise e
def getPage(self, url): """ Connect to the target url or proxy and return the target url page. """ m = self._vuln.getMutant() m.setDc( urlParser.getQueryString( url ) ) m.setURL( urlParser.uri2url( url ) ) response = self._sendMutant( m , analyze=False ) if response.getCode() in range( 500, 599 ): raise w3afException('getPage request returned an HTTP error 500.') return response.getBody()
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 setOptions(self, optionsMap): """ This method sets all the options that are configured using the user interface generated by the framework using the result of getOptions(). @parameter OptionList: A dictionary with the options for the plugin. @return: No value is returned. """ self._url = optionsMap["url"].getValue() self._method = optionsMap["method"].getValue() self._data = urlParser.getQueryString(optionsMap["data"].getValue()) self._fileVars = optionsMap["fileVars"].getValue() self._fileDest = optionsMap["fileDest"].getValue()
def setOptions( self, optionsMap ): ''' This method sets all the options that are configured using the user interface generated by the framework using the result of getOptions(). @parameter optionsMap: A dict with the options for the plugin. @return: No value is returned. ''' self._changeToPost = optionsMap['changeToPost'].getValue() self._url = optionsMap['url'].getValue() self._method = optionsMap['method'].getValue() self._data = urlParser.getQueryString( optionsMap['data'].getValue() ) self._inj_var = optionsMap['injvar'].getValue() self._generateOnlyOne = optionsMap['generateOnlyOne'].getValue()
def modifyRequest(self, request ): ''' Mangles the request @parameter request: urllib2.Request instance that is going to be modified by the evasion plugin ''' # Mangle the postdata data = request.get_data() if data: # Only mangle the postdata if it is a url encoded string try: urlParser.getQueryString('http://w3af/?' + data ) except: pass else: data = '\x00' + data headers_copy = copy.deepcopy(request.headers) headers_copy['content-length'] = str(len(data)) request = urllib2.Request( request.get_full_url() , data, headers_copy, request.get_origin_req_host() ) return request
def _generate_qs( self, fuzzableRequest ): ''' Check the URL query string. @return: A list of mutants. ''' # The result result = [] query_string = urlParser.getQueryString( fuzzableRequest.getURI() ) for parameter_name in query_string: # this for loop was added to address the repeated parameter name issue for element_index in xrange(len(query_string[parameter_name])): wordnet_result = self._search_wn( query_string[parameter_name][element_index] ) result.extend( self._generate_URL_from_result( parameter_name, element_index, wordnet_result, fuzzableRequest ) ) return result
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.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 = urlParser.getQueryString( real_url ) 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 sql_webshell fastExploit.' ) 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 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
def setURI( self, uri ): self._dc = urlParser.getQueryString(uri) self._uri = uri.replace(' ', '%20') self._url = urlParser.uri2url( uri )
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 = urlParser.getQueryString(httpResponse.getURI()) # 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
postData = self._getPostData() try: httpCommandMethod = getattr( self._urlOpener, self.command ) res = httpCommandMethod( path, data=postData, headers=self.headers ) except w3afException, w: om.out.error('The proxy request failed, error: ' + str(w) ) except Exception, e: raise e else: return res else: # most likely a GET request url = uri2url( path ) qs = getQueryString( self.path ) try: httpCommandMethod = getattr( self._urlOpener, self.command ) res = httpCommandMethod( url, data=str(qs), headers=self.headers, grepResult=grep ) except w3afException, w: traceback.print_exc() om.out.error('The proxy request failed, error: ' + str(w) ) raise w except Exception, e: traceback.print_exc() raise e else: return res def _sendError( self, exceptionObj, trace=None ): '''
def grep(self, request, response): ''' Plugin entry point. @parameter request: The HTTP request object. @parameter response: The HTTP response object @return: None, all results are saved in the kb. ''' try: dp = dpCache.dpc.getDocumentParserFor( response ) except w3afException: pass else: # Note: # - With parsed_references I'm 100% that it's really something in the HTML # that the developer intended to add. # # - The re_references are the result of regular expressions, which in some cases # are just false positives. parsed_references, re_references = dp.getReferences() for ref in parsed_references: qs = urlParser.getQueryString( ref ) for param_name in qs: # This for loop is to address the repeated parameter name issue for element_index in xrange(len(qs[param_name])): if self._is_strange( request, param_name, qs[param_name][element_index] )\ and ref not in self._already_reported: # Don't repeat findings self._already_reported.add(ref) i = info.info() i.setPluginName(self.getName()) i.setName('Strange parameter') i.setURI( ref ) i.setId( response.id ) msg = 'The URI: "' + i.getURI() + '" has a parameter named: "' + param_name msg += '" with value: "' + qs[param_name][element_index] + '", which is quite odd.' i.setDesc( msg ) i.setVar( param_name ) i['parameterValue'] = qs[param_name][element_index] i.addToHighlight(qs[param_name][element_index]) kb.kb.append( self , 'strangeParameters' , i ) # To find this kind of vulns # http://thedailywtf.com/Articles/Oklahoma- # Leaks-Tens-of-Thousands-of-Social-Security-Numbers,-Other- # Sensitive-Data.aspx if self._is_SQL( request, param_name, qs[param_name][element_index] )\ and ref not in self._already_reported: # Don't repeat findings self._already_reported.add(ref) v = vuln.vuln() v.setPluginName(self.getName()) v.setName('Parameter has SQL sentence') v.setURI( ref ) v.setId( response.id ) msg = 'The URI: "' + v.getURI() + '" has a parameter named: "' + param_name msg +='" with value: "' + qs[param_name][element_index] + '", which is a SQL sentence.' v.setDesc( msg ) v.setVar( param_name ) v['parameterValue'] = qs[param_name][element_index] i.addToHighlight(qs[param_name][element_index]) kb.kb.append( self , 'strangeParameters' , v )