Metabase CVE-2021-41277路径遍历漏洞:原理剖析与本地靶场复现指南

Metabase CVE-2021-41277路径遍历漏洞:原理剖析与本地靶场复现指南 1. 项目概述一次对Metabase文件读取漏洞的深度剖析最近在整理内部安全审计的案例库翻到了一个老但经典的漏洞——Metabase的CVE-2021-41277。这个漏洞虽然已经过去几年但其原理和利用方式在理解现代Web应用安全特别是API接口和路径遍历攻击方面依然极具教学价值。很多刚入门安全研究的朋友或者负责企业应用运维的工程师可能都听说过“文件读取漏洞”但具体到某个流行应用如Metabase它是怎么发生的攻击者如何一步步利用我们又该如何在本地环境安全地复现以加深理解这里面有不少细节值得深挖。简单来说CVE-2021-41277是一个存在于Metabase一个流行的开源商业智能BI和数据分析平台中的路径遍历漏洞。在特定版本的Metabase中其API接口对用户输入的处理存在缺陷导致攻击者能够构造特殊的请求读取服务器上的任意文件。这听起来很危险事实也确实如此一旦成功数据库连接字符串、配置文件、甚至系统关键文件都可能泄露。今天我就带大家从零开始在完全受控的实验室环境中手把手搭建靶场、分析漏洞原理、构造利用请求并最终获取到“敏感信息”。整个过程旨在学习与研究请务必仅在你自己拥有完全控制权的环境中进行。2. 漏洞背景与核心原理拆解在动手之前我们必须先搞清楚“敌人”是谁以及它的“命门”在哪里。盲目操作只会事倍功半。2.1 Metabase 与受影响版本Metabase 是一个让非技术人员也能轻松查询数据库、制作图表和仪表盘的工具。它通常部署在内网用于企业数据分析。正是由于其常常接触核心业务数据库一旦出现安全漏洞危害性会被放大。CVE-2021-41277 影响的是 Metabase 从 v0.40.0 到 v0.40.4不含以及 v0.39.0 到 v0.39.4不含的版本。漏洞在 v0.40.4 和 v0.39.4 中得到了修复。这个漏洞的根源在于一个用于提供应用程序静态资源的API端点。2.2 漏洞原理API端点与路径遍历Metabase 提供了一个API端点通常是/api/geojson/其本意是让前端能够按需加载一些地理信息JSON文件例如用于地图图表。设计上它应该只允许读取程序内置的、位于特定安全目录下的.json文件。漏洞的关键在于路径遍历。路径遍历也叫目录穿越核心问题是应用程序未能对用户提供的文件名或路径参数进行充分的安全校验。攻击者可以通过输入包含../这样的序列来“跳出”程序设定的安全目录访问其上级甚至根目录下的任意文件。在 Metabase 的这个漏洞中有问题的代码大致逻辑是这样的这是一个概念性还原并非直接源码前端请求一个地理JSON文件例如country.json。请求到达/api/geojson/:filename这个API。后端代码直接使用用户传入的:filename参数拼接上一个固定的基础路径形成最终的文件路径。如果拼接过程没有过滤../那么攻击者传入../../../etc/passwd拼接后的路径就可能指向系统的/etc/passwd文件。更具体地说漏洞存在于metabase.api.geojson命名空间下的api路由定义中。它对filename参数的处理不够严格导致了这次路径遍历。2.3 利用链与敏感信息获取理解了路径遍历利用链就清晰了信息收集首先攻击者需要发现目标正在运行有漏洞版本的 Metabase。这可以通过识别默认的登录页面、特定的HTTP响应头或错误信息来完成。漏洞探测向/api/geojson/端点发送包含../的请求尝试读取一个已知存在的系统文件如/etc/passwd在Linux上以确认漏洞存在。敏感文件读取确认漏洞后攻击者开始系统地读取敏感文件目标通常包括Metabase 配置文件寻找./目录下的metabase.dbH2数据库文件或配置文件里面极有可能含有数据库连接密码。应用服务器文件读取/proc/self/environLinux可以获取进程环境变量可能泄露密钥。系统文件如/etc/passwd,/etc/shadow需高权限用于了解系统用户信息。信息提炼与利用从读取到的文件中提取数据库凭证、API密钥等可能用于进一步入侵数据库或服务器。注意在复现过程中我们读取的“敏感信息”将是我们在自己搭建的靶场环境中预先放置的测试文件例如一个包含假密码的config.txt。绝对禁止在非授权环境中进行任何探测或攻击行为。3. 靶场环境搭建与准备“工欲善其事必先利其器”。一个隔离、可控的实验室环境是安全研究的基石。这里我推荐使用 Docker它能快速构建一个干净且与宿主机隔离的漏洞环境。3.1 环境与工具清单操作系统Ubuntu 20.04/22.04 LTS 或 Kali Linux推荐Windows/macOS 也可但命令略有不同。Docker Docker Compose用于容器化部署 Metabase。curl / Postman用于发送HTTP请求探测和利用漏洞。文本编辑器用于查看和编辑配置文件。靶机运行有漏洞版本的 Metabase 容器。3.2 部署有漏洞的 Metabase 版本我们将使用 Docker 拉取一个特定的有漏洞的 Metabase 镜像。这里我选择metabase/metabase:v0.40.3。首先创建一个工作目录例如metabase_cve_labmkdir ~/metabase_cve_lab cd ~/metabase_cve_lab然后创建一个docker-compose.yml文件来定义我们的服务。这种方式比直接docker run更易于管理端口、卷挂载等配置。version: 3.8 services: metabase: image: metabase/metabase:v0.40.3 # 指定有漏洞的版本 container_name: metabase-vulnerable ports: - 3000:3000 # 将容器的3000端口映射到宿主机的3000端口 environment: MB_DB_TYPE: h2 MB_DB_FILE: /metabase-data/metabase.db volumes: - ./metabase-data:/metabase-data # 挂载数据卷方便我们查看数据库文件 - ./test-secret.txt:/tmp/test-secret.txt:ro # 挂载一个我们自己的“敏感文件”用于测试 restart: unless-stopped解释一下这个配置image: 明确指定了存在漏洞的v0.40.3版本。ports: Metabase 默认运行在3000端口我们映射到宿主机的3000端口这样就能通过http://localhost:3000访问。environment: 设置环境变量告诉 Metabase 使用 H2 嵌入式数据库并将数据库文件存储在容器内的/metabase-data/metabase.db。volumes:./metabase-data:/metabase-data: 将宿主机当前目录下的metabase-data文件夹挂载到容器的相同路径。这样Metabase 创建的数据库文件就会保存在宿主机上方便我们后续检查。./test-secret.txt:/tmp/test-secret.txt:ro: 这是关键我们在宿主机上创建一个名为test-secret.txt的文件并将其以只读 (ro) 方式挂载到容器的/tmp目录下。这个文件就是我们要模拟读取的“敏感信息”。现在在同一个目录下创建我们的“诱饵”敏感文件echo DATABASE_PASSWORDSuperSecretPassword123! test-secret.txt echo API_KEYeyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... test-secret.txt最后启动容器docker-compose up -d使用docker ps命令检查容器是否正常运行。当你在浏览器中访问http://localhost:3000看到 Metabase 的初始化设置页面时说明环境已经就绪。你可以按照页面提示完成初始设置设置管理员账号等但这对于漏洞复现不是必须的漏洞存在于未认证的API端点。3.3 实操心得Docker 环境下的路径理解这里有一个非常重要的细节当我们在容器内进行路径遍历时需要清楚容器内的文件系统布局。我们挂载的test-secret.txt在容器内的路径是/tmp/test-secret.txt。Metabase 应用程序本身运行在容器内其工作目录通常是/app或根目录/。因此为了从漏洞API读取到/tmp/test-secret.txt我们需要计算从API预期资源目录到目标文件的相对路径。这通常需要一些猜测和尝试也是真实渗透测试中信息收集的一部分。在我们的复现中由于我们知道文件确切位置可以更有针对性地构造载荷。4. 漏洞复现与利用实操环境准备好了现在进入最核心的环节动手验证并利用这个漏洞。4.1 确认服务与漏洞端点首先确认 Metabase 服务是否响应。我们可以用curl简单访问一下首页curl -I http://localhost:3000应该能看到返回HTTP/1.1 200 OK。接下来找到存在漏洞的端点。根据公开的漏洞信息这个端点是/api/geojson/。我们可以先尝试一个合法的请求看看正常响应是什么curl http://localhost:3000/api/geojson/country.json很可能会返回一个404 Not Found因为v0.40.3这个容器镜像里可能没有默认的country.json文件。但这没关系404响应本身也说明这个端点存在且可访问。如果端点不存在通常会返回404或403但路径遍历漏洞的利用不依赖于目标文件是否存在。4.2 构造路径遍历Payload现在开始尝试路径遍历。我们的目标是读取容器内的/tmp/test-secret.txt文件。我们需要猜测API处理文件时的基础路径。常见的做法是尝试使用多个../来向上回溯目录。由于我们不确定API的当前工作目录可以从尝试读取Linux系统的一个常见文件开始以验证漏洞是否存在例如/etc/passwdcurl --path-as-is http://localhost:3000/api/geojson/../../../etc/passwd注意这里使用了--path-as-is参数这是curl的一个关键选项。它告诉curl不要对URL中的特殊字符如/和.进行编码或规范化处理原样发送我们的Payload。如果没有这个参数curl可能会在发送请求前“清理”路径导致../被处理掉从而使攻击失效。如果漏洞存在你可能会看到两种结果成功返回/etc/passwd文件的内容。返回一个空响应、错误信息或者被重定向。但这不一定代表漏洞不存在可能只是路径深度不对。4.3 精准定位并读取目标文件假设读取/etc/passwd成功了证明漏洞确实存在。现在我们要精准定位到我们挂载的测试文件/tmp/test-secret.txt。我们需要计算从漏洞端点“认为”的资源目录到/tmp的相对路径。这需要一些尝试。我们可以从更多的../开始# 尝试1假设基础目录较深 curl --path-as-is http://localhost:3000/api/geojson/../../../../../../tmp/test-secret.txt # 尝试2另一种尝试 curl --path-as-is http://localhost:3000/api/geojson/../../../../../tmp/test-secret.txt在我的测试环境中经过几次尝试使用../../../../tmp/test-secret.txt成功了。这意味着API在处理geojson请求时其基础路径可能是类似/app/或/app/resources/这样的位置向上回溯四层到达根目录/再进入tmp目录。当你看到终端输出了我们之前写入test-secret.txt的内容DATABASE_PASSWORDSuperSecretPassword123!...时恭喜你漏洞复现成功你刚刚完成了一次模拟的任意文件读取攻击。4.4 尝试读取 Metabase 数据库文件作为学习的延伸我们可以尝试读取 Metabase 自己的 H2 数据库文件这通常是攻击者的主要目标。根据我们的docker-compose.yml配置数据库文件在容器内的路径是/metabase-data/metabase.db。curl --path-as-is -o metabase.db http://localhost:3000/api/geojson/../../../../metabase-data/metabase.db这里使用了-o metabase.db参数将响应的内容即数据库文件保存到本地的metabase.db文件中。H2数据库文件是二进制的直接curl查看会是乱码。我们可以用file命令检查一下file metabase.db应该会显示类似SQLite 3.x database的信息Metabase 的 H2 数据库在某些版本下使用 SQLite 格式。虽然我们不一定能直接解读但这证明了攻击者可以下载整个数据库文件之后可以离线进行破解和分析危害极大。5. 漏洞深度分析与修复方案成功复现漏洞让我们看到了攻击者的视角。现在让我们切换到防御者视角深入分析漏洞根源和修复方法。5.1 漏洞代码层面分析虽然我们无法直接看到 Metabase 的私有代码但根据漏洞公告和补丁提交记录我们可以推断问题出在路由处理逻辑上。在 ClojureMetabase 的开发语言中路由通常使用compojure库定义。有问题的代码可能类似于(GET /api/geojson/:filename [] (let [file-path (str “/app/resources/geojson/” filename)] ; 危险拼接 (slurp file-path))) ; 读取文件内容问题一目了然用户控制的filename参数被直接拼接到基础路径后没有进行任何净化处理。修复方案同样清晰在拼接前对filename进行规范化并检查是否包含路径遍历序列。Metabase 官方在修复版本v0.40.4中很可能引入了以下一种或多种措施输入验证检查filename参数是否只包含预期的字符如字母、数字、连字符、下划线拒绝任何包含/、\、..的输入。路径规范化与校验使用安全的路径处理函数将用户输入和基础路径合并后解析为规范化的绝对路径然后检查这个绝对路径是否仍然位于允许的资源目录内。白名单机制如果可能维护一个允许访问的地理JSON文件白名单只响应列表内的文件请求。5.2 修复方案与升级建议对于使用 Metabase 的企业或个人修复措施非常明确立即升级将 Metabase 升级到 v0.40.4、v0.39.4 或任何更高的安全版本。这是最根本、最有效的解决方法。安全配置如果因故无法立即升级可以考虑通过反向代理如 Nginx对/api/geojson/这个路径进行访问控制或拦截作为临时的缓解措施。但这并非长久之计。最小权限原则运行 Metabase 的进程应该使用非特权用户限制其文件系统访问权限这样即使发生路径遍历能读取的文件范围也有限。5.3 从漏洞中学习的防御性编程思维CVE-2021-41277 给我们上了生动的一课永远不要信任用户输入这是安全领域的铁律。任何来自外部的参数都必须经过严格的验证和净化。使用安全的API在处理文件路径时使用编程语言提供的、经过安全审计的库函数来进行路径拼接和解析而不是简单的字符串连接。深度防御除了在应用代码层进行校验在操作系统层使用非root用户运行、网络层WAF进行防护可以构建多道防线。及时更新关注使用组件的安全公告建立及时的补丁更新流程。6. 复现过程中的常见问题与排查在复现过程中你可能会遇到一些问题。这里我总结了一些常见的坑和解决方法。6.1 请求返回404或其它错误问题发送Payload后返回404 Not Found或400 Bad Request。排查检查容器状态docker ps确认容器正在运行docker logs metabase-vulnerable查看容器日志是否有错误。确认端点确保你访问的地址和端口正确。Metabase 的API端点路径是/api/geojson/注意不要漏写斜杠。检查curl参数最重要的一点确保使用了--path-as-is参数。没有这个参数复现几乎肯定会失败。尝试不同路径深度../的数量可能需要调整。从../../../etc/passwd开始逐步增加或减少../的数量进行尝试。版本差异确保你拉取的镜像版本确实是受影响的版本如v0.40.3。有些最新的latest标签可能已经修复了漏洞。6.2 读取不到/etc/passwd但漏洞可能存在问题读取/etc/passwd失败但怀疑漏洞存在。排查容器内无此文件某些极简的Docker镜像可能没有/etc/passwd文件。可以尝试读取其他肯定存在的文件如/proc/version显示内核信息或/etc/hostname。权限问题Metabase 进程可能以低权限用户运行无法读取某些系统文件。但这不影响漏洞原理只是利用条件受限。可以尝试读取我们挂载的test-secret.txt这个文件我们设置了只读通常可读。使用绝对路径尝试有时利用绝对路径的Payload也可能有效取决于代码实现例如直接尝试/api/geojson//etc/passwd注意开头的双斜杠或编码但这在大多数正确实现路径拼接的代码中无效。6.3 数据库文件无法直接查看问题成功下载了metabase.db文件但用文本编辑器打开是乱码。解释这是正常的。H2数据库文件是二进制格式。要查看其内容你需要使用H2数据库的控制台工具或支持SQLite的工具因为格式兼容。你可以使用sqlite3命令行工具来浏览如果它是SQLite格式sqlite3 metabase.db .tables # 查看所有表 sqlite3 metabase.db “SELECT * FROM core_user;” # 查看用户表表名可能需要猜测或从其他资料获取这模拟了攻击者在获取数据库文件后的下一步动作。6.4 网络请求工具的使用技巧使用 Postman 或 Burp Suite对于更复杂的请求或需要观察完整HTTP交互的情况图形化工具更直观。在Burp Suite的Repeater模块中你可以方便地修改和重放请求无需担心--path-as-is这样的参数。编码问题有时为了防止WAF或输入过滤攻击者会对Payload进行URL编码。例如../编码为%2e%2e%2f。在复现时如果原始Payload不行可以尝试编码后的版本。不过对于这个CVE通常直接使用--path-as-is的curl即可。整个复现过程的核心在于理解路径遍历的原理并耐心地调整Payload以适配目标应用的具体上下文。每一次失败的尝试都是对应用行为的一次探测这些信息对于理解漏洞同样宝贵。