Nmap识别MongoDB 6.0指纹

释放双眼,带上耳机,听听看~!
00:00
00:00

Nmap识别MongoDB 6.0指纹

朋友反馈一个问题,说使用Nmap扫描MongoDB服务时对于6.0以上的版本默认无法识别到服务版本信息。

如上图所示,对应的VERSION信息是空的,在提示信息中可以看到,官方推荐将指纹信息上传以帮助更新服务指纹,又或者可以通过Nmap的默认脚本mongodb-info来做版本识别。前一种方式需要等待官方做指纹文件更新或产品版本更新,后者使用脚本又会影响到扫描速度。如果想在当前的Nmap版本下不使用脚本来识别MongoDB 6.0以上版本,唯一的办法是使用Nmap内置的服务指纹将MongoDB 6.0服务版本识别出来。

 

Nmap服务指纹解析

 

Nmap默认的服务指纹文件位于/usr/share/nmap目录下,文件名是nmap-service-probes,根据默认端口27017可以找到Nmap识别MongoDB服务的内容:

上图中的匹配规则用到了四个指令:

  • Probe:为识别服务发送的字符串信息;
  • rarity:1-9范围的数字,是服务识别和可靠性的概率,数字越大,扫描概率越小,返回信息价值越低;
  • ports:服务识别常用的端口,效果与nmap的-p参数相同;
  • match:根据Probe指令发送字符串返回的信息,匹配服务指纹规则;

 

上图中用于识别MongoDB服务,通过TCP协议发送到服务端的字符串是:

  1. x41x3ax30xffxffxffxffxd4x07test.$cmdxffxffxffxffx1bx01serverStatusxf0x3f

 

match指令的格式包括:

  • 服务名称(service)
  • 匹配规则(pattern)
  • 版本信息(versioninfo)

 

以第一行的match指令为例:

  1. match mongodb m|^.*version.....([.d]+)|s p/MongoDB/ v/$1/ cpe:/a:mongodb:mongodb:$1/
  • 服务名称:MongoDB
  • 匹配规则:m|^.*version…..([.d]+)|s,采用Perl格式的正则表达式,s表示包含换行符;
  • 版本信息:p/MongoDB/ v/$1/ cpe:/a:mongodb:mongodb:$1/,其中p指的是厂商信息,v指的是版本信息,这里引用自匹配规则里匹配的参数,cpe指的CPE(Common Platform Enumeration)格式,用于识别服务、操作系统和硬件;

CPE的格式如下:

  1. cpe:/<part>:<vendor>:<product>:<version>:<update>:<edition>:<language>

 

除了v指令之外,CPE的第4个值也是产品版本信息,因此上图匹配规则中,只有第一行的规则可以用于识别MongoDB服务版本,通过匹配返回信息中的version信息来显示MongoDB版本。

 

MongoDB 5.0.1指纹解析

 

以MongoDB 5.0.1版本为例,当使用nmap对目标的27017端口进行扫描时:

nmap会发送上文中的Probe指令:

MongoDB响应数据包中可以匹配上文中第一条match指令:

因为在Nmap的扫描结果中,可以看到version(版本号)是p指令+指令,即“MongoDB v5.0.1”。

 

MongoDB有线协议

 

MongoDB有线协议(Wire Protocol)是请求/响应形式的TCP/IP的Socket,客户端和服务端通过该协议进行通讯,服务端实例(mongod和mongos)的默认端口是27017。

 

以MongoDB 6.0为例,客户端和服务端在通讯时的消息格式采用的是OP_MSG操作符,即采用以下格式对消息进行编码:

  1. OP_MSG {
  2. MsgHeader header; // standard message header
  3. uint32 flagBits; // message flags
  4. Sections[] sections; // data sections
  5. optional<uint32> checksum; // optional CRC-32C checksum
  6. }

 

其中,MsgHeader是标准信息头,客户端与服务端来往的消息都包含标准信息头(在SSL/TLS连接时没有checksum字段),其后才是消息内容(如OP_MSG结构中sections数组,数组每一项由Kind值开头,后接载体数据),标准信息头的结构如下:

  1. struct MsgHeader {
  2. int32 messageLength; // total message size, including this
  3. int32 requestID; // identifier for this message
  4. int32 responseTo; // requestID from the original request
  5. // (used in responses from the database)
  6. int32 opCode; // message type
  7. }
  8. MsgHeaderopCode消息类型会决定OP_MSG的结构,以OP_QUERY为例:
  9. struct OP_QUERY {
  10. MsgHeader header; // standard message header
  11. int32 flags; // bit values of query options. See below for details.
  12. cstring fullCollectionName ; // "dbname.collectionname"
  13. int32 numberToSkip; // number of documents to skip
  14. int32 numberToReturn; // number of documents to return
  15. // in the first OP_REPLY batch
  16. document query; // query object. See below for details.
  17. [ document returnFieldsSelector; ] // Optional. Selector indicating the fields
  18. // to return. See below for details.
  19. }

 

OP_QUERY类型用于查询数据库中的集合,也是Nmap进行指纹识别时发送的请求消息类型,即上文图中的Probe指令。

 

但在MongoDB 5.1版本之后,OP_QUERY等消息类型已被淘汰,而相应的Nmap的服务指纹文件和NSE脚本中的服务识别脚本都未更新MongoDB的OP_MSG消息格式,因此无法正确识别出MongoDB的版本信息。

 

MongoDB 6.0指纹识别

 

知道MongoDB 5.0.1版本的指纹识别方式,以及MongoDB的OP_MSG消息格式,便可以通过抓取MongoDB 6.0的版本信息请求的流量,修改Nmap的服务探针文件nmap-service-probe来识别服务版本。

 

首先写一段Python代码作为客户端向MongoDB服务端发起版本信息的请求:

  1. #!/bin/env python
  2. import pymongo
  3. client = pymongo.MongoClient("mongodb://192.168.168.133:27017")
  4. server_status = client.admin.command("serverStatus")
  5. print(server_status["version"])

 

同时,在脚本执行的同一环境下使用TCPDump抓取流量包

  1. sudo tcpdump -i eth0 host 192.168.168.133 -w mongo.pcap

 

基于抓取的流量包进行分析,可以看到获得最终版本信息的请求头:

从/0x5f/0x00/0x00/0x00开始的便是Python脚本发送给MongoDB服务端的OP_MSG消息头,使用该十六进制字符串替换/usr/share/nmap/nmap-service-probes文件中的Probe指令,由于响应消息中的version匹配格式与原match指令中相同,所以不用变更match指令,这样即可实现对于MongoDB 6.0版本的识别。

但现在的识别仅是相当于流量重放的结果,不一定能够识别所有MongoDB 6.0的指纹,因此需要解析上面流量的OP_MSG消息,剥离出通用版本的OP_MSG请求。

 

根据6.0版本的OP_MSG格式,可以得知上图中的十六进制字符串的构成:

  1. x5fx00x00x00OP_MSG.MsgHeader.messageLength
  2. x51xdcxb0x74OP_MSG.MsgHeader.requestID
  3. x00x00x00x00OP_MSG.MsgHeader.responseTo
  4. xddx07x00x00OP_MSG.MsgHeader.opCode
  5. x00x00x00x00OP_MSG.flagBits
  6. x00OP_MSG.sections[0].kind
  7. x4ax00x00x00x10x73x65x72x76x65x72x53x74x61x74x75x73x00x01x00x00x00x03x6cx73x69x64x00x1ex00x00x00x05x69x64x00x10x00x00x00x04x7cxc1xc2x6cxd7x11x40x21xb6xf7x5ax52x08xafxcax5ex00x02x24x64x62x00x06x00x00x00x61x64x6dx69x6ex00x00OP_MSG.sections[0].payload

 

核心部分是OP_MSG中sections的payload,使用Python中的bson模块对payload解析:

 

其中的lsid是客户端请求服务端的会话ID,是bson类型的UUID:7cc1c26cd7114021b6f75a5208afca5e。

 

因此,可以使用自定义生成uuid来创建能够查询MongoDB版本信息的OP_MSG消息头:

  1. import socket
  2. import bson
  3. import binascii
  4. import uuid
  5. def send_op_msg_request(request):
  6. host = '192.168.168.133'
  7. port = 27017
  8. client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  9. client.connect((host, port))
  10. client.sendall(request)
  11. response = client.recv(4096)
  12. client.close()
  13. return response
  14. def build_op_msg_request():
  15. msg_header = b'x5fx00x00x00' # total message size
  16. msg_header += b'x00x00x00x00' # identifier for message
  17. msg_header += b'x00x00x00x00' # request id
  18. msg_header += b'xddx07x00x00' # message type
  19. op_msg = msg_header + b'x00x00x00x00' # message flags
  20. section = {
  21. 'serverStatus': 1,
  22. 'lsid': {'id': bson.binary.Binary(uuid.uuid4().bytes, bson.binary.UUID_SUBTYPE)},
  23. '$db': 'admin'
  24. }
  25. request = op_msg + b'x00' + bson.encode(section)
  26. return request
  27. op_msg_request = build_op_msg_request()
  28. response = send_op_msg_request(op_msg_request)
  29. print(response)

上面代码中的request值就是发送至服务端的OP_MSG消息,可以用于替换nmap-service-probe文件的指纹信息。

 

以下是更换指纹前后的对比效果:

更改指纹文件前

更改指纹文件后

 

参考材料

 

https://www.mongodb.com/docs/v6.0/reference/mongodb-wire-protocol/

https://www.mongodb.com/docs/v6.0/reference/bson-types/#std-label-bson-types

https://nmap.org/book/vscan-fileformat.html

 

洞源实验室
安全工程师:裴伟伟
2024 年 11 月 19 日 

行业热点

埃文科技与郑州航空港医疗健康产业对接会共绘医疗健康新蓝图

2024-11-25 9:53:24

行业热点

数据资产入表案例

2024-11-25 9:53:38

欢迎您,新朋友,感谢参与互动!
0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
购物车
优惠劵
今日签到
私信列表
搜索