local bin = require "bin" local io = require "io" local nmap = require "nmap" local shortport = require "shortport" local stdnse = require "stdnse" local table = require "table" description = [[ Guesses Oracle instance/SID names against the TNS-listener. If the oraclesids script argument is not used to specify an alternate file, the default oracle-sids file will be used. License to use the oracle-sids file was granted by its author, Alexander Kornbrust (http://seclists.org/nmap-dev/2009/q4/645). ]] --- -- @args oraclesids A file containing SIDs to try. -- -- @usage -- nmap --script=oracle-sid-brute --script-args=oraclesids=/path/to/sidfile -p 1521-1560 -- nmap --script=oracle-sid-brute -p 1521-1560 -- -- @output -- PORT STATE SERVICE REASON -- 1521/tcp open oracle syn-ack -- | oracle-sid-brute: -- | orcl -- | prod -- |_ devel -- Version 0.3 -- Created 12/10/2009 - v0.1 - created by Patrik Karlsson -- Revised 12/11/2009 - v0.2 - Added tns_type, split packet creation to header & data -- Revised 12/14/2009 - v0.3 - Fixed ugly file_exist kludge author = "Patrik Karlsson" license = "Same as Nmap--See http://nmap.org/book/man-legal.html" categories = {"intrusive", "brute"} portrule = shortport.port_or_service(1521, 'oracle-tns') -- A table containing the different TNS types ... not complete :) local tns_type = {CONNECT=1, REFUSE=4, REDIRECT=5, RESEND=11} --- Creates a TNS header -- A lot of values are still hardcoded ... -- -- @param packetType string containing the type of TNS packet -- @param packetLength number defining the length of the DATA segment of the packet -- -- @return string with the raw TNS header -- local function create_tns_header(packetType, packetLength) local request = bin.pack( ">SSCCS", packetLength + 34, -- Packet Length 0, -- Packet Checksum tns_type[packetType], -- Packet Type 0, -- Reserved Byte 0 -- Header Checksum ) return request end --- Creates a TNS connect packet -- -- @param host_ip string containing the IP of the remote host -- @param port_no number containing the remote port of the Oracle instance -- @param sid string containing the SID against which to attempt to connect -- -- @return string containing the raw TNS packet -- local function create_connect_packet( host_ip, port_no, sid ) local connect_data = "(DESCRIPTION=(CONNECT_DATA=(SID=" .. sid .. ")" connect_data = connect_data .. "(CID=(PROGRAM=)(HOST=__jdbc__)(USER=)))" connect_data = connect_data .. "(ADDRESS=(PROTOCOL=tcp)(HOST=" .. host_ip .. ")" connect_data = connect_data .. "(PORT=" .. port_no .. ")))" local data = bin.pack(">SSSSSSSSSSICCA", 308, -- Version 300, -- Version (Compatibility) 0, -- Service Options 2048, -- Session Data Unit Size 32767, -- Maximum Transmission Data Unit Size 20376, -- NT Protocol Characteristics 0, -- Line Turnaround Value 1, -- Value of 1 in Hardware connect_data:len(), -- Length of connect data 34, -- Offset to connect data 0, -- Maximum Receivable Connect Data 1, -- Connect Flags 0 1, -- Connect Flags 1 connect_data ) local header = create_tns_header("CONNECT", connect_data:len() ) return header .. data end --- Process a TNS response and extracts Length, Checksum and Type -- -- @param packet string as a raw TNS response -- @return table with Length, Checksum and Type set -- local function process_tns_packet( packet ) local tnspacket = {} -- just pull out the bare minimum to be able to match _, tnspacket.Length, tnspacket.Checksum, tnspacket.Type = bin.unpack(">SSC", packet) return tnspacket end action = function(host, port) local found_sids = {} local socket = nmap.new_socket() local catch = function() socket:close() end local try = nmap.new_try(catch) local request, response, tns_packet local sidfile socket:set_timeout(5000) -- open the sid file specified by the user or fallback to the default oracle-sids file sidfilename = nmap.registry.args.oraclesids or nmap.fetchfile("nselib/data/oracle-sids") sidfile = io.open(sidfilename) if not sidfile then return end -- read sids line-by-line from the sidfile for sid in sidfile:lines() do -- check for comments if not sid:match("#!comment:") then try(socket:connect(host, port)) request = create_connect_packet( host.ip, port.number, sid ) try(socket:send(request)) response = try(socket:receive_bytes(1)) tns_packet = process_tns_packet(response) -- If we get anything other than REFUSE consider it as a valid SID if tns_packet.Type ~= tns_type.REFUSE then table.insert(found_sids, sid) end try(socket:close()) end end sidfile:close() return stdnse.format_output(true, found_sids) end