mirror of
https://github.com/shadow1ng/fscan.git
synced 2025-09-14 14:06:44 +08:00
212 lines
5.9 KiB
Go
212 lines
5.9 KiB
Go
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()
|
||
} |