mirror of
https://github.com/nmap/nmap.git
synced 2025-12-07 13:11:28 +00:00
New script fingerprint-strings
This commit is contained in:
@@ -1,5 +1,8 @@
|
||||
# Nmap Changelog ($Id$); -*-text-*-
|
||||
|
||||
o [NSE] Script fingerprint-strings will print the ASCII strings it finds in the
|
||||
service fingerprints that Nmap shows for unidentified services. [Daniel Miller]
|
||||
|
||||
o [GH#505] Updated Russian translation of Zenmap by Alexander Kozlov.
|
||||
|
||||
o [NSE][GH#588] Fix a crash in smb.lua when using smb-ls due to a
|
||||
|
||||
@@ -12,6 +12,7 @@ local stdnse = require "stdnse"
|
||||
local pairs = pairs
|
||||
local string = require "string"
|
||||
local tonumber = tonumber
|
||||
local rawset = rawset
|
||||
|
||||
_ENV = {}
|
||||
|
||||
@@ -142,12 +143,26 @@ do
|
||||
}
|
||||
|
||||
--- Turn the service fingerprint reply to a probe into a binary blob
|
||||
--@param fp the <code>port.version.service_fp</code> provided by the NSE API.
|
||||
--@param probe the probe name to match, e.g. GetRequest, TLSSessionReq, etc.
|
||||
--@return the raw probe response received to that probe, or nil if there was no response.
|
||||
function get_response (fp, probe)
|
||||
fp = string.gsub(fp, "\nSF:", "")
|
||||
local i, e = string.find(fp, string.format("%s,%%x+,", probe))
|
||||
if i == nil then return nil end
|
||||
return unescape:match(getquote:match(fp, e+1))
|
||||
end
|
||||
|
||||
local svfp_parser = lpeg.P ({
|
||||
anywhere("%r(") * lpeg.Cf(lpeg.Ct("") * (lpeg.V "probematch" * lpeg.P(")%r(")^-1)^1, rawset),
|
||||
probematch = lpeg.Cg(lpeg.C((lpeg.P(1) - ",")^1) * "," * (lpeg.R("09") + lpeg.R("AF"))^1 * "," * lpeg.Cs(getquote/function(q) return unescape:match(q) end)),
|
||||
})
|
||||
--- Get the service fingerprint reply to a probe into a binary blob
|
||||
--@param fp the <code>port.version.service_fp</code> provided by the NSE API.
|
||||
function parse_fp (fp)
|
||||
fp = string.gsub(fp, "\nSF:", "")
|
||||
return svfp_parser:match(fp)
|
||||
end
|
||||
end
|
||||
|
||||
return _ENV
|
||||
|
||||
120
scripts/fingerprint-strings.nse
Normal file
120
scripts/fingerprint-strings.nse
Normal file
@@ -0,0 +1,120 @@
|
||||
local stdnse = require "stdnse"
|
||||
local nmap = require "nmap"
|
||||
local lpeg = require "lpeg"
|
||||
local U = require "lpeg-utility"
|
||||
local table = require "table"
|
||||
|
||||
description = [[
|
||||
Prints the readable strings from service fingerprints of unknown services.
|
||||
]]
|
||||
|
||||
---
|
||||
--@output
|
||||
--| fingerprint-strings:
|
||||
--| DNSStatusRequest, GenericLines, LANDesk-RC, TLSSessionReq:
|
||||
--| bobo
|
||||
--| bobobo
|
||||
--| GetRequest, HTTPOptions, LPDString, NULL, RTSPRequest, giop, oracle-tns:
|
||||
--| bobobo
|
||||
--| Help, LDAPSearchReq, TerminalServer:
|
||||
--| bobobo
|
||||
--| bobobo
|
||||
--| Kerberos, NotesRPC, SIPOptions:
|
||||
--| bobo
|
||||
--| LDAPBindReq:
|
||||
--| bobobo
|
||||
--| bobo
|
||||
--| bobobo
|
||||
--| SSLSessionReq, SSLv23SessionReq:
|
||||
--| bobo
|
||||
--| bobobo
|
||||
--| bobo
|
||||
--| afp:
|
||||
--| bobo
|
||||
--|_ bobo
|
||||
--
|
||||
--@args fingerprint-strings.n The number of printable ASCII characters required to make up a "string" (Default: 4)
|
||||
|
||||
author = "Daniel Miller"
|
||||
categories = {"version"}
|
||||
|
||||
portrule = function (host, port)
|
||||
-- Run for any port that has a service fingerprint indicating an unknown service
|
||||
return port.version and port.version.service_fp
|
||||
end
|
||||
|
||||
-- Create a table if necessary and append to it
|
||||
local function safe_append (t, v)
|
||||
if t then
|
||||
t[#t+1] = v
|
||||
else
|
||||
t = {v}
|
||||
end
|
||||
return t
|
||||
end
|
||||
|
||||
-- Extract strings of length n or greater.
|
||||
local function strings (blob, n)
|
||||
local pat = lpeg.P {
|
||||
(lpeg.V "plain" + lpeg.V "skip")^1,
|
||||
-- Collect long-enough string of printable and space characters
|
||||
plain = (lpeg.R "\x21\x7e" + lpeg.V "space")^n,
|
||||
-- Collapse white space
|
||||
space = (lpeg.S " \t"^1)/" ",
|
||||
-- Skip anything else
|
||||
skip = ((lpeg.R "\x21\x7e"^-(n-1) * (lpeg.R "\0 " + lpeg.R "\x7f\xff")^1)^1)/"\n ",
|
||||
}
|
||||
return lpeg.match(lpeg.Cs(pat), blob)
|
||||
end
|
||||
|
||||
action = function(host, port)
|
||||
-- Get the table of probe responses
|
||||
local responses = U.parse_fp(port.version.service_fp)
|
||||
-- extract the probe names
|
||||
local probes = stdnse.keys(responses)
|
||||
-- If there were no probes (WEIRD!) we're done.
|
||||
if #probes <= 0 then
|
||||
return nil
|
||||
end
|
||||
|
||||
local min = stdnse.get_script_args(SCRIPT_NAME .. ".n") or 4
|
||||
|
||||
-- Ensure probes show up in the same order every time
|
||||
table.sort(probes)
|
||||
local invert = {}
|
||||
for i=1, #probes do
|
||||
-- Extract the strings from this probe
|
||||
local plain = strings(responses[probes[i]], min)
|
||||
if plain then
|
||||
stdnse.debug1("%s:>>>%s<<<", probes[i], plain)
|
||||
-- rearrange some whitespace to look nice
|
||||
plain = plain:gsub("^[\n ]*", "\n "):gsub("[\n ]+$", "")
|
||||
-- Gather all the probes that had this same set of strings.
|
||||
if plain ~= "" then
|
||||
invert[plain] = safe_append(invert[plain], probes[i])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- If none of the probes had sufficiently long strings, then we're done.
|
||||
if not next(invert) then
|
||||
return nil
|
||||
end
|
||||
|
||||
-- Now reverse the representation so that strings are listed under probes
|
||||
local labels = {}
|
||||
local lookup = {}
|
||||
for plain, plist in pairs(invert) do
|
||||
local label = table.concat(plist, ", ")
|
||||
labels[#labels+1] = label
|
||||
lookup[label] = plain
|
||||
end
|
||||
-- Always keep sorted order!
|
||||
table.sort(labels)
|
||||
local out = stdnse.output_table()
|
||||
for i=1, #labels do
|
||||
out[labels[i]] = lookup[labels[i]]
|
||||
end
|
||||
-- XML output will not be very useful because this is intended for users eyes only.
|
||||
return out
|
||||
end
|
||||
@@ -111,6 +111,7 @@ Entry { filename = "epmd-info.nse", categories = { "default", "discovery", "safe
|
||||
Entry { filename = "eppc-enum-processes.nse", categories = { "discovery", "safe", } }
|
||||
Entry { filename = "fcrdns.nse", categories = { "discovery", "safe", } }
|
||||
Entry { filename = "finger.nse", categories = { "default", "discovery", "safe", } }
|
||||
Entry { filename = "fingerprint-strings.nse", categories = { "version", } }
|
||||
Entry { filename = "firewalk.nse", categories = { "discovery", "safe", } }
|
||||
Entry { filename = "firewall-bypass.nse", categories = { "intrusive", "vuln", } }
|
||||
Entry { filename = "flume-master-info.nse", categories = { "default", "discovery", "safe", } }
|
||||
|
||||
Reference in New Issue
Block a user