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

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

506 lines
12 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 (
"context"
"crypto/des"
"encoding/binary"
"fmt"
"net"
"strings"
"time"
"github.com/shadow1ng/fscan/common"
"github.com/shadow1ng/fscan/common/i18n"
)
// VNCPlugin VNC远程桌面服务扫描和利用插件 - 包含屏幕信息收集利用功能
type VNCPlugin struct {
name string
ports []int
}
// NewVNCPlugin 创建VNC插件
func NewVNCPlugin() *VNCPlugin {
return &VNCPlugin{
name: "vnc",
ports: []int{5900, 5901, 5902, 5903, 5904, 5905, 5906, 5907, 5908, 5909}, // VNC端口
}
}
// GetName 实现Plugin接口
func (p *VNCPlugin) GetName() string {
return p.name
}
// GetPorts 实现Plugin接口
func (p *VNCPlugin) GetPorts() []int {
return p.ports
}
// Scan 执行VNC扫描 - 弱密码检测和未授权访问检测
func (p *VNCPlugin) 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)
}
// 首先测试未授权访问
if result := p.testUnauthAccess(ctx, info); result != nil && result.Success {
common.LogSuccess(i18n.GetText("vnc_unauth_success", target))
return result
}
// 生成测试密码VNC通常只有密码没有用户名
passwords := p.generatePasswords()
// 逐个测试密码
for _, password := range passwords {
// 检查Context是否被取消
select {
case <-ctx.Done():
return &ScanResult{
Success: false,
Service: "vnc",
Error: ctx.Err(),
}
default:
}
// 测试密码
if p.testPassword(ctx, info, password) {
// VNC认证成功
common.LogSuccess(i18n.GetText("vnc_scan_success", target, password))
return &ScanResult{
Success: true,
Service: "vnc",
Username: "", // VNC没有用户名概念
Password: password,
}
}
}
// 所有密码都失败
return &ScanResult{
Success: false,
Service: "vnc",
Error: fmt.Errorf("未发现弱密码或未授权访问"),
}
}
// Exploit 执行VNC利用操作 - 实现屏幕信息收集功能
func (p *VNCPlugin) Exploit(ctx context.Context, info *common.HostInfo, creds Credential) *ExploitResult {
target := fmt.Sprintf("%s:%s", info.Host, info.Ports)
common.LogSuccess(fmt.Sprintf("VNC利用开始: %s", target))
var output strings.Builder
output.WriteString(fmt.Sprintf("=== VNC利用结果 - %s ===\n", target))
if creds.Password != "" {
output.WriteString(fmt.Sprintf("认证密码: %s\n", creds.Password))
} else {
output.WriteString("认证方式: 无密码访问\n")
}
// 建立VNC连接
conn, version, err := p.connectVNC(ctx, info, creds.Password)
if err != nil {
output.WriteString(fmt.Sprintf("\n[连接失败] %v\n", err))
return &ExploitResult{
Success: false,
Output: output.String(),
Error: err,
}
}
defer conn.Close()
output.WriteString(fmt.Sprintf("\n[VNC版本] %s\n", version))
output.WriteString("[连接状态] ✅ 成功建立VNC连接\n")
// 获取服务器信息
if serverInfo := p.getServerInfo(conn); serverInfo != "" {
output.WriteString(fmt.Sprintf("\n[服务器信息]\n%s\n", serverInfo))
}
// 获取桌面信息
if desktopInfo := p.getDesktopInfo(conn); desktopInfo != "" {
output.WriteString(fmt.Sprintf("\n[桌面信息]\n%s\n", desktopInfo))
}
// 获取像素格式信息
if pixelFormat := p.getPixelFormat(conn); pixelFormat != "" {
output.WriteString(fmt.Sprintf("\n[像素格式]\n%s\n", pixelFormat))
}
// 尝试获取屏幕截图信息(不实际截图,只是获取能力信息)
output.WriteString("\n[屏幕访问]\n")
output.WriteString("✅ 可以访问远程桌面画面\n")
output.WriteString("⚠️ 完整屏幕截图需要VNC客户端支持\n")
common.LogSuccess(fmt.Sprintf("VNC利用完成: %s", target))
return &ExploitResult{
Success: true,
Output: output.String(),
}
}
// testUnauthAccess 测试未授权访问
func (p *VNCPlugin) testUnauthAccess(ctx context.Context, info *common.HostInfo) *ScanResult {
target := fmt.Sprintf("%s:%s", info.Host, info.Ports)
conn, err := net.DialTimeout("tcp", target, time.Duration(common.Timeout)*time.Second)
if err != nil {
return nil
}
defer conn.Close()
conn.SetDeadline(time.Now().Add(time.Duration(common.Timeout) * time.Second))
// VNC握手
_, err = p.performHandshake(conn)
if err != nil {
return nil
}
// 读取认证类型
authTypes, err := p.readAuthTypes(conn)
if err != nil {
return nil
}
// 检查是否支持无认证
for _, authType := range authTypes {
if authType == 1 { // None authentication
return &ScanResult{
Success: true,
Service: "vnc",
Banner: "未授权访问",
}
}
}
return nil
}
// generatePasswords 生成VNC测试密码
func (p *VNCPlugin) generatePasswords() []string {
// VNC常见弱密码
return []string{
"",
"123456",
"password",
"admin",
"vnc",
"123",
"1234",
"12345",
"root",
"test",
"guest",
"user",
"welcome",
"qwerty",
"abc123",
}
}
// testPassword 测试单个密码
func (p *VNCPlugin) testPassword(ctx context.Context, info *common.HostInfo, password string) bool {
target := fmt.Sprintf("%s:%s", info.Host, info.Ports)
conn, err := net.DialTimeout("tcp", target, time.Duration(common.Timeout)*time.Second)
if err != nil {
return false
}
defer conn.Close()
conn.SetDeadline(time.Now().Add(time.Duration(common.Timeout) * time.Second))
// VNC握手
_, err = p.performHandshake(conn)
if err != nil {
return false
}
// 读取认证类型
authTypes, err := p.readAuthTypes(conn)
if err != nil {
return false
}
// 查找VNC认证类型
var hasVNCAuth bool
for _, authType := range authTypes {
if authType == 2 { // VNC authentication
hasVNCAuth = true
break
}
}
if !hasVNCAuth {
return false
}
// 选择VNC认证
if _, err := conn.Write([]byte{2}); err != nil {
return false
}
// 读取挑战
challenge := make([]byte, 16)
if _, err := conn.Read(challenge); err != nil {
return false
}
// 准备密码最多8字节不足补零
key := make([]byte, 8)
copy(key, []byte(password))
// DES加密挑战
response, err := p.encryptChallenge(challenge, key)
if err != nil {
return false
}
// 发送响应
if _, err := conn.Write(response); err != nil {
return false
}
// 读取认证结果
result := make([]byte, 4)
if _, err := conn.Read(result); err != nil {
return false
}
// 检查认证结果 (0表示成功)
return binary.BigEndian.Uint32(result) == 0
}
// connectVNC 建立认证后的VNC连接
func (p *VNCPlugin) connectVNC(ctx context.Context, info *common.HostInfo, password string) (net.Conn, string, error) {
target := fmt.Sprintf("%s:%s", info.Host, info.Ports)
conn, err := net.DialTimeout("tcp", target, time.Duration(common.Timeout)*time.Second)
if err != nil {
return nil, "", err
}
conn.SetDeadline(time.Now().Add(time.Duration(common.Timeout) * time.Second))
// VNC握手
version, err := p.performHandshake(conn)
if err != nil {
conn.Close()
return nil, "", err
}
// 读取认证类型
authTypes, err := p.readAuthTypes(conn)
if err != nil {
conn.Close()
return nil, "", err
}
// 处理认证
authenticated := false
for _, authType := range authTypes {
if authType == 1 && password == "" { // None authentication
conn.Write([]byte{1})
authenticated = true
break
} else if authType == 2 && password != "" { // VNC authentication
conn.Write([]byte{2})
// 读取挑战
challenge := make([]byte, 16)
conn.Read(challenge)
// 准备密码
key := make([]byte, 8)
copy(key, []byte(password))
// 加密挑战
response, err := p.encryptChallenge(challenge, key)
if err != nil {
continue
}
// 发送响应
conn.Write(response)
// 读取认证结果
result := make([]byte, 4)
conn.Read(result)
if binary.BigEndian.Uint32(result) == 0 {
authenticated = true
break
}
}
}
if !authenticated {
conn.Close()
return nil, "", fmt.Errorf("认证失败")
}
return conn, version, nil
}
// performHandshake 执行VNC握手
func (p *VNCPlugin) performHandshake(conn net.Conn) (string, error) {
// 读取服务器版本
versionBuf := make([]byte, 12)
if _, err := conn.Read(versionBuf); err != nil {
return "", err
}
version := strings.TrimSpace(string(versionBuf))
// 发送客户端版本(使用相同版本)
if _, err := conn.Write(versionBuf); err != nil {
return "", err
}
return version, nil
}
// readAuthTypes 读取认证类型
func (p *VNCPlugin) readAuthTypes(conn net.Conn) ([]byte, error) {
// 读取认证类型数量
countBuf := make([]byte, 1)
if _, err := conn.Read(countBuf); err != nil {
return nil, err
}
count := countBuf[0]
if count == 0 {
return nil, fmt.Errorf("无可用认证类型")
}
// 读取认证类型列表
authTypes := make([]byte, count)
if _, err := conn.Read(authTypes); err != nil {
return nil, err
}
return authTypes, nil
}
// encryptChallenge 使用DES加密挑战
func (p *VNCPlugin) encryptChallenge(challenge, key []byte) ([]byte, error) {
// VNC使用反向位序的DES
reversedKey := make([]byte, 8)
for i := 0; i < 8; i++ {
reversedKey[i] = p.reverseBits(key[i])
}
cipher, err := des.NewCipher(reversedKey)
if err != nil {
return nil, err
}
response := make([]byte, 16)
cipher.Encrypt(response[0:8], challenge[0:8])
cipher.Encrypt(response[8:16], challenge[8:16])
return response, nil
}
// reverseBits 反转字节的位序
func (p *VNCPlugin) reverseBits(b byte) byte {
var result byte
for i := 0; i < 8; i++ {
result = (result << 1) | ((b >> i) & 1)
}
return result
}
// getServerInfo 获取服务器信息
func (p *VNCPlugin) getServerInfo(conn net.Conn) string {
var info strings.Builder
// 发送客户端初始化消息
conn.Write([]byte{1}) // 共享桌面
// 读取服务器初始化消息
serverInit := make([]byte, 24)
n, err := conn.Read(serverInit)
if err != nil || n < 20 {
return "无法获取服务器信息"
}
width := binary.BigEndian.Uint16(serverInit[0:2])
height := binary.BigEndian.Uint16(serverInit[2:4])
info.WriteString(fmt.Sprintf("桌面分辨率: %dx%d\n", width, height))
// 读取桌面名称长度
nameLength := binary.BigEndian.Uint32(serverInit[20:24])
if nameLength > 0 && nameLength < 1024 {
nameBuf := make([]byte, nameLength)
if n, err := conn.Read(nameBuf); err == nil && n == int(nameLength) {
info.WriteString(fmt.Sprintf("桌面名称: %s\n", string(nameBuf)))
}
}
return info.String()
}
// getDesktopInfo 获取桌面信息
func (p *VNCPlugin) getDesktopInfo(conn net.Conn) string {
// 简化实现实际需要完整的RFB协议支持
return "桌面信息获取需要完整VNC客户端支持"
}
// getPixelFormat 获取像素格式信息
func (p *VNCPlugin) getPixelFormat(conn net.Conn) string {
return "像素格式信息获取需要完整VNC客户端支持"
}
// identifyService 服务识别 - 检测VNC服务
func (p *VNCPlugin) identifyService(ctx context.Context, info *common.HostInfo) *ScanResult {
target := fmt.Sprintf("%s:%s", info.Host, info.Ports)
conn, err := net.DialTimeout("tcp", target, time.Duration(common.Timeout)*time.Second)
if err != nil {
return &ScanResult{
Success: false,
Service: "vnc",
Error: err,
}
}
defer conn.Close()
conn.SetDeadline(time.Now().Add(time.Duration(common.Timeout) * time.Second))
// 尝试VNC握手
version, err := p.performHandshake(conn)
if err != nil {
return &ScanResult{
Success: false,
Service: "vnc",
Error: err,
}
}
banner := fmt.Sprintf("VNC远程桌面服务 (%s)", version)
common.LogSuccess(i18n.GetText("vnc_service_identified", target, banner))
return &ScanResult{
Success: true,
Service: "vnc",
Banner: banner,
}
}
// init 自动注册插件
func init() {
RegisterPlugin("vnc", func() Plugin {
return NewVNCPlugin()
})
}