diff --git a/CHANGELOG b/CHANGELOG index db23b28e1..a0826c789 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,8 @@ # Nmap Changelog ($Id$); -*-text-*- +o [NSE] Added nping-brute.nse by Toni Ruottu, which tries to guess + the passphrase of an Nping Echo server. + o Removed the -sR (RPC scan) option--it is now an alias for -sV (version scan), which always does RPC scan when appropriate. diff --git a/scripts/nping-brute.nse b/scripts/nping-brute.nse new file mode 100644 index 000000000..1754400a8 --- /dev/null +++ b/scripts/nping-brute.nse @@ -0,0 +1,186 @@ +description = [[ +Performs brute force password auditing against the nping echo service. +]] + +--- +-- @usage +-- nmap -p 9929 --script nping-brute +-- +-- @output +-- 9929/tcp open nping-echo +-- | nping-brute: +-- | Accounts +-- | 123abc => Login correct +-- | Statistics +-- |_ Perfomed 204 guesses in 204 seconds, average tps: 1 + +author = "Toni Ruottu" +license = "Same as Nmap--See http://nmap.org/book/man-legal.html" +categories = {"auth", "intrusive"} + +require("bin") +require("nmap") +require("brute") +require("stdnse") +require("openssl") +require("shortport") + +portrule = shortport.port_or_service(9929, "nping-echo") + +local function ip6tobin(address) + local sides = stdnse.strsplit("::", address) + local head = stdnse.strsplit(":", sides[1]) + if #sides > 1 then + local tail = stdnse.strsplit(":", sides[2]) + local missing = 8 - #head - #tail + while missing > 0 do + table.insert(head, "0") + missing = missing - 1 + end + for _, e in ipairs(tail) do + table.insert(head, e) + end + end + local binaddress = "" + for _, e in ipairs(head) do + local part = "" + local zeros = 4 - #e + while zeros > 0 do + part = part .. "0" + zeros = zeros - 1 + end + part = part .. e + binaddress = binaddress .. bin.pack("S", tonumber(part, 16)) + end + return binaddress +end + +local function randombytes(x) + local bytes = "" + for i = 1, x do + bytes = bytes .. bin.pack("C", math.random(0x00, 0xff)) + end + return bytes +end + +local function readmessage(socket, length) + local msg = "" + while #msg < length do + local status, tmp = socket:receive_bytes(1) + if not status then + return nil + end + msg = msg .. tmp + end + return msg +end + +Driver = +{ + NEP_VERSION = 0x01, + AES_128_CBC = "aes-128-cbc", + SHA256 = "sha256", + + new = function(self, host, port) + local o = {} + setmetatable(o, self) + self.__index = self + o.host = host + o.port = port + return o + end, + + nepkey = function(self, password, nonce, typeid) + local seed = password .. nonce .. typeid + local h = openssl.digest(self.SHA256, seed) + for i = 1, 1000 do + h = openssl.digest(self.SHA256, h) + end + local _, key = bin.unpack("A16", h) + return key + end, + + getservernonce = function(self, serverhs) + local parts = {bin.unpack("CC>S>I>Ix4A32x15A32", serverhs)} + return parts[7] + end, + + chsbody = function(self) + local IP4 = 0x04 + local IP6 = 0x06 + local target = self.host.bin_ip + if target then + return bin.pack("Ax12Cx15", target, IP4) + end + target = ip6tobin(self.host.ip) + return bin.pack("ACx15", target, IP6) + + end, + + clienths = function(self, snonce, password) + local NEP_HANDSHAKE_CLIENT = 0x02 + local NEP_HANDSHAKE_CLIENT_LEN = 36 + local NEP_CLIENT_CIPHER_ID = "NEPkeyforCiphertextClient2Server" + local NEP_CLIENT_MAC_ID = "NEPkeyforMACClient2Server" + + local now = nmap.clock() + local seqb = randombytes(4) + local cnonce = randombytes(32) + local nonce = snonce .. cnonce + local enckey = self:nepkey(password, nonce, NEP_CLIENT_CIPHER_ID) + local mackey = self:nepkey(password, nonce, NEP_CLIENT_MAC_ID) + local _, iv = bin.unpack("A16", cnonce) + local plain = self:chsbody() + local crypted = openssl.encrypt(self.AES_128_CBC, enckey, iv, plain) + local head = bin.pack("CC>SA>Ix4A", self.NEP_VERSION, NEP_HANDSHAKE_CLIENT, NEP_HANDSHAKE_CLIENT_LEN, seqb, now, nonce) + local mac = openssl.hmac(self.SHA256, mackey, head .. plain) + + return head .. crypted .. mac + end, + + testpass = function(self, password) + local SERVERHS_LEN = 96 + local FINALHS_LEN = 112 + local serverhs = readmessage(self.socket, SERVERHS_LEN) + if serverhs == nil then + return false + end + local snonce = self:getservernonce(serverhs) + local response = self:clienths(snonce, password) + self.socket:send(response) + local finalhs = readmessage(self.socket, FINALHS_LEN) + if finalhs == nil then + return false + end + return true + end, + + connect = function(self) + self.socket = nmap.new_socket() + return self.socket:connect(self.host, self.port) + end, + + login = function(self, _, password) + if self:testpass(password) then + return true, brute.Account:new("", password, "OPEN") + end + return false, brute.Error:new("Incorrect password") + end, + + disconnect = function(self) + return self.socket:close() + end, + + check = function(self) --deprecated + return true + end, +} + +action = function(host, port) + math.randomseed(nmap.clock()) + local engine = brute.Engine:new(Driver, host, port) + engine.options.firstonly = true + engine.options:setOption("passonly", true) + local status, result = engine:start() + return result +end diff --git a/scripts/script.db b/scripts/script.db index 2164c91d3..1f8185023 100644 --- a/scripts/script.db +++ b/scripts/script.db @@ -111,6 +111,7 @@ Entry { filename = "netbus-version.nse", categories = { "version", } } Entry { filename = "nfs-ls.nse", categories = { "discovery", "safe", } } Entry { filename = "nfs-showmount.nse", categories = { "discovery", "safe", } } Entry { filename = "nfs-statfs.nse", categories = { "discovery", "safe", } } +Entry { filename = "nping-brute.nse", categories = { "auth", "intrusive", } } Entry { filename = "nrpe-enum.nse", categories = { "discovery", "intrusive", } } Entry { filename = "ntp-info.nse", categories = { "default", "discovery", "safe", } } Entry { filename = "ntp-monlist.nse", categories = { "discovery", "intrusive", } }