1
0
mirror of https://github.com/nmap/nmap.git synced 2025-12-06 04:31:29 +00:00

Support ATEN/Supermicro KVM VNC

This commit is contained in:
dmiller
2017-03-15 19:39:34 +00:00
parent c98a547104
commit d244738246
2 changed files with 104 additions and 36 deletions

View File

@@ -370,25 +370,57 @@ VNC = {
return true return true
end, end,
handshake_aten = function(self, buffer)
-- buffer is 24 bytes, currently unused, no idea what it's for.
if #buffer ~= 24 then
return false
end
self.aten = buffer
return true
end,
login_aten = function(self, username, password)
self.socket:send(username .. ("\0"):rep(24 - #username) .. password .. ("\0"):rep(24 - #password))
return self:check_auth_result()
end,
handshake_tight = function(self) handshake_tight = function(self)
-- TightVNC security type
-- https://vncdotool.readthedocs.org/en/0.8.0/rfbproto.html#tight-security-type -- https://vncdotool.readthedocs.org/en/0.8.0/rfbproto.html#tight-security-type
local status, buf = self.socket:receive_buf(match.numbytes(4), true) -- Sometimes also ATEN KVM VNC:
-- https://github.com/thefloweringash/chicken-aten-ikvm
local status, buf = self.socket:receive_bytes(4)
if not status then if not status then
return false, "Failed to get number of tunnels" return false, "Failed to get number of tunnels"
end end
-- If it's ATEN, it sends 24 bytes right away. Need to try parsing these
-- in case it's TightVNC instead, though.
local aten = #buf == 24 and buf
local pos, ntunnels = bin.unpack(">I", buf) local pos, ntunnels = bin.unpack(">I", buf)
-- reasonable limits: ATEN might send a huge number here
if ntunnels > 0x10000 then
return self:handshake_aten(aten)
end
local tight = { local tight = {
tunnels = {}, tunnels = {},
types = {} types = {}
} }
if ntunnels > 0 then if ntunnels > 0 then
status, buf = self.socket:receive_buf(match.numbytes(16 * ntunnels), true) local have = #buf - pos + 1
if not status then if have < 16 * ntunnels then
return false, "Failed to get list of tunnels" local newbuf
status, newbuf = self.socket:receive_bytes(16 * ntunnels - have)
if not status then
if aten then
return self:handshake_aten(aten)
end
return false, "Failed to get list of tunnels"
end
buf = buf .. newbuf
aten = false -- we must have read something beyond 24 bytes
end end
local have_none_tunnel = false local have_none_tunnel = false
pos = 1
for i=1, ntunnels do for i=1, ntunnels do
local tunnel = {} local tunnel = {}
pos, tunnel.code, tunnel.vendor, tunnel.signature = bin.unpack(">IA4A8", buf, pos) pos, tunnel.code, tunnel.vendor, tunnel.signature = bin.unpack(">IA4A8", buf, pos)
@@ -398,6 +430,7 @@ VNC = {
tight.tunnels[#tight.tunnels+1] = tunnel tight.tunnels[#tight.tunnels+1] = tunnel
end end
-- at this point, might still be ATEN with a first byte of 1, but chances are it's Tight
if have_none_tunnel then if have_none_tunnel then
-- Try the "NOTUNNEL" tunnel, for simplicity, if it's available. -- Try the "NOTUNNEL" tunnel, for simplicity, if it's available.
self.socket:send(bin.pack(">I", 0)) self.socket:send(bin.pack(">I", 0))
@@ -407,18 +440,40 @@ VNC = {
end end
end end
status, buf = self.socket:receive_buf(match.numbytes(4), true) local have = #buf - pos + 1
if not status then if have < 4 then
return false, "Failed to get number of Tight auth types" local newbuf
end status, newbuf = self.socket:receive_bytes(4 - have)
local pos, nauth = bin.unpack(">I", buf)
if nauth > 0 then
status, buf = self.socket:receive_buf(match.numbytes(16 * nauth), true)
if not status then if not status then
return false, "Failed to get list of Tight auth types" if aten then
return self:handshake_aten(aten)
end
return false, "Failed to get number of Tight auth types"
end
buf = buf .. newbuf
if #buf > 24 then aten = false end
end
local nauth
pos, nauth = bin.unpack(">I", buf, pos)
-- reasonable limits: ATEN might send a huge number here
if nauth > 0x10000 then
return self:handshake_aten(aten)
end
if nauth > 0 then
have = #buf - pos + 1
if have < 16 * nauth then
local newbuf
status, newbuf = self.socket:receive_bytes(16 * nauth - have)
if not status then
if aten then
return self:handshake_aten(aten)
end
return false, "Failed to get list of Tight auth types"
end
buf = buf .. newbuf
if #buf > 24 then aten = false end
end end
pos = 1
for i=1, nauth do for i=1, nauth do
local auth = {} local auth = {}
pos, auth.code, auth.vendor, auth.signature = bin.unpack(">IA4A8", buf, pos) pos, auth.code, auth.vendor, auth.signature = bin.unpack(">IA4A8", buf, pos)
@@ -426,6 +481,11 @@ VNC = {
end end
end end
if aten and pos < 24 then
-- server sent 24 bytes but we could only parse some of them. Probably ATEN KVM
return self:handshake_aten(aten)
end
self.tight = tight self.tight = tight
return true return true
@@ -437,6 +497,10 @@ VNC = {
return status, err return status, err
end end
if self.aten then
return self:login_aten(username, password)
end
if #self.tight.types == 0 then if #self.tight.types == 0 then
-- nothing further, no auth -- nothing further, no auth
return true return true

View File

@@ -101,32 +101,36 @@ action = function(host, port)
if not status then if not status then
stdnse.debug1("Failed to handshake Tight: %s", data) stdnse.debug1("Failed to handshake Tight: %s", data)
else else
local mt = { if v.aten then
__tostring = function(t) result["Tight auth"] = "ATEN KVM VNC"
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 else
local subtypes = {} local mt = {
for _, t in ipairs(v.tight.types) do __tostring = function(t)
if t.code == 1 then return string.format("%s %s (%d)", t.vendor, t.signature, t.code)
none_auth = true
end end
}
local tunnels = {}
for _, t in ipairs(v.tight.tunnels) do
setmetatable(t, mt) setmetatable(t, mt)
subtypes[#subtypes+1] = t 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
result["Tight auth subtypes"] = subtypes
end end
end end
-- Reset the connection for further tests -- Reset the connection for further tests