1. 项目概述从一次“意外”的数据库连接说起几年前我在一次内部安全巡检中用扫描器对一个测试环境的IP段进行常规探测。当扫描报告弹出一个“MongoDB 27017端口开放”的提示时我并没有太在意毕竟很多开发测试环境都会部署MongoDB。然而当我习惯性地尝试用mongo命令行客户端去连接那个地址时终端上直接返回了提示符——我竟然在没有输入任何用户名和密码的情况下直接进入了数据库的Shell并且拥有最高权限。那一刻我背后瞬间冒出一层冷汗。这不是某个精心搭建的靶场而是一个真实存在的、承载着部分业务数据的测试库。里面虽然没有核心生产数据但依然存放着大量的应用配置、测试用户信息乃至部分脱敏后的数据样本。任何一个能访问到这个网络段的人都可以像回家一样随意进出查看、删除甚至勒索数据。这次经历让我对MongoDB未授权访问漏洞的普遍性和危害性有了刻骨铭心的认识。它不像某些漏洞需要复杂的利用链它简单、直接却因为运维和开发人员的疏忽而广泛存在。今天我就以一个安全从业者和数据库使用者的双重身份带你彻底拆解这个漏洞。我们不仅要搞清楚它为什么会产生、如何快速验证其存在更重要的是我会分享一套从信息收集、漏洞复现到深度利用和最终加固的完整实操流程以及我在多年渗透测试和应急响应中积累的独家技巧和避坑指南。无论你是安全工程师想提升漏洞挖掘能力还是运维/开发人员想自查和加固自己的数据库这篇文章都能给你提供可直接落地的参考。2. 漏洞原理深度剖析默认配置的“便利”与“陷阱”要理解MongoDB未授权访问我们不能停留在“没设密码”这个表面必须深入到其设计哲学和版本演进中去。2.1 默认监听绑定安全观念的变迁MongoDB在早期3.0版本之前的定位是“易于开发”为了最大化开发者的便利性其默认安装后的行为是相当“开放”的无认证启动默认情况下mongod服务进程启动时不启用任何身份验证--auth参数。绑定所有接口默认监听地址是0.0.0.0这意味着它不仅接受本地127.0.0.1连接也接受来自任何网络接口如公网IP、内网IP的连接。全权信任本地连接即使使用了--auth参数在3.0版本之前从本地主机127.0.0.1发起的连接依然可以绕过认证直接获得访问权限。这个设计本意是方便本地管理但却成了一个巨大的逻辑漏洞。注意很多开发者在测试时习惯在本地用--auth启动然后用mongo本地连接测试发现一切正常就误以为认证已生效。实际上在3.0前这个本地连接走的正是“后门”。MongoDB 3.0版本是一个重要的安全分水岭。社区意识到了默认开放的危险性做出了两项关键改变默认绑定本地回环地址新安装的MongoDB默认监听地址从0.0.0.0改为了127.0.0.1。这从根本上杜绝了安装后即暴露在网络中的情况。废除本地特权当以--auth模式启动时任何连接包括来自127.0.0.1的都必须进行身份验证。无合法账号则无任何权限。这个改变极大地提升了默认安全性但“历史遗留问题”和“配置覆盖”导致了漏洞的长期存在。2.2 漏洞产生的典型场景理解了版本差异我们就能勾勒出漏洞出现的几个高频场景遗留的旧版本实例大量在3.0版本之前部署的MongoDB服务如果没有跟随版本升级并重新评估安全配置很可能一直处于裸奔状态。“便利性”配置覆盖很多运维脚本、Docker镜像或者自动化部署工具为了确保服务能被远程访问到会主动将bindIp设置为0.0.0.0同时可能因为疏忽而未配置认证。这在云服务器、容器环境中尤为常见。开发与测试环境开发人员为了图省事在搭建测试环境时直接使用默认配置或简易启动命令完全忽略了安全设置。这些环境通常防护薄弱更容易从互联网或内网被扫描发现。对云安全组的误解用户认为只要云服务商的安全组限制了27017端口的入站数据库就是安全的。然而安全组只能防护来自外网的直接攻击对于已经处在VPC内网、或通过其他漏洞如Web应用漏洞进入服务器的攻击者来说形同虚设。2.3 漏洞的实质危害未授权访问意味着攻击者获得了对MongoDB实例的最高级别控制权其危害远超普通数据泄露数据泄露直接导出所有数据库、集合表中的敏感数据如用户信息、商业资料、配置文件可能含密钥。数据破坏删除或篡改数据库导致业务瘫痪。攻击者可能会删除所有数据并留下勒索信息。权限提升与持久化攻击者可以为自己创建一个高权限用户为后续长期控制留下后门。即使管理员后续开启了认证攻击者的账号依然有效。作为跳板在某些配置下MongoDB的JavaScript引擎如$wheremapReduce可能被滥用来执行系统命令从而将数据库漏洞转化为服务器远程命令执行RCE漏洞获得服务器控制权。供应链攻击如果被入侵的是开发测试环境的数据库攻击者可能篡改其中的应用程序配置、测试数据或脚本污染整个开发流程。3. 信息收集与漏洞探测如何高效发现目标在复现漏洞之前我们需要先找到存在漏洞的目标。这里的信息收集分为主动扫描和被动发现两种思路。3.1 主动扫描探测对于授权测试范围内的目标我们可以使用多种工具进行主动探测。3.1.1 使用Nmap进行端口扫描与服务识别Nmap是网络探测的瑞士军刀。针对MongoDB我们不仅扫描端口更要用脚本获取详细信息。# 基本端口扫描 nmap -p 27017 目标IP或网段 # 使用Nmap的MongoDB信息枚举脚本这能直接判断是否存在未授权访问 nmap -p 27017 --script mongodb-info 目标IP如果目标MongoDB存在未授权访问mongodb-info脚本会成功返回数据库的版本、运行时间、基础信息等如下图所示模拟输出PORT STATE SERVICE 27017/tcp open mongodb | mongodb-info: | MongoDB Build info | versionArray | 4 | 0 | 8 | 0 | gitVersion | sysInfo | ... | Server status | localTime | uptime | ...如果访问需要认证脚本通常会返回错误信息或没有mongodb-info输出。3.1.2 使用专用工具MongoDB Scanner有一些开源工具专门用于快速扫描MongoDB未授权漏洞例如mongoaudit、mongo-scanner等。它们通常集成了连接测试、信息拉取和简单的弱口令爆破功能。使用这些工具可以批量处理目标列表提高效率。3.1.3 手动连接验证这是最直接的方法。在本地安装MongoDB客户端mongo或新版mongosh尝试连接。# 使用旧版mongo shell (MongoDB 4.x及以下常用) mongo --host 目标IP --port 27017 # 使用新版MongoDB Shell (mongosh 更通用) mongosh mongodb://目标IP:27017如果连接成功且直接进入了交互式Shell显示test或类似的数据库提示符那么几乎可以确定存在未授权访问。你可以立即执行show dbs来验证权限。实操心得在渗透测试中不要一连接成功就执行show dbs或db.version()。一些蜜罐系统会监控此类探测行为。更隐蔽的做法是连接成功后先等待或者执行一些看似无害的命令如db.getMongo()来获取连接对象信息观察响应是否异常。3.2 被动信息收集与搜索引擎利用对于更广泛的威胁情报收集或红队评估被动发现手段同样重要。3.2.1 网络空间搜索引擎Shodan、ZoomEye、Fofa等网络空间测绘引擎是发现暴露在公网的MongoDB实例的利器。搜索语法非常直接Shodan:port:27017 mongodbFofa:port27017 protocolmongodb或port27017 bannerMongoDBZoomEye:port:27017 service:mongodb这些引擎不仅能找到IP还能直接看到返回的banner信息有时甚至能直接判断是否开启认证。通过分析这些数据可以了解全球或特定区域MongoDB的暴露情况、版本分布为漏洞趋势分析提供依据。3.2.2 日志分析与流量监控在企业内部可以通过分析防火墙、WAF、IDS/IPS的日志寻找对内部27017端口的异常扫描连接。此外如果服务器上部署了MongoDB其日志文件默认在/var/log/mongodb/mongod.log是金矿。关注连接日志寻找来源IP异常、认证失败频率过高等记录。3.2.3 GitHub等代码仓库信息泄露开发人员有时会将包含MongoDB连接字符串的配置文件如application.propertiesconfig.json误提交到公开的代码仓库。这些连接字符串可能直接包含IP、端口甚至用户名和密码。使用GitHub搜索语法如filename:config.json 27017或mongodb:// password可能会发现意外线索。4. 漏洞复现与深度利用从连接到控制当我们确认一个目标存在未授权访问后复现漏洞的过程就是模拟攻击者的操作路径。我将在自己搭建的隔离测试环境使用Docker运行一个无认证的MongoDB 4.4实例中演示。4.1 环境搭建与基础复现首先我们快速搭建一个漏洞环境# 拉取MongoDB镜像 docker pull mongo:4.4 # 运行一个无需认证的MongoDB容器并映射端口到主机 docker run -d --name mongodb-unauth -p 27017:27017 mongo:4.4现在一个监听在本机0.0.0.0:27017且无认证的MongoDB服务就绪了。4.1.1 基础信息收集使用mongosh进行连接和信息收集mongosh mongodb://127.0.0.1:27017连接成功后执行以下命令// 查看所有数据库 show dbs // 查看当前数据库版本 db.version() // 查看服务器状态包含运行时间、连接数等 db.serverStatus() // 查看所有用户在未授权情况下可以查看admin等系统库的用户集合 use admin db.system.users.find()在无认证环境下这些命令都会成功执行泄露大量系统信息。4.1.2 数据库操作验证漏洞创建一个测试数据库和集合插入并读取数据验证完整的增删改查权限// 切换到或创建一个测试数据库 use test_vuln // 创建一个集合并插入数据 db.test_collection.insertOne({ username: admin, password: weak123, role: superuser, leaked: true }) // 查询数据 db.test_collection.find() // 更新数据 db.test_collection.updateOne({ username: admin }, { $set: { password: hacked! } }) // 删除数据 db.test_collection.deleteOne({ username: admin }) // 最后甚至可以删除整个集合或数据库高危仅在测试环境操作 // db.test_collection.drop() // db.dropDatabase()这一系列操作直观地证明了攻击者可以对此数据库为所欲为。4.2 深度利用权限维持与横向移动一个成熟的攻击者不会满足于简单的数据操作他们会寻求建立持久化后门和探索横向移动的可能。4.2.1 创建后门用户即使目标当前未开启认证攻击者也可以提前创建用户。一旦管理员未来启用认证这个后门账户就生效了。use admin db.createUser({ user: backdoor_user, pwd: s3cr3tPssw0rd!, roles: [ { role: root, db: admin } ] // 赋予最高权限的root角色 })执行成功后攻击者就拥有了一个隐藏在系统用户列表中的超级管理员账号。注意事项在MongoDB中用户是存储在所属数据库的system.users集合中的。admin数据库的用户是全局用户。检查用户时务必查看admin.system.users。有些管理员只检查业务数据库的用户可能会遗漏admin库中的后门。4.2.2 利用JavaScript引擎执行命令条件利用这是一个更高级的利用点成功率取决于MongoDB的版本和配置。在早期版本或特定配置下MongoDB允许在查询中使用JavaScript表达式。如果服务器端JavaScript引擎未被禁用默认是启用的且攻击者有一定权限可能通过$where操作符或mapReduce、$function等命令执行系统命令。// 示例通过$where进行简单探测此命令本身不执行系统命令但可探测JS引擎是否可用 db.test_collection.find({ $where: function() { return 112; } }) // 更危险的利用通常需要结合其他漏洞或特性例如在旧版本中db.eval()可以执行任意JS。 // 注意db.eval()在大多数现代部署中已被禁用因为它存在严重安全风险。重要警告利用JavaScript执行系统命令通常需要特定的环境配置如未启用noscripting安全选项并且利用方式复杂。在实际渗透测试中这不应作为首选方法但它揭示了未授权访问可能引发更严重链式攻击的风险。4.2.3 配置文件窃取与信息收集通过未授权访问攻击者可以尝试读取数据库中的敏感信息这些信息可能有助于横向移动寻找连接字符串在业务数据库的配置集合中可能存有其他数据库如MySQL、Redis或内部服务的连接信息。分析数据结构通过分析集合和文档的结构可以推断出应用逻辑有助于发现其他Web或API漏洞。备份文件与日志虽然不能直接通过MongoDB Shell访问文件系统但可以查看数据库的备份集合或日志相关的集合如果应用将日志存入了MongoDB。4.3 自动化利用工具演示手动操作适合单点目标对于批量评估我们可以使用自动化脚本或框架。这里以Python为例使用pymongo库编写一个简单的漏洞验证脚本。#!/usr/bin/env python3 import pymongo import sys from pymongo.errors import ConnectionFailure, OperationFailure def check_mongodb_unauth(host, port27017): try: # 尝试无认证连接 client pymongo.MongoClient(hosthost, portport, serverSelectionTimeoutMS5000) # 触发一个简单命令来测试连接和权限 db_list client.list_database_names() print(f[] 目标 {host}:{port} 存在MongoDB未授权访问漏洞) print(f[] 可访问的数据库列表: {db_list}) # 尝试获取更多信息 admin_db client.admin server_info admin_db.command(serverStatus) print(f[] MongoDB版本: {server_info.get(version)}) print(f[] 运行时间(秒): {server_info.get(uptime)}) # 尝试查看admin库用户如果允许 try: users admin_db.command(usersInfo) print(f[] Admin数据库用户数: {len(users.get(users, []))}) except OperationFailure: print([-] 无法读取用户信息可能权限受限或版本差异) client.close() return True except OperationFailure as e: print(f[-] 目标 {host}:{port} 需要认证。错误信息: {e.details.get(errmsg)}) return False except ConnectionFailure as e: print(f[-] 无法连接到 {host}:{port}。错误: {e}) return False except Exception as e: print(f[-] 检查 {host}:{port} 时发生未知错误: {e}) return False if __name__ __main__: if len(sys.argv) ! 2: print(用法: python3 mongodb_check.py 目标IP) sys.exit(1) target_ip sys.argv[1] check_mongodb_unauth(target_ip)这个脚本能快速判断一个目标是否存在未授权访问并获取基础信息。你可以将其扩展为多线程用于扫描一个IP列表。5. 漏洞修复与安全加固实战指南复现漏洞是为了更好地修复它。作为运维或开发人员绝不能止步于发现漏洞必须立即进行加固。以下是我总结的从紧急处置到长期防护的完整方案。5.1 紧急处置措施一旦发现线上数据库存在未授权访问必须立即按以下顺序操作网络隔离这是最快、最有效的手段。立即在防火墙或云安全组上设置规则只允许特定的、可信的应用程序服务器IP地址访问数据库的27017端口。拒绝所有其他来源的访问。命令示例如下假设应用服务器IP是10.0.1.100# 使用iptablesLinux服务器 iptables -A INPUT -s 10.0.1.100 -p tcp --dport 27017 -j ACCEPT iptables -A INPUT -p tcp --dport 27017 -j DROP # 对于云服务器在控制台配置安全组添加入站规则 # 协议: TCP, 端口: 27017, 源: 10.0.1.100/32 (或应用服务器所在的安全组) # 并删除任何允许0.0.0.0/0访问27017端口的规则。这能在不重启数据库服务的情况下立刻阻断外部攻击。评估损失在隔离网络后立即审查数据库日志(mongod.log)寻找异常连接IP、异常操作时间点。检查用户列表(admin.system.users)排查是否有新增的未知用户。对比业务关键数据检查是否有篡改或删除痕迹。修改绑定地址如果业务允许将MongoDB的监听地址从0.0.0.0改为127.0.0.1或内网IP。这是根本性解决暴露问题的办法。修改配置文件找到MongoDB的配置文件通常是/etc/mongod.conf修改net.bindIp项。net: port: 27017 bindIp: 127.0.0.1,10.0.1.100 # 绑定到本地回环和一个特定内网IP命令行启动如果通过命令行启动使用--bind_ip参数。mongod --bind_ip 127.0.0.1,10.0.1.100 --dbpath /your/data/path修改后必须重启MongoDB服务生效。5.2 启用认证与权限最小化网络隔离是临时措施启用强认证才是长治久安之道。5.2.1 创建管理员用户在无认证模式下首先你需要在一个安全的环境下如通过本地控制台或已做IP限制的SSH连接以无认证模式重启或连接到MongoDB创建第一个管理员用户。# 1. 连接到本地MongoDB实例确保连接时无需认证 mongosh mongodb://127.0.0.1:27017 # 2. 切换到admin数据库 use admin # 3. 创建具有用户管理权限的管理员用户 db.createUser({ user: globalAdmin, pwd: A_Strong_Password_Here!#2023, // 务必使用强密码 roles: [ { role: userAdminAnyDatabase, db: admin } ] }) # 4. 可选但建议创建一个具有集群管理权限的用户如果部署了复制集或分片 db.createUser({ user: clusterAdmin, pwd: Another_Strong_Password_Cluster, roles: [ { role: clusterAdmin, db: admin } ] })实操心得不要使用root角色作为日常管理角色。userAdminAnyDatabase角色已经足够管理所有数据库的用户。遵循权限最小化原则。5.2.2 启用认证并重启服务创建用户后启用认证并重启服务。通过配置文件启用推荐 在/etc/mongod.conf中添加或修改security: authorization: enabled然后重启服务sudo systemctl restart mongod(Systemd) 或sudo service mongod restart(SysVinit)。通过命令行启用mongod --auth --bind_ip 127.0.0.1,your_internal_ip --dbpath /your/data/path5.2.3 为每个应用创建专属数据库用户管理员用户用于管理每个具体的应用程序应该使用自己专属的、权限受限的数据库用户。// 使用globalAdmin用户登录后为myapp数据库创建一个应用用户 use myapp db.createUser({ user: myapp_user, pwd: App_Specific_Password123, roles: [ { role: readWrite, db: myapp } ] // 只拥有myapp库的读写权限 })这样即使myapp_user的凭证泄露攻击者也只能影响myapp这一个数据库。5.3 进阶安全配置完成基础认证后可以考虑以下进阶加固措施构建纵深防御启用TLS/SSL加密传输防止数据在网络上被窃听。需要在配置文件中配置证书和密钥。net: ssl: mode: requireSSL PEMKeyFile: /etc/ssl/mongodb.pem CAFile: /etc/ssl/ca.pem客户端连接时也需要使用--ssl选项并提供证书。配置审计日志记录所有数据库操作便于事后追溯和合规检查。在配置文件中启用auditLog: destination: file format: JSON path: /var/log/mongodb/audit.log禁用服务器端JavaScript如果业务完全用不到$where、mapReduce等需要JS引擎的功能可以彻底禁用以消除潜在RCE风险。在配置文件中设置security: javascriptEnabled: false注意这会影响依赖这些功能的应用程序修改前需充分测试。使用配置文件并设置严格权限永远通过配置文件来管理MongoDB设置而不是依赖命令行参数。确保配置文件 (mongod.conf) 的权限为600且属主为mongodb用户或对应的运行用户。sudo chmod 600 /etc/mongod.conf sudo chown mongodb:mongodb /etc/mongod.conf定期更新与漏洞扫描保持MongoDB版本更新到稳定版定期使用Nmap、Nessus、OpenVAS等工具或商业漏洞扫描器对数据库服务进行扫描及时发现新漏洞或错误配置。6. 常见问题排查与实战避坑指南在实际操作中从探测到加固每一步都可能遇到坑。我把自己和同行们踩过的雷整理成了这份速查表。6.1 连接与探测阶段问题问题现象可能原因排查思路与解决方案nmap扫描显示端口开放但mongosh连接超时或被拒绝。1. 防火墙或云安全组拦截了连接。2. MongoDB配置了bindIp未绑定到扫描的IP上。3. 服务异常或已崩溃。1. 检查服务器本地防火墙(iptables -L -n)和云平台安全组规则。2. 如果可能在服务器本地执行ss -tlnp | grep 27017查看服务实际监听的IP。3. 检查MongoDB服务状态和日志(systemctl status mongod,tail -f /var/log/mongodb/mongod.log)。连接成功但执行show dbs返回空或提示无权限。1. 数据库确实为空。2.MongoDB 3.0版本以--auth启动但无用户此时本地连接也无任何权限show dbs返回空。这是正常安全行为不代表漏洞不存在只是当前状态无数据。尝试执行db.version()或db.serverStatus()等不需要特定数据库权限的命令。如果这些能执行说明存在未授权访问且有一定权限。如果连这些命令都报认证错误则说明服务已开启强认证。使用工具脚本连接时报“ServerSelectionTimeoutError”等超时错误。1. 网络问题。2. MongoDB副本集配置导致。客户端驱动尝试连接主节点但当前节点可能不是主节点或配置复杂。1. 先用telnet IP 27017测试基础TCP连通性。2. 尝试在连接字符串中指定directConnectiontrue参数强制直连当前节点mongodb://IP:27017/?directConnectiontrue。公网搜索引擎发现大量开放27017端口的IP但手动连接不上。1. 目标已修复漏洞添加了IP白名单。2. 可能是蜜罐。蜜罐会响应端口扫描但在你进行实质性操作如show dbs时断开连接或返回虚假数据。保持警惕。在授权测试中对公网目标进行任何连接尝试都需谨慎。观察连接后的响应速度、返回的数据是否合理如版本号是否怪异。不要使用真实信息进行测试。6.2 配置与加固阶段问题问题现象可能原因排查思路与解决方案在配置文件中设置了bindIp: 127.0.0.1并重启但应用服务器无法连接。应用服务器与MongoDB不在同一台机器上127.0.0.1只允许本机连接。将bindIp设置为127.0.0.1,应用服务器内网IP多个IP用逗号分隔。确保重启服务。启用认证(authorization: enabled)后应用无法连接数据库。1. 应用连接字符串未配置用户名密码。2. 创建的用户权限不足或密码错误。3. 用户创建在了错误的数据库。1. 检查应用配置连接字符串格式应为mongodb://username:passwordhost:port/database?authSourceadmin。2. 用管理员用户登录检查对应用户是否存在、密码是否正确、角色是否分配use admin; db.system.users.find({user: “app_user”})。3. 注意authSource参数它指定了用户凭证所在的数据库通常是admin。重启MongoDB服务失败日志报错。1. 配置文件语法错误YAML格式非常严格缩进错误是常见原因。2. 数据文件权限问题。3. 端口被占用。1. 使用mongod -f /etc/mongod.conf --fork --logpath /tmp/mongod_test.log来测试配置文件并查看详细日志。2. 检查数据目录(dbpath)的属主和权限确保MongoDB运行用户有读写权限。3. 使用netstat -tlnp | grep 27017检查端口占用。为数据库创建了用户但该用户登录后无法看到其他数据库。这是正常且正确的行为。MongoDB的用户权限是数据库级别的。一个在dbA创建的用户默认只能看到和操作dbA除非被授予了其他数据库的角色。如果需要跨库权限需要在创建用户时明确指定。例如在admin库创建一个能管理所有库的用户roles: [ { role: “readWriteAnyDatabase”, db: “admin” } ]。6.3 我的独家避坑技巧“测试环境”不应该是“不安全环境”的借口很多漏洞都源于测试环境。建议为测试环境也建立一套简化的安全基线至少做到绑定内网IP和设置密码。可以使用简单的密码但绝不能没有。Docker部署尤其要注意使用Docker运行MongoDB时很多人图方便直接docker run -p 27017:27017 mongo这会将端口暴露给主机所有接口。更安全的做法是使用Docker网络让MongoDB只与特定的应用容器通信。如果必须暴露端口结合--bind_ip和宿主机的防火墙规则。一定要设置环境变量MONGO_INITDB_ROOT_USERNAME和MONGO_INITDB_ROOT_PASSWORD来初始化root用户。docker run -d --name some-mongo \ -p 127.0.0.1:27017:27017 \ -e MONGO_INITDB_ROOT_USERNAMEadmin \ -e MONGO_INITDB_ROOT_PASSWORDcomplexpassword \ mongo密码不是万能的网络隔离是基础即使设置了复杂的密码如果数据库端口暴露在公网依然会面临暴力破解和潜在未知漏洞的风险。网络层面限制访问源永远是最有效、最应该优先实施的安全措施。善用mongod的--syslog和--quiet参数在生产环境建议将日志重定向到系统日志(--syslog)便于集中管理。同时使用--quiet参数可以减少日志中的噪音但注意这可能会过滤掉一些有用的调试信息建议在稳定运行后开启。定期进行安全配置审计可以编写脚本或使用Ansible等自动化工具定期检查线上MongoDB实例的配置项如bindIp、security.authorization并与安全基线进行比对及时发现配置漂移。