fscan/plugins/services/ldap/plugin.go
ZacharyZcR 4a3f281b6b refactor: 统一Plugins目录大小写为小写
- 将所有Plugins路径重命名为plugins
- 修复Git索引与实际文件系统大小写不一致问题
- 确保跨平台兼容性和路径一致性
2025-08-12 13:08:06 +08:00

212 lines
5.9 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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()
}