fscan/Plugins/services/ssh/exploiter.go
ZacharyZcR 60e59f5a78 refactor: 精简利用功能,只保留真正有攻击价值的利用方法
- 完全移除FTP、MySQL、SSH、ActiveMQ的利用功能,只保留弱密码扫描
- 重构Redis插件利用方法,严格按参数控制启用:
  * arbitrary_file_write: 需要-rwp和(-rwc或-rwf)参数
  * ssh_key_write: 需要-rf参数
  * crontab_injection: 需要-rs参数
- 修复Redis未授权访问时的利用条件检查问题
- 去除所有信息收集类利用,只保留GetShell和文件写入等实际攻击能力

现在利用功能完全参数驱动,只有提供对应参数时才启动相应利用方法
2025-08-08 09:40:56 +08:00

194 lines
5.8 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 ssh
import (
"context"
"fmt"
"github.com/shadow1ng/fscan/common"
"github.com/shadow1ng/fscan/common/i18n"
"github.com/shadow1ng/fscan/plugins/base"
"golang.org/x/crypto/ssh"
"strings"
)
// SSHExploiter SSH利用器实现
type SSHExploiter struct {
*base.BaseExploiter
connector *SSHConnector
}
// NewSSHExploiter 创建SSH利用器
func NewSSHExploiter() *SSHExploiter {
exploiter := &SSHExploiter{
BaseExploiter: base.NewBaseExploiter("ssh"),
connector: NewSSHConnector(),
}
// 添加利用方法
exploiter.setupExploitMethods()
return exploiter
}
// setupExploitMethods 设置利用方法
func (e *SSHExploiter) setupExploitMethods() {
// SSH插件不提供利用功能-sshkey参数用于私钥文件认证而非命令执行
// SSH的价值在于弱密码发现获取SSH访问权限本身就是目标
}
// exploitSystemInfo 系统信息收集利用
func (e *SSHExploiter) exploitSystemInfo(ctx context.Context, info *common.HostInfo, creds *base.Credential) (*base.ExploitResult, error) {
client, err := e.connectSSH(ctx, info, creds)
if err != nil {
return base.CreateFailedExploitResult(base.ExploitDataExtraction, "system_info", err), nil
}
defer client.Close()
result := base.CreateSuccessExploitResult(base.ExploitDataExtraction, "system_info")
// 收集系统信息的命令
commands := map[string]string{
"hostname": "hostname",
"os": "cat /etc/os-release 2>/dev/null || uname -a",
"whoami": "whoami",
"id": "id",
"uptime": "uptime",
"pwd": "pwd",
}
for name, cmd := range commands {
output, err := e.executeCommand(client, cmd)
if err == nil && output != "" {
base.AddOutputToResult(result, i18n.GetText("ssh_command_result", name, strings.TrimSpace(output)))
result.Extra[name] = strings.TrimSpace(output)
}
}
return result, nil
}
// exploitCommandTest 命令执行测试利用
func (e *SSHExploiter) exploitCommandTest(ctx context.Context, info *common.HostInfo, creds *base.Credential) (*base.ExploitResult, error) {
client, err := e.connectSSH(ctx, info, creds)
if err != nil {
return base.CreateFailedExploitResult(base.ExploitCommandExec, "command_test", err), nil
}
defer client.Close()
result := base.CreateSuccessExploitResult(base.ExploitCommandExec, "command_test")
// 测试基本命令执行
testCommands := []string{"echo 'SSH_COMMAND_TEST'", "date", "whoami"}
for _, cmd := range testCommands {
output, err := e.executeCommand(client, cmd)
if err == nil && output != "" {
base.AddOutputToResult(result, i18n.GetText("ssh_test_command", cmd, strings.TrimSpace(output)))
}
}
return result, nil
}
// exploitPrivilegeCheck 权限检查利用
func (e *SSHExploiter) exploitPrivilegeCheck(ctx context.Context, info *common.HostInfo, creds *base.Credential) (*base.ExploitResult, error) {
client, err := e.connectSSH(ctx, info, creds)
if err != nil {
return base.CreateFailedExploitResult(base.ExploitDataExtraction, "privilege_check", err), nil
}
defer client.Close()
result := base.CreateSuccessExploitResult(base.ExploitDataExtraction, "privilege_check")
// 检查sudo权限
sudoOutput, err := e.executeCommand(client, "sudo -l 2>/dev/null")
if err == nil && sudoOutput != "" && !strings.Contains(sudoOutput, "may not run sudo") {
base.AddOutputToResult(result, i18n.GetText("ssh_sudo_check", strings.TrimSpace(sudoOutput)))
result.Extra["sudo_privileges"] = strings.TrimSpace(sudoOutput)
}
// 检查是否为root用户
whoami, err := e.executeCommand(client, "whoami")
if err == nil && strings.TrimSpace(whoami) == "root" {
base.AddOutputToResult(result, i18n.GetText("ssh_root_access"))
result.Extra["is_root"] = true
}
// 检查用户组
groups, err := e.executeCommand(client, "groups")
if err == nil && groups != "" {
base.AddOutputToResult(result, i18n.GetText("ssh_user_groups", strings.TrimSpace(groups)))
result.Extra["groups"] = strings.TrimSpace(groups)
}
return result, nil
}
// =============================================================================
// SSH操作辅助函数
// =============================================================================
// connectSSH 使用凭据连接SSH
func (e *SSHExploiter) connectSSH(ctx context.Context, info *common.HostInfo, creds *base.Credential) (*ssh.Client, error) {
config := &ssh.ClientConfig{
Timeout: 0, // 禁用SSH库超时使用Context控制
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
}
// 设置认证方法
if creds.KeyData != nil && len(creds.KeyData) > 0 {
// 密钥认证
signer, err := ssh.ParsePrivateKey(creds.KeyData)
if err != nil {
return nil, fmt.Errorf("解析私钥失败: %v", err)
}
config.User = creds.Username
config.Auth = []ssh.AuthMethod{ssh.PublicKeys(signer)}
} else {
// 密码认证
config.User = creds.Username
config.Auth = []ssh.AuthMethod{ssh.Password(creds.Password)}
}
// 直接使用传入的Context它已经包含了正确的超时设置
target := fmt.Sprintf("%s:%s", info.Host, info.Ports)
// 使用Context控制超时
type sshResult struct {
client *ssh.Client
err error
}
resultChan := make(chan sshResult, 1)
go func() {
client, err := ssh.Dial("tcp", target, config)
resultChan <- sshResult{client: client, err: err}
}()
// 等待结果或超时
select {
case result := <-resultChan:
if result.err != nil {
return nil, fmt.Errorf("SSH连接失败: %v", result.err)
}
return result.client, nil
case <-ctx.Done():
return nil, fmt.Errorf("SSH连接超时: %v", ctx.Err())
}
}
// executeCommand 执行SSH命令
func (e *SSHExploiter) executeCommand(client *ssh.Client, command string) (string, error) {
session, err := client.NewSession()
if err != nil {
return "", err
}
defer session.Close()
output, err := session.CombinedOutput(command)
if err != nil {
return "", err
}
return string(output), nil
}