Обещал скриптик, который бы чекал IPMI даже если нет его открытых портов.
Код:
local creds = require "creds"
local nmap = require "nmap"
local shortport = require "shortport"
local stdnse = require "stdnse"
local string = require "string"
local tab = require "table"
local sslcert = require "sslcert"
local tls = require "tls"
description = [[Check ipmi by http headers and ssl certificates.]]
author = "Morozov Alexey"
license = "Same as Nmap--See https://nmap.org/book/man-legal.html"
categories = {"discovery", "same"}
local comm = require "comm"
require "shortport"
portrule = function(host, port)
return ((port.number == 80 or port.number == 443 or port.number == 5901 or port.number == 5900 or port.number == 623)
and port.protocol == "tcp" and port.state == "open") or (port.number == 623 and port.protocol == "udp" and port.state == "open")
end
local NON_VERBOSE_FIELDS = { "commonName", "organizationName",
"stateOrProvinceName", "countryName" }
function table_find(t, value)
local i, v
for i, v in ipairs(t) do
if v == value then
return i
end
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(date)
end
end
local function maybe_decode(str)
-- If length is not even, then return as-is
if #str 0 and str:byte(2) == 0 then
-- little-endian UTF-16
return unicode.transcode(str, unicode.utf16_dec, unicode.utf8_enc, false, nil)
elseif str:byte(1) == 0 and str:byte(2) > 0 then
-- big-endian UTF-16
return unicode.transcode(str, unicode.utf16_dec, unicode.utf8_enc, true, nil)
else
return str
end
end
function stringify_name(name)
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, maybe_decode(v) or '')
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, maybe_decode(v) or '')
end
end
end
return stdnse.strjoin("/", fields)
end
local function output_str(cert)
local lines = {}
lines[#lines + 1] = "Subject: " .. stringify_name(cert.subject)
if cert.extensions then
for _, e in ipairs(cert.extensions) do
if e.name == "X509v3 Subject Alternative Name" then
lines[#lines + 1] = "Subject Alternative Name: " .. e.value
break
end
end
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
lines[#lines + 1] = "Signature Algorithm: " .. cert.sig_algorithm
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)
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)
end
action = function(host, port)
local resports = {}
if (port.number == 623 or port.number == 5900 or port.number == 5901) or (port.number == 623 and port.protocol == "udp") then
port.version.name = "ipmi"
nmap.set_port_version(host, port)
-- return
end
local domains = {}
-- domains['name'] = 'IPMI_DETECT_BY_PORT'
if (port.number == 80) then
local status, result = comm.exchange(host, port, "GET / HTTP/1.0\r\n\r\n", {bytes=260, proto=port.protocol})
if (status) then
-- print(result)
local goAheads = string.find(result, 'GoAhead-Webs', 1, true)
print(goAheads)
if (goAheads ~= nil) then
table.insert(domains, 'DETECTED IPMI')
end
end
end
if (port.number == 443) then
-- host.targetname = tls.servername(host)
local status, cert = sslcert.getCertificate(host, port)
if (status) then
local res_cert = output_str(cert)
local certIpmi = string.find(res_cert, 'IPMI', 1, true)
local certAmi = string.find(res_cert, 'American Megatrends', 1, true)
if (certIpmi ~= nil) or (certAmi ~= nil) then
table.insert(domains, 'DETECTED IPMI')
end
end
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"
--
-- nmap.set_port_version(host, port)
-- return
return stdnse.format_output(true, domains)
end