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

- 删除整个legacy插件系统(7794行代码) - 完成所有插件向单文件架构迁移 - 移除19个插件的虚假Exploit功能,只保留真实利用: * Redis: 文件写入、SSH密钥注入、计划任务 * SSH: 命令执行 * MS17010: EternalBlue漏洞利用 - 统一插件接口,简化架构复杂度 - 清理临时文件和备份文件 重构效果: - 代码行数: -7794行 - 插件文件数: 从3文件架构→单文件架构 - 真实利用插件: 从22个→3个 - 架构复杂度: 大幅简化
283 lines
6.8 KiB
Go
283 lines
6.8 KiB
Go
package services
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
|
|
_ "github.com/lib/pq"
|
|
"github.com/shadow1ng/fscan/common"
|
|
"github.com/shadow1ng/fscan/common/i18n"
|
|
)
|
|
|
|
// PostgreSQLPlugin PostgreSQL数据库扫描和利用插件 - 包含数据库查询利用功能
|
|
type PostgreSQLPlugin struct {
|
|
name string
|
|
ports []int
|
|
}
|
|
|
|
// NewPostgreSQLPlugin 创建PostgreSQL插件
|
|
func NewPostgreSQLPlugin() *PostgreSQLPlugin {
|
|
return &PostgreSQLPlugin{
|
|
name: "postgresql",
|
|
ports: []int{5432, 5433, 5434}, // PostgreSQL端口
|
|
}
|
|
}
|
|
|
|
// GetName 实现Plugin接口
|
|
func (p *PostgreSQLPlugin) GetName() string {
|
|
return p.name
|
|
}
|
|
|
|
// GetPorts 实现Plugin接口
|
|
func (p *PostgreSQLPlugin) GetPorts() []int {
|
|
return p.ports
|
|
}
|
|
|
|
// Scan 执行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 {
|
|
// PostgreSQL默认凭据
|
|
credentials = []Credential{
|
|
{Username: "postgres", Password: ""},
|
|
{Username: "postgres", Password: "postgres"},
|
|
{Username: "postgres", Password: "password"},
|
|
{Username: "postgres", Password: "admin"},
|
|
{Username: "postgres", Password: "123456"},
|
|
{Username: "admin", Password: "admin"},
|
|
{Username: "root", Password: "root"},
|
|
}
|
|
}
|
|
|
|
// 逐个测试凭据
|
|
for _, cred := range credentials {
|
|
// 检查Context是否被取消
|
|
select {
|
|
case <-ctx.Done():
|
|
return &ScanResult{
|
|
Success: false,
|
|
Service: "postgresql",
|
|
Error: ctx.Err(),
|
|
}
|
|
default:
|
|
}
|
|
|
|
// 测试凭据
|
|
if db := p.testCredential(ctx, info, cred); db != nil {
|
|
db.Close() // 关闭测试连接
|
|
|
|
// PostgreSQL认证成功
|
|
common.LogSuccess(i18n.GetText("postgresql_scan_success", 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("未发现弱密码"),
|
|
}
|
|
}
|
|
|
|
|
|
// testCredential 测试单个凭据 - 返回数据库连接或nil
|
|
func (p *PostgreSQLPlugin) testCredential(ctx context.Context, info *common.HostInfo, cred Credential) *sql.DB {
|
|
// 构建连接字符串
|
|
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 {
|
|
db.Close()
|
|
return nil
|
|
}
|
|
|
|
return db
|
|
}
|
|
|
|
// getVersion 获取PostgreSQL版本信息
|
|
func (p *PostgreSQLPlugin) getVersion(db *sql.DB) string {
|
|
var version string
|
|
err := db.QueryRow("SELECT version()").Scan(&version)
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
return version
|
|
}
|
|
|
|
// getDatabases 获取数据库列表
|
|
func (p *PostgreSQLPlugin) getDatabases(db *sql.DB) []string {
|
|
query := "SELECT datname FROM pg_database WHERE datistemplate = false"
|
|
rows, err := db.Query(query)
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
defer rows.Close()
|
|
|
|
var databases []string
|
|
for rows.Next() {
|
|
var dbName string
|
|
if err := rows.Scan(&dbName); err == nil {
|
|
databases = append(databases, dbName)
|
|
}
|
|
}
|
|
|
|
return databases
|
|
}
|
|
|
|
// getTables 获取表列表
|
|
func (p *PostgreSQLPlugin) getTables(db *sql.DB) []string {
|
|
query := `SELECT tablename FROM pg_tables WHERE schemaname = 'public'
|
|
UNION SELECT viewname FROM pg_views WHERE schemaname = 'public'
|
|
ORDER BY 1 LIMIT 20`
|
|
|
|
rows, err := db.Query(query)
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
defer rows.Close()
|
|
|
|
var tables []string
|
|
for rows.Next() {
|
|
var tableName string
|
|
if err := rows.Scan(&tableName); err == nil {
|
|
tables = append(tables, tableName)
|
|
}
|
|
}
|
|
|
|
return tables
|
|
}
|
|
|
|
// getUsers 获取用户列表
|
|
func (p *PostgreSQLPlugin) getUsers(db *sql.DB) []string {
|
|
query := "SELECT usename FROM pg_user ORDER BY usename"
|
|
rows, err := db.Query(query)
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
defer rows.Close()
|
|
|
|
var users []string
|
|
for rows.Next() {
|
|
var userName string
|
|
if err := rows.Scan(&userName); err == nil {
|
|
users = append(users, userName)
|
|
}
|
|
}
|
|
|
|
return users
|
|
}
|
|
|
|
// getPrivileges 获取用户权限信息
|
|
func (p *PostgreSQLPlugin) getPrivileges(db *sql.DB, username string) string {
|
|
var privileges strings.Builder
|
|
|
|
// 检查是否为超级用户
|
|
var isSuperUser bool
|
|
err := db.QueryRow("SELECT usesuper FROM pg_user WHERE usename = $1", username).Scan(&isSuperUser)
|
|
if err == nil {
|
|
if isSuperUser {
|
|
privileges.WriteString("超级用户权限: YES\n")
|
|
} else {
|
|
privileges.WriteString("超级用户权限: NO\n")
|
|
}
|
|
}
|
|
|
|
// 获取用户属性
|
|
var createDB, createRole bool
|
|
err = db.QueryRow("SELECT usecreatedb, usecreaterole FROM pg_user WHERE usename = $1",
|
|
username).Scan(&createDB, &createRole)
|
|
if err == nil {
|
|
privileges.WriteString(fmt.Sprintf("创建数据库权限: %v\n", createDB))
|
|
privileges.WriteString(fmt.Sprintf("创建角色权限: %v\n", createRole))
|
|
}
|
|
|
|
return privileges.String()
|
|
}
|
|
|
|
// identifyService 服务识别 - 检测PostgreSQL服务
|
|
func (p *PostgreSQLPlugin) identifyService(ctx context.Context, info *common.HostInfo) *ScanResult {
|
|
target := fmt.Sprintf("%s:%s", info.Host, info.Ports)
|
|
|
|
// 尝试连接PostgreSQL服务
|
|
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)
|
|
|
|
var banner string
|
|
if err != nil && strings.Contains(strings.ToLower(err.Error()), "postgres") {
|
|
banner = "PostgreSQL数据库服务"
|
|
} else if err == nil {
|
|
banner = "PostgreSQL数据库 (连接成功)"
|
|
} else {
|
|
return &ScanResult{
|
|
Success: false,
|
|
Service: "postgresql",
|
|
Error: fmt.Errorf("无法识别为PostgreSQL服务"),
|
|
}
|
|
}
|
|
|
|
common.LogSuccess(i18n.GetText("postgresql_service_identified", target, banner))
|
|
|
|
return &ScanResult{
|
|
Success: true,
|
|
Service: "postgresql",
|
|
Banner: banner,
|
|
}
|
|
}
|
|
|
|
// init 自动注册插件
|
|
func init() {
|
|
RegisterPlugin("postgresql", func() Plugin {
|
|
return NewPostgreSQLPlugin()
|
|
})
|
|
} |