diff --git a/vulners.nse b/vulners.nse index d0b1ac5..94eed8d 100644 --- a/vulners.nse +++ b/vulners.nse @@ -1,5 +1,5 @@ description = [[ -For each availible cpe it prints the known vulns (links to the correspondent info). +For each available cpe it prints the known vulns (links to the correspondent info). Its work is pretty simple: - work only when some software version is identified for an open port @@ -14,13 +14,16 @@ Its work is pretty simple: -- -- @output -- --- 22/tcp open ssh OpenSSH 6.7p1 Debian 5+deb8u4 (protocol 2.0) +-- 53/tcp open domain ISC BIND DNS -- | vulners: --- | cpe:/a:openbsd:openssh:6.7p1: --- | CVE-2016-8858 https://vulners.com/cve/CVE-2016-8858 --- | CVE-2016-0777 https://vulners.com/cve/CVE-2016-0777 --- | CVE-2017-15906 https://vulners.com/cve/CVE-2017-15906 --- |_ CVE-2016-0778 https://vulners.com/cve/CVE-2016-0778 +-- | 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 -- author = 'gmedian AT vulners DOT com' @@ -31,8 +34,9 @@ categories = {"vuln", "safe"} local http = require "http" local json = require "json" local string = require "string" +local table = require "table" -local api_version="1.0" +local api_version="1.1" portrule = function(host, port) @@ -40,6 +44,7 @@ portrule = function(host, port) return vers ~= nil and vers.version ~= nil end + --- -- Return a string with all the found cve's and correspondent links -- @@ -47,11 +52,32 @@ end -- function make_links(vulns) local output_str="" + local is_exploit=false + local cvss_score="" + + -- 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 - for _, vuln in ipairs(vulns.data.search) do - output_str = string.format("%s\n\t%s", output_str, vuln._source.id .. '\t\thttps://vulners.com/' .. vuln._source.type .. '/' .. vuln._source.id) + local vulns_result = {} + for _, v in ipairs(vulns.data.search) do + table.insert(vulns_result, v) 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 ("\t\t" .. vuln._source.cvss.score) or "" + output_str = string.format("%s\n\t%s", output_str, vuln._source.id .. cvss_score .. '\t\thttps://vulners.com/' .. vuln._source.type .. '/' .. vuln._source.id .. (is_exploit and '\t\t*EXPLOIT*' or '')) + end + return output_str end @@ -59,7 +85,7 @@ end --- -- Issues the requests, receives json and parses it, calls make_links when successfull -- --- @param what String, future value for the software query argument +-- @param what string, future value for the software query argument -- @param vers string, the version query argument -- @param type string, the type query argument -- @@ -67,7 +93,8 @@ function get_results(what, vers, type) local v_host="vulners.com" local v_port=443 local response, path - local status, vulns + local status, error + local vulns local option={header={}} option['header']['User-Agent'] = string.format('Vulners NMAP Plugin %s', api_version) @@ -75,6 +102,20 @@ function get_results(what, vers, type) path = '/api/v3/burp/software/' .. '?software=' .. what .. '&version=' .. vers .. '&type=' .. type response = http.get(v_host, v_port, path, option) + + 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 "" + elseif status == 418 then + -- Too many requests + return "You are doing it too fast. Lower the rate or contact isox AT vulners DOT com." + elseif status ~= 200 then + -- Again just die silently + return "" + end + status, vulns = json.parse(response.body) if status == true then @@ -90,7 +131,7 @@ end --- -- Calls get_results for type="software" -- --- It is called from action when nothing is found for the availible cpe's +-- It is called from action when nothing is found for the available cpe's -- -- @param software string, the software name -- @param version string, the software version @@ -118,7 +159,7 @@ function get_vulns_by_cpe(cpe) -- 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 just the numeric part of the version + -- NOTE[gmedian]: take only the numeric part of the version _, _, vers = cpe:find(vers_regexp)