package mongodb import ( "context" "fmt" "io" "net" "strings" "time" "github.com/shadow1ng/fscan/common" "github.com/shadow1ng/fscan/common/i18n" "github.com/shadow1ng/fscan/plugins/base" ) // MongoDBPlugin MongoDB插件实现 type MongoDBPlugin struct { *base.ServicePlugin exploiter *MongoDBExploiter } // NewMongoDBPlugin 创建MongoDB插件 func NewMongoDBPlugin() *MongoDBPlugin { // 插件元数据 metadata := &base.PluginMetadata{ Name: "mongodb", Version: "2.0.0", Author: "fscan-team", Description: "MongoDB NoSQL数据库扫描和利用插件", Category: "service", Ports: []int{27017, 27018, 27019}, // 默认MongoDB端口 Protocols: []string{"tcp"}, Tags: []string{"mongodb", "nosql", "database", "unauthorized"}, } // 创建连接器和服务插件 connector := NewMongoDBConnector() servicePlugin := base.NewServicePlugin(metadata, connector) // 创建MongoDB插件 plugin := &MongoDBPlugin{ ServicePlugin: servicePlugin, exploiter: NewMongoDBExploiter(), } // 设置能力 plugin.SetCapabilities([]base.Capability{ base.CapUnauthorized, base.CapDataExtraction, base.CapWeakPassword, // 将来可能支持弱密码扫描 }) return plugin } // Scan 重写扫描方法,检测未授权访问 func (p *MongoDBPlugin) Scan(ctx context.Context, info *common.HostInfo) (*base.ScanResult, error) { // 如果禁用了暴力破解,只进行服务识别 if common.DisableBrute { return p.performServiceIdentification(ctx, info) } target := fmt.Sprintf("%s:%s", info.Host, info.Ports) // MongoDB主要检查未授权访问 unauthCred := &base.Credential{Username: "", Password: ""} result, err := p.ScanCredential(ctx, info, unauthCred) if err == nil && result.Success { // 未授权访问成功 common.LogSuccess(i18n.GetText("mongodb_unauth_access", target)) return &base.ScanResult{ Success: true, Service: "MongoDB", Credentials: []*base.Credential{unauthCred}, Extra: map[string]interface{}{ "service": "MongoDB", "port": info.Ports, "unauthorized": true, "access_type": "no_authentication", }, }, nil } // 如果未授权访问失败,返回失败结果 return &base.ScanResult{ Success: false, Error: fmt.Errorf("MongoDB服务需要认证或连接失败"), }, nil } // generateCredentials MongoDB主要用于未授权访问检测 func (p *MongoDBPlugin) generateCredentials() []*base.Credential { // MongoDB主要检查未授权访问,但也可以尝试一些默认凭据 credentials := []*base.Credential{ {Username: "", Password: ""}, // 未授权访问 } // 如果有MongoDB专用字典,添加常见凭据 usernames := common.Userdict["mongodb"] if len(usernames) == 0 { usernames = []string{"admin", "root", "mongo", "mongodb"} } // 添加一些常见的MongoDB凭据 for _, username := range usernames { credentials = append(credentials, &base.Credential{ Username: username, Password: "", // MongoDB常见空密码 }) credentials = append(credentials, &base.Credential{ Username: username, Password: username, // 用户名等于密码 }) } return credentials } // Exploit 使用exploiter执行利用 func (p *MongoDBPlugin) Exploit(ctx context.Context, info *common.HostInfo, creds *base.Credential) (*base.ExploitResult, error) { return p.exploiter.Exploit(ctx, info, creds) } // GetExploitMethods 获取利用方法 func (p *MongoDBPlugin) GetExploitMethods() []base.ExploitMethod { return p.exploiter.GetExploitMethods() } // IsExploitSupported 检查利用支持 func (p *MongoDBPlugin) IsExploitSupported(method base.ExploitType) bool { return p.exploiter.IsExploitSupported(method) } // performServiceIdentification 执行MongoDB服务识别(-nobr模式) func (p *MongoDBPlugin) performServiceIdentification(ctx context.Context, info *common.HostInfo) (*base.ScanResult, error) { target := fmt.Sprintf("%s:%s", info.Host, info.Ports) // 尝试识别MongoDB服务 mongoInfo, isMongo := p.identifyMongoDBService(ctx, info) if isMongo { // 记录服务识别成功 common.LogSuccess(i18n.GetText("mongodb_service_identified", target, mongoInfo)) return &base.ScanResult{ Success: true, Service: "MongoDB", Banner: mongoInfo, Extra: map[string]interface{}{ "service": "MongoDB", "port": info.Ports, "info": mongoInfo, }, }, nil } // 如果无法识别为MongoDB,返回失败 return &base.ScanResult{ Success: false, Error: fmt.Errorf("无法识别为MongoDB服务"), }, nil } // identifyMongoDBService 通过协议识别MongoDB服务 func (p *MongoDBPlugin) identifyMongoDBService(ctx context.Context, info *common.HostInfo) (string, bool) { target := fmt.Sprintf("%s:%s", info.Host, info.Ports) timeout := time.Duration(common.Timeout) * time.Second // 尝试建立TCP连接 conn, err := net.DialTimeout("tcp", target, timeout) if err != nil { return "", false } defer conn.Close() // 设置操作超时 conn.SetDeadline(time.Now().Add(timeout)) // 尝试发送MongoDB查询来识别服务 msgPacket := createOpMsgPacket() if _, err := conn.Write(msgPacket); err != nil { return "", false } // 读取响应 reply := make([]byte, 1024) n, err := conn.Read(reply) if err != nil && err != io.EOF { // 尝试OP_QUERY查询 queryPacket := createOpQueryPacket() if _, err := conn.Write(queryPacket); err != nil { return "", false } n, err = conn.Read(reply) if err != nil && err != io.EOF { return "", false } } if n > 0 { response := string(reply[:n]) // 检查是否包含MongoDB相关内容 if strings.Contains(response, "totalLinesWritten") || strings.Contains(response, "MongoDB") || strings.Contains(response, "WiredTiger") || strings.Contains(response, "unauthorized") { // 尝试提取版本信息 if strings.Contains(response, "MongoDB") { return "MongoDB服务 (已识别协议响应)", true } return "MongoDB服务", true } } return "", false } // ============================================================================= // 插件注册 // ============================================================================= // RegisterMongoDBPlugin 注册MongoDB插件 func RegisterMongoDBPlugin() { factory := base.NewSimplePluginFactory( &base.PluginMetadata{ Name: "mongodb", Version: "2.0.0", Author: "fscan-team", Description: "MongoDB NoSQL数据库扫描和利用插件", Category: "service", Ports: []int{27017, 27018, 27019}, // 默认MongoDB端口 Protocols: []string{"tcp"}, Tags: []string{"mongodb", "nosql", "database", "unauthorized"}, }, func() base.Plugin { return NewMongoDBPlugin() }, ) base.GlobalPluginRegistry.Register("mongodb", factory) } // 自动注册 func init() { RegisterMongoDBPlugin() }