'''
exploit.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 w3af
import core.controllers.w3afCore
import core.controllers.outputManager as om
from core.controllers.w3afException import w3afException
import core.controllers.sessionManager as sessionManager
import core.data.kb.knowledgeBase as kb
from core.ui.consoleUi.pluginConfig import pluginConfig
from core.ui.consoleUi.consoleMenu import consoleMenu
from core.controllers.misc.exploitAll import exploitAll

class exploit(consoleMenu):
	'''
	This is the exploit configuration menu for the console.
	
	@author: Andres Riancho ( andres.riancho@gmail.com )	
	'''
	def __init__( self, w3af, commands=[] ):
		consoleMenu.__init__(self)
		self._menu = {'help':self._help, 'exploit':self._exploitWrapper,'fastexploit':self._fastexploit\
		,'list':self._list,'back':self._back}
		self._w3af = w3af
		self._commands = commands
		
	def sh( self ):
		'''
		Starts the shell's main loop.
		
		@return: The prompt
		'''
		prompt = 'w3af/exploit>>> '
		self._mainloop( prompt )
		
	def _exec( self, command ):
		'''
		Executes a user input.
		'''
		command, parameters = self._parse( command )

		if command in self._menu.keys():
			func = self._menu[command]
			try:
				res = func(parameters)
			except KeyboardInterrupt,k:
				raise k
			return res
		else:
			om.out.console( 'command not found\n' )
			return True
		
	def _help( self, parameters ):
		'''
		Prints a help message to the user.
		'''
		if len( parameters ) == 0:
			self.mprint('The following commands are available:\n','')
			self.mprint('help','You are here. help [command] prints more specific help.')
			self.mprint('list','List available exploits.')
			self.mprint('exploit','Exploit a vulnerability found by other plugin.')
			self.mprint('fastexploit','Exploit a vulnerability based on external parameters.')
			self.mprint('back','Return to previous menu.')			

		else:
			if len( parameters ) == 1:
				if parameters[0] in self._menu.keys():
					if parameters[0] == 'exploit':
						om.out.console( 'Exploit a vulnerability found by audit plugins.')
						om.out.console( 'Sintax: exploit {exploit plugin|*}')
						om.out.console( 'When using "exploit *" you will be running all exploit plugins, ordered by the probability of getting a root shell.')
					elif parameters[0] == 'fastexploit':
						om.out.console( 'Exploit a vulnerability based on external parameters.')
						om.out.console( 'Sintax: fastexploit {plugin name}')
						om.out.console( 'Example: fastexploit bsql')
					elif parameters[0] == 'list':
						om.out.console( 'List all available exploit plugins.')
						om.out.console( 'Sintax: list' )
		
		return True
	
	def _exploitWrapper( self, parameters ):
		'''
		This is a simple wrapper for the _exploit method.
		'''
		try:
			self._exploit( parameters )
		except w3afException, w:
			om.out.console( str(w) )
		return True
	
	def _exploit( self , parameters):
		'''
		Exploits a vuln.
		'''
		if not len(parameters):
			self._help(['exploit'])
		else:
			if parameters[0] == 'config':
				pConf = pluginConfig( self._w3af, self._commands )
				pluginName = parameters[1]
				prompt = 'w3af/plugin/' + pluginName + '>>> '
				try:
					configurableObject = self._w3af.getPluginInstance( pluginName )
				except w3afException, e:
					om.out.console('Error: ' + str(e) )
				else:						
					pConf.sh( prompt, configurableObject )
			elif parameters[0] == '*':
				self._exploitAll()
			else:
				# Exploit using a single plugin.
				pluginName = parameters[0]
				if pluginName not in self._w3af.getPluginList('attack'):
					om.out.console( 'Unknown plugin. Use the list command to view available plugins.' )
				else:
					self._plugin = plugin = self._w3af.getPluginInstance( pluginName )
					try:
						response = plugin.canExploit()
					except w3afException, e:
						raise e
					else:
						if not response:
							raise w3afException( 'No exploitable vulnerabilities found.' )
						else:
							if not plugin.exploit():
								raise w3afException( 'Failed to exploit vulnerability.')
							else:
								om.out.console( 'Vulnerability successfully exploited.' )
								what = plugin.getType()
								if what == 'proxy':
									om.out.console( 'Execute "exitPlugin" to get out of proxy menu. The proxy will keep running until you run "stop" inside this menu.' )
								elif what == 'shell':
									om.out.console( 'Execute "exitPlugin" to get out of the remote shell. Commands typed in this menu will be runned on the remote web server.' )
								
								prompt = 'w3af/exploit/'+pluginName+'>>> '
								self._mainloop( prompt, callback = self._callback )
		return True

	def _callback( self, command ):
		if command == 'exitPlugin':
			return False
		else:
			response = self._plugin.rexec( command )
			om.out.console( response )
			return True
			
	def _fastexploit( self , parameters):
		'''
		Prints a help message to the user.
		'''
		if not len( parameters ):
			om.out.console( 'Incorrect call to fastexploit, please see the help:' )
			self._help( ['fastexploit'] )
		else:
			if parameters[0] == 'config':
				pConf = pluginConfig( self._w3af, self._commands )
				pluginName = parameters[1]
				prompt = 'w3af/plugin/' + pluginName + '>>> '
				try:
					configurableObject = self._w3af.getPluginInstance( pluginName )
				except w3afException, e:
					om.out.console('Error: ' + str(e) )
				else:						
					pConf.sh( prompt, configurableObject )
			else:
				pluginName = parameters[0]
				if pluginName not in self._w3af.getPluginList('attack'):
					om.out.console( 'Unknown plugin. Use the list command to view available plugins.' )
				else:
					self._plugin = self._w3af.getPluginInstance( pluginName )
					self._plugin.fastExploit()
					what = self._plugin.getType()
					if what == 'proxy':
						om.out.console( 'Execute "exitPlugin" to get out of proxy menu. The proxy will keep running until you run "stop" inside this menu.' )
					elif what == 'shell':
						om.out.console( 'Execute "exitPlugin" to get out of the remote shell. Commands typed in this menu will be runned on the remote web server.' )
					
					prompt = 'w3af/exploit/'+pluginName+'>>> '
					self._mainloop( prompt, callback = self._callback )
			
			return True
		
	def _list(self , parameters):
		'''
		Lists all available exploit plugins.
		'''
		list = self._w3af.getPluginList('attack')
		for plugin in list:
			desc = self._w3af.getPluginInstance( plugin ).getDesc()
			self.mprint( plugin, desc )
		return True
		
	def _back( self, parameters ):
		return False
	
	def _exploitAll( self ):
		'''
		This function creates an instance of every attack plugin, then orders it using the returning value of
		the getRootProbability method and finnaly runs every one of them until a vulnerability is successfully 
		exploited.
		
		@return: True if a vuln was successfully exploited.
		'''
		attackPluginList = self._w3af.getPluginList( 'attack' )
		#Now I create the instances...
		instanceList = []
		for pluginName in attackPluginList:
			instanceList.append( self._w3af.getPluginInstance( pluginName ) )
		
		# Its time to sort...
		def sortfunc(x,y):
			# reverse ordering...
			return cmp( y.getRootProbability() ,x.getRootProbability() )
		instanceList.sort( sortfunc )
		
		# Exploit !
		for ap in instanceList:
			om.out.console('Using plugin: ' + ap.getName() )
			exploitedOK = True
			try:
				self._exploit( [ap.getName(),] )
			except w3afException, w:
				exploitedOK = False
				om.out.console( str(w) )
			
			if exploitedOK:
				return True
		
		return False
