* 本文中涉及到的相关漏洞已报送厂商并得到修复, 本文仅限技术研究与讨论, 严禁用于非法用途, 否则产生的一切后果自行承担
在前段时间的 Defcon 会议上, Nmap 发布了 7.8 版本, 包括了 Npcap 抓包驱动程序更新和 80 多项 NSE 脚本, 库等 更新 https://nmap.org/changelog#7.80 , 中文版更新详情看这里. 查看更新内容是, 突然发现关于 MongoDB 的 NSE 脚本更新似曾相识, 点进去一看, 还真是之前我提交的 #1565bug https://github.com/nmap/nmap/issues/1565 修复请求.
年初 MongoDB 数据库未授权访问漏洞造成的数据泄露事件频发, 因工作需要关注了这个漏洞, 网上搜索资料, 发现从漏洞检测到漏洞整改, 网上已有的方法, 工具均存在或多或少的问题, 漏洞整改建议也不完整或过时. 于是对当时已有的各个 MongoDB 版本均做了测试, 重新梳理了漏洞检测方法及漏洞整改建议, 形成了这篇文档.
一, 漏洞危害
对外开放的 MongoDB 服务, 未配置访问认证授权, 无需认证连接数据库后对数据库进行任意操作(增, 删, 改, 查高危动作), 存在严重的数据泄露风险.
二, 漏洞成因
MongoDB 服务安装后, 默认未开启权限验证. 如果服务监听在 0.0.0.0, 则可远程无需授权访问数据库.
3.0 之前版本的 MongoDB, 默认监听在 0.0.0.0,3.0 及之后版本默认监听在 127.0.0.1.
3.0 之前版本, 如未添加用户管理员账号及数据库账号, 使用 --auth 参数启动时, 在本地通过 127.0.0.1 仍可无需账号密码登陆访问数据库, 远程访问则提示需认证;
3.0 及之后版本, 使用 --auth 参数启动后, 无账号则本地和远程均无任何数据库访问权限.
三, 漏洞验证
MongoDB 存在未授权访问漏洞时, 使用 MongoDB 的高版本 shell 工具 mongo (高版本兼容低版本) 直接连接成功, 执行 show dbs 命令, 无报错信息, 列表存在默认库 local 库, 则判断存在未授权访问(local 库即便删除, 重启 MongoDB 后仍会生成).
- C:\ "C:\Program Files\MongoDB\Server\4.0\bin\mongo.exe" 127.0.0.1
- > show dbs
- admin 0.000GB
- config 0.000GB
- local 0.000GB
- test 0.000GB
部分版本默认不存在 admin 库, 但如果创建了用户管理员账号, 这个库一定存在, 如果存在 admin 库, 可以进入该库查看是否有用户, 有用户则表示添加了用户管理员账号, 但未使用 --auth 参数启动.
- > use admin
- switched to db admin
- > show users
- {
- "_id" : "admin.admin",
- "user" : "admin",
- "db" : "admin",
- "roles" : [
- {
- "role" : "userAdminAnyDatabase",
- "db" : "admin"
- }
- ],
- "mechanisms" : [
- "SCRAM-SHA-1",
- "SCRAM-SHA-256"
- ]
- }
也可以使用图形化界面管理工具连接服务进行测试.
Robo 3T 下载地址: https://robomongo.org/download , 建议使用 1.2.1 版本, 使用 1.3 及之后版本的话, 服务端如果使用较低版本 MongoDB, 存在未授权访问时仍无法连接.
四, 整改建议
4.1. 方案 1: 本地监听
如 MongoDB 只需在本地使用, 建议只在本地开启监听服务, 使用 --bind_ip 127.0.0.1 绑定监听地址.
$ mongod --bind_ip 127.0.0.1 --dbpath /tmp/test
或: 在配置文件中指定监听 IP,Linux 下默认配置文件为 / etc/mongod.conf.
- # network interfaces
- net:
- port: 27017
- bindIp: 127.0.0.1
指定配置文件启动
mongod --config /etc/mongod.conf
3.0 及之后版本的 MongoDB, 监听服务默认在 127.0.0.1 开启.
4.2. 方案 2: 限制访问源
如果仅对内网服务器提供服务, 建议禁止将 MongoDB 服务发布到互联网上, 并在主机上通过防火墙限制访问源 IP.
Linux 主机, 使用 iptables 进行限制:
- iptables -A INPUT -s <ip-address> -p tcp --destination-port 27017 -m state --state NEW,ESTABLISHED -j ACCEPT
- iptables -A OUTPUT -d <ip-address> -p tcp --source-port 27017 -m state --state ESTABLISHED -j ACCEPT
- <ip-address> 替换成需访问 MongoDB 服务的 IP; 第一条规则允许外部应用访问 MongoDB 默认服务端口 27017, 第二条规则允许 MongoDB 外出流量到达外部应用. Windows 主机, 可以通过图形管理界面添加防火墙策略或使用 netsh 命令添加, 参考文档:
4.3. 方案 3: 启动基于角色的登录认证功能
MongoDB 支持 SCRAM,x.509 证书认证等多种认证机制, SCRAM(Salted Challenge Response Authentication Mechanism)是 3.x 版本的默认认证机制, 该机制通过用户名, 密码验证, 基于用户角色进行访问控制. 下面是添加账号认证的方法:
MongoDB 3.0 及以上版本启动时添加 --auth 参数开启认证访问, 此时若数据库中无账号, 本地登录则无权限进行任何操作, 因此需要先以无认证的方式启动服务并创建系统用户管理员账号.
1)以无访问认证的方式启动 MongoDB
$ mongod --dbpath /data/db
2)未开启认证的环境下, 登录到数据库
- $ mongo --host 127.0.0.1 --port 27017
- MongoDB shell version v4.0.8
- connecting to: MongoDB://127.0.0.1:27017/test?gssapiServiceName=MongoDB
3)创建系统用户管理员创建一个用户名为 myUserAdmin , 密码为 Passw0rd 的系统用户管理员账号.
- # 切换到 admin 库:
- > use admin
- switched to db admin
- # 创建用户
- > db.createUser(
- {
- user: "myUserAdmin",
- pwd: "Passw0rd",
- roles: [ { role: "userAdminAnyDatabase", db: "admin" } ]
- }
- )
- # 创建成功后提示信息:
- Successfully added user: {
- "user" : "myUserAdmin",
- "roles" : [
- {
- "role" : "userAdminAnyDatabase",
- "db" : "admin"
- }
- ]
- }
说明: 3.0 之前版本使用 db.addUser 方法创建用户.
4)重启 MongoDB 服务, 开启访问认证启动时添加 --auth 参数
$ mongod --auth --port 27017 --dbpath /tmp/test
或: 在配置文件中添加以下内容, 指定配置文件启动服务:
- #security:
- security:
- authorization: enabled
指定配置文件启动 MongoDB
$ mongod --config /etc/mongod.conf
5)使用系统用户管理员账号登录
$ mongo --port 27017 -u "myUserAdmin" -p "Passw0rd" --authenticationDatabase "admin"
也可以在连接 MongoDB 是不指定认证信息, 连接成功后通过 db.auth()方法进行认证:
- > use admin
- switched to db admin
- > db.auth("myUserAdmin","Passw0rd")
- 1 #返回 1, 表示认证成功
6)创建数据库用户创建完系统用户管理员账号并通过系统用户管理员登陆后, 对每个库创建指定的用户. 下面以对 products 库创建一个具有读写权限的用户 accountUser 为例:
- > use products
- > db.createUser(
- {
- user: "accountUser",
- pwd: "password",
- roles: [ "readWrite", "dbAdmin" ]
- }
- )
- # 用户创建成功
- Successfully added user: { "user" : "accountUser", "roles" : [ "readWrite", "dbAdmin" ] }
7)使用数据库用户访问指定库
$ mongo --port 27017 -u "accountUser" -p "password" --authenticationDatabase "products"
五, MongoDB 安装
5.1. Windows 下安装
MongoDB 支持以下系统:
- Windows 7/Server 2008 R2
- Windows 8/2012 R2 and later
Windows 2012 Server 和 Windows 10 需要安装 KB2999226 用以提供 C Runtime 支持.
1. 下载安装文件 MongoDB Download Center 操作系统 (OS) 选择 Windows 64-bit x64, 然后点击 Download 按钮进行下载.
2. 安装 MongoDB 双击下载好的 MSI 文件开始安装, 注意不要勾选 "Install MongoDB as a Service". 3. 启动服务 在 C 盘根目录下创建一个目录 data , 用于存放 MongoDB 数据文件. 使用管理员权限开启 CMD 窗口, 执行以下命令:
"C:\Program Files\MongoDB\Server\4.0\bin\mongod.exe" --bind_ip 0.0.0.0 --port 27017 --dbpath="c:\data"
5.2. Ubuntu 下安装
1. 添加 MongoDB GPG 公钥
sudo apt-key adv --keyserver hkp://keyserver.Ubuntu.com:80 --recv 9DA31620334BD75D9DCB49F368818C72E52529D4
2. 添加 MongoDB 软件源需根据 Ubuntu 版本添加软件源
- Ubuntu 14.04 (Trusty)
- echo "deb [ arch=amd64 ] https://repo.mongodb.org/apt/ubuntu trusty/mongodb-org/4.0 multiverse" | sudo tee /etc/apt/sources.list.d/MongoDB-org-4.0.list
- Ubuntu 16.04 (Xenial)
- echo "deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu xenial/mongodb-org/4.0 multiverse" | sudo tee /etc/apt/sources.list.d/MongoDB-org-4.0.list
- Ubuntu 18.04 (Bionic)
- echo "deb [ arch=amd64 ] https://repo.mongodb.org/apt/ubuntu bionic/mongodb-org/4.0 multiverse" | sudo tee /etc/apt/sources.list.d/MongoDB-org-4.0.list
3. 更新软件包列表
sudo apt-get update
4. 安装 MongoDB
sudo apt-get install -y MongoDB-org
1. 启动服务在 / tmp 目录下创建一个文件夹, 用于存放 MongoDB 数据库文件(实际生产环境中根据需求指定 MongoDB 目录路径, 严禁存放在 / tmp 目录下). 执行以下命令启动服务
mongod --bind_ip 0.0.0.0 --port 27017 --dbpath /tmp/mongodb_test
六, 各类工具检测漏报原因分析
6.1. Python 脚本检测
网上流传的各类 Python 检测脚本, 主要检测方式是以下两种:
1. 一种检测方式是使用 Python 的 PyMongo 库, 不使用账号密码连接 MongoDB 服务, 连接成功则证明存在未授权访问漏洞. 由于 PyMongo 库只支持 MongoDB 2.6 及之后版本, 低版本 MongoDB 存在漏洞时无法发现.
2. 另一种检测方式是使用 socket 与 MongoDB 服务端口建立连接后发送数据库查询指令, 返回的数据中, 存在默认库 local , 则表示数据库查询成功, 存在未授权访问漏洞. 在 1.9 版本的 MongoDB 服务中, 报错信息中包含 local 字符, 引起误报.
6.2. Nmap 脚本
Nmap 的 MongoDB-brute 脚本, 先判断是否开启验证, 如果开启验证再判断是否存在弱口令. 脚本运行的条件是端口为 27017 或服务名称为 MongoDB, 由于 Nmap 端口检测结果中的服务名称是 mongod, 名称不一致, 脚本不运行, 存在漏报. 另外脚本执行过程中, 脚本执行出错, 也会引起漏报.
该问题已向 Nmap 提交更新请求, 检测非默认端口的 MongoDB 服务. https://github.com/nmap/nmap/issues/1565
七, Python 检测脚本示例
- def MongoDB(self, ip):
- try:
- socket.setdefaulttimeout(timeout)
- s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- s.connect((ip, 27017)) s.send('\x3F\x00\x00\x00\x7E\x00\x00\x00\x00\x00\x00\x00\xD4\x07\x00\x00\x04\x00\x00\x00\x61\x64\x6D\x69\x6E\x2E\x24\x63\x6D\x64\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\x18\x00\x00\x00\x10\x6C\x69\x73\x74\x44\x61\x74\x61\x62\x61\x73\x65\x73\x00\x01\x00\x00\x00\x00')
- result = s.recv(1024)
- if 'local' in result and 'errmsg' not in result: #增加一个 "errmsg" 字符串判断, 避免 1.9 版本中的误报.
- print(u'{}[+] {}:27017\tMongoDB 存在未授权访问{}'.format(G, ip, W))
- with open('weakpass.txt', 'a+') as f:
- f.write('{}\t27017\tMongoDB 存在未授权访问 \ n'.format(ip,))
- except Exception as e:
- pass
- finally:
- s.close()
- mongodb_check("192.168.1.10","27017")
八, 参考资料
账号管理命令
修改用户权限
db.updateUser("root", {roles:[{role:"readWriteAnyDatabase", db:"admin"}] })
修改用户密码
db.changeUserPassword("username","newPasswd")
删除用户
db.changeUserPassword("username","newPasswd")
MongoDB 内建角色:
认证相关官方文档该整改建议中命令方法在 4.0 版本中测试通过, 低版本的 MongoDB 中, 命令或角色定义可能有所变化, 整改过程中, 建议根据实际使用版本, 查询 MongoDB 官方文档.
MongoDB 安装文档
各版本 MongoDB Windows 服务端下载
来源: http://www.tuicool.com/articles/3EZ7buB