mirror of
https://github.com/shadow1ng/fscan.git
synced 2025-09-14 14:06:44 +08:00

- 修复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 - 添加详细的调试日志以便排查认证问题
121 lines
2.9 KiB
Go
121 lines
2.9 KiB
Go
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})
|
||
} |