180 lines
4.9 KiB
Ruby
180 lines
4.9 KiB
Ruby
# -*- coding: binary -*-
|
|
###
|
|
#
|
|
# This module provides methods for communicating with a host running oracle.
|
|
# Dependencies:
|
|
# - Oracle Instant Client
|
|
# - ruby-oci8
|
|
#
|
|
# Rory McCune <rorym@nmrconsult.net>
|
|
# MC <mc@metasploit.com>
|
|
#
|
|
###
|
|
|
|
|
|
module Msf
|
|
module Exploit::ORACLE
|
|
|
|
|
|
def initialize(info = {})
|
|
super
|
|
|
|
register_options(
|
|
[
|
|
OptString.new('RHOST', [ true, 'The Oracle host.', '']),
|
|
OptPort.new('RPORT', [ true, 'The TNS port.', 1521]),
|
|
OptString.new('SID', [ true, 'The sid to authenticate with.', 'ORCL']),
|
|
OptString.new('DBUSER', [ true, 'The username to authenticate with.', 'SCOTT']),
|
|
OptString.new('DBPASS', [ true, 'The password to authenticate with.', 'TIGER']),
|
|
], Msf::Exploit::ORACLE
|
|
)
|
|
|
|
begin
|
|
olang = ENV['NLS_LANG']
|
|
ENV['NLS_LANG'] = 'AMERICAN_AMERICA.WE8ISO8859P1'
|
|
require 'oci8'
|
|
ENV['NLS_LANG'] = olang
|
|
@oci8_loaded = true
|
|
rescue ::Exception => e
|
|
@oci8_loaded = false
|
|
@oci8_error = e
|
|
end
|
|
end
|
|
|
|
def check_dependencies
|
|
if not @oci8_loaded
|
|
print_error("Failed to load the OCI library: #{@oci8_error}")
|
|
print_error("Try 'gem install ruby-oci8'")
|
|
return false
|
|
end
|
|
return true
|
|
end
|
|
|
|
def connect
|
|
handle = nil
|
|
|
|
if(not @oci8_loaded)
|
|
raise RuntimeError, "Could not load the Oracle driver (oci8): #{@oci8_error}"
|
|
end
|
|
|
|
# Create a Connection to the Database
|
|
if datastore['DBUSER'] == 'SYS' || datastore['DBUSER'] == 'SYSTEM'
|
|
begin
|
|
handle = OCI8.new(
|
|
datastore['DBUSER'],
|
|
datastore['DBPASS'],
|
|
"//#{datastore['RHOST']}:#{datastore['RPORT']}/#{datastore['SID']}",
|
|
:SYSDBA
|
|
)
|
|
rescue ::OCIError
|
|
# Try again without a request for SYSDBA
|
|
vprint_status('Insufficient privileges, trying without SYSDBA')
|
|
handle = OCI8.new(
|
|
datastore['DBUSER'],
|
|
datastore['DBPASS'],
|
|
"//#{datastore['RHOST']}:#{datastore['RPORT']}/#{datastore['SID']}"
|
|
)
|
|
end
|
|
else
|
|
handle = OCI8.new(
|
|
datastore['DBUSER'],
|
|
datastore['DBPASS'],
|
|
"//#{datastore['RHOST']}:#{datastore['RPORT']}/#{datastore['SID']}"
|
|
)
|
|
end
|
|
handle
|
|
end
|
|
|
|
def disconnect
|
|
connect.logoff
|
|
end
|
|
|
|
def prepare_exec(sql)
|
|
begin
|
|
sploit = connect.parse(sql)
|
|
rescue ::OCIError => e
|
|
print_error("#{e.to_s}")
|
|
return
|
|
end
|
|
# DEBUG
|
|
# print_status("did the parse sploit type is " + sploit.type.to_s)
|
|
begin
|
|
sploit.exec
|
|
rescue ::OCIError => e
|
|
if ( e.to_s =~ /ORA-00942: table or view does not exist/ )
|
|
print_status("ORA-00942: table or view does not exist")
|
|
raise RuntimeError, "ORA-00942: table or view does not exist"
|
|
end
|
|
print_status e.to_s
|
|
end
|
|
|
|
# The Handling is a little different for certain types of query
|
|
# Mainly Select needs a fetch statement to get the data
|
|
# Also return types are a little different (some return rows changed so we can used that)
|
|
# The case statement could probably be collapsed a bit but leaving it as is for the moment
|
|
# in case it's useful later...
|
|
|
|
# Select Queries
|
|
case sploit.type
|
|
when 1, :select_stmt
|
|
# Create an array to return to the calling function
|
|
results = Array.new
|
|
while r = sploit.fetch()
|
|
str = r.join(',')
|
|
# Removed this as it should really be down to the exploit to decide what to print
|
|
# eg leaving this in messes up oraenum.
|
|
# print_status(str)
|
|
results << str
|
|
end
|
|
|
|
return results
|
|
|
|
# Update Queries
|
|
when 2, :update_stmt
|
|
connect.commit
|
|
|
|
# If we were successful our return should be a Integer with the number of rows updated
|
|
result = ['UPDATE Successful ' + sploit.row_count.to_s + ' Rows Updated']
|
|
return result
|
|
# Delete Queries
|
|
when 3, :delete_stmt
|
|
connect.commit
|
|
# If we were successful our return should be a Integer with the number of rows updated
|
|
result = ['DELETE Successful ' + sploit.row_count.to_s + ' Rows Deleted']
|
|
return result
|
|
# Insert Queries
|
|
when 4, :insert_stmt
|
|
connect.commit
|
|
# If we were successful our return should be a Integer with the number of rows updated
|
|
result = ['INSERT Successful ' + sploit.row_count.to_s + ' Rows Inserted']
|
|
return result
|
|
# Create Queries
|
|
when 5, :create_stmt
|
|
connect.commit
|
|
if sploit
|
|
print_status('CREATE successful')
|
|
end
|
|
when 6, :drop_stmt
|
|
connect.commit
|
|
if sploit
|
|
print_status('DROP successful')
|
|
end
|
|
when 7, :alter_stmt
|
|
connect.commit
|
|
if sploit
|
|
print_status('Alter successful')
|
|
end
|
|
when 8, :begin_stmt
|
|
connect.commit
|
|
when 9, :declare_stmt
|
|
connect.commit
|
|
else
|
|
print_status("Didn't match Query Type!")
|
|
print_status("Query type passed was " + sploit.type.to_s)
|
|
end
|
|
end
|
|
|
|
end
|
|
end
|
|
|