Improve partial results for mongodb scanner

* Separate out isMaster and buildInfo commands
* Return results of both in separate sub-structs
* Include isMaster results regardless of whether buildInfo succeeds
This commit is contained in:
Paul A. Parkanzky 2018-08-22 11:49:17 -04:00
parent 68bb3e9790
commit d9aec0ae40
2 changed files with 58 additions and 36 deletions

@ -160,13 +160,31 @@ type BuildEnvironment_t struct {
TargetOS string `bson:"target_os,omitempty" json:"target_os,omitempty"`
}
// Result holds the data returned by the scan
type Result struct {
// BuildInfo_t holds the data returned by the the buildInfo query
type BuildInfo_t struct {
Version string `bson:"version,omitempty" json:"version,omitempty"`
GitVersion string `bson:"gitVersion,omitempty" json:"git_version,omitempty"`
BuildEnvironment BuildEnvironment_t `bson:"buildEnvironment,omitempty" json:"build_environment,omitempty"`
}
// IsMaster_t holds the data returned by an isMaster query
type IsMaster_t struct {
IsMaster bool `bson:"isMaster" json:"is_master"`
MaxWireVersion int32 `bson:"maxWireVersion,omitempty" json:"max_wire_version,omitempty"`
MinWireVersion int32 `bson:"minWireVersion,omitempty" json:"min_wire_version,omitempty"`
MaxBsonObjectSize int32 `bson:"maxBsonObjectSize,omitempty" json:"max_bson_object_size,omitempty"`
MaxWriteBatchSize int32 `bson:"maxWriteBatchSize,omitempty" json:"max_write_batch_size,omitempty"`
LogicalSessionTimeoutMinutes int32 `bson:"logicalSessionTimeoutMinutes,omitempty" json:"logical_session_timeout_minutes,omitempty"`
MaxMessageSizeBytes int32 `bson:"maxMessageSizeBytes,omitempty" json:"max_message_size_bytes,omitempty"`
ReadOnly bool `bson:"readOnly" json:"read_only"`
}
// Result holds the data returned by a scan
type Result struct {
IsMaster *IsMaster_t `json:"is_master"`
BuildInfo *BuildInfo_t `json:"build_info"`
}
// Init initializes the scanner
func (scanner *Scanner) Init(flags zgrab2.ScanFlags) error {
f, _ := flags.(*Flags)
@ -241,45 +259,39 @@ func (scanner *Scanner) StartScan(target *zgrab2.ScanTarget) (*scan, error) {
}, nil
}
// IsMasterResult hold the result of an isMaster command. Currently,
// only interested in maxWireVersion.
type IsMasterResult struct {
MaxWireVersion int32 `bson:"maxWireVersion"`
}
// getMaxWireVersion retrieves the maxWireVersion value reported by the MongoDB server.
func getMaxWireVersion(conn *Connection) (int32, error) {
document := &IsMasterResult{}
// getIsMaster issues the isMaster command to the MongoDB server and returns the result.
func getIsMaster(conn *Connection) (*IsMaster_t, error) {
document := &IsMaster_t{}
doc_offset := MSGHEADER_LEN + 20
conn.Write(conn.scanner.isMasterMsg)
msg, err := conn.ReadMsg()
if err != nil {
return 0, err
return nil, err
}
if len(msg) < MSGHEADER_LEN + 4 {
err = fmt.Errorf("Server truncated message - no query reply (%d bytes: %s)", len(msg), hex.EncodeToString(msg))
return 0, err
return nil, err
}
respFlags := binary.LittleEndian.Uint32(msg[MSGHEADER_LEN:MSGHEADER_LEN + 5])
if respFlags & QUERY_RESP_FAILED != 0 {
err = fmt.Errorf("isMaster query failed")
return 0, err
return nil, err
}
doclen := int(binary.LittleEndian.Uint32(msg[doc_offset:doc_offset + 4]))
if len(msg[doc_offset:]) < doclen {
err = fmt.Errorf("Server truncated BSON reply doc (%d bytes: %s)",
len(msg[doc_offset:]), hex.EncodeToString(msg))
return 0, err
return nil, err
}
err = bson.Unmarshal(msg[doc_offset:], &document)
if err != nil {
err = fmt.Errorf("Server sent invalid BSON reply doc (%d bytes: %s)",
len(msg[doc_offset:]), hex.EncodeToString(msg))
return 0, err
return nil, err
}
return document.MaxWireVersion, nil
return document, nil
}
// Scan connects to a host and performs a scan.
@ -291,7 +303,7 @@ func (scanner *Scanner) Scan(target zgrab2.ScanTarget) (zgrab2.ScanStatus, inter
defer scan.Close()
result := scan.result
max_wirev, err := getMaxWireVersion(scan.conn)
result.IsMaster, err = getIsMaster(scan.conn)
if err != nil {
return zgrab2.SCAN_PROTOCOL_ERROR, nil, err
}
@ -304,7 +316,7 @@ func (scanner *Scanner) Scan(target zgrab2.ScanTarget) (zgrab2.ScanStatus, inter
// "build info" command should be sent in an OP_COMMAND with the query sent
// and response retrieved at "metadata" offset. At 7 and above, should
// be sent as an OP_MSG in the "section" field, and response is at "body" offset
if max_wirev < 7 {
if result.IsMaster.MaxWireVersion < 7 {
query = scanner.buildInfoCommandMsg
resplen_offset = 4
resp_offset = 0
@ -317,21 +329,21 @@ func (scanner *Scanner) Scan(target zgrab2.ScanTarget) (zgrab2.ScanStatus, inter
scan.conn.Write(query)
msg, err := scan.conn.ReadMsg()
if err != nil {
return zgrab2.TryGetScanStatus(err), nil, err
return zgrab2.TryGetScanStatus(err), &result, err
}
if len(msg) < MSGHEADER_LEN + resplen_offset {
err = fmt.Errorf("Server truncated message - no metadata doc (%d bytes: %s)", len(msg), hex.EncodeToString(msg))
return zgrab2.SCAN_PROTOCOL_ERROR, nil, err
return zgrab2.SCAN_PROTOCOL_ERROR, &result, err
}
responselen := int(binary.LittleEndian.Uint32(msg[MSGHEADER_LEN:MSGHEADER_LEN + resplen_offset]))
if len(msg[MSGHEADER_LEN:]) < responselen {
err = fmt.Errorf("Server truncated BSON response doc (%d bytes: %s)",
len(msg[MSGHEADER_LEN:]), hex.EncodeToString(msg))
return zgrab2.SCAN_PROTOCOL_ERROR, nil, err
return zgrab2.SCAN_PROTOCOL_ERROR, &result, err
}
bson.Unmarshal(msg[MSGHEADER_LEN+resp_offset:], &result)
bson.Unmarshal(msg[MSGHEADER_LEN+resp_offset:], &result.BuildInfo)
return zgrab2.SCAN_SUCCESS, &result, err
}

@ -9,19 +9,29 @@ import zgrab2
mongodb_scan_response = SubRecord({
"result": SubRecord({
"version": String(doc="Version of mongodb server"),
"git_version": String(doc="Git Version of mongodb server"),
"build_environment": SubRecord({
"dist_mod": String(),
"dist_arch": String(),
"cc": String(),
"cc_flags": String(),
"cxx": String(),
"cxx_flags": String(),
"link_flags": String(),
"target_arch": String(),
"target_os": String()})
})
"build_info": SubRecord({
"version": String(doc="Version of mongodb server"),
"git_version": String(doc="Git Version of mongodb server"),
"max_wire_version": Signed32BitInteger(),
"build_environment": SubRecord({
"dist_mod": String(),
"dist_arch": String(),
"cc": String(),
"cc_flags": String(),
"cxx": String(),
"cxx_flags": String(),
"link_flags": String(),
"target_arch": String(),
"target_os": String()})}),
"is_master": SubRecord({
"is_master": Boolean(),
"max_wire_version": Signed32BitInteger(),
"min_wire_version": Signed32BitInteger(),
"max_bson_object_size": Signed32BitInteger(),
"max_write_batch_size": Signed32BitInteger(),
"logical_session_timeout_minutes": Signed32BitInteger(),
"max_message_size_bytes": Signed32BitInteger(),
"read_only": Boolean()})})
}, extends=zgrab2.base_scan_response)
zschema.registry.register_schema("zgrab2-mongodb", mongodb_scan_response)