Skip to content

Commit

Permalink
Update the main script from official nmap repo.
Browse files Browse the repository at this point in the history
Thanks to the guys from nmap for making some adjustments to the script.
Just want to be consistent between repositories.
  • Loading branch information
Ilya Govorkov committed Jun 27, 2019
1 parent f766ed3 commit 4facc43
Showing 1 changed file with 100 additions and 84 deletions.
184 changes: 100 additions & 84 deletions vulners.nse
Original file line number Diff line number Diff line change
Expand Up @@ -2,35 +2,52 @@ description = [[
For each available CPE the script prints out known vulns (links to the correspondent info) and correspondent CVSS scores.
Its work is pretty simple:
- work only when some software version is identified for an open port
- take all the known CPEs for that software (from the standard nmap -sV output)
- make a request to a remote server (vulners.com API) to learn whether any known vulns exist for that CPE
- if no info is found this way - try to get it using the software name alone
- print the obtained info out
* work only when some software version is identified for an open port
* take all the known CPEs for that software (from the standard nmap -sV output)
* make a request to a remote server (vulners.com API) to learn whether any known vulns exist for that CPE
* if no info is found this way, try to get it using the software name alone
* print the obtained info out
NB:
Since the size of the DB with all the vulns is more than 250GB there is no way to use a local db.
So we do make requests to a remote service. Still all the requests contain just two fields - the
Since the size of the DB with all the vulns is more than 250GB there is no way to use a local db.
So we do make requests to a remote service. Still all the requests contain just two fields - the
software name and its version (or CPE), so one can still have the desired privacy.
]]

---
-- @usage
-- @usage
-- nmap -sV --script vulners [--script-args mincvss=<arg_val>] <target>
--
-- @args vulners.mincvss Limit CVEs shown to those with this CVSS score or greater.
--
-- @output
--
-- 53/tcp open domain ISC BIND DNS
-- | vulners:
-- | ISC BIND DNS:
-- | CVE-2012-1667 8.5 https://vulners.com/cve/CVE-2012-1667
-- | CVE-2002-0651 7.5 https://vulners.com/cve/CVE-2002-0651
-- | CVE-2002-0029 7.5 https://vulners.com/cve/CVE-2002-0029
-- | CVE-2015-5986 7.1 https://vulners.com/cve/CVE-2015-5986
-- | CVE-2010-3615 5.0 https://vulners.com/cve/CVE-2010-3615
-- | CVE-2006-0987 5.0 https://vulners.com/cve/CVE-2006-0987
-- | CVE-2014-3214 5.0 https://vulners.com/cve/CVE-2014-3214
-- | CVE-2012-1667 8.5 https://vulners.com/cve/CVE-2012-1667
-- | CVE-2002-0651 7.5 https://vulners.com/cve/CVE-2002-0651
-- | CVE-2002-0029 7.5 https://vulners.com/cve/CVE-2002-0029
-- | CVE-2015-5986 7.1 https://vulners.com/cve/CVE-2015-5986
-- | CVE-2010-3615 5.0 https://vulners.com/cve/CVE-2010-3615
-- | CVE-2006-0987 5.0 https://vulners.com/cve/CVE-2006-0987
-- |_ CVE-2014-3214 5.0 https://vulners.com/cve/CVE-2014-3214
--
-- @xmloutput
-- <table key="cpe:/a:isc:bind:9.8.2rc1">
-- <table>
-- <elem key="is_exploit">false</elem>
-- <elem key="cvss">8.5</elem>
-- <elem key="id">CVE-2012-1667</elem>
-- <elem key="type">cve</elem>
-- </table>
-- <table>
-- <elem key="is_exploit">false</elem>
-- <elem key="cvss">7.8</elem>
-- <elem key="id">CVE-2015-4620</elem>
-- <elem key="type">cve</elem>
-- </table>
-- </table>

author = 'gmedian AT vulners DOT com'
license = "Same as Nmap--See https://nmap.org/book/man-legal.html"
Expand All @@ -41,55 +58,60 @@ local http = require "http"
local json = require "json"
local string = require "string"
local table = require "table"
local nmap = require "nmap"
local stdnse = require "stdnse"

local api_version="1.2"
local mincvss=nmap.registry.args.mincvss and tonumber(nmap.registry.args.mincvss) or 0.0

local mincvss=stdnse.get_script_args("vulners.mincvss")
mincvss = tonumber(mincvss) or 0.0

portrule = function(host, port)
local vers=port.version
return vers ~= nil and vers.version ~= nil
local vers=port.version
return vers ~= nil and vers.version ~= nil
end

local cve_meta = {
__tostring = function(me)
return ("\t%s\t%s\thttps://vulners.com/%s/%s%s"):format(me.id, me.cvss or "", me.type, me.id, me.is_exploit and '\t*EXPLOIT*' or '')
end,
}

---
-- Return a string with all the found cve's and correspondent links
--
-- @param vulns a table with the parsed json response from the vulners server
--
-- @param vulns a table with the parsed json response from the vulners server
--
function make_links(vulns)
local output_str=""
local is_exploit=false
local cvss_score=""
local output = {}

-- NOTE[gmedian]: data.search is a "list" already, so just use table.sort with a custom compare function
-- However, for the future it might be wiser to create a copy rather than do it in-place

local vulns_result = {}
for _, v in ipairs(vulns.data.search) do
table.insert(vulns_result, v)
if not vulns or not vulns.data or not vulns.data.search then
return
end

-- Sort the acquired vulns by the CVSS score
table.sort(vulns_result, function(a, b)
return a._source.cvss.score > b._source.cvss.score
end
)

for _, vuln in ipairs(vulns_result) do
-- Mark the exploits out
is_exploit = vuln._source.bulletinFamily:lower() == "exploit"

-- Sometimes it might happen, so check the score availability
cvss_score = vuln._source.cvss and (type(vuln._source.cvss.score) == "number") and (vuln._source.cvss.score) or ""
for _, vuln in ipairs(vulns.data.search) do
local v = {
id = vuln._source.id,
type = vuln._source.type,
-- Mark the exploits out
is_exploit = vuln._source.bulletinFamily:lower() == "exploit",
-- Sometimes it might happen, so check the score availability
cvss = tonumber(vuln._source.cvss.score),
}

-- NOTE[gmedian]: exploits seem to have cvss == 0, so print them anyway
if is_exploit or (cvss_score ~= "" and mincvss <= tonumber(cvss_score)) then
output_str = string.format("%s\n\t%s", output_str, vuln._source.id .. "\t\t" .. cvss_score .. '\t\thttps://vulners.com/' .. vuln._source.type .. '/' .. vuln._source.id .. (is_exploit and '\t\t*EXPLOIT*' or ''))
if v.is_exploit or (v.cvss and mincvss <= v.cvss) then
setmetatable(v, cve_meta)
output[#output+1] = v
end
end

return output_str

if #output > 0 then
-- Sort the acquired vulns by the CVSS score
table.sort(output, function(a, b)
return a.cvss > b.cvss or (a.cvss == b.cvss and a.id > b.id)
end)
return output
end
end


Expand All @@ -101,27 +123,25 @@ end
-- @param type string, the type query argument
--
function get_results(what, vers, type)
local v_host="vulners.com"
local v_port=443
local response, path
local status, error
local api_endpoint = "https://vulners.com/api/v3/burp/software/"
local vulns
local option={header={}}

option['header']['User-Agent'] = string.format('Vulners NMAP Plugin %s', api_version)
local option={
header={
['User-Agent'] = string.format('Vulners NMAP Plugin %s', api_version)
},
any_af = true,
}

path = '/api/v3/burp/software/' .. '?software=' .. what .. '&version=' .. vers .. '&type=' .. type
local response = http.get_url(('%s?software=%s&version=%s&type=%s'):format(api_endpoint, what, vers, type), option)

response = http.get(v_host, v_port, path, option)

status = response.status
local status = response.status
if status == nil then
-- Something went really wrong out there
-- According to the NSE way we will die silently rather than spam user with error messages
return ""
return
elseif status ~= 200 then
-- Again just die silently
return ""
return
end

status, vulns = json.parse(response.body)
Expand All @@ -131,15 +151,13 @@ function get_results(what, vers, type)
return make_links(vulns)
end
end

return ""
end


---
-- Calls <code>get_results</code> for type="software"
--
-- It is called from <code>action</code> when nothing is found for the available cpe's
--
-- It is called from <code>action</code> when nothing is found for the available cpe's
--
-- @param software string, the software name
-- @param version string, the software version
Expand All @@ -151,7 +169,7 @@ end

---
-- Calls <code>get_results</code> for type="cpe"
--
--
-- Takes the version number from the given <code>cpe</code> and tries to get the result.
-- If none found, changes the given <code>cpe</code> a bit in order to possibly separate version number from the patch version
-- And makes another attempt.
Expand All @@ -160,58 +178,56 @@ end
-- @param cpe string, the given cpe
--
function get_vulns_by_cpe(cpe)
local vers
local vers_regexp=":([%d%.%-%_]+)([^:]*)$"
local output_str=""


-- TODO[gmedian]: add check for cpe:/a as we might be interested in software rather than in OS (cpe:/o) and hardware (cpe:/h)
-- TODO[gmedian]: work not with the LAST part but simply with the THIRD one (according to cpe doc it must be version)

-- NOTE[gmedian]: take only the numeric part of the version
_, _, vers = cpe:find(vers_regexp)
local _, _, vers = cpe:find(vers_regexp)


if not vers then
return ""
return
end

output_str = get_results(cpe, vers, "cpe")
local output = get_results(cpe, vers, "cpe")

if output_str == "" then
if not output then
local new_cpe

new_cpe = cpe:gsub(vers_regexp, ":%1:%2")
output_str = get_results(new_cpe, vers, "cpe")
output = get_results(new_cpe, vers, "cpe")
end
return output_str

return output
end


action = function(host, port)
local tab={}
local tab=stdnse.output_table()
local changed=false
local response
local output_str=""
local output

for i, cpe in ipairs(port.version.cpe) do
output_str = get_vulns_by_cpe(cpe, port.version)
if output_str ~= "" then
tab[cpe] = output_str
for i, cpe in ipairs(port.version.cpe) do
output = get_vulns_by_cpe(cpe, port.version)
if output then
tab[cpe] = output
changed = true
end
end

-- NOTE[gmedian]: issue request for type=software, but only when nothing is found so far
if not changed then
local vendor_version = port.version.product .. " " .. port.version.version
output_str = get_vulns_by_software(port.version.product, port.version.version)
if output_str ~= "" then
tab[vendor_version] = output_str
output = get_vulns_by_software(port.version.product, port.version.version)
if output then
tab[vendor_version] = output
changed = true
end
end

if (not changed) then
return
end
Expand Down

0 comments on commit 4facc43

Please sign in to comment.