Hi all,

I was browsing through the code for the xss-module, when I came upon a
strange thing :

       # Analyze the response
        allowed = []
        if response.getBody().count(list_delimiter) == 2:
           ...[checks for what chars are available]...
        else:
            raise w3afException('The delimiter was not echoed back!')


What if the input parameter is echoed twice on the page, resulting in 4
list_delimiters?

Also, when checking if echo occurs, this is used - which I guess checks
the whole response object (incl headers) :
 
        # Analyze and return response
        if rndNum in response:
            om.out.debug('The variable ' + mutant.getVar() + ' is being
echoed back.' )
            return True

However, when checking for our xss, only the body is checked, as shown
above.

I created a spin-off from xss.py called xssLite.py. It is basically a
stripped down xss.py which only stores to kb if
any of a set of special characters are echoed back to the page: < > ' "
So it checks only four characters (and could probably skip '>' since it
usually is treated (encoded dropped or left alone) exactly as '<' by the
server.
It has the option to test each character separately, increasing the
chance of not getting filtered.

I also shortened the resulting payload string by placing all chars
between random value :  <RND1>C<RND2>D<RND3>... , so all random parts
can be
used by two times, once as suffix and once as prefix. (match for d =
body.find("<RND2>D<RND3>")). And fixed the error with if the stuff is
echoed back several times.

Personally, I prefer using it this way (i.e: getting to know what chars
are unfiltered) without totally relying on the framework to deduce
exactly what vector can be used or not.

Regards,
Martin Holst Swende

'''
xss.py

Copyright 2006 Andres Riancho

This file is part of w3af, w3af.sourceforge.net .

w3af is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation version 2 of the License.

w3af is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with w3af; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

'''

import core.controllers.outputManager as om

# options
from core.data.options.option import option
from core.data.options.optionList import optionList

from core.controllers.basePlugin.baseAuditPlugin import baseAuditPlugin
from core.data.fuzzer.fuzzer import createMutants, createRandAlNum
from core.controllers.w3afException import w3afException

import core.data.kb.knowledgeBase as kb
import core.data.kb.vuln as vuln
import core.data.constants.severity as severity

import core.data.constants.browsers as browsers


class xssLite(baseAuditPlugin):
    '''
    Find cross site scripting vulnerabilities.
    @author: Martin Holst Swende 2010, based on xss.py by Andres Riancho
    '''

    def __init__(self):
        baseAuditPlugin.__init__(self)
        
        # Some internal variables to keep track of remote web application sanitization
        self._fuzzableRequests = []
        self._xssMutants = []
        self._special_characters = ['<', '>', '"', "'"]
        self._thoroughTest = False
        
    def audit(self, freq ):
        '''
        Tests an URL for potential XSS vulnerabilities.
        These are the special characters that are tested:
          ['<', '>', '"', "'"]
        
        These characters should not be echoed back onto page without html encoding, 
        since they may break html-contexts, html-attributes or javascript declarations
        
        @param freq: A fuzzableRequest
        '''
        om.out.debug( 'XssLite plugin is testing: ' + freq.getURL() )
        
        # This list is just to test if the parameter is echoed back
        fake_mutants = createMutants( freq , ['', ] )
            
        for mutant in fake_mutants:
            # verify if the variable we are fuzzing is actually being echoed back
            if self._is_echoed( mutant ):
                # Search for reflected XSS
                
                if self._thoroughTest : 
                    self._thorough_test(mutant)
                else:
                    self._quick_test(mutant)    
    
    
    def _report(self,allowed, mutant, response):
        '''
        Writes to kb or log if anything interesting is found
        '''
        if len(allowed) == len(self._special_characters):
            om.out.debug('All special characters are allowed.')
        
        if len(allowed)  > 0 :
            # Save it to the KB
            v = vuln.vuln( mutant )
            v.setId( response.id )
            v.setName( 'Improper html encoding of special chars' )
            v.setSeverity(severity.MEDIUM)
            msg = 'HTML or javascript special characters that are not properly encoded were found at : ' + mutant.foundAt() 
            msg += '\r\n The following characters were echoed on the page: %s \r\n' % ''.join(allowed)
            v.setDesc( msg )

            kb.kb.append( self, 'Possible xss', v )
            
    def _thorough_test(self,mutant):
        '''
        Tests for injection of special characters by creating one request per character
        @return: nothing, stores result in kb
        '''
        
        # Create a random number and assign it to the mutant modified parameter
        oldValue = mutant.getModValue() 
        allowed = []
        for char in self._special_characters: 
            prefix = str( createRandAlNum( 3 ) )
            suffix = str( createRandAlNum( 3 ) )   #112
            payload = prefix + char + suffix      #111<112
        
            mutant.setModValue(payload)
        
            # send
            response = self._sendMutant( mutant, analyze=False )
            # Analyze the response
            if response.getBody().find(payload) > -1 :
                allowed.append(char)
                #What about response header ? 
        
        self._report(allowed, mutant, response)
        
        mutant.setModValue(oldValue)
        
    def _quick_test(self, mutant):
        '''
        Tests for injection of special characters by bundling them in one request
        @return: nothing, stores result in kb
        '''
        
        # Create a random number and assign it to the mutant modified parameter
        matchList = {}
        allowed = []
        prefix = str( createRandAlNum( 3 ) )
        payload = prefix  # 111
        for char in self._special_characters: 
            suffix = str( createRandAlNum( 3 ) )   #112
            payload = payload + char + suffix      #111<112
            matchList[char] = prefix+ char + suffix
            prefix = suffix
        
        oldValue = mutant.getModValue() 
        mutant.setModValue(payload)             #111<112"113'114
        
        # send
        response = self._sendMutant( mutant, analyze=False )
      
        # Analyze the response
        for char,match in matchList.items():
                print "Checking body for %s" % match
                if response.getBody().find(match) > -1 :
                    allowed.append(char)
                #What about response header ? 
        
        self._report(allowed, mutant, response)
            
        mutant.setModValue(oldValue)
  
    def _is_echoed( self, mutant ):
        '''
        Verify if the parameter we are fuzzing is really being echoed back in the
        HTML response or not. If it isn't echoed there is no chance we are going to
        find a reflected XSS here.
        
        Also please note that I send a random alphanumeric value, and not a numeric
        value, because even if the number is echoed back (and only numbers are echoed
        back by the application) that won't be of any use in the XSS detection.
        
        @parameter mutant: The request to send.
        @return: True if variable is echoed
        '''
        # Create a random number and assign it to the mutant modified
        # parameter
        rndNum = str( createRandAlNum( 5 ) )
        oldValue = mutant.getModValue() 
        mutant.setModValue(rndNum)

        # send
        response = self._sendMutant( mutant, analyze=False )
        
        # restore the mutant values
        mutant.setModValue(oldValue)
        
        # Analyze and return response
        if rndNum in response:
            om.out.debug('The variable ' + mutant.getVar() + ' is being echoed back.' )
            return True
        else:
            om.out.debug('The variable ' + mutant.getVar() + ' is NOT being echoed back.' )
            return False
    
  
 

    def getOptions( self ):
        '''
        @return: A list of option objects for this plugin.
        '''
        d1 = 'Thorough test'
        h1 = 'If set to True, w3af will test for each special character separately,'
        h1 += ' creating %d times more requests than compared to the quick version.' % len(self._special_characters)
        h1 += 'The quick version will try all characters in a single request, but'
        h1 += ' is therefore more probable to trigger filters and fail'
        o1 = option('thoroughTest', self._thoroughTest, d1, 'boolean', help=h1)
        
        ol = optionList()
        ol.add(o1)
        return ol
    
    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._thoroughTest = optionsMap['thoroughTest'].getValue()
       
    def getPluginDeps( self ):
        '''
        @return: A list with the names of the plugins that should be runned before the
        current one.
        '''
        return []

    def getLongDesc( self ):
        '''
        @return: A DETAILED description of the plugin functions and features.
        '''
        return '''
        This plugin finds potential Cross Site Scripting (XSS) vulnerabilities.
        
        One configurable parameter exist:
            - thoroughTest (default False)
        
        What it does it checks if special characters (html and javascript) are
        echoed back on the page (i.e without having been html-encoded). 
        The quick mode tries several characters per request, with higher
        chance of failing. The thorough mode checks one character per
        request, which makes more requests but with better accuracy.
        '''
------------------------------------------------------------------------------
Download Intel&#174; Parallel Studio Eval
Try the new software tools for yourself. Speed compiling, find bugs
proactively, and fine-tune applications for parallel performance.
See why Intel Parallel Studio got high marks during beta.
http://p.sf.net/sfu/intel-sw-dev
_______________________________________________
W3af-develop mailing list
W3af-develop@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/w3af-develop

Reply via email to