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

将所有服务插件移动到plugins/services/目录下,使目录结构更加清晰直观: • 创建plugins/services/目录统一管理服务扫描插件 • 添加init.go提供类型别名和函数导出 • 更新main.go导入路径 • 所有20个服务插件功能验证正常 新的目录结构更便于插件管理和维护。
506 lines
12 KiB
Go
506 lines
12 KiB
Go
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()
|
||
})
|
||
} |