diff --git a/CHANGELOG b/CHANGELOG index b359879a4..2e364104d 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,8 @@ # Nmap Changelog ($Id$); -*-text-*- +o [NSE] Added brute scripts rlogin-brute and rexec-brute for the rlogin and + rexec services [Patrik] + o [NSE] Added broadcast-rip-discover which gets RIPv2 routers and their routing information by querying the multicast address [Patrik] diff --git a/scripts/rexec-brute.nse b/scripts/rexec-brute.nse new file mode 100644 index 000000000..ba9638a55 --- /dev/null +++ b/scripts/rexec-brute.nse @@ -0,0 +1,98 @@ +description=[[ +Performs password guessing against the rexec service. +]] + +--- +-- @usage +-- nmap -p 512 --script rexec-brute +-- +-- @output +-- PORT STATE SERVICE +-- 512/tcp open exec +-- | rexec-brute: +-- | Accounts +-- | nmap:test - Valid credentials +-- | Statistics +-- |_ Performed 16 guesses in 7 seconds, average tps: 2 +-- +-- @args rexec-brute.timeout number + +-- Version 0.1 +-- Created 11/02/2011 - v0.1 - created by Patrik Karlsson + +require 'brute' +require 'shortport' + +author = "Patrik Karlsson" +license = "Same as Nmap--See http://nmap.org/book/man-legal.html" +categories = {"brute", "intrusive"} + +portrule = shortport.port_or_service(512, "exec", "tcp") + + +Driver = { + + -- creates a new Driver instance + -- @param host table as received by the action function + -- @param port table as received by the action function + -- @return o instance of Driver + new = function(self, host, port, options) + local o = { host = host, port = port, timeout = options.timeout } + setmetatable(o, self) + self.__index = self + return o + end, + + connect = function(self) + self.socket = nmap.new_socket() + self.socket:set_timeout(self.timeout) + local status, err = self.socket:connect(self.host, self.port) + if ( not(status) ) then + local err = brute.Error:new("Connection failed") + err:setRetry( true ) + return false, err + end + return true + end, + + login = function(self, username, password) + local cmd = "id" + local data = ("\0%s\0%s\0\%s\0"):format(username, password, cmd) + + local status, err = self.socket:send(data) + if ( not(status) ) then + local err = brute.Error:new("Send failed") + err:setRetry( true ) + return false, err + end + + local response + status, response = self.socket:receive() + if ( status ) then + return true, brute.Account:new(username, password, creds.State.VALID) + end + return false, brute.Error:new( "Incorrect password" ) + end, + + disconnect = function(self) + self.socket:close() + end, + +} + + + +action = function(host, port) + local options = { + timeout = stdnse.get_script_args("rexec-brute.timeout") + } + + options.timeout = options.timeout and + tonumber(options.timeout) * 1000 or + 10000 + + local engine = brute.Engine:new(Driver, host, port, options) + engine.options.script_name = SCRIPT_NAME + status, result = engine:start() + return result +end \ No newline at end of file diff --git a/scripts/rlogin-brute.nse b/scripts/rlogin-brute.nse new file mode 100644 index 000000000..6c1bb47f6 --- /dev/null +++ b/scripts/rlogin-brute.nse @@ -0,0 +1,154 @@ +description=[[ +Performs password guessing against the rlogin service +The script needs to be run in privileged mode. +]] + +--- +-- @usage +-- nmap -p 513 --script rlogin-brute +-- +-- @output +-- PORT STATE SERVICE +-- 513/tcp open login +-- | rlogin-brute: +-- | Accounts +-- | nmap:test - Valid credentials +-- | Statistics +-- |_ Performed 4 guesses in 5 seconds, average tps: 0 +-- +-- @args rlogin-brute.timeout number + +-- Version 0.1 +-- Created 11/02/2011 - v0.1 - created by Patrik Karlsson + +require 'brute' +require 'shortport' + +author = "Patrik Karlsson" +license = "Same as Nmap--See http://nmap.org/book/man-legal.html" +categories = {"brute", "intrusive"} + +portrule = shortport.port_or_service(513, "login", "tcp") + +-- The rlogin Driver, check the brute.lua documentation for more details +Driver = { + + -- creates a new Driver instance + -- @param host table as received by the action function + -- @param port table as received by the action function + -- @return o instance of Driver + new = function(self, host, port, options) + local o = { host = host, port = port, timeout = options.timeout } + setmetatable(o, self) + self.__index = self + return o + end, + + -- connects to the rlogin service + -- it sets the source port to a random value between 513 and 1024 + connect = function(self) + + local status + + self.socket = nmap.new_socket() + -- apparently wee need a source port below 1024 + -- this approach is not very elegant as it causes address already in + -- use errors when the same src port is hit in a short time frame. + -- hopefully the retry count should take care of this as a retry + -- should choose a new random port as source. + local srcport = math.random(513, 1024) + self.socket:bind(nil, srcport) + self.socket:set_timeout(self.timeout) + status, err = self.socket:connect(self.host, self.port) + + if ( status ) then + local lport, _ + status, _, lport = self.socket:get_info() + if (not(status) ) then + return false, "failed to retrieve socket status" + end + else + self.socket:close() + end + if ( not(status) ) then + stdnse.print_debug(3, "ERROR: failed to connect to server") + end + return status + end, + + login = function(self, username, password) + local data = ("\0%s\0%s\0vt100/9600\0"):format(username, username) + local status, err = self.socket:send(data) + + status, data = self.socket:receive() + if (not(status)) then + local err = brute.Error:new("Failed to read response from server") + err:setRetry( true ) + return false, err + end + if ( data ~= "\0" ) then + stdnse.print_debug(2, "ERROR: Expected null byte") + local err = brute.Error:new( "Expected null byte" ) + err:setRetry( true ) + return false, err + end + + status, data = self.socket:receive() + if (not(status)) then + local err = brute.Error:new("Failed to read response from server") + err:setRetry( true ) + return false, err + end + if ( data ~= "Password: " ) then + stdnse.print_debug(2, "ERROR: Expected password prompt") + local err = brute.Error:new( "Expected password prompt" ) + err:setRetry( true ) + return false, err + end + + status, err = self.socket:send(password .. "\r") + status, data = self.socket:receive() + if (not(status)) then + local err = brute.Error:new("Failed to read response from server") + err:setRetry( true ) + return false, err + end + + status, data = self.socket:receive() + if (not(status)) then + local err = brute.Error:new("Failed to read response from server") + err:setRetry( true ) + return false, err + end + + if ( data:match("[Pp]assword") or data:match("[Ii]ncorrect") ) then + return false, brute.Error:new( "Incorrect password" ) + end + + return true, brute.Account:new(username, password, creds.State.VALID) + end, + + disconnect = function(self) + return self.socket:close() + end, +} + +action = function(host, port) + + if ( not(nmap.is_privileged()) ) then + return "\n ERROR: rlogin-brute needs Nmap to be run in privileged mode" + end + + local options = { + timeout = stdnse.get_script_args("rlogin-brute.timeout") + } + + options.timeout = options.timeout and + tonumber(options.timeout) * 1000 or + 10000 + + local engine = brute.Engine:new(Driver, host, port, options) + engine.options.script_name = SCRIPT_NAME + status, result = engine:start() + return result +end diff --git a/scripts/script.db b/scripts/script.db index 5b2d332fe..7eb53992f 100644 --- a/scripts/script.db +++ b/scripts/script.db @@ -190,6 +190,8 @@ Entry { filename = "quake3-info.nse", categories = { "default", "discovery", "sa Entry { filename = "quake3-master-getservers.nse", categories = { "default", "discovery", "safe", } } Entry { filename = "realvnc-auth-bypass.nse", categories = { "auth", "default", "safe", } } Entry { filename = "resolveall.nse", categories = { "discovery", "safe", } } +Entry { filename = "rexec-brute.nse", categories = { "brute", "intrusive", } } +Entry { filename = "rlogin-brute.nse", categories = { "brute", "intrusive", } } Entry { filename = "rmi-dumpregistry.nse", categories = { "default", "discovery", "safe", } } Entry { filename = "rpcinfo.nse", categories = { "default", "discovery", "safe", } } Entry { filename = "rtsp-methods.nse", categories = { "default", "safe", } }