local bin = require "bin"
local io = require "io"
local nmap = require "nmap"
local shortport = require "shortport"
local stdnse = require "stdnse"
local string = require "string"
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 https://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 = string.format(
"(DESCRIPTION=(CONNECT_DATA=(SID=%s)(CID=(PROGRAM=)(HOST=__jdbc__)(USER=)))\z
(ADDRESS=(PROTOCOL=tcp)(HOST=%s)(PORT=%d)))", sid, host_ip, 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
local _
_, 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
local 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