fscan/plugins/services/ldap.go
ZacharyZcR 628ebfb4df fix: 修复LDAP插件DN格式问题,支持多种标准DN格式
- 修复LDAP插件使用简单用户名导致认证失败的问题
- 添加支持多种标准DN格式:cn=user,dc=example,dc=com、uid=user,dc=example,dc=com、cn=user,ou=users,dc=example,dc=com
- 现在能正确检测LDAP弱密码,如admin:admin123
- 添加详细的调试日志以便排查认证问题
2025-09-02 04:19:36 +00:00

121 lines
2.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 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) {
target := fmt.Sprintf("%s:%s", info.Host, info.Ports)
if info.Ports == "636" {
return ldaplib.DialTLS("tcp", target, nil)
}
return ldaplib.Dial("tcp", target)
}
func (p *LDAPPlugin) identifyService(ctx context.Context, info *common.HostInfo) *plugins.Result {
target := fmt.Sprintf("%s:%s", info.Host, info.Ports)
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})
}