diff --git a/CHANGELOG b/CHANGELOG index 004a68816..ce8587f4c 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -3,7 +3,7 @@ o [NSE] Added vnc-title for logging in to VNC servers and grabbing the desktop title, geometry, and color depth. [Daniel Miller] -o [NSE] More VNC updates: Support for VeNCrypt auth type, output of +o [NSE] More VNC updates: Support for VeNCrypt and Tight auth types, output of authentication sub-types in vnc-info, and all zero-authentication types are recognized and reported. [Daniel Miller] diff --git a/nselib/vnc.lua b/nselib/vnc.lua index 24b510738..e8226a9be 100644 --- a/nselib/vnc.lua +++ b/nselib/vnc.lua @@ -297,6 +297,9 @@ VNC = { elseif self:supportsSecType( VNC.sectypes.VENCRYPT ) then return self:login_vencrypt(username, password) + elseif self:supportsSecType( VNC.sectypes.TIGHT ) then + return self:login_tight(username, password) + else return false, "The server does not support any matching security type" end @@ -358,6 +361,89 @@ VNC = { return true end, + handshake_tight = function(self) + local status = self.socket:send( bin.pack("C", VNC.sectypes.TIGHT) ) + if not status then + return false, "Failed to select TIGHT authentication type" + end + + -- https://vncdotool.readthedocs.org/en/0.8.0/rfbproto.html#tight-security-type + local status, buf = self.socket:receive_buf(match.numbytes(4), true) + if not status then + return false, "Failed to get number of tunnels" + end + local pos, ntunnels = bin.unpack(">I", buf) + status, buf = self.socket:receive_buf(match.numbytes(16 * ntunnels), true) + if not status then + return false, "Failed to get list of tunnels" + end + + pos = 1 + local tight = { + tunnels = {}, + types = {} + } + for i=1, ntunnels do + local tunnel = {} + pos, tunnel.code, tunnel.vendor, tunnel.signature = bin.unpack(">IA4A8", buf, pos) + tight.tunnels[#tight.tunnels+1] = tunnel + end + + if ntunnels > 0 then + -- for now, just return the first one. TODO: choose a supported tunnel type + self.socket:send(bin.pack(">I", tight.tunnels[1].code)) + end + + status, buf = self.socket:receive_buf(match.numbytes(4), true) + if not status then + return false, "Failed to get number of Tight auth types" + end + local pos, nauth = bin.unpack(">I", buf) + status, buf = self.socket:receive_buf(match.numbytes(16 * nauth), true) + if not status then + return false, "Failed to get list of Tight auth types" + end + + pos = 1 + for i=1, nauth do + local auth = {} + pos, auth.code, auth.vendor, auth.signature = bin.unpack(">IA4A8", buf, pos) + tight.types[#tight.types+1] = auth + end + + self.tight = tight + + return true + end, + + login_tight = function(self, username, password) + local status, err = self:handshake_tight() + if not status then + return status, err + end + + self.socket:send("\0\0\0") -- send auth types as int32 + + if #self.tight.types == 0 then + -- nothing further, no auth + return true + end + + -- choose a supported auth type + for _, auth in ipairs({ + {1, "login_none"}, + {2, "login_vncauth"}, + {19, "login_vencrypt"}, + }) do + for _, t in ipairs(self.tight.types) do + if t.code == auth[1] then + return self[auth[2]](self, username, password) + end + end + end + return false, "The server does not support any supported Tight security type" + end, + handshake_tls = function(self) local status = self.socket:send( bin.pack("C", VNC.sectypes.TLS) ) if not status then diff --git a/scripts/vnc-info.nse b/scripts/vnc-info.nse index 41ef5b035..b72cb06d3 100644 --- a/scripts/vnc-info.nse +++ b/scripts/vnc-info.nse @@ -1,5 +1,6 @@ local shortport = require "shortport" local stdnse = require "stdnse" +local string = require "string" local vnc = require "vnc" description = [[ @@ -88,6 +89,48 @@ action = function(host, port) v:disconnect() end + if v:supportsSecType(v.sectypes.TIGHT) then + if not v.socket:get_info() then + -- reconnect if necessary + v:connect() + v:handshake() + end + status, data = v:handshake_tight() + if not status then + stdnse.debug1("Failed to handshake Tight: %s", data) + else + local mt = { + __tostring = function(t) + return string.format("%s %s (%d)", t.vendor, t.signature, t.code) + end + } + local tunnels = {} + for _, t in ipairs(v.tight.tunnels) do + setmetatable(t, mt) + tunnels[#tunnels+1] = t + end + if #tunnels > 0 then + result["Tight auth tunnels"] = tunnels + end + if #v.tight.types == 0 then + none_auth = true + result["Tight auth subtypes"] = {"None"} + else + local subtypes = {} + for _, t in ipairs(v.tight.types) do + if t.code == 1 then + none_auth = true + end + setmetatable(t, mt) + subtypes[#subtypes+1] = t + end + result["Tight auth subtypes"] = subtypes + end + end + -- Reset the connection for further tests + v:disconnect() + end + if v:supportsSecType(v.sectypes.TLS) then if not v.socket:get_info() then -- reconnect if necessary