package plugins import ( "bufio" "context" "fmt" "net" "strings" "time" "github.com/shadow1ng/fscan/common" "github.com/shadow1ng/fscan/common/i18n" ) // RsyncPlugin Rsync文件同步服务扫描和利用插件 - 包含文件列表利用功能 type RsyncPlugin struct { name string ports []int } // NewRsyncPlugin 创建Rsync插件 func NewRsyncPlugin() *RsyncPlugin { return &RsyncPlugin{ name: "rsync", ports: []int{873}, // Rsync端口 } } // GetName 实现Plugin接口 func (p *RsyncPlugin) GetName() string { return p.name } // GetPorts 实现Plugin接口 func (p *RsyncPlugin) GetPorts() []int { return p.ports } // Scan 执行Rsync扫描 - 未授权访问检测 func (p *RsyncPlugin) Scan(ctx context.Context, info *common.HostInfo) *ScanResult { target := fmt.Sprintf("%s:%s", info.Host, info.Ports) // 如果禁用暴力破解,只做服务识别 if common.DisableBrute { return p.identifyService(ctx, info) } // Rsync主要检查未授权访问和弱密码 if result := p.testUnauthorizedAccess(ctx, info); result != nil && result.Success { common.LogSuccess(i18n.GetText("rsync_unauth_success", target)) return result } // 如果未授权访问失败,尝试弱密码 if result := p.testWeakPasswords(ctx, info); result != nil && result.Success { common.LogSuccess(i18n.GetText("rsync_weak_pwd_success", target)) return result } // 所有尝试都失败 return &ScanResult{ Success: false, Service: "rsync", Error: fmt.Errorf("Rsync服务连接失败或需要认证"), } } // Exploit 执行Rsync利用操作 - 实现文件列表功能 func (p *RsyncPlugin) Exploit(ctx context.Context, info *common.HostInfo, creds Credential) *ExploitResult { // 建立Rsync连接 conn := p.connectToRsync(ctx, info) if conn == nil { return &ExploitResult{ Success: false, Error: fmt.Errorf("Rsync连接失败"), } } defer conn.Close() target := fmt.Sprintf("%s:%s", info.Host, info.Ports) common.LogSuccess(fmt.Sprintf("Rsync利用开始: %s", target)) var output strings.Builder output.WriteString(fmt.Sprintf("=== Rsync利用结果 - %s ===\n", target)) // 获取模块列表 modules := p.getModules(conn) if len(modules) > 0 { output.WriteString(fmt.Sprintf("\n[可用模块] (共%d个)\n", len(modules))) for _, module := range modules { output.WriteString(fmt.Sprintf(" %s\n", module)) } // 尝试列出每个模块的内容 for i, module := range modules { if i >= 3 { // 限制最多列出3个模块内容 break } moduleName := strings.Fields(module)[0] // 获取模块名 if files := p.getModuleFiles(ctx, info, moduleName); len(files) > 0 { output.WriteString(fmt.Sprintf("\n[模块 %s 文件列表] (共%d个)\n", moduleName, len(files))) for j, file := range files { if j >= 20 { // 每个模块最多显示20个文件 output.WriteString("... (更多文件)\n") break } output.WriteString(fmt.Sprintf(" %s\n", file)) } } } } else { output.WriteString("\n[模块列表] 无可访问模块或服务受限\n") } // 测试写权限 if writeTest := p.testWritePermission(ctx, info); writeTest != "" { output.WriteString(fmt.Sprintf("\n[写权限测试]\n%s\n", writeTest)) } common.LogSuccess(fmt.Sprintf("Rsync利用完成: %s", target)) return &ExploitResult{ Success: true, Output: output.String(), } } // testUnauthorizedAccess 测试未授权访问 func (p *RsyncPlugin) testUnauthorizedAccess(ctx context.Context, info *common.HostInfo) *ScanResult { conn := p.connectToRsync(ctx, info) if conn == nil { return nil } defer conn.Close() // 尝试列出模块 modules := p.getModules(conn) if len(modules) > 0 { return &ScanResult{ Success: true, Service: "rsync", Banner: "未授权访问", } } return nil } // testWeakPasswords 测试弱密码 func (p *RsyncPlugin) testWeakPasswords(ctx context.Context, info *common.HostInfo) *ScanResult { // Rsync密码通常在模块级别设置,这里简化处理 // 实际场景中需要针对具体模块进行认证 return nil } // connectToRsync 连接到Rsync服务 func (p *RsyncPlugin) connectToRsync(ctx context.Context, info *common.HostInfo) net.Conn { target := fmt.Sprintf("%s:%s", info.Host, info.Ports) timeout := time.Duration(common.Timeout) * time.Second conn, err := net.DialTimeout("tcp", target, timeout) if err != nil { return nil } // 设置操作超时 conn.SetDeadline(time.Now().Add(timeout)) return conn } // getModules 获取Rsync模块列表 func (p *RsyncPlugin) getModules(conn net.Conn) []string { // 发送RSYNCD协议的模块列表请求 timeout := time.Duration(common.Timeout) * time.Second conn.SetWriteDeadline(time.Now().Add(timeout)) if _, err := conn.Write([]byte("\n")); err != nil { return nil } conn.SetReadDeadline(time.Now().Add(timeout)) scanner := bufio.NewScanner(conn) var modules []string for scanner.Scan() { line := strings.TrimSpace(scanner.Text()) if line == "" { continue } // Rsync协议结束标记 if strings.HasPrefix(line, "@RSYNCD: EXIT") { break } // 跳过协议头 if strings.HasPrefix(line, "@RSYNCD:") { continue } modules = append(modules, line) } return modules } // getModuleFiles 获取指定模块的文件列表 func (p *RsyncPlugin) getModuleFiles(ctx context.Context, info *common.HostInfo, module string) []string { conn := p.connectToRsync(ctx, info) if conn == nil { return nil } defer conn.Close() timeout := time.Duration(common.Timeout) * time.Second // 发送模块名和协议版本 request := fmt.Sprintf("%s\n", module) conn.SetWriteDeadline(time.Now().Add(timeout)) if _, err := conn.Write([]byte(request)); err != nil { return nil } conn.SetReadDeadline(time.Now().Add(timeout)) scanner := bufio.NewScanner(conn) var files []string for scanner.Scan() { line := strings.TrimSpace(scanner.Text()) if line == "" { continue } // 检查错误响应 if strings.Contains(line, "@ERROR") { break } // Rsync协议结束标记 if strings.HasPrefix(line, "@RSYNCD: EXIT") { break } // 跳过协议头 if strings.HasPrefix(line, "@RSYNCD:") { continue } files = append(files, line) // 限制文件数量避免过多输出 if len(files) >= 50 { break } } return files } // testWritePermission 测试写权限 func (p *RsyncPlugin) testWritePermission(ctx context.Context, info *common.HostInfo) string { // Rsync写权限测试比较复杂,需要实际的rsync客户端 // 这里简化为检测是否支持上传 return "❌ 写权限检测需要完整的rsync客户端支持" } // identifyService 服务识别 - 检测Rsync服务 func (p *RsyncPlugin) identifyService(ctx context.Context, info *common.HostInfo) *ScanResult { target := fmt.Sprintf("%s:%s", info.Host, info.Ports) conn := p.connectToRsync(ctx, info) if conn == nil { return &ScanResult{ Success: false, Service: "rsync", Error: fmt.Errorf("无法连接到Rsync服务"), } } defer conn.Close() // 尝试Rsync协议握手 timeout := time.Duration(common.Timeout) * time.Second conn.SetWriteDeadline(time.Now().Add(timeout)) if _, err := conn.Write([]byte("\n")); err != nil { return &ScanResult{ Success: false, Service: "rsync", Error: err, } } conn.SetReadDeadline(time.Now().Add(timeout)) response := make([]byte, 1024) n, err := conn.Read(response) if err != nil { return &ScanResult{ Success: false, Service: "rsync", Error: err, } } responseStr := string(response[:n]) var banner string if strings.Contains(responseStr, "@RSYNCD") { // 解析版本信息 lines := strings.Split(responseStr, "\n") for _, line := range lines { if strings.HasPrefix(line, "@RSYNCD:") { banner = fmt.Sprintf("Rsync服务 (%s)", strings.TrimSpace(line)) break } } if banner == "" { banner = "Rsync文件同步服务" } } else { return &ScanResult{ Success: false, Service: "rsync", Error: fmt.Errorf("无法识别为Rsync服务"), } } common.LogSuccess(i18n.GetText("rsync_service_identified", target, banner)) return &ScanResult{ Success: true, Service: "rsync", Banner: banner, } } // init 自动注册插件 func init() { RegisterPlugin("rsync", func() Plugin { return NewRsyncPlugin() }) }