fscan/plugins/services/postgresql.go
ZacharyZcR 622795740f refactor: 重构PostgreSQL和MongoDB插件使用统一发包控制
- 修改PostgreSQL插件,在testCredential和identifyService中添加发包控制
- 修改MongoDB插件,在checkMongoAuth和testMongoCredential中添加发包控制
- 统一包计数逻辑,确保TCP连接成功和失败都正确计数
- 保持现有功能完整性,提升发包控制一致性
2025-09-02 11:45:12 +00:00

182 lines
4.3 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"
"database/sql"
"fmt"
"strings"
"time"
_ "github.com/lib/pq"
"github.com/shadow1ng/fscan/common"
"github.com/shadow1ng/fscan/plugins"
)
type PostgreSQLPlugin struct {
plugins.BasePlugin
}
func NewPostgreSQLPlugin() *PostgreSQLPlugin {
return &PostgreSQLPlugin{
BasePlugin: plugins.NewBasePlugin("postgresql"),
}
}
func (p *PostgreSQLPlugin) Scan(ctx context.Context, info *common.HostInfo) *ScanResult {
target := fmt.Sprintf("%s:%s", info.Host, info.Ports)
if common.DisableBrute {
return p.identifyService(ctx, info)
}
credentials := GenerateCredentials("postgresql")
if len(credentials) == 0 {
return &ScanResult{
Success: false,
Service: "postgresql",
Error: fmt.Errorf("没有可用的测试凭据"),
}
}
for _, cred := range credentials {
if db := p.testCredential(ctx, info, cred); db != nil {
db.Close()
common.LogSuccess(fmt.Sprintf("PostgreSQL %s %s:%s", target, cred.Username, cred.Password))
return &ScanResult{
Success: true,
Service: "postgresql",
Username: cred.Username,
Password: cred.Password,
}
}
}
return &ScanResult{
Success: false,
Service: "postgresql",
Error: fmt.Errorf("未发现弱密码"),
}
}
func (p *PostgreSQLPlugin) testCredential(ctx context.Context, info *common.HostInfo, cred Credential) *sql.DB {
// 检查发包限制
if canSend, reason := common.CanSendPacket(); !canSend {
common.LogError(fmt.Sprintf("PostgreSQL连接 %s:%s 受限: %s", info.Host, info.Ports, reason))
return nil
}
connStr := fmt.Sprintf("postgres://%s:%s@%s:%s/postgres?sslmode=disable&connect_timeout=%d",
cred.Username, cred.Password, info.Host, info.Ports, common.Timeout)
db, err := sql.Open("postgres", connStr)
if err != nil {
return nil
}
db.SetConnMaxLifetime(time.Duration(common.Timeout) * time.Second)
db.SetMaxOpenConns(1)
db.SetMaxIdleConns(0)
pingCtx, cancel := context.WithTimeout(ctx, time.Duration(common.Timeout)*time.Second)
defer cancel()
err = db.PingContext(pingCtx)
if err != nil {
common.IncrementTCPFailedPacketCount()
db.Close()
return nil
}
// 连接成功计数TCP成功包
common.IncrementTCPSuccessPacketCount()
return db
}
func (p *PostgreSQLPlugin) identifyService(ctx context.Context, info *common.HostInfo) *ScanResult {
target := fmt.Sprintf("%s:%s", info.Host, info.Ports)
// 检查发包限制
if canSend, reason := common.CanSendPacket(); !canSend {
common.LogError(fmt.Sprintf("PostgreSQL识别 %s 受限: %s", target, reason))
return &ScanResult{
Success: false,
Service: "postgresql",
Error: fmt.Errorf("发包受限: %s", reason),
}
}
connStr := fmt.Sprintf("postgres://invalid:invalid@%s:%s/postgres?sslmode=disable&connect_timeout=%d",
info.Host, info.Ports, common.Timeout)
db, err := sql.Open("postgres", connStr)
if err != nil {
return &ScanResult{
Success: false,
Service: "postgresql",
Error: err,
}
}
defer db.Close()
pingCtx, cancel := context.WithTimeout(ctx, time.Duration(common.Timeout)*time.Second)
defer cancel()
err = db.PingContext(pingCtx)
// 统计包数量
if err != nil {
common.IncrementTCPFailedPacketCount()
} else {
common.IncrementTCPSuccessPacketCount()
}
// 改进识别逻辑任何PostgreSQL相关的响应都认为是有效服务
var banner string
if err != nil {
errMsg := strings.ToLower(err.Error())
// PostgreSQL常见错误关键词
if strings.Contains(errMsg, "postgres") ||
strings.Contains(errMsg, "authentication") ||
strings.Contains(errMsg, "database") ||
strings.Contains(errMsg, "password") ||
strings.Contains(errMsg, "role") ||
strings.Contains(errMsg, "user") ||
strings.Contains(errMsg, "pq:") {
banner = "PostgreSQL"
} else {
return &ScanResult{
Success: false,
Service: "postgresql",
Error: fmt.Errorf("无法识别为PostgreSQL服务: %s", err.Error()),
}
}
} else {
banner = "PostgreSQL (连接成功)"
}
common.LogSuccess(fmt.Sprintf("PostgreSQL %s %s", target, banner))
return &ScanResult{
Success: true,
Service: "postgresql",
Banner: banner,
}
}
func init() {
// 使用高效注册方式:直接传递端口信息,避免实例创建
RegisterPluginWithPorts("postgresql", func() Plugin {
return NewPostgreSQLPlugin()
}, []int{5432, 5433, 5434})
}