mirror of
https://github.com/nmap/nmap.git
synced 2025-12-09 22:21:29 +00:00
Run ventrilo-info only once for TCP and UDP.
Patch by Marin Maržić. http://seclists.org/nmap-dev/2013/q2/413.
This commit is contained in:
@@ -31,7 +31,6 @@ Original reversing of the protocol was done by Luigi Auriemma
|
||||
(http://aluigi.altervista.org/papers.htm#ventrilo).
|
||||
]]
|
||||
|
||||
---
|
||||
-- @usage
|
||||
-- nmap -sV <target>
|
||||
-- @usage
|
||||
@@ -151,7 +150,7 @@ Original reversing of the protocol was done by Luigi Auriemma
|
||||
-- <elem>COMM</elem>
|
||||
-- </table>
|
||||
|
||||
author = "Marin Marzic"
|
||||
author = "Marin Maržić"
|
||||
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
|
||||
categories = { "default", "discovery", "safe", "version" }
|
||||
|
||||
@@ -313,11 +312,11 @@ local dec_data = function(str, len, key)
|
||||
-- skip the header (first 20 bytes)
|
||||
local data = { string.byte(str, 21, 20 + len) }
|
||||
|
||||
local a1 = bit.band(key, 0xFF)
|
||||
a1 = bit.band(key, 0xFF)
|
||||
if a1 == 0 then
|
||||
return table.concat(data)
|
||||
end
|
||||
local a2 = bit.rshift(key, 8)
|
||||
a2 = bit.rshift(key, 8)
|
||||
|
||||
for i = 1,len do
|
||||
data[i] = bit.band(data[i] - (crypt_data[a2 + 1] + ((i - 1) % 72)), 0xFF)
|
||||
@@ -343,7 +342,7 @@ end
|
||||
-- Calculates the CRC checksum used for checking the integrity of the received
|
||||
-- status response data segment.
|
||||
-- @param data data to calculate the checksum of
|
||||
-- @return 2byte CRC checksum as seen in Ventrilo UDP status response headers
|
||||
-- @return 2 byte CRC checksum as seen in Ventrilo UDP status headers
|
||||
local crc = function(data)
|
||||
local sum = 0
|
||||
for i = 1,#data do
|
||||
@@ -525,20 +524,30 @@ end
|
||||
portrule = shortport.version_port_or_service({3784}, "ventrilo", {"tcp", "udp"})
|
||||
|
||||
action = function(host, port)
|
||||
local mutex = nmap.mutex("ventrilo-info:" .. host.ip .. ":" .. port.number)
|
||||
mutex("lock")
|
||||
|
||||
if host.registry["ventrilo-info"] == nil then
|
||||
host.registry["ventrilo-info"] = {}
|
||||
end
|
||||
-- Maybe the script already ran for this port number on another protocol
|
||||
local r = host.registry["ventrilo-info"][port.number]
|
||||
if r == nil then
|
||||
r = {}
|
||||
host.registry["ventrilo-info"][port.number] = r
|
||||
|
||||
local socket = nmap.new_socket()
|
||||
socket:set_timeout(2000)
|
||||
|
||||
local catch = function()
|
||||
local cleanup = function()
|
||||
socket:close()
|
||||
mutex("done")
|
||||
end
|
||||
local try = nmap.new_try(catch)
|
||||
local try = nmap.new_try(cleanup)
|
||||
|
||||
try(socket:connect(host.ip, port.number, "udp"))
|
||||
local udpport = { number = port.number, protocol = "udp" }
|
||||
try(socket:connect(host.ip, udpport))
|
||||
|
||||
local fulldata = {}
|
||||
local curlen = 0
|
||||
local head_crc_sum
|
||||
repeat
|
||||
local status, response
|
||||
-- try a couple of times on timeout, the service seems to not
|
||||
-- respond if multiple requests come within a short timeframe
|
||||
@@ -546,37 +555,71 @@ action = function(host, port)
|
||||
try(socket:send(static_probe_payload))
|
||||
status, response = socket:receive()
|
||||
if status then
|
||||
if port.protocol == "udp" then
|
||||
nmap.set_port_state(host, port, "open")
|
||||
end
|
||||
nmap.set_port_state(host, udpport, "open")
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if not status then
|
||||
-- 3 timeouts, no response
|
||||
cleanup()
|
||||
return
|
||||
end
|
||||
|
||||
-- received the first packet, process it and others if they come
|
||||
local fulldata = {}
|
||||
local fulldatalen = 0
|
||||
local curlen = 0
|
||||
local head_crc_sum
|
||||
while true do
|
||||
-- decrypt received header and extract relevant information
|
||||
local id, len, totlen, pck, totpck, key, crc_sum = dec_head(response)
|
||||
head_crc_sum = crc_sum
|
||||
|
||||
if id == static_probe_id then
|
||||
curlen = curlen + len
|
||||
head_crc_sum = crc_sum
|
||||
|
||||
-- check for an invalid response
|
||||
if #response < 20 or pck >= totpck or
|
||||
len > 492 or curlen > totlen then
|
||||
stdnse.print_debug("Invalid response, aborting script.")
|
||||
stdnse.print_debug("Invalid response. Aborting script.")
|
||||
cleanup()
|
||||
return
|
||||
end
|
||||
|
||||
-- keep track of the length of fulldata (# isn't applicable)
|
||||
if fulldata[pck + 1] == nil then
|
||||
fulldatalen = fulldatalen + 1
|
||||
end
|
||||
-- accumulate UDP packets that may not necessarily come in proper
|
||||
-- order; arrange them by packet id
|
||||
fulldata[pck + 1] = dec_data(response, len, key)
|
||||
end
|
||||
until #fulldata == totpck and curlen == totlen
|
||||
|
||||
-- check for invalid states in communication
|
||||
if (fulldatalen > totpck) or (curlen > totlen)
|
||||
or (fulldatalen == totpck and curlen ~= totlen)
|
||||
or (curlen == totlen and fulldatalen ~= totpck) then
|
||||
stdnse.print_debug("Invalid state (fulldatalen = " .. fulldatalen ..
|
||||
"; totpck = " .. totpck .. "; curlen = " .. curlen ..
|
||||
"; totlen = " .. totlen .. "). Aborting script.")
|
||||
cleanup()
|
||||
return
|
||||
end
|
||||
|
||||
-- check for valid end of communication
|
||||
if fulldatalen == totpck and curlen == totlen then
|
||||
break
|
||||
end
|
||||
|
||||
-- receive another packet
|
||||
status, response = socket:receive()
|
||||
if not status then
|
||||
stdnse.print_debug("Response packets stopped coming midway. Aborting script.")
|
||||
cleanup()
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
socket:close()
|
||||
|
||||
-- concatenate received data into a single string for further use
|
||||
@@ -586,28 +629,37 @@ action = function(host, port)
|
||||
local fulldata_crc_sum = crc(fulldata_str)
|
||||
if fulldata_crc_sum ~= head_crc_sum then
|
||||
stdnse.print_debug("Invalid CRC sum, received = %04X, calculated = %04X", head_crc_sum, fulldata_crc_sum)
|
||||
cleanup()
|
||||
return
|
||||
end
|
||||
|
||||
-- parse the received data string into an output table
|
||||
local info = o_table(fulldata_str)
|
||||
r.info = o_table(fulldata_str)
|
||||
end
|
||||
|
||||
mutex("done")
|
||||
|
||||
-- If the registry is empty the port was probed but Ventrilo wasn't detected
|
||||
if next(r) == nil then
|
||||
return
|
||||
end
|
||||
|
||||
port.version.name = "ventrilo"
|
||||
port.version.name_confidence = 10
|
||||
port.version.product = "Ventrilo"
|
||||
port.version.version = info.version
|
||||
port.version.ostype = info.platform
|
||||
port.version.extrainfo = "; name: ".. info.name
|
||||
port.version.version = r.info.version
|
||||
port.version.ostype = r.info.platform
|
||||
port.version.extrainfo = "; name: ".. r.info.name
|
||||
if port.protocol == "tcp" then
|
||||
port.version.extrainfo = "voice port" .. port.version.extrainfo
|
||||
else
|
||||
port.version.extrainfo = "status port" .. port.version.extrainfo
|
||||
end
|
||||
port.version.extrainfo = port.version.extrainfo .. "; uptime: " .. uptime_str(info.uptime)
|
||||
port.version.extrainfo = port.version.extrainfo .. "; auth: " .. auth_str(info.auth)
|
||||
port.version.extrainfo = port.version.extrainfo .. "; uptime: " .. uptime_str(r.info.uptime)
|
||||
port.version.extrainfo = port.version.extrainfo .. "; auth: " .. auth_str(r.info.auth)
|
||||
|
||||
nmap.set_port_version(host, port, "hardmatched")
|
||||
|
||||
-- an output table for XML output and a custom string for normal output
|
||||
return info, o_str(info)
|
||||
return r.info, o_str(r.info)
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user