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 214 215 216 217 218 219 220 221 222 223 224 225 226
|
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 tns = require "tns"
local openssl = stdnse.silent_require "openssl"
description = [[
Performs brute force password auditing against Oracle servers.
Running it in default mode it performs an audit against a list of common
Oracle usernames and passwords. The mode can be changed by supplying the
argument oracle-brute.nodefault at which point the script will use the
username- and password- lists supplied with Nmap. Custom username- and
password- lists may be supplied using the userdb and passdb arguments.
The default credential list can be changed too by using the brute.credfile
argument. In case the userdb or passdb arguments are supplied, the script
assumes that it should run in the nodefault mode.
In modern versions of Oracle password guessing speeds decrease after a few
guesses and remain slow, due to connection throttling.
WARNING: The script makes no attempt to discover the amount of guesses
that can be made before locking an account. Running this script may therefor
result in a large number of accounts being locked out on the database server.
]]
---
-- @usage
-- nmap --script oracle-brute -p 1521 --script-args oracle-brute.sid=ORCL <host>
--
-- @output
-- PORT STATE SERVICE REASON
-- 1521/tcp open oracle syn-ack
-- | oracle-brute:
-- | Accounts
-- | system:powell => Account locked
-- | haxxor:haxxor => Valid credentials
-- | Statistics
-- |_ Perfomed 157 guesses in 8 seconds, average tps: 19
--
-- @args oracle-brute.sid - the instance against which to perform password
-- guessing
-- @args oracle-brute.nodefault - do not attempt to guess any Oracle default
-- accounts
--
-- Version 0.3
-- Created 07/12/2010 - v0.1 - created by Patrik Karlsson <patrik@cqure.net>
-- Revised 07/23/2010 - v0.2 - added script usage and output and
-- - oracle-brute.sid argument
-- Revised 07/25/2011 - v0.3 - added support for guessing default accounts
-- changed code to use ConnectionPool
-- Revised 03/13/2012 - v0.4 - revised by László Tóth
-- added support for SYSDBA accounts
-- Revised 08/07/2012 - v0.5 - revised to suit the changes in brute
-- library [Aleksandar Nikolic]
--
-- Summary
-- -------
-- x The Driver class contains the driver implementation used by the brute
-- library
author = "Patrik Karlsson"
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 sysdba = {}
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:Login( username, password )
if ( sysdba[username] ) then
return false, brute.Error:new("Account already discovered")
end
if ( status ) then
self.helper:Close()
ConnectionPool[coroutine.running()] = nil
return true, creds.Account:new(username, password, creds.State.VALID)
-- Check for account locked message
elseif ( data:match("ORA[-]28000") ) then
return true, creds.Account:new(username, password, creds.State.LOCKED)
-- Check for account is SYSDBA message
elseif ( data:match("ORA[-]28009") ) then
sysdba[username] = true
return true, creds.Account:new(username .. " as sysdba", password, creds.State.VALID)
-- check for any other message
elseif ( data:match("ORA[-]%d+")) then
stdnse.debug3("username: %s, password: %s, error: %s", username, password, data )
return false, brute.Error:new(data)
-- any other errors are likely communication related, attempt to re-try
else
self.helper:Close()
ConnectionPool[coroutine.running()] = nil
local err = brute.Error:new(data)
err:setRetry(true)
return false, err
end
return false, brute.Error:new( data )
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('oracle-brute.sid') or
stdnse.get_script_args('tns.sid')
local engine = brute.Engine:new(Driver, host, port, sid)
local mode = "default"
if ( not(sid) ) then
return fail("Oracle instance not set (see oracle-brute.sid or tns.sid)")
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()
local f
if ( stdnse.get_script_args('userdb') or
stdnse.get_script_args('passdb') or
stdnse.get_script_args('oracle-brute.nodefault') or
stdnse.get_script_args('brute.credfile') ) then
mode = nil
end
if ( mode == "default" ) then
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)
end
engine.options.script_name = SCRIPT_NAME
status, result = engine:start()
return result
end
|