fscan/plugins/services/ldap.go
ZacharyZcR 1febb54fe6 refactor: 重构SMB和LDAP插件使用统一发包控制
- 修改SMB插件,在testCredential和identifyService中添加发包控制
- 修改LDAP插件,在connectLDAP中添加发包控制和包计数
- 统一包计数逻辑,确保TCP连接成功和失败都正确计数
- 保持现有功能不变,提升网络操作一致性
2025-09-02 11:48:52 +00:00

150 lines
3.6 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 services
import (
"context"
"fmt"
ldaplib "github.com/go-ldap/ldap/v3"
"github.com/shadow1ng/fscan/common"
"github.com/shadow1ng/fscan/plugins"
)
type LDAPPlugin struct {
plugins.BasePlugin
}
func NewLDAPPlugin() *LDAPPlugin {
return &LDAPPlugin{
BasePlugin: plugins.NewBasePlugin("ldap"),
}
}
func (p *LDAPPlugin) Scan(ctx context.Context, info *common.HostInfo) *plugins.Result {
target := fmt.Sprintf("%s:%s", info.Host, info.Ports)
if common.DisableBrute {
return p.identifyService(ctx, info)
}
credentials := plugins.GenerateCredentials("ldap")
for _, cred := range credentials {
// 检查上下文是否已取消
select {
case <-ctx.Done():
return &plugins.Result{
Success: false,
Service: "ldap",
Error: ctx.Err(),
}
default:
}
if p.testCredential(ctx, info, cred) {
common.LogSuccess(fmt.Sprintf("LDAP %s %s:%s", target, cred.Username, cred.Password))
return &plugins.Result{
Success: true,
Service: "ldap",
Username: cred.Username,
Password: cred.Password,
}
}
}
return &plugins.Result{
Success: false,
Service: "ldap",
Error: fmt.Errorf("未发现弱密码"),
}
}
func (p *LDAPPlugin) testCredential(ctx context.Context, info *common.HostInfo, cred plugins.Credential) bool {
conn, err := p.connectLDAP(ctx, info, cred)
if err != nil {
return false
}
defer conn.Close()
// 尝试多种DN格式进行绑定测试
dnFormats := []string{
fmt.Sprintf("cn=%s,dc=example,dc=com", cred.Username), // 标准格式
fmt.Sprintf("uid=%s,dc=example,dc=com", cred.Username), // uid格式
fmt.Sprintf("cn=%s,ou=users,dc=example,dc=com", cred.Username), // ou格式
cred.Username, // 直接用户名(某些配置)
}
for _, dn := range dnFormats {
if err := conn.Bind(dn, cred.Password); err == nil {
common.LogDebug(fmt.Sprintf("LDAP绑定成功DN: %s", dn))
return true
}
common.LogDebug(fmt.Sprintf("LDAP绑定失败DN: %s, 错误: %v", dn, err))
}
return false
}
func (p *LDAPPlugin) connectLDAP(ctx context.Context, info *common.HostInfo, creds plugins.Credential) (*ldaplib.Conn, error) {
// 检查发包限制
if canSend, reason := common.CanSendPacket(); !canSend {
common.LogError(fmt.Sprintf("LDAP连接 %s:%s 受限: %s", info.Host, info.Ports, reason))
return nil, fmt.Errorf("发包受限: %s", reason)
}
target := fmt.Sprintf("%s:%s", info.Host, info.Ports)
var conn *ldaplib.Conn
var err error
if info.Ports == "636" {
conn, err = ldaplib.DialTLS("tcp", target, nil)
} else {
conn, err = ldaplib.Dial("tcp", target)
}
// 统计包数量
if err != nil {
common.IncrementTCPFailedPacketCount()
} else {
common.IncrementTCPSuccessPacketCount()
}
return conn, err
}
func (p *LDAPPlugin) identifyService(ctx context.Context, info *common.HostInfo) *plugins.Result {
target := fmt.Sprintf("%s:%s", info.Host, info.Ports)
// 检查发包限制
if canSend, reason := common.CanSendPacket(); !canSend {
common.LogError(fmt.Sprintf("LDAP识别 %s 受限: %s", target, reason))
return &plugins.Result{
Success: false,
Service: "ldap",
Error: fmt.Errorf("发包受限: %s", reason),
}
}
conn, err := p.connectLDAP(ctx, info, plugins.Credential{})
if err != nil {
return &plugins.Result{
Success: false,
Service: "ldap",
Error: err,
}
}
defer conn.Close()
banner := "LDAP"
common.LogSuccess(fmt.Sprintf("LDAP %s %s", target, banner))
return &plugins.Result{
Success: true,
Service: "ldap",
Banner: banner,
}
}
func init() {
plugins.RegisterWithPorts("ldap", func() plugins.Plugin {
return NewLDAPPlugin()
}, []int{389, 636, 3268, 3269})
}