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

- 完全移除FTP、MySQL、SSH、ActiveMQ的利用功能,只保留弱密码扫描 - 重构Redis插件利用方法,严格按参数控制启用: * arbitrary_file_write: 需要-rwp和(-rwc或-rwf)参数 * ssh_key_write: 需要-rf参数 * crontab_injection: 需要-rs参数 - 修复Redis未授权访问时的利用条件检查问题 - 去除所有信息收集类利用,只保留GetShell和文件写入等实际攻击能力 现在利用功能完全参数驱动,只有提供对应参数时才启动相应利用方法
194 lines
5.8 KiB
Go
194 lines
5.8 KiB
Go
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
|
||
} |