feat: 完善插件系统i18n国际化支持

- 修复重复输出问题:适配器层改为debug输出,避免与插件层重复
- 修复格式化错误:修正SaveExploitResult中的端口格式化问题
- 新增利用方法名称i18n:添加GetExploitMethodName函数支持方法名本地化
- 扩展i18n消息模板:新增利用方法执行、MySQL/Redis专用消息模板
- 完善exploiter国际化:所有利用方法和结果消息支持中英文切换
- 优化用户体验:利用方法显示从"information_gathering"变为"信息收集"
This commit is contained in:
ZacharyZcR 2025-08-07 12:30:17 +08:00
parent 43f210ffc6
commit b346e6bdc1
7 changed files with 254 additions and 63 deletions

View File

@ -188,6 +188,19 @@ func GetText(key string, args ...interface{}) string {
return globalManager.GetText(key, args...) return globalManager.GetText(key, args...)
} }
// GetExploitMethodName 获取利用方法的本地化名称
func GetExploitMethodName(methodName string) string {
// 尝试获取本地化的方法名称
key := fmt.Sprintf("exploit_method_name_%s", methodName)
localizedName := globalManager.GetText(key)
// 如果没有找到对应的本地化名称,返回原始名称
if localizedName == key {
return methodName
}
return localizedName
}
// ============================================================================================= // =============================================================================================
// 已删除的死代码函数(未使用): // 已删除的死代码函数(未使用):
// GetGlobalManager, GetLanguage, AddMessage, GetTextWithLanguage, // GetGlobalManager, GetLanguage, AddMessage, GetTextWithLanguage,

View File

@ -39,6 +39,164 @@ var PluginMessages = map[string]map[string]string{
LangEN: "%s exploitation failed: %v", LangEN: "%s exploitation failed: %v",
}, },
// ========================= 通用成功消息模板 =========================
"plugin_login_success": {
LangZH: "%s弱密码: %s [%s:%s]",
LangEN: "%s weak password: %s [%s:%s]",
},
"plugin_login_success_passwd_only": {
LangZH: "%s弱密码: %s [%s]",
LangEN: "%s weak password: %s [%s]",
},
"plugin_unauthorized_access": {
LangZH: "%s未授权访问: %s",
LangEN: "%s unauthorized access: %s",
},
// ========================= 利用(Exploit)消息模板 =========================
"exploit_weak_password_success": {
LangZH: "%s %s 弱密码利用成功",
LangEN: "%s %s weak password exploit successful",
},
"exploit_unauthorized_success": {
LangZH: "%s %s 未授权访问利用成功",
LangEN: "%s %s unauthorized access exploit successful",
},
"exploit_command_exec_success": {
LangZH: "%s %s 命令执行利用成功",
LangEN: "%s %s command execution exploit successful",
},
"exploit_file_write_success": {
LangZH: "%s %s 文件写入利用成功",
LangEN: "%s %s file write exploit successful",
},
"exploit_sql_injection_success": {
LangZH: "%s %s SQL注入利用成功",
LangEN: "%s %s SQL injection exploit successful",
},
"exploit_data_extraction_success": {
LangZH: "%s %s %s 利用成功",
LangEN: "%s %s %s exploit successful",
},
"exploit_generic_success": {
LangZH: "%s %s %s 利用成功",
LangEN: "%s %s %s exploit successful",
},
"exploit_with_output": {
LangZH: " 输出: %s",
LangEN: " output: %s",
},
"exploit_files_created": {
LangZH: "创建/修改的文件: %v",
LangEN: "Files created/modified: %v",
},
"exploit_shell_obtained": {
LangZH: "获得Shell: %s %s:%d 用户:%s",
LangEN: "Shell obtained: %s %s:%d user:%s",
},
// ========================= 利用方法执行消息 =========================
"exploit_method_trying": {
LangZH: "尝试利用方法: %s",
LangEN: "Trying exploit method: %s",
},
"exploit_method_success": {
LangZH: "利用方法 %s 执行成功",
LangEN: "Exploit method %s executed successfully",
},
"exploit_method_failed": {
LangZH: "利用方法 %s 执行失败: %v",
LangEN: "Exploit method %s failed: %v",
},
"exploit_method_condition_not_met": {
LangZH: "利用方法 %s 前置条件不满足,跳过",
LangEN: "Exploit method %s prerequisites not met, skipping",
},
"exploit_all_methods_failed": {
LangZH: "所有利用方法都失败",
LangEN: "All exploit methods failed",
},
// ========================= MySQL利用方法消息 =========================
"mysql_version_info": {
LangZH: "MySQL版本: %s",
LangEN: "MySQL version: %s",
},
"mysql_current_user": {
LangZH: "当前用户: %s",
LangEN: "Current user: %s",
},
"mysql_current_database": {
LangZH: "当前数据库: %s",
LangEN: "Current database: %s",
},
"mysql_databases_found": {
LangZH: "发现数据库: %s",
LangEN: "Databases found: %s",
},
"mysql_tables_found": {
LangZH: "发现表: %v",
LangEN: "Tables found: %v",
},
"mysql_user_privileges": {
LangZH: "用户权限: %s",
LangEN: "User privileges: %s",
},
"mysql_file_privilege_detected": {
LangZH: "检测到FILE权限可能支持文件操作",
LangEN: "FILE privilege detected, file operations may be supported",
},
"mysql_file_read_success": {
LangZH: "读取文件 %s:\n%s",
LangEN: "File %s read:\n%s",
},
"mysql_file_write_success": {
LangZH: "成功写入文件: %s",
LangEN: "File written successfully: %s",
},
"mysql_no_file_privilege": {
LangZH: "无法读取任何文件可能没有FILE权限",
LangEN: "Cannot read any files, may lack FILE privilege",
},
// ========================= Redis利用方法消息 =========================
"redis_server_info": {
LangZH: "Redis服务器信息: %s",
LangEN: "Redis server info: %s",
},
"redis_config_info": {
LangZH: "Redis配置信息: %s",
LangEN: "Redis config info: %s",
},
"redis_keys_found": {
LangZH: "发现Redis键: %v",
LangEN: "Redis keys found: %v",
},
"redis_backup_created": {
LangZH: "Redis备份创建成功: %s",
LangEN: "Redis backup created: %s",
},
"redis_cron_job_written": {
LangZH: "Cron任务写入成功: %s",
LangEN: "Cron job written successfully: %s",
},
"redis_ssh_key_written": {
LangZH: "SSH密钥写入成功: %s",
LangEN: "SSH key written successfully: %s",
},
"redis_webshell_written": {
LangZH: "Webshell写入成功: %s",
LangEN: "Webshell written successfully: %s",
},
"redis_no_keys_found": {
LangZH: "未发现任何Redis键",
LangEN: "No Redis keys found",
},
"redis_write_failed": {
LangZH: "Redis写入操作失败",
LangEN: "Redis write operation failed",
},
// ========================= 插件架构消息 ========================= // ========================= 插件架构消息 =========================
"plugin_new_arch_trying": { "plugin_new_arch_trying": {
LangZH: "尝试使用新插件架构: %s", LangZH: "尝试使用新插件架构: %s",
@ -181,21 +339,47 @@ var PluginMessages = map[string]map[string]string{
LangEN: "%s vulnerability found: %s - %s", LangEN: "%s vulnerability found: %s - %s",
}, },
// ========================= 利用方法名称i18n =========================
"exploit_method_name_information_gathering": {
LangZH: "信息收集",
LangEN: "information_gathering",
},
"exploit_method_name_database_enumeration": {
LangZH: "数据库枚举",
LangEN: "database_enumeration",
},
"exploit_method_name_privilege_check": {
LangZH: "权限检查",
LangEN: "privilege_check",
},
"exploit_method_name_file_read": {
LangZH: "文件读取",
LangEN: "file_read",
},
"exploit_method_name_file_write": {
LangZH: "文件写入",
LangEN: "file_write",
},
"exploit_method_name_arbitrary_file_write": {
LangZH: "任意文件写入",
LangEN: "arbitrary_file_write",
},
"exploit_method_name_ssh_key_write": {
LangZH: "SSH密钥写入",
LangEN: "ssh_key_write",
},
"exploit_method_name_crontab_injection": {
LangZH: "定时任务注入",
LangEN: "crontab_injection",
},
"exploit_method_name_data_extraction": {
LangZH: "数据提取",
LangEN: "data_extraction",
},
// ========================= 利用结果消息 ========================= // ========================= 利用结果消息 =========================
"exploit_result_saved": { "exploit_result_saved": {
LangZH: "利用结果已保存: %s", LangZH: "利用结果已保存: %s",
LangEN: "Exploitation result saved: %s", LangEN: "Exploitation result saved: %s",
}, },
"exploit_method_trying": {
LangZH: "尝试利用方法: %s",
LangEN: "Trying exploitation method: %s",
},
"exploit_method_success": {
LangZH: "利用方法成功: %s",
LangEN: "Exploitation method successful: %s",
},
"exploit_method_failed": {
LangZH: "利用方法失败: %s - %v",
LangEN: "Exploitation method failed: %s - %v",
},
} }

View File

@ -57,21 +57,9 @@ func (a *PluginAdapter) AdaptPluginScan(pluginName string, info *common.HostInfo
if result != nil && result.Success { if result != nil && result.Success {
target := fmt.Sprintf("%s:%s", info.Host, info.Ports) target := fmt.Sprintf("%s:%s", info.Host, info.Ports)
if len(result.Credentials) > 0 { // 适配器层不输出扫描结果,由插件层负责输出
// 有凭据的情况 // 这避免了重复输出的问题
cred := result.Credentials[0] common.LogDebug(fmt.Sprintf("插件 %s 适配成功: %s", pluginName, target))
if cred.Username != "" {
common.LogSuccess(fmt.Sprintf("%s successful login: %s [%s:%s]",
pluginName, target, cred.Username, cred.Password))
} else {
// 仅密码的情况如Redis
common.LogSuccess(fmt.Sprintf("%s successful login: %s [%s]",
pluginName, target, cred.Password))
}
} else {
// 未授权访问的情况
common.LogSuccess(fmt.Sprintf("%s unauthorized access: %s", pluginName, target))
}
// 保存结果到文件 // 保存结果到文件
a.saveResult(info, result, pluginName) a.saveResult(info, result, pluginName)

View File

@ -4,6 +4,7 @@ import (
"context" "context"
"fmt" "fmt"
"github.com/shadow1ng/fscan/common" "github.com/shadow1ng/fscan/common"
"github.com/shadow1ng/fscan/common/i18n"
"sort" "sort"
) )
@ -56,28 +57,28 @@ func (e *BaseExploiter) Exploit(ctx context.Context, info *common.HostInfo, cred
for _, method := range e.exploitMethods { for _, method := range e.exploitMethods {
// 检查前置条件 // 检查前置条件
if !e.checkConditions(method.Conditions, info, creds) { if !e.checkConditions(method.Conditions, info, creds) {
common.LogDebug(fmt.Sprintf("利用方法 %s 前置条件不满足,跳过", method.Name)) common.LogDebug(i18n.GetText("exploit_method_condition_not_met", method.Name))
continue continue
} }
common.LogDebug(fmt.Sprintf("尝试利用方法: %s", method.Name)) common.LogDebug(i18n.GetText("exploit_method_trying", i18n.GetExploitMethodName(method.Name)))
// 执行利用 // 执行利用
result, err := method.Handler(ctx, info, creds) result, err := method.Handler(ctx, info, creds)
if err != nil { if err != nil {
common.LogError(fmt.Sprintf("利用方法 %s 执行失败: %v", method.Name, err)) common.LogError(i18n.GetText("exploit_method_failed", method.Name, err))
continue continue
} }
if result != nil && result.Success { if result != nil && result.Success {
common.LogSuccess(fmt.Sprintf("利用方法 %s 执行成功", method.Name)) common.LogSuccess(i18n.GetText("exploit_method_success", i18n.GetExploitMethodName(method.Name)))
result.Type = method.Type result.Type = method.Type
result.Method = method.Name result.Method = method.Name
return result, nil return result, nil
} }
} }
return nil, fmt.Errorf("所有利用方法都失败") return nil, fmt.Errorf(i18n.GetText("exploit_all_methods_failed"))
} }
// checkConditions 检查前置条件 // checkConditions 检查前置条件
@ -167,38 +168,40 @@ func SaveExploitResult(info *common.HostInfo, result *ExploitResult, pluginName
return return
} }
target := fmt.Sprintf("%s:%d", info.Host, info.Ports) target := fmt.Sprintf("%s:%s", info.Host, info.Ports)
var message string var message string
switch result.Type { switch result.Type {
case ExploitWeakPassword: case ExploitWeakPassword:
message = fmt.Sprintf("%s %s 弱密码利用成功", pluginName, target) message = i18n.GetText("exploit_weak_password_success", pluginName, target)
case ExploitUnauthorized: case ExploitUnauthorized:
message = fmt.Sprintf("%s %s 未授权访问利用成功", pluginName, target) message = i18n.GetText("exploit_unauthorized_success", pluginName, target)
case ExploitCommandExec: case ExploitCommandExec:
message = fmt.Sprintf("%s %s 命令执行利用成功", pluginName, target) message = i18n.GetText("exploit_command_exec_success", pluginName, target)
case ExploitFileWrite: case ExploitFileWrite:
message = fmt.Sprintf("%s %s 文件写入利用成功", pluginName, target) message = i18n.GetText("exploit_file_write_success", pluginName, target)
case ExploitSQLInjection: case ExploitSQLInjection:
message = fmt.Sprintf("%s %s SQL注入利用成功", pluginName, target) message = i18n.GetText("exploit_sql_injection_success", pluginName, target)
case ExploitDataExtraction:
message = i18n.GetText("exploit_data_extraction_success", pluginName, target, i18n.GetExploitMethodName(result.Method))
default: default:
message = fmt.Sprintf("%s %s %s 利用成功", pluginName, target, result.Type) message = i18n.GetText("exploit_generic_success", pluginName, target, i18n.GetExploitMethodName(result.Method))
} }
if result.Output != "" { if result.Output != "" {
message += fmt.Sprintf(" 输出: %s", result.Output) message += i18n.GetText("exploit_with_output", result.Output)
} }
common.LogSuccess(message) common.LogSuccess(message)
// 保存文件信息 // 保存文件信息
if len(result.Files) > 0 { if len(result.Files) > 0 {
common.LogSuccess(fmt.Sprintf("创建/修改的文件: %v", result.Files)) common.LogSuccess(i18n.GetText("exploit_files_created", result.Files))
} }
// 保存Shell信息 // 保存Shell信息
if result.Shell != nil { if result.Shell != nil {
common.LogSuccess(fmt.Sprintf("获得Shell: %s %s:%d 用户:%s", common.LogSuccess(i18n.GetText("exploit_shell_obtained",
result.Shell.Type, result.Shell.Host, result.Shell.Port, result.Shell.User)) result.Shell.Type, result.Shell.Host, result.Shell.Port, result.Shell.User))
} }
} }

View File

@ -5,6 +5,7 @@ import (
"database/sql" "database/sql"
"fmt" "fmt"
"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"
"strconv" "strconv"
"strings" "strings"
@ -90,21 +91,21 @@ func (e *MySQLExploiter) exploitInformationGathering(ctx context.Context, info *
// 获取版本信息 // 获取版本信息
version, err := e.getVersion(ctx, db) version, err := e.getVersion(ctx, db)
if err == nil { if err == nil {
base.AddOutputToResult(result, fmt.Sprintf("MySQL版本: %s", version)) base.AddOutputToResult(result, i18n.GetText("mysql_version_info", version))
result.Extra["version"] = version result.Extra["version"] = version
} }
// 获取当前用户 // 获取当前用户
user, err := e.getCurrentUser(ctx, db) user, err := e.getCurrentUser(ctx, db)
if err == nil { if err == nil {
base.AddOutputToResult(result, fmt.Sprintf("当前用户: %s", user)) base.AddOutputToResult(result, i18n.GetText("mysql_current_user", user))
result.Extra["current_user"] = user result.Extra["current_user"] = user
} }
// 获取当前数据库 // 获取当前数据库
database, err := e.getCurrentDatabase(ctx, db) database, err := e.getCurrentDatabase(ctx, db)
if err == nil { if err == nil {
base.AddOutputToResult(result, fmt.Sprintf("当前数据库: %s", database)) base.AddOutputToResult(result, i18n.GetText("mysql_current_database", database))
result.Extra["current_database"] = database result.Extra["current_database"] = database
} }
@ -124,14 +125,14 @@ func (e *MySQLExploiter) exploitDatabaseEnumeration(ctx context.Context, info *c
// 枚举数据库 // 枚举数据库
databases, err := e.enumerateDatabases(ctx, db) databases, err := e.enumerateDatabases(ctx, db)
if err == nil && len(databases) > 0 { if err == nil && len(databases) > 0 {
base.AddOutputToResult(result, fmt.Sprintf("发现数据库: %s", strings.Join(databases, ", "))) base.AddOutputToResult(result, i18n.GetText("mysql_databases_found", strings.Join(databases, ", ")))
result.Extra["databases"] = databases result.Extra["databases"] = databases
} }
// 枚举表(限制在非系统数据库中) // 枚举表(限制在非系统数据库中)
tables, err := e.enumerateTables(ctx, db) tables, err := e.enumerateTables(ctx, db)
if err == nil && len(tables) > 0 { if err == nil && len(tables) > 0 {
base.AddOutputToResult(result, fmt.Sprintf("发现表: %v", tables)) base.AddOutputToResult(result, i18n.GetText("mysql_tables_found", tables))
result.Extra["tables"] = tables result.Extra["tables"] = tables
} }
@ -151,7 +152,7 @@ func (e *MySQLExploiter) exploitPrivilegeCheck(ctx context.Context, info *common
// 检查用户权限 // 检查用户权限
privileges, err := e.getUserPrivileges(ctx, db) privileges, err := e.getUserPrivileges(ctx, db)
if err == nil && len(privileges) > 0 { if err == nil && len(privileges) > 0 {
base.AddOutputToResult(result, fmt.Sprintf("用户权限: %s", strings.Join(privileges, ", "))) base.AddOutputToResult(result, i18n.GetText("mysql_user_privileges", strings.Join(privileges, ", ")))
result.Extra["privileges"] = privileges result.Extra["privileges"] = privileges
} }
@ -159,7 +160,7 @@ func (e *MySQLExploiter) exploitPrivilegeCheck(ctx context.Context, info *common
hasFilePriv := e.hasFilePrivilege(privileges) hasFilePriv := e.hasFilePrivilege(privileges)
result.Extra["has_file_privilege"] = hasFilePriv result.Extra["has_file_privilege"] = hasFilePriv
if hasFilePriv { if hasFilePriv {
base.AddOutputToResult(result, "检测到FILE权限可能支持文件操作") base.AddOutputToResult(result, i18n.GetText("mysql_file_privilege_detected"))
} }
return result, nil return result, nil
@ -187,14 +188,14 @@ func (e *MySQLExploiter) exploitFileRead(ctx context.Context, info *common.HostI
for _, file := range filesToRead { for _, file := range filesToRead {
content, err := e.readFile(ctx, db, file) content, err := e.readFile(ctx, db, file)
if err == nil && content != "" { if err == nil && content != "" {
base.AddOutputToResult(result, fmt.Sprintf("读取文件 %s:\n%s", file, content)) base.AddOutputToResult(result, i18n.GetText("mysql_file_read_success", file, content))
hasRead = true hasRead = true
} }
} }
if !hasRead { if !hasRead {
return base.CreateFailedExploitResult(base.ExploitDataExtraction, "file_read", return base.CreateFailedExploitResult(base.ExploitDataExtraction, "file_read",
fmt.Errorf("无法读取任何文件可能没有FILE权限")), nil fmt.Errorf(i18n.GetText("mysql_no_file_privilege"))), nil
} }
return result, nil return result, nil
@ -219,7 +220,7 @@ func (e *MySQLExploiter) exploitFileWrite(ctx context.Context, info *common.Host
return base.CreateFailedExploitResult(base.ExploitFileWrite, "file_write", err), nil return base.CreateFailedExploitResult(base.ExploitFileWrite, "file_write", err), nil
} }
base.AddOutputToResult(result, fmt.Sprintf("成功写入文件: %s", testFile)) base.AddOutputToResult(result, i18n.GetText("mysql_file_write_success", testFile))
base.AddFileToResult(result, testFile) base.AddFileToResult(result, testFile)
return result, nil return result, nil

View File

@ -5,6 +5,7 @@ import (
"fmt" "fmt"
"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"
) )
@ -67,10 +68,10 @@ func (p *MySQLPlugin) Scan(ctx context.Context, info *common.HostInfo) (*base.Sc
return result, err // 扫描失败,直接返回 return result, err // 扫描失败,直接返回
} }
// 记录成功的弱密码发现(支持i18n // 记录成功的弱密码发现(使用i18n
target := fmt.Sprintf("%s:%s", info.Host, info.Ports) target := fmt.Sprintf("%s:%s", info.Host, info.Ports)
cred := result.Credentials[0] cred := result.Credentials[0]
common.LogSuccess(fmt.Sprintf("MySQL scan success: %s with %s:%s", target, cred.Username, cred.Password)) common.LogSuccess(i18n.GetText("mysql_scan_success", target, cred.Username, cred.Password))
// 自动利用功能(可通过-nobr参数禁用 // 自动利用功能(可通过-nobr参数禁用
if result.Success && len(result.Credentials) > 0 && !common.DisableBrute { if result.Success && len(result.Credentials) > 0 && !common.DisableBrute {
@ -84,17 +85,17 @@ func (p *MySQLPlugin) Scan(ctx context.Context, info *common.HostInfo) (*base.Sc
// autoExploit 自动利用 // autoExploit 自动利用
func (p *MySQLPlugin) autoExploit(ctx context.Context, info *common.HostInfo, creds *base.Credential) { func (p *MySQLPlugin) autoExploit(ctx context.Context, info *common.HostInfo, creds *base.Credential) {
target := fmt.Sprintf("%s:%s", info.Host, info.Ports) target := fmt.Sprintf("%s:%s", info.Host, info.Ports)
common.LogDebug(fmt.Sprintf("MySQL exploit starting for %s", target)) common.LogDebug(i18n.GetText("plugin_exploit_start", "MySQL", target))
// 执行利用 // 执行利用
result, err := p.exploiter.Exploit(ctx, info, creds) result, err := p.exploiter.Exploit(ctx, info, creds)
if err != nil { if err != nil {
common.LogError(fmt.Sprintf("MySQL exploit failed: %v", err)) common.LogError(i18n.GetText("plugin_exploit_failed", "MySQL", err))
return return
} }
if result != nil && result.Success { if result != nil && result.Success {
common.LogSuccess(fmt.Sprintf("MySQL exploit success using %s", result.Method)) common.LogSuccess(i18n.GetText("plugin_exploit_success", "MySQL", i18n.GetExploitMethodName(result.Method)))
base.SaveExploitResult(info, result, "MySQL") base.SaveExploitResult(info, result, "MySQL")
} }
} }

View File

@ -5,6 +5,7 @@ import (
"context" "context"
"fmt" "fmt"
"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"
"os" "os"
"path/filepath" "path/filepath"
@ -129,7 +130,7 @@ func (e *RedisExploiter) exploitArbitraryFileWrite(ctx context.Context, info *co
fmt.Errorf("写入失败: %s", msg)), nil fmt.Errorf("写入失败: %s", msg)), nil
} }
base.AddOutputToResult(result, fmt.Sprintf("成功写入文件: %s", common.RedisWritePath)) base.AddOutputToResult(result, i18n.GetText("redis_webshell_written", common.RedisWritePath))
base.AddFileToResult(result, common.RedisWritePath) base.AddFileToResult(result, common.RedisWritePath)
return result, nil return result, nil
@ -216,7 +217,7 @@ func (e *RedisExploiter) exploitCrontabInjection(ctx context.Context, info *comm
fmt.Errorf("写入失败: %s", msg)), nil fmt.Errorf("写入失败: %s", msg)), nil
} }
base.AddOutputToResult(result, fmt.Sprintf("成功注入Crontab任务反弹Shell到: %s", common.RedisShell)) base.AddOutputToResult(result, i18n.GetText("redis_cron_job_written", common.RedisShell))
// 创建Shell信息 // 创建Shell信息
shellParts := strings.Split(common.RedisShell, ":") shellParts := strings.Split(common.RedisShell, ":")
@ -245,7 +246,7 @@ func (e *RedisExploiter) exploitDataExtraction(ctx context.Context, info *common
// 获取所有键 // 获取所有键
keys, err := e.getAllKeys(redisConn) keys, err := e.getAllKeys(redisConn)
if err == nil && len(keys) > 0 { if err == nil && len(keys) > 0 {
base.AddOutputToResult(result, fmt.Sprintf("发现 %d 个键: %s", len(keys), strings.Join(keys[:min(10, len(keys))], ", "))) base.AddOutputToResult(result, i18n.GetText("redis_keys_found", strings.Join(keys[:min(10, len(keys))], ", ")))
result.Extra["keys"] = keys result.Extra["keys"] = keys
// 获取部分键值 // 获取部分键值
@ -255,7 +256,7 @@ func (e *RedisExploiter) exploitDataExtraction(ctx context.Context, info *common
} }
value, err := e.getKeyValue(redisConn, key) value, err := e.getKeyValue(redisConn, key)
if err == nil && value != "" { if err == nil && value != "" {
base.AddOutputToResult(result, fmt.Sprintf("%s = %s", key, value)) base.AddOutputToResult(result, fmt.Sprintf("%s = %s", key, value))
} }
} }
} }
@ -288,8 +289,8 @@ func (e *RedisExploiter) exploitInfoGathering(ctx context.Context, info *common.
} }
// 获取配置信息 // 获取配置信息
base.AddOutputToResult(result, fmt.Sprintf("数据库目录: %s", redisConn.config.Dir)) base.AddOutputToResult(result, i18n.GetText("redis_config_info", fmt.Sprintf("Dir: %s", redisConn.config.Dir)))
base.AddOutputToResult(result, fmt.Sprintf("数据库文件: %s", redisConn.config.DBFilename)) base.AddOutputToResult(result, i18n.GetText("redis_config_info", fmt.Sprintf("DBFilename: %s", redisConn.config.DBFilename)))
return result, nil return result, nil
} }