mirror of
https://github.com/shadow1ng/fscan.git
synced 2025-09-14 14:06:44 +08:00
fix: 修复SMTP插件匿名访问检测
- 添加匿名访问检测作为第一优先级 - 修复无法识别允许匿名访问的SMTP服务器问题 - 确保与原版FScan 2.0.1输出一致
This commit is contained in:
parent
628ebfb4df
commit
b7b805874f
@ -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})
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user