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

经Linus式架构审计,发现并修复插件系统中的具体问题: ## 核心修复 ### 1. 消除local插件GetPorts()方法冗余 - 删除21个local插件中无意义的GetPorts()方法 - 简化local.Plugin接口:移除端口概念 - 理由:本地插件不涉及网络,端口概念完全多余 ### 2. 消除web插件GetPorts()方法冗余 - 删除2个web插件中无用的GetPorts()方法 - 简化web.WebPlugin接口:专注智能HTTP检测 - 理由:Web插件使用动态HTTP检测,预定义端口无价值 ### 3. 统一插件命名规范 - 统一所有插件接口使用Name()方法(符合Go惯例) - 消除GetName()与Name()不一致问题 - 简化适配器:不再需要方法名转换 ## 技术改进 接口精简: - local插件:GetName() + GetPorts() → Name() - web插件:GetName() + GetPorts() → Name() - services插件:GetName() → Name()(保留GetPorts(),业务必需) 代码减少: - 删除23个无用GetPorts()方法 - 重命名52个Name()方法 - 简化3个插件接口定义 ## 影响范围 修改文件:55个插件文件 代码变更:-155行 +61行(净减少94行) 功能影响:零破坏性,保持所有业务逻辑不变 这是基于业务需求分析的精准重构,消除真正多余的部分, 保持系统架构合理性和向后兼容性。
266 lines
6.2 KiB
Go
266 lines
6.2 KiB
Go
package services
|
||
|
||
import (
|
||
"bytes"
|
||
"context"
|
||
"fmt"
|
||
"strings"
|
||
"time"
|
||
|
||
"github.com/shadow1ng/fscan/common"
|
||
)
|
||
|
||
// SmbGhostPlugin CVE-2020-0796 SMB Ghost漏洞检测插件
|
||
type SmbGhostPlugin struct {
|
||
name string
|
||
ports []int
|
||
}
|
||
|
||
// SMB Ghost 检测数据包
|
||
const smbGhostPacket = "\x00" + // session
|
||
"\x00\x00\xc0" + // length
|
||
|
||
"\xfeSMB@\x00" + // protocol
|
||
|
||
//[MS-SMB2]: SMB2 NEGOTIATE Request
|
||
//https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/e14db7ff-763a-4263-8b10-0c3944f52fc5
|
||
|
||
"\x00\x00" +
|
||
"\x00\x00" +
|
||
"\x00\x00" +
|
||
"\x00\x00" +
|
||
"\x1f\x00" +
|
||
"\x00\x00\x00\x00" +
|
||
"\x00\x00\x00\x00" +
|
||
"\x00\x00\x00\x00" +
|
||
"\x00\x00\x00\x00" +
|
||
"\x00\x00\x00\x00" +
|
||
"\x00\x00\x00\x00" +
|
||
"\x00\x00\x00\x00" +
|
||
"\x00\x00\x00\x00" +
|
||
"\x00\x00\x00\x00" +
|
||
"\x00\x00\x00\x00" +
|
||
"\x00\x00\x00\x00" +
|
||
"\x00\x00\x00\x00" +
|
||
|
||
// [MS-SMB2]: SMB2 NEGOTIATE_CONTEXT
|
||
// https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/15332256-522e-4a53-8cd7-0bd17678a2f7
|
||
|
||
"$\x00" +
|
||
"\x08\x00" +
|
||
"\x01\x00" +
|
||
"\x00\x00" +
|
||
"\x7f\x00\x00\x00" +
|
||
"\x00\x00\x00\x00" +
|
||
"\x00\x00\x00\x00" +
|
||
"\x00\x00\x00\x00" +
|
||
"\x00\x00\x00\x00" +
|
||
"x\x00" +
|
||
"\x00\x00" +
|
||
"\x02\x00" +
|
||
"\x00\x00" +
|
||
"\x02\x02" +
|
||
"\x10\x02" +
|
||
"\x22\x02" +
|
||
"$\x02" +
|
||
"\x00\x03" +
|
||
"\x02\x03" +
|
||
"\x10\x03" +
|
||
"\x11\x03" +
|
||
"\x00\x00\x00\x00" +
|
||
|
||
// [MS-SMB2]: SMB2_PREAUTH_INTEGRITY_CAPABILITIES
|
||
// https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/5a07bd66-4734-4af8-abcf-5a44ff7ee0e5
|
||
|
||
"\x01\x00" +
|
||
"&\x00" +
|
||
"\x00\x00\x00\x00" +
|
||
"\x01\x00" +
|
||
"\x20\x00" +
|
||
"\x01\x00" +
|
||
"\x00\x00\x00\x00" +
|
||
"\x00\x00\x00\x00" +
|
||
"\x00\x00\x00\x00" +
|
||
"\x00\x00\x00\x00" +
|
||
"\x00\x00\x00\x00" +
|
||
"\x00\x00\x00\x00" +
|
||
"\x00\x00\x00\x00" +
|
||
"\x00\x00\x00\x00" +
|
||
"\x00\x00" +
|
||
|
||
// [MS-SMB2]: SMB2_COMPRESSION_CAPABILITIES
|
||
// https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/78e0c942-ab41-472b-b117-4a95ebe88271
|
||
|
||
"\x03\x00" +
|
||
"\x0e\x00" +
|
||
"\x00\x00\x00\x00" +
|
||
"\x01\x00" + //CompressionAlgorithmCount
|
||
"\x00\x00" +
|
||
"\x01\x00\x00\x00" +
|
||
"\x01\x00" + //LZNT1
|
||
"\x00\x00" +
|
||
"\x00\x00\x00\x00"
|
||
|
||
// NewSmbGhostPlugin 创建SMB Ghost插件
|
||
func NewSmbGhostPlugin() *SmbGhostPlugin {
|
||
return &SmbGhostPlugin{
|
||
name: "smbghost",
|
||
ports: []int{445},
|
||
}
|
||
}
|
||
|
||
// GetName 实现Plugin接口
|
||
func (p *SmbGhostPlugin) Name() string {
|
||
return p.name
|
||
}
|
||
|
||
// GetPorts 实现Plugin接口
|
||
func (p *SmbGhostPlugin) GetPorts() []int {
|
||
return p.ports
|
||
}
|
||
|
||
// Scan 执行SMB Ghost漏洞检测
|
||
func (p *SmbGhostPlugin) Scan(ctx context.Context, info *common.HostInfo) *ScanResult {
|
||
target := fmt.Sprintf("%s:%s", info.Host, info.Ports)
|
||
|
||
// 检查端口
|
||
if info.Ports != "445" {
|
||
return &ScanResult{
|
||
Success: false,
|
||
Service: "smbghost",
|
||
Error: fmt.Errorf("SMB Ghost插件仅支持445端口"),
|
||
}
|
||
}
|
||
|
||
// 执行漏洞检测
|
||
vulnerable, err := p.detectVulnerability(ctx, info)
|
||
if err != nil {
|
||
return &ScanResult{
|
||
Success: false,
|
||
Service: "smbghost",
|
||
Error: fmt.Errorf("检测失败: %v", err),
|
||
}
|
||
}
|
||
|
||
if vulnerable {
|
||
common.LogSuccess(fmt.Sprintf("SMB Ghost %s CVE-2020-0796 漏洞存在", target))
|
||
return &ScanResult{
|
||
Success: true,
|
||
Service: "smbghost",
|
||
Banner: "CVE-2020-0796 SMB Ghost漏洞",
|
||
}
|
||
}
|
||
|
||
return &ScanResult{
|
||
Success: false,
|
||
Service: "smbghost",
|
||
Error: fmt.Errorf("未发现CVE-2020-0796漏洞"),
|
||
}
|
||
}
|
||
|
||
|
||
// detectVulnerability 检测SMB Ghost漏洞
|
||
func (p *SmbGhostPlugin) detectVulnerability(ctx context.Context, info *common.HostInfo) (bool, error) {
|
||
addr := fmt.Sprintf("%s:445", info.Host)
|
||
timeout := time.Duration(common.Timeout) * time.Second
|
||
|
||
// 建立TCP连接
|
||
conn, err := common.WrapperTcpWithTimeout("tcp", addr, timeout)
|
||
if err != nil {
|
||
return false, fmt.Errorf("连接失败: %v", err)
|
||
}
|
||
defer conn.Close()
|
||
|
||
// 设置连接超时
|
||
if err = conn.SetDeadline(time.Now().Add(timeout)); err != nil {
|
||
return false, fmt.Errorf("设置超时失败: %v", err)
|
||
}
|
||
|
||
// 发送SMB Ghost检测数据包
|
||
if _, err = conn.Write([]byte(smbGhostPacket)); err != nil {
|
||
return false, fmt.Errorf("发送数据包失败: %v", err)
|
||
}
|
||
|
||
// 读取响应
|
||
buff := make([]byte, 1024)
|
||
n, err := conn.Read(buff)
|
||
if err != nil || n == 0 {
|
||
return false, fmt.Errorf("读取响应失败: %v", err)
|
||
}
|
||
|
||
// 分析响应,检测CVE-2020-0796特征
|
||
// 检查条件:
|
||
// 1. 响应包含"Public"字符串
|
||
// 2. 响应长度大于等于76字节
|
||
// 3. 特征字节匹配 (0x11,0x03) 和 (0x02,0x00)
|
||
if bytes.Contains(buff[:n], []byte("Public")) &&
|
||
len(buff[:n]) >= 76 &&
|
||
bytes.Equal(buff[72:74], []byte{0x11, 0x03}) &&
|
||
bytes.Equal(buff[74:76], []byte{0x02, 0x00}) {
|
||
return true, nil
|
||
}
|
||
|
||
return false, nil
|
||
}
|
||
|
||
// gatherSystemInfo 收集系统信息
|
||
func (p *SmbGhostPlugin) gatherSystemInfo(ctx context.Context, info *common.HostInfo) string {
|
||
addr := fmt.Sprintf("%s:445", info.Host)
|
||
timeout := time.Duration(common.Timeout) * time.Second
|
||
|
||
conn, err := common.WrapperTcpWithTimeout("tcp", addr, timeout)
|
||
if err != nil {
|
||
return ""
|
||
}
|
||
defer conn.Close()
|
||
|
||
// 发送标准SMB协商请求获取系统信息
|
||
negotiateReq := []byte{
|
||
0x00, 0x00, 0x00, 0x85,
|
||
0xfe, 0x53, 0x4d, 0x42, 0x40, 0x00, 0x00, 0x00,
|
||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||
0x24, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||
0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x02,
|
||
0x22, 0x02, 0x24, 0x02, 0x00, 0x03, 0x02, 0x03,
|
||
0x10, 0x03, 0x11, 0x03,
|
||
}
|
||
|
||
conn.SetDeadline(time.Now().Add(timeout))
|
||
conn.Write(negotiateReq)
|
||
|
||
buff := make([]byte, 1024)
|
||
n, err := conn.Read(buff)
|
||
if err != nil || n < 65 {
|
||
return ""
|
||
}
|
||
|
||
var sysInfo strings.Builder
|
||
sysInfo.WriteString("SMB服务检测成功\n")
|
||
|
||
// 解析SMB版本
|
||
if n >= 72 {
|
||
dialect := uint16(buff[70]) | uint16(buff[71])<<8
|
||
switch dialect {
|
||
case 0x0311:
|
||
sysInfo.WriteString("SMB版本: 3.1.1 (存在CVE-2020-0796风险)\n")
|
||
case 0x0302:
|
||
sysInfo.WriteString("SMB版本: 3.0.2\n")
|
||
case 0x0300:
|
||
sysInfo.WriteString("SMB版本: 3.0.0\n")
|
||
default:
|
||
sysInfo.WriteString(fmt.Sprintf("SMB版本: 未知 (0x%04x)\n", dialect))
|
||
}
|
||
}
|
||
|
||
return sysInfo.String()
|
||
}
|
||
|
||
// init 自动注册插件
|
||
func init() {
|
||
// 使用高效注册方式:直接传递端口信息,避免实例创建
|
||
RegisterPluginWithPorts("smbghost", func() Plugin {
|
||
return NewSmbGhostPlugin()
|
||
}, []int{445})
|
||
}
|