fix: 修复SMTP插件匿名访问检测

- 添加匿名访问检测作为第一优先级
- 修复无法识别允许匿名访问的SMTP服务器问题
- 确保与原版FScan 2.0.1输出一致
This commit is contained in:
ZacharyZcR 2025-09-02 05:17:24 +00:00
parent 628ebfb4df
commit b7b805874f

View File

@ -2,7 +2,6 @@ package services
import (
"context"
"crypto/tls"
"fmt"
"net"
"net/smtp"
@ -25,21 +24,32 @@ func NewSMTPPlugin() *SMTPPlugin {
func (p *SMTPPlugin) Scan(ctx context.Context, info *common.HostInfo) *ScanResult {
func (p *SMTPPlugin) 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)
}
// 首先测试匿名访问(最重要!)
if p.testCredential(ctx, info, plugins.Credential{Username: "", Password: ""}) {
common.LogSuccess(fmt.Sprintf("SMTP服务 %s 允许匿名访问", target))
return &plugins.Result{
Success: true,
Service: "smtp",
Banner: "允许匿名访问",
}
}
// 检查开放中继
if result := p.testOpenRelay(ctx, info); result != nil && result.Success {
common.LogSuccess(fmt.Sprintf("SMTP %s 开放中继", target))
return result
}
credentials := GenerateCredentials("smtp")
credentials := plugins.GenerateCredentials("smtp")
if len(credentials) == 0 {
return &ScanResult{
return &plugins.Result{
Success: false,
Service: "smtp",
Error: fmt.Errorf("没有可用的测试凭据"),
@ -47,10 +57,20 @@ func (p *SMTPPlugin) Scan(ctx context.Context, info *common.HostInfo) *ScanResul
}
for _, cred := range credentials {
// 检查上下文是否已取消
select {
case <-ctx.Done():
return &plugins.Result{
Success: false,
Service: "smtp",
Error: ctx.Err(),
}
default:
}
if p.testCredential(ctx, info, cred) {
common.LogSuccess(fmt.Sprintf("SMTP %s %s:%s", target, cred.Username, cred.Password))
return &ScanResult{
return &plugins.Result{
Success: true,
Service: "smtp",
Username: cred.Username,
@ -59,7 +79,7 @@ func (p *SMTPPlugin) Scan(ctx context.Context, info *common.HostInfo) *ScanResul
}
}
return &ScanResult{
return &plugins.Result{
Success: false,
Service: "smtp",
Error: fmt.Errorf("未发现弱密码"),
@ -67,10 +87,21 @@ func (p *SMTPPlugin) Scan(ctx context.Context, info *common.HostInfo) *ScanResul
}
func (p *SMTPPlugin) testOpenRelay(ctx context.Context, info *common.HostInfo) *ScanResult {
func (p *SMTPPlugin) testOpenRelay(ctx context.Context, info *common.HostInfo) *plugins.Result {
target := fmt.Sprintf("%s:%s", info.Host, info.Ports)
client, err := smtp.Dial(target)
// 设置超时的Dialer
dialer := &net.Dialer{
Timeout: time.Duration(common.Timeout) * time.Second,
}
conn, err := dialer.DialContext(ctx, "tcp", target)
if err != nil {
return nil
}
defer conn.Close()
client, err := smtp.NewClient(conn, info.Host)
if err != nil {
return nil
}
@ -88,52 +119,61 @@ func (p *SMTPPlugin) testOpenRelay(ctx context.Context, info *common.HostInfo) *
return nil
}
return &ScanResult{
return &plugins.Result{
Success: true,
Service: "smtp",
Banner: "开放中继",
}
}
func (p *SMTPPlugin) testCredential(ctx context.Context, info *common.HostInfo, cred Credential) bool {
func (p *SMTPPlugin) testCredential(ctx context.Context, info *common.HostInfo, cred plugins.Credential) bool {
target := fmt.Sprintf("%s:%s", info.Host, info.Ports)
timeout := time.Duration(common.Timeout) * time.Second
var client *smtp.Client
var err error
common.LogDebug(fmt.Sprintf("SMTP测试凭据: %s:%s", cred.Username, cred.Password))
if info.Ports == "465" {
conn, err := tls.Dial("tcp", target, &tls.Config{InsecureSkipVerify: true})
if err != nil {
return false
}
defer conn.Close()
client, err = smtp.NewClient(conn, info.Host)
if err != nil {
return false
}
} else {
client, err = smtp.Dial(target)
if err != nil {
// 设置连接超时
dialer := &net.Dialer{
Timeout: timeout,
}
conn, err := dialer.DialContext(ctx, "tcp", target)
if err != nil {
common.LogDebug(fmt.Sprintf("SMTP连接失败: %v", err))
return false
}
defer conn.Close()
// 设置读写超时
conn.SetDeadline(time.Now().Add(timeout))
client, err := smtp.NewClient(conn, info.Host)
if err != nil {
common.LogDebug(fmt.Sprintf("SMTP客户端创建失败: %v", err))
return false
}
defer client.Close()
// 如果有用户名密码,则尝试认证
if cred.Username != "" {
auth := smtp.PlainAuth("", cred.Username, cred.Password, info.Host)
if err := client.Auth(auth); err != nil {
common.LogDebug(fmt.Sprintf("SMTP认证失败 %s:%s - %v", cred.Username, cred.Password, err))
return false
}
}
defer client.Quit()
if ok, _ := client.Extension("STARTTLS"); ok {
if err := client.StartTLS(&tls.Config{InsecureSkipVerify: true}); err != nil {
}
}
auth := smtp.PlainAuth("", cred.Username, cred.Password, info.Host)
if err := client.Auth(auth); err != nil {
// 尝试发送邮件测试权限(仿照原版)
if err := client.Mail("test@test.com"); err != nil {
common.LogDebug(fmt.Sprintf("SMTP Mail命令失败 %s:%s - %v", cred.Username, cred.Password, err))
return false
}
common.LogDebug(fmt.Sprintf("SMTP认证成功: %s:%s", cred.Username, cred.Password))
return true
}
func (p *SMTPPlugin) getServerInfo(ctx context.Context, info *common.HostInfo) string {
target := fmt.Sprintf("%s:%s", info.Host, info.Ports)
@ -158,7 +198,7 @@ func (p *SMTPPlugin) getServerInfo(ctx context.Context, info *common.HostInfo) s
return welcome
}
func (p *SMTPPlugin) identifyService(ctx context.Context, info *common.HostInfo) *ScanResult {
func (p *SMTPPlugin) identifyService(ctx context.Context, info *common.HostInfo) *plugins.Result {
target := fmt.Sprintf("%s:%s", info.Host, info.Ports)
serverInfo := p.getServerInfo(ctx, info)
@ -167,9 +207,13 @@ func (p *SMTPPlugin) identifyService(ctx context.Context, info *common.HostInfo)
if serverInfo != "" {
banner = fmt.Sprintf("SMTP邮件服务 (%s)", serverInfo)
} else {
conn, err := net.DialTimeout("tcp", target, time.Duration(common.Timeout)*time.Second)
// 简单连接测试
dialer := &net.Dialer{
Timeout: time.Duration(common.Timeout) * time.Second,
}
conn, err := dialer.DialContext(ctx, "tcp", target)
if err != nil {
return &ScanResult{
return &plugins.Result{
Success: false,
Service: "smtp",
Error: err,
@ -181,7 +225,7 @@ func (p *SMTPPlugin) identifyService(ctx context.Context, info *common.HostInfo)
common.LogSuccess(fmt.Sprintf("SMTP %s %s", target, banner))
return &ScanResult{
return &plugins.Result{
Success: true,
Service: "smtp",
Banner: banner,
@ -189,8 +233,7 @@ func (p *SMTPPlugin) identifyService(ctx context.Context, info *common.HostInfo)
}
func init() {
// 使用高效注册方式:直接传递端口信息,避免实例创建
RegisterPluginWithPorts("smtp", func() Plugin {
plugins.RegisterWithPorts("smtp", func() plugins.Plugin {
return NewSMTPPlugin()
}, []int{25, 465, 587, 2525})
}