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

- 修改VNC插件,在所有网络连接点添加发包控制和计数 - 修改RDP插件,在testRDPConnection和checkNLAStatus中添加发包控制 - 统一包计数逻辑,确保TCP连接成功和失败都正确计数 - 保持现有远程桌面检测功能完整性
225 lines
5.6 KiB
Go
225 lines
5.6 KiB
Go
package services
|
||
|
||
import (
|
||
"bytes"
|
||
"context"
|
||
"fmt"
|
||
"net"
|
||
"time"
|
||
|
||
"github.com/shadow1ng/fscan/common"
|
||
"github.com/shadow1ng/fscan/plugins"
|
||
)
|
||
|
||
// RDPPlugin RDP远程桌面服务扫描插件 - 弱密码检测和服务识别
|
||
type RDPPlugin struct {
|
||
plugins.BasePlugin
|
||
}
|
||
|
||
// NewRDPPlugin 创建RDP插件
|
||
func NewRDPPlugin() *RDPPlugin {
|
||
return &RDPPlugin{
|
||
BasePlugin: plugins.NewBasePlugin("rdp"),
|
||
}
|
||
}
|
||
|
||
|
||
// GetPorts 实现Plugin接口
|
||
|
||
// Scan 执行RDP扫描 - 基础服务识别
|
||
func (p *RDPPlugin) Scan(ctx context.Context, info *common.HostInfo) *ScanResult {
|
||
target := fmt.Sprintf("%s:%s", info.Host, info.Ports)
|
||
|
||
// 检查端口
|
||
if info.Ports != "3389" {
|
||
return &ScanResult{
|
||
Success: false,
|
||
Service: "rdp",
|
||
Error: fmt.Errorf("RDP插件仅支持3389端口"),
|
||
}
|
||
}
|
||
|
||
// 如果禁用暴力破解,只做服务识别
|
||
if common.DisableBrute {
|
||
return p.identifyService(ctx, info)
|
||
}
|
||
|
||
// 生成测试凭据
|
||
credentials := GenerateCredentials("rdp")
|
||
if len(credentials) == 0 {
|
||
// RDP默认凭据
|
||
credentials = []Credential{
|
||
{Username: "administrator", Password: ""},
|
||
{Username: "administrator", Password: "administrator"},
|
||
{Username: "administrator", Password: "password"},
|
||
{Username: "administrator", Password: "123456"},
|
||
{Username: "admin", Password: "admin"},
|
||
{Username: "admin", Password: "123456"},
|
||
{Username: "user", Password: "user"},
|
||
{Username: "test", Password: "test"},
|
||
}
|
||
}
|
||
|
||
// 逐个测试凭据(简化版本,实际RDP认证需要复杂的协议处理)
|
||
for _, cred := range credentials {
|
||
// 检查Context是否被取消
|
||
select {
|
||
case <-ctx.Done():
|
||
return &ScanResult{
|
||
Success: false,
|
||
Service: "rdp",
|
||
Error: ctx.Err(),
|
||
}
|
||
default:
|
||
}
|
||
|
||
// 简化的RDP连接测试(实际需要完整的RDP协议实现)
|
||
if p.testRDPConnection(ctx, info) {
|
||
common.LogSuccess(fmt.Sprintf("RDP %s 服务可用", target))
|
||
|
||
return &ScanResult{
|
||
Success: true,
|
||
Service: "rdp",
|
||
Username: cred.Username,
|
||
Password: cred.Password,
|
||
Banner: "RDP服务可用",
|
||
}
|
||
}
|
||
}
|
||
|
||
// 所有凭据都失败
|
||
return &ScanResult{
|
||
Success: false,
|
||
Service: "rdp",
|
||
Error: fmt.Errorf("RDP认证失败"),
|
||
}
|
||
}
|
||
|
||
|
||
// testRDPConnection 测试RDP连接
|
||
func (p *RDPPlugin) testRDPConnection(ctx context.Context, info *common.HostInfo) bool {
|
||
target := fmt.Sprintf("%s:%s", info.Host, info.Ports)
|
||
|
||
// 检查发包限制
|
||
if canSend, reason := common.CanSendPacket(); !canSend {
|
||
common.LogError(fmt.Sprintf("RDP连接 %s 受限: %s", target, reason))
|
||
return false
|
||
}
|
||
|
||
conn, err := net.DialTimeout("tcp", target, time.Duration(common.Timeout)*time.Second)
|
||
if err != nil {
|
||
common.IncrementTCPFailedPacketCount()
|
||
return false
|
||
}
|
||
common.IncrementTCPSuccessPacketCount()
|
||
defer conn.Close()
|
||
|
||
conn.SetDeadline(time.Now().Add(time.Duration(common.Timeout) * time.Second))
|
||
|
||
// 发送简单的RDP连接请求
|
||
rdpRequest := []byte{
|
||
0x03, 0x00, 0x00, 0x13, 0x0e, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x08, 0x00, 0x03,
|
||
0x00, 0x00, 0x00,
|
||
}
|
||
|
||
_, err = conn.Write(rdpRequest)
|
||
if err != nil {
|
||
return false
|
||
}
|
||
|
||
// 读取响应
|
||
response := make([]byte, 1024)
|
||
n, err := conn.Read(response)
|
||
if err != nil || n == 0 {
|
||
return false
|
||
}
|
||
|
||
// 简单检查是否为RDP响应
|
||
if len(response) >= 4 && response[0] == 0x03 && response[1] == 0x00 {
|
||
return true
|
||
}
|
||
|
||
return false
|
||
}
|
||
|
||
// checkNLAStatus 检查网络级别身份验证状态
|
||
func (p *RDPPlugin) checkNLAStatus(ctx context.Context, info *common.HostInfo) string {
|
||
// 检查发包限制
|
||
target := fmt.Sprintf("%s:%s", info.Host, info.Ports)
|
||
if canSend, reason := common.CanSendPacket(); !canSend {
|
||
common.LogError(fmt.Sprintf("RDP NLA检测 %s 受限: %s", target, reason))
|
||
return "检测失败"
|
||
}
|
||
|
||
conn, err := net.DialTimeout("tcp", target, time.Duration(common.Timeout)*time.Second)
|
||
if err != nil {
|
||
common.IncrementTCPFailedPacketCount()
|
||
return "检测失败"
|
||
}
|
||
common.IncrementTCPSuccessPacketCount()
|
||
defer conn.Close()
|
||
|
||
conn.SetDeadline(time.Now().Add(time.Duration(common.Timeout) * time.Second))
|
||
|
||
// 发送支持NLA的连接请求
|
||
nlaRequest := []byte{
|
||
0x03, 0x00, 0x00, 0x2a, 0x25, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x6f, 0x6f, 0x6b, 0x69,
|
||
0x65, 0x3a, 0x20, 0x6d, 0x73, 0x74, 0x73, 0x68, 0x61, 0x73, 0x68, 0x3d, 0x61, 0x64, 0x6d, 0x69,
|
||
0x6e, 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x0d, 0x0a,
|
||
}
|
||
|
||
_, err = conn.Write(nlaRequest)
|
||
if err != nil {
|
||
return "检测失败"
|
||
}
|
||
|
||
response := make([]byte, 1024)
|
||
n, err := conn.Read(response)
|
||
if err != nil || n == 0 {
|
||
return "检测失败"
|
||
}
|
||
|
||
// 简化的NLA状态检测
|
||
if bytes.Contains(response, []byte("SSL")) || bytes.Contains(response, []byte("TLS")) {
|
||
return "已启用"
|
||
}
|
||
|
||
return "未启用"
|
||
}
|
||
|
||
// detectRDPVersion 检测RDP版本
|
||
func (p *RDPPlugin) detectRDPVersion(ctx context.Context, info *common.HostInfo) string {
|
||
// 简化实现,返回通用信息
|
||
return "RDP Protocol (Remote Desktop Protocol)"
|
||
}
|
||
|
||
// identifyService 服务识别 - 检测RDP服务
|
||
func (p *RDPPlugin) identifyService(ctx context.Context, info *common.HostInfo) *ScanResult {
|
||
target := fmt.Sprintf("%s:%s", info.Host, info.Ports)
|
||
|
||
if p.testRDPConnection(ctx, info) {
|
||
banner := "RDP远程桌面服务"
|
||
common.LogSuccess(fmt.Sprintf("RDP %s %s", target, banner))
|
||
|
||
return &ScanResult{
|
||
Success: true,
|
||
Service: "rdp",
|
||
Banner: banner,
|
||
}
|
||
}
|
||
|
||
return &ScanResult{
|
||
Success: false,
|
||
Service: "rdp",
|
||
Error: fmt.Errorf("无法识别为RDP服务"),
|
||
}
|
||
}
|
||
|
||
// init 自动注册插件
|
||
func init() {
|
||
// 使用高效注册方式:直接传递端口信息,避免实例创建
|
||
RegisterPluginWithPorts("rdp", func() Plugin {
|
||
return NewRDPPlugin()
|
||
}, []int{3389})
|
||
}
|