diff --git a/scripts/nje-node-brute.nse b/scripts/nje-node-brute.nse index 90da97f2e..bd47f2e0c 100644 --- a/scripts/nje-node-brute.nse +++ b/scripts/nje-node-brute.nse @@ -33,13 +33,17 @@ target IP (OIP) and a 1 byte response value (R) as outlined below: * TYPE: Can either be 'OPEN', 'ACK', or 'NAK', in EBCDIC, padded by spaces to make 8 bytes. This script always send 'OPEN' type. -* RHOST: Name of the local machine initiating the connection. Set to 'FAKE' +* RHOST: Node name of the local machine initiating the connection. Set to 'FAKE'. * RIP: Hex value of the local systems IP address. Set to '0.0.0.0' -* OHOST: The value being enumerated to determine the target system name. +* OHOST: The value being enumerated to determine the targets NJE node name. * OIP: IP address, in hex, of the target system. Set to '0.0.0.0'. -* R: The response. NJE will send an 'R' of 0x01 if the OHOST is wrong or 0x04/0x00 if the OHOST is correct. +* R: The response. NJE will send an 'R' of 0x01 if the OHOST is wrong or 0x04 if the OHOST is correct. -Since most systems will only have one node name, it is recommended to use the +By default this script will attempt the brute force a mainframes OHOST. If supplied with +the argument nje-node-brute.ohost this script will attempt the bruteforce +the RHOST, setting OHOST to the value supplied to the argument. + +Since most systems will only have one OHOST name, it is recommended to use the brute.firstonly script argument. ]] @@ -52,24 +56,27 @@ Since most systems will only have one node name, it is recommended to use the -- @args nje-node-brute.hostlist The filename of a list of node names to try. -- Defaults to "nselib/data/vhosts-default.lst" -- +-- @args nje-node-brute.ohost The target mainframe OHOST. Used to bruteforce RHOST. +-- -- @output -- PORT STATE SERVICE REASON -- 175/tcp open nje syn-ack -- | nje-node-brute: -- | Node Name: --- | Node Name:WASHDC - Valid credentials +-- | POTATO:CACTUS - Valid credentials -- |_ Statistics: Performed 6 guesses in 14 seconds, average tps: 0 -- -- @changelog -- 2015-06-15 - v0.1 - created by Soldier of Fortran +-- 2016-03-22 - v0.2 - Added RHOST Brute forcing. author = "Soldier of Fortran" -license = "Same as Nmap--See https://nmap.org/book/man-legal.html" +license = "Same as Nmap--See http://nmap.org/book/man-legal.html" categories = {"intrusive", "brute"} portrule = shortport.port_or_service({175,2252}, "nje") -local openNJEfmt = "\xd6\xd7\xc5\xd5@@@@\xc6\xc1\xd2\xc5@@@@\0\0\0\0%s\0\0\0\0\0" +local openNJEfmt = "\xd6\xd7\xc5\xd5@@@@%s\0\0\0\0%s\0\0\0\0\0" Driver = { new = function(self, host, port, options) @@ -86,6 +93,7 @@ Driver = { -- the high timeout should take delays into consideration local s, r, opts, _ = comm.tryssl(self.host, self.port, '', { timeout = 50000 } ) if ( not(s) ) then + stdnse.debug2("Failed to connect") return false, "Failed to connect to server" end self.socket = s @@ -100,15 +108,24 @@ Driver = { -- Generates an NJE 'OPEN' packet with the node name password = string.upper(password) stdnse.verbose(2,"Trying... %s", password) - local openNJE = openNJEfmt:format( drda.StringUtil.toEBCDIC(("%-8s"):format(password)) ) + local openNJE + if self.options['ohost'] then + -- One RHOST may have many valid OHOSTs + if password == self.options['ohost'] then return false, brute.Error:new( "RHOST cannot be OHOST" ) end + openNJE = openNJEfmt:format(drda.StringUtil.toEBCDIC(("%-8s"):format(password)), + drda.StringUtil.toEBCDIC(("%-8s"):format(self.options['ohost'])) ) + else + openNJE = openNJEfmt:format(drda.StringUtil.toEBCDIC(("%-8s"):format('FAKE')), + drda.StringUtil.toEBCDIC(("%-8s"):format(password)) ) + end local status, err = self.socket:send( openNJE ) if not status then return false, "Failed to send" end local status, data = self.socket:receive_bytes(33) if not status then return false, "Failed to receive" end - if ( data:sub(-1) == "\0" ) or - ( data:sub(-1) == "\x04" ) then - stdnse.verbose("Valid Node Name Found: %s", password) - return true, creds.Account:new("Node Name", password, creds.State.VALID) + if ( not self.options['ohost'] and ( data:sub(-1) == "\x04" ) ) or + ( self.options['ohost'] and ( data:sub(-1) == "\0" ) ) then + -- stdnse.verbose(2,"Valid Node Name Found: %s", password) + return true, creds.Account:new((self.options['ohost'] or "Node Name"), password, creds.State.VALID) end return false, brute.Error:new( "Invalid Node Name" ) end, @@ -131,6 +148,9 @@ end action = function( host, port ) -- Oftentimes the LPAR will be one of the subdomain of a system. local names = host.name and stdnse.strsplit("%.", host.name) or {} + local o_host = stdnse.get_script_args('nje-node-brute.ohost') or nil + local options = {} + if o_host then options = { ohost = o_host:upper() } end if host.targetname then host.targetname:gsub("[^.]+", function(n) table.insert(names, n) end) end @@ -142,12 +162,13 @@ action = function( host, port ) table.insert(names, l) end end - local engine = brute.Engine:new(Driver, host, port) - local users = unpwdb.filter_iterator(iter(names), valid_name) + if o_host then stdnse.verbose(2,'RHOST Mode, using OHOST: %s', o_host:upper()) end + local engine = brute.Engine:new(Driver, host, port, options) + local nodes = unpwdb.filter_iterator(iter(names), valid_name) engine.options:setOption("passonly", true ) - engine:setPasswordIterator(users) + engine:setPasswordIterator(nodes) engine.options.script_name = SCRIPT_NAME - engine.options:setTitle("Node Name") + engine.options:setTitle("Node Name(s)") local status, result = engine:start() return result end