diff options
Diffstat (limited to 'scripts/nping-brute.nse')
-rw-r--r-- | scripts/nping-brute.nse | 151 |
1 files changed, 151 insertions, 0 deletions
diff --git a/scripts/nping-brute.nse b/scripts/nping-brute.nse new file mode 100644 index 0000000..89577f9 --- /dev/null +++ b/scripts/nping-brute.nse @@ -0,0 +1,151 @@ +local brute = require "brute" +local creds = require "creds" +local nmap = require "nmap" +local shortport = require "shortport" +local stdnse = require "stdnse" +local string = require "string" + +local openssl = stdnse.silent_require "openssl" + +description = [[ +Performs brute force password auditing against an Nping Echo service. + +See https://nmap.org/book/nping-man-echo-mode.html for Echo Mode +documentation. +]] + +--- +-- @usage +-- nmap -p 9929 --script nping-brute <target> +-- +-- @output +-- 9929/tcp open nping-echo +-- | nping-brute: +-- | Accounts +-- | 123abc => Valid credentials +-- | Statistics +-- |_ Perfomed 204 guesses in 204 seconds, average tps: 1 + +author = "Toni Ruottu" +license = "Same as Nmap--See https://nmap.org/book/man-legal.html" +categories = {"brute", "intrusive"} + + +portrule = shortport.port_or_service(9929, "nping-echo") + +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 + return string.unpack("c16", h) + end, + + getservernonce = function(self, serverhs) + local offset = 63 -- 63 bytes of header before the nonce + return serverhs:sub(offset+1, offset+4) + end, + + chsbody = function(self) + local IP4 = "\x04" + local IP6 = "\x06" + local family = IP6 + local target = self.host.bin_ip + if #target == 4 then + target = target .. ("\0"):rep(12) + family = IP4 + end + return target .. family .. ("\0"):rep(15) + 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 = openssl.rand_bytes(4) + local cnonce = openssl.rand_bytes(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 = string.unpack("c16", cnonce) + local plain = self:chsbody() + local crypted = openssl.encrypt(self.AES_128_CBC, enckey, iv, plain) + local head = string.pack(">BB I2 c4 I4 x4", 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 = brute.new_socket() + return self.socket:connect(self.host, self.port) + end, + + login = function(self, _, password) + if self:testpass(password) then + return true, creds.Account:new("", password, creds.State.VALID) + end + return false, brute.Error:new("Incorrect password") + end, + + disconnect = function(self) + return self.socket:close() + end, +} + +action = function(host, port) + local engine = brute.Engine:new(Driver, host, port) + engine.options.firstonly = true + engine.options:setOption("passonly", true) + engine.options.script_name = SCRIPT_NAME + local status, result = engine:start() + return result +end |