fscan/Plugins/services/cassandra/connector.go
ZacharyZcR 516225a11f feat: 实现Apache Cassandra数据库服务专业扫描插件
• 新增Cassandra插件支持Native Protocol (9042端口)
• 实现弱密码检测和未授权访问检测
• 支持自动利用:信息收集、Keyspace枚举、数据提取
• 集成-nobr模式服务识别和-ne自动利用功能
• 完整的Context超时机制和代理支持
• 添加中英文国际化消息支持
• 基于新插件架构实现模块化设计

功能特性:
- 支持gocql驱动的CQL查询和认证
- 智能识别Cassandra Native Protocol v4
- 三种利用方法:系统信息、数据库结构、敏感数据
- 完整的错误处理和超时控制机制
2025-08-08 04:34:32 +08:00

169 lines
3.9 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 cassandra
import (
"context"
"fmt"
"net"
"strconv"
"time"
"github.com/gocql/gocql"
"github.com/shadow1ng/fscan/common"
"github.com/shadow1ng/fscan/plugins/base"
)
// CassandraConnector Cassandra连接器实现
type CassandraConnector struct {
host string
port string
}
// CassandraProxyDialer 实现gocql.Dialer接口支持代理连接
type CassandraProxyDialer struct {
timeout time.Duration
}
// DialContext 实现代理拨号
func (d *CassandraProxyDialer) DialContext(ctx context.Context, network, addr string) (net.Conn, error) {
host, port, err := net.SplitHostPort(addr)
if err != nil {
return nil, err
}
return common.WrapperTcpWithContext(ctx, network, fmt.Sprintf("%s:%s", host, port))
}
// NewCassandraConnector 创建Cassandra连接器
func NewCassandraConnector() *CassandraConnector {
return &CassandraConnector{}
}
// Connect 连接到Cassandra服务
func (c *CassandraConnector) Connect(ctx context.Context, info *common.HostInfo) (interface{}, error) {
c.host = info.Host
c.port = info.Ports
// 创建Cassandra集群配置
cluster := gocql.NewCluster(c.host)
// 解析端口
port, err := strconv.Atoi(c.port)
if err != nil {
return nil, fmt.Errorf("无效的端口号: %s", c.port)
}
cluster.Port = port
// 设置连接参数
timeout := time.Duration(common.Timeout) * time.Second
cluster.Timeout = timeout
cluster.ConnectTimeout = timeout
cluster.ProtoVersion = 4
cluster.Consistency = gocql.One
// 如果配置了代理设置自定义Dialer
if common.Socks5Proxy != "" {
cluster.Dialer = &CassandraProxyDialer{
timeout: timeout,
}
}
// 设置重试策略
cluster.RetryPolicy = &gocql.SimpleRetryPolicy{NumRetries: 3}
return cluster, nil
}
// Authenticate 认证
func (c *CassandraConnector) Authenticate(ctx context.Context, conn interface{}, cred *base.Credential) error {
cluster, ok := conn.(*gocql.ClusterConfig)
if !ok {
return fmt.Errorf("无效的连接类型")
}
// 创建集群配置副本
authCluster := *cluster
// 设置认证信息
if cred.Username != "" || cred.Password != "" {
authCluster.Authenticator = gocql.PasswordAuthenticator{
Username: cred.Username,
Password: cred.Password,
}
}
// 创建会话通道以支持Context超时
sessionChan := make(chan struct {
session *gocql.Session
err error
}, 1)
// 在goroutine中创建会话以便可以通过Context取消
go func() {
session, err := authCluster.CreateSession()
select {
case <-ctx.Done():
if session != nil {
session.Close()
}
case sessionChan <- struct {
session *gocql.Session
err error
}{session, err}:
}
}()
// 等待会话创建或Context取消
var session *gocql.Session
var err error
select {
case result := <-sessionChan:
session, err = result.session, result.err
if err != nil {
return fmt.Errorf("Cassandra认证失败: %v", err)
}
case <-ctx.Done():
return fmt.Errorf("Cassandra连接超时: %v", ctx.Err())
}
defer session.Close()
// 尝试执行查询验证连接
resultChan := make(chan struct {
success bool
err error
}, 1)
go func() {
var err error
// 尝试两种查询,确保至少一种成功
err = session.Query("SELECT peer FROM system.peers").WithContext(ctx).Scan(nil)
if err != nil {
err = session.Query("SELECT now() FROM system.local").WithContext(ctx).Scan(nil)
}
select {
case <-ctx.Done():
case resultChan <- struct {
success bool
err error
}{err == nil, err}:
}
}()
// 等待查询结果或Context取消
select {
case result := <-resultChan:
if !result.success && result.err != nil {
return fmt.Errorf("Cassandra查询验证失败: %v", result.err)
}
return nil
case <-ctx.Done():
return fmt.Errorf("Cassandra查询超时: %v", ctx.Err())
}
}
// Close 关闭连接
func (c *CassandraConnector) Close(conn interface{}) error {
// Cassandra集群配置无需显式关闭
return nil
}