package ldap import ( "context" "fmt" "net" "strings" "time" "github.com/shadow1ng/fscan/common" "github.com/shadow1ng/fscan/common/i18n" "github.com/shadow1ng/fscan/plugins/base" ) // LDAPPlugin LDAP插件实现 type LDAPPlugin struct { *base.ServicePlugin exploiter *LDAPExploiter } // NewLDAPPlugin 创建LDAP插件 func NewLDAPPlugin() *LDAPPlugin { // 插件元数据 metadata := &base.PluginMetadata{ Name: "ldap", Version: "2.0.0", Author: "fscan-team", Description: "LDAP轻量级目录访问协议扫描和利用插件", Category: "service", Ports: []int{389, 636, 3268, 3269}, // 389: LDAP, 636: LDAPS, 3268/3269: Global Catalog Protocols: []string{"tcp"}, Tags: []string{"ldap", "directory", "bruteforce", "anonymous"}, } // 创建连接器和服务插件 connector := NewLDAPConnector() servicePlugin := base.NewServicePlugin(metadata, connector) // 创建LDAP插件 plugin := &LDAPPlugin{ ServicePlugin: servicePlugin, exploiter: NewLDAPExploiter(), } // 设置能力 plugin.SetCapabilities([]base.Capability{ base.CapWeakPassword, base.CapUnauthorized, base.CapDataExtraction, }) return plugin } // Scan 重写扫描方法,先检测匿名访问 func (p *LDAPPlugin) 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) // 先尝试匿名访问 anonymousCred := &base.Credential{Username: "", Password: ""} anonymousResult, err := p.ScanCredential(ctx, info, anonymousCred) if err == nil && anonymousResult.Success { // 匿名访问成功 common.LogSuccess(i18n.GetText("ldap_anonymous_access", target)) return &base.ScanResult{ Success: true, Service: "LDAP", Credentials: []*base.Credential{anonymousCred}, Extra: map[string]interface{}{ "service": "LDAP", "port": info.Ports, "unauthorized": true, "access_type": "anonymous", }, }, nil } // 执行基础的密码扫描 result, err := p.ServicePlugin.Scan(ctx, info) if err != nil || !result.Success { return result, err } // 记录成功的弱密码发现 cred := result.Credentials[0] common.LogSuccess(i18n.GetText("ldap_weak_pwd_success", target, cred.Username, cred.Password)) return result, nil } // 已移除未使用的 generateCredentials 方法 // Exploit 使用exploiter执行利用 func (p *LDAPPlugin) Exploit(ctx context.Context, info *common.HostInfo, creds *base.Credential) (*base.ExploitResult, error) { return p.exploiter.Exploit(ctx, info, creds) } // GetExploitMethods 获取利用方法 func (p *LDAPPlugin) GetExploitMethods() []base.ExploitMethod { return p.exploiter.GetExploitMethods() } // IsExploitSupported 检查利用支持 func (p *LDAPPlugin) IsExploitSupported(method base.ExploitType) bool { return p.exploiter.IsExploitSupported(method) } // performServiceIdentification 执行LDAP服务识别(-nobr模式) func (p *LDAPPlugin) performServiceIdentification(ctx context.Context, info *common.HostInfo) (*base.ScanResult, error) { target := fmt.Sprintf("%s:%s", info.Host, info.Ports) // 尝试连接LDAP服务获取基本信息 ldapInfo, isLDAP := p.identifyLDAPService(ctx, info) if isLDAP { // 记录服务识别成功 common.LogSuccess(i18n.GetText("ldap_service_identified", target, ldapInfo)) return &base.ScanResult{ Success: true, Service: "LDAP", Banner: ldapInfo, Extra: map[string]interface{}{ "service": "LDAP", "port": info.Ports, "info": ldapInfo, }, }, nil } // 如果无法识别为LDAP,返回失败 return &base.ScanResult{ Success: false, Error: fmt.Errorf("无法识别为LDAP服务"), }, nil } // identifyLDAPService 通过连接识别LDAP服务 func (p *LDAPPlugin) identifyLDAPService(ctx context.Context, info *common.HostInfo) (string, bool) { target := fmt.Sprintf("%s:%s", info.Host, info.Ports) // 尝试建立TCP连接 conn, err := net.DialTimeout("tcp", target, time.Duration(common.Timeout)*time.Second) if err != nil { return "", false } defer conn.Close() // 尝试创建LDAP连接并获取基本信息 connector := NewLDAPConnector() // 使用context包装TCP连接 ldapConn, err := connector.Connect(ctx, info) if err != nil { return "", false } defer connector.Close(ldapConn) // 尝试匿名绑定以确认LDAP服务 anonymousCred := &base.Credential{Username: "", Password: ""} err = connector.Authenticate(ctx, ldapConn, anonymousCred) if err != nil { // 检查错误是否表明这是LDAP服务但需要认证 errStr := strings.ToLower(err.Error()) if strings.Contains(errStr, "ldap") || strings.Contains(errStr, "bind") || strings.Contains(errStr, "authentication") || strings.Contains(errStr, "49") { // LDAP认证失败错误码 return fmt.Sprintf("LDAP服务 (需要认证): %v", err), true } return "", false } // 匿名绑定成功,这是LDAP服务 return "LDAP服务 (支持匿名访问)", true } // ============================================================================= // 插件注册 // ============================================================================= // RegisterLDAPPlugin 注册LDAP插件 func RegisterLDAPPlugin() { factory := base.NewSimplePluginFactory( &base.PluginMetadata{ Name: "ldap", Version: "2.0.0", Author: "fscan-team", Description: "LDAP轻量级目录访问协议扫描和利用插件", Category: "service", Ports: []int{389, 636, 3268, 3269}, // 389: LDAP, 636: LDAPS, 3268/3269: Global Catalog Protocols: []string{"tcp"}, Tags: []string{"ldap", "directory", "bruteforce", "anonymous"}, }, func() base.Plugin { return NewLDAPPlugin() }, ) base.GlobalPluginRegistry.Register("ldap", factory) } // 自动注册 func init() { RegisterLDAPPlugin() }