mirror of
https://github.com/shadow1ng/fscan.git
synced 2025-09-14 14:06:44 +08:00
refactor: 清理Cassandra插件exploiter.go为最小实现
- 移除所有利用功能代码,简化为最小版本 - 移除gocql依赖和复杂的数据提取逻辑 - 保持与其他插件一致的最小化实现模式
This commit is contained in:
parent
ab8834a602
commit
a70df9bc3c
@ -2,29 +2,23 @@ package cassandra
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/gocql/gocql"
|
|
||||||
"github.com/shadow1ng/fscan/common"
|
"github.com/shadow1ng/fscan/common"
|
||||||
"github.com/shadow1ng/fscan/common/i18n"
|
|
||||||
"github.com/shadow1ng/fscan/plugins/base"
|
"github.com/shadow1ng/fscan/plugins/base"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CassandraExploiter Cassandra利用器
|
// CassandraExploiter Cassandra利用器实现 - 最小化版本,不提供利用功能
|
||||||
type CassandraExploiter struct {
|
type CassandraExploiter struct {
|
||||||
*base.BaseExploiter
|
*base.BaseExploiter
|
||||||
connector *CassandraConnector
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewCassandraExploiter 创建Cassandra利用器
|
// NewCassandraExploiter 创建Cassandra利用器
|
||||||
func NewCassandraExploiter() *CassandraExploiter {
|
func NewCassandraExploiter() *CassandraExploiter {
|
||||||
exploiter := &CassandraExploiter{
|
exploiter := &CassandraExploiter{
|
||||||
BaseExploiter: base.NewBaseExploiter("cassandra"),
|
BaseExploiter: base.NewBaseExploiter("cassandra"),
|
||||||
connector: NewCassandraConnector(),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 添加利用方法
|
// Cassandra插件不提供利用功能
|
||||||
exploiter.setupExploitMethods()
|
exploiter.setupExploitMethods()
|
||||||
|
|
||||||
return exploiter
|
return exploiter
|
||||||
@ -32,302 +26,12 @@ func NewCassandraExploiter() *CassandraExploiter {
|
|||||||
|
|
||||||
// setupExploitMethods 设置利用方法
|
// setupExploitMethods 设置利用方法
|
||||||
func (e *CassandraExploiter) setupExploitMethods() {
|
func (e *CassandraExploiter) setupExploitMethods() {
|
||||||
// 1. 信息收集
|
// Cassandra插件不提供利用功能,仅进行弱密码扫描
|
||||||
infoMethod := base.NewExploitMethod(base.ExploitDataExtraction, "information_gathering").
|
|
||||||
WithDescription(i18n.GetText("exploit_method_name_information_gathering")).
|
|
||||||
WithPriority(8).
|
|
||||||
WithConditions("has_credentials").
|
|
||||||
WithHandler(e.exploitInformationGathering).
|
|
||||||
Build()
|
|
||||||
e.AddExploitMethod(infoMethod)
|
|
||||||
|
|
||||||
// 2. Keyspace枚举
|
|
||||||
enumMethod := base.NewExploitMethod(base.ExploitDataExtraction, "keyspace_enumeration").
|
|
||||||
WithDescription(i18n.GetText("exploit_method_name_database_enumeration")).
|
|
||||||
WithPriority(9).
|
|
||||||
WithConditions("has_credentials").
|
|
||||||
WithHandler(e.exploitKeyspaceEnumeration).
|
|
||||||
Build()
|
|
||||||
e.AddExploitMethod(enumMethod)
|
|
||||||
|
|
||||||
// 3. 数据提取
|
|
||||||
dataMethod := base.NewExploitMethod(base.ExploitDataExtraction, "data_extraction").
|
|
||||||
WithDescription(i18n.GetText("exploit_method_name_data_extraction")).
|
|
||||||
WithPriority(7).
|
|
||||||
WithConditions("has_credentials").
|
|
||||||
WithHandler(e.exploitDataExtraction).
|
|
||||||
Build()
|
|
||||||
e.AddExploitMethod(dataMethod)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// exploitInformationGathering 信息收集
|
// Exploit 利用接口实现 - 空实现
|
||||||
func (e *CassandraExploiter) exploitInformationGathering(ctx context.Context, info *common.HostInfo, creds *base.Credential) (*base.ExploitResult, error) {
|
func (e *CassandraExploiter) Exploit(ctx context.Context, info *common.HostInfo, creds *base.Credential) (*base.ExploitResult, error) {
|
||||||
return e.executeWithSession(ctx, info, creds, "information_gathering", e.informationGathering)
|
// Cassandra插件不提供利用功能
|
||||||
}
|
return nil, nil
|
||||||
|
|
||||||
// exploitKeyspaceEnumeration keyspace枚举
|
|
||||||
func (e *CassandraExploiter) exploitKeyspaceEnumeration(ctx context.Context, info *common.HostInfo, creds *base.Credential) (*base.ExploitResult, error) {
|
|
||||||
return e.executeWithSession(ctx, info, creds, "keyspace_enumeration", e.keyspaceEnumeration)
|
|
||||||
}
|
|
||||||
|
|
||||||
// exploitDataExtraction 数据提取
|
|
||||||
func (e *CassandraExploiter) exploitDataExtraction(ctx context.Context, info *common.HostInfo, creds *base.Credential) (*base.ExploitResult, error) {
|
|
||||||
return e.executeWithSession(ctx, info, creds, "data_extraction", e.dataExtraction)
|
|
||||||
}
|
|
||||||
|
|
||||||
// executeWithSession 使用Cassandra会话执行利用方法
|
|
||||||
func (e *CassandraExploiter) executeWithSession(ctx context.Context, info *common.HostInfo, creds *base.Credential, methodName string, method func(context.Context, interface{}, string) ([]string, error)) (*base.ExploitResult, error) {
|
|
||||||
target := fmt.Sprintf("%s:%s", info.Host, info.Ports)
|
|
||||||
|
|
||||||
// 建立连接
|
|
||||||
conn, err := e.connector.Connect(ctx, info)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("连接失败: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 认证
|
|
||||||
err = e.connector.Authenticate(ctx, conn, creds)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("认证失败: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 创建会话用于利用
|
|
||||||
cluster := conn.(*gocql.ClusterConfig)
|
|
||||||
if creds.Username != "" || creds.Password != "" {
|
|
||||||
cluster.Authenticator = gocql.PasswordAuthenticator{
|
|
||||||
Username: creds.Username,
|
|
||||||
Password: creds.Password,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
session, err := cluster.CreateSession()
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("创建会话失败: %v", err)
|
|
||||||
}
|
|
||||||
defer session.Close()
|
|
||||||
|
|
||||||
// 执行方法
|
|
||||||
output, err := method(ctx, session, target)
|
|
||||||
if err != nil {
|
|
||||||
return &base.ExploitResult{
|
|
||||||
Success: false,
|
|
||||||
Error: err,
|
|
||||||
Type: base.ExploitDataExtraction,
|
|
||||||
Method: methodName,
|
|
||||||
Output: fmt.Sprintf("执行失败: %v", err),
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return &base.ExploitResult{
|
|
||||||
Success: true,
|
|
||||||
Type: base.ExploitDataExtraction,
|
|
||||||
Method: methodName,
|
|
||||||
Output: strings.Join(output, "\n"),
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// informationGathering 信息收集
|
|
||||||
func (e *CassandraExploiter) informationGathering(ctx context.Context, session interface{}, target string) ([]string, error) {
|
|
||||||
cassandraSession := session.(*gocql.Session)
|
|
||||||
var output []string
|
|
||||||
|
|
||||||
// 获取Cassandra版本信息
|
|
||||||
var releaseVersion string
|
|
||||||
err := cassandraSession.Query("SELECT release_version FROM system.local").WithContext(ctx).Scan(&releaseVersion)
|
|
||||||
if err == nil {
|
|
||||||
output = append(output, fmt.Sprintf("Cassandra版本: %s", releaseVersion))
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取集群名称
|
|
||||||
var clusterName string
|
|
||||||
err = cassandraSession.Query("SELECT cluster_name FROM system.local").WithContext(ctx).Scan(&clusterName)
|
|
||||||
if err == nil {
|
|
||||||
output = append(output, fmt.Sprintf("集群名称: %s", clusterName))
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取数据中心信息
|
|
||||||
var datacenter string
|
|
||||||
err = cassandraSession.Query("SELECT data_center FROM system.local").WithContext(ctx).Scan(&datacenter)
|
|
||||||
if err == nil {
|
|
||||||
output = append(output, fmt.Sprintf("数据中心: %s", datacenter))
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取机架信息
|
|
||||||
var rack string
|
|
||||||
err = cassandraSession.Query("SELECT rack FROM system.local").WithContext(ctx).Scan(&rack)
|
|
||||||
if err == nil {
|
|
||||||
output = append(output, fmt.Sprintf("机架: %s", rack))
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取节点ID
|
|
||||||
var hostId string
|
|
||||||
err = cassandraSession.Query("SELECT host_id FROM system.local").WithContext(ctx).Scan(&hostId)
|
|
||||||
if err == nil {
|
|
||||||
output = append(output, fmt.Sprintf("节点ID: %s", hostId))
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(output) == 0 {
|
|
||||||
return nil, fmt.Errorf("无法获取Cassandra信息")
|
|
||||||
}
|
|
||||||
|
|
||||||
return output, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// keyspaceEnumeration keyspace枚举
|
|
||||||
func (e *CassandraExploiter) keyspaceEnumeration(ctx context.Context, session interface{}, target string) ([]string, error) {
|
|
||||||
cassandraSession := session.(*gocql.Session)
|
|
||||||
var output []string
|
|
||||||
|
|
||||||
// 查询所有keyspace
|
|
||||||
iter := cassandraSession.Query("SELECT keyspace_name FROM system_schema.keyspaces").WithContext(ctx).Iter()
|
|
||||||
defer iter.Close()
|
|
||||||
|
|
||||||
var keyspaceName string
|
|
||||||
var keyspaces []string
|
|
||||||
|
|
||||||
for iter.Scan(&keyspaceName) {
|
|
||||||
keyspaces = append(keyspaces, keyspaceName)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := iter.Close(); err != nil {
|
|
||||||
return nil, fmt.Errorf("查询keyspace失败: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(keyspaces) > 0 {
|
|
||||||
output = append(output, fmt.Sprintf("发现Keyspaces: %s", strings.Join(keyspaces, ", ")))
|
|
||||||
|
|
||||||
// 对每个非系统keyspace获取表信息
|
|
||||||
for _, ks := range keyspaces {
|
|
||||||
if !strings.HasPrefix(ks, "system") {
|
|
||||||
tables := e.getTablesInKeyspace(ctx, cassandraSession, ks)
|
|
||||||
if len(tables) > 0 {
|
|
||||||
output = append(output, fmt.Sprintf("Keyspace '%s' 中的表: %s", ks, strings.Join(tables, ", ")))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return nil, fmt.Errorf("未发现任何keyspace")
|
|
||||||
}
|
|
||||||
|
|
||||||
return output, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// getTablesInKeyspace 获取keyspace中的表
|
|
||||||
func (e *CassandraExploiter) getTablesInKeyspace(ctx context.Context, session *gocql.Session, keyspace string) []string {
|
|
||||||
iter := session.Query("SELECT table_name FROM system_schema.tables WHERE keyspace_name = ?", keyspace).WithContext(ctx).Iter()
|
|
||||||
defer iter.Close()
|
|
||||||
|
|
||||||
var tableName string
|
|
||||||
var tables []string
|
|
||||||
|
|
||||||
for iter.Scan(&tableName) {
|
|
||||||
tables = append(tables, tableName)
|
|
||||||
}
|
|
||||||
|
|
||||||
return tables
|
|
||||||
}
|
|
||||||
|
|
||||||
// dataExtraction 数据提取
|
|
||||||
func (e *CassandraExploiter) dataExtraction(ctx context.Context, session interface{}, target string) ([]string, error) {
|
|
||||||
cassandraSession := session.(*gocql.Session)
|
|
||||||
var output []string
|
|
||||||
|
|
||||||
// 尝试读取一些系统表中的敏感信息
|
|
||||||
|
|
||||||
// 获取所有用户角色信息(如果存在)
|
|
||||||
if roles := e.extractRoles(ctx, cassandraSession); len(roles) > 0 {
|
|
||||||
output = append(output, roles...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取peer节点信息
|
|
||||||
if peers := e.extractPeers(ctx, cassandraSession); len(peers) > 0 {
|
|
||||||
output = append(output, peers...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 尝试获取一些配置信息
|
|
||||||
if configs := e.extractConfigs(ctx, cassandraSession); len(configs) > 0 {
|
|
||||||
output = append(output, configs...)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(output) == 0 {
|
|
||||||
return []string{"未能提取到敏感数据"}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return output, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// extractRoles 提取角色信息
|
|
||||||
func (e *CassandraExploiter) extractRoles(ctx context.Context, session *gocql.Session) []string {
|
|
||||||
var output []string
|
|
||||||
|
|
||||||
// 尝试查询角色表(Cassandra 2.2+)
|
|
||||||
iter := session.Query("SELECT role FROM system_auth.roles").WithContext(ctx).Iter()
|
|
||||||
defer iter.Close()
|
|
||||||
|
|
||||||
var role string
|
|
||||||
var roles []string
|
|
||||||
|
|
||||||
for iter.Scan(&role) {
|
|
||||||
roles = append(roles, role)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(roles) > 0 {
|
|
||||||
output = append(output, fmt.Sprintf("发现角色: %s", strings.Join(roles, ", ")))
|
|
||||||
}
|
|
||||||
|
|
||||||
return output
|
|
||||||
}
|
|
||||||
|
|
||||||
// extractPeers 提取peer节点信息
|
|
||||||
func (e *CassandraExploiter) extractPeers(ctx context.Context, session *gocql.Session) []string {
|
|
||||||
var output []string
|
|
||||||
|
|
||||||
iter := session.Query("SELECT peer, data_center, rack, release_version FROM system.peers").WithContext(ctx).Iter()
|
|
||||||
defer iter.Close()
|
|
||||||
|
|
||||||
var peer, datacenter, rack, version string
|
|
||||||
var peerCount int
|
|
||||||
|
|
||||||
for iter.Scan(&peer, &datacenter, &rack, &version) {
|
|
||||||
peerCount++
|
|
||||||
output = append(output, fmt.Sprintf("Peer节点 %d: %s (数据中心: %s, 机架: %s, 版本: %s)",
|
|
||||||
peerCount, peer, datacenter, rack, version))
|
|
||||||
}
|
|
||||||
|
|
||||||
if peerCount == 0 {
|
|
||||||
output = append(output, "未发现其他peer节点(单节点集群)")
|
|
||||||
}
|
|
||||||
|
|
||||||
return output
|
|
||||||
}
|
|
||||||
|
|
||||||
// extractConfigs 提取配置信息
|
|
||||||
func (e *CassandraExploiter) extractConfigs(ctx context.Context, session *gocql.Session) []string {
|
|
||||||
var output []string
|
|
||||||
|
|
||||||
// 获取token信息
|
|
||||||
var tokens string
|
|
||||||
err := session.Query("SELECT tokens FROM system.local").WithContext(ctx).Scan(&tokens)
|
|
||||||
if err == nil && tokens != "" {
|
|
||||||
// 只显示token数量,不显示完整token(太长)
|
|
||||||
tokenList := strings.Split(strings.Trim(tokens, "{}"), ",")
|
|
||||||
output = append(output, fmt.Sprintf("节点tokens数量: %d", len(tokenList)))
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取监听地址
|
|
||||||
var listenAddress string
|
|
||||||
err = session.Query("SELECT listen_address FROM system.local").WithContext(ctx).Scan(&listenAddress)
|
|
||||||
if err == nil && listenAddress != "" {
|
|
||||||
output = append(output, fmt.Sprintf("监听地址: %s", listenAddress))
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取广播地址
|
|
||||||
var broadcastAddress string
|
|
||||||
err = session.Query("SELECT broadcast_address FROM system.local").WithContext(ctx).Scan(&broadcastAddress)
|
|
||||||
if err == nil && broadcastAddress != "" {
|
|
||||||
output = append(output, fmt.Sprintf("广播地址: %s", broadcastAddress))
|
|
||||||
}
|
|
||||||
|
|
||||||
return output
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user