fscan/plugins/services/rsync.go
ZacharyZcR e082e2bb59 refactor: 重组插件目录结构,提升管理直观性
将所有服务插件移动到plugins/services/目录下,使目录结构更加清晰直观:
• 创建plugins/services/目录统一管理服务扫描插件
• 添加init.go提供类型别名和函数导出
• 更新main.go导入路径
• 所有20个服务插件功能验证正常

新的目录结构更便于插件管理和维护。
2025-08-26 00:02:13 +08:00

344 lines
8.2 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 services
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()
})
}