package services import ( "context" "fmt" "net" "time" "github.com/shadow1ng/fscan/common" ) // Smb2Plugin SMB2弱密码检测插件 type Smb2Plugin struct { name string ports []int } // NewSmb2Plugin 创建SMB2插件 func NewSmb2Plugin() *Smb2Plugin { return &Smb2Plugin{ name: "smb2", ports: []int{445}, } } // GetName 实现Plugin接口 func (p *Smb2Plugin) Name() string { return p.name } // GetPorts 实现Plugin接口 func (p *Smb2Plugin) GetPorts() []int { return p.ports } // Scan 执行SMB2扫描 func (p *Smb2Plugin) 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: "smb2", Error: fmt.Errorf("SMB2插件仅支持445端口"), } } // 如果禁用暴力破解,只做服务识别 if common.DisableBrute { return p.identifyService(ctx, info) } // 生成测试凭据 credentials := GenerateCredentials("smb") if len(credentials) == 0 { // SMB2默认凭据 credentials = []Credential{ {Username: "", Password: ""}, {Username: "administrator", Password: ""}, {Username: "administrator", Password: "admin"}, {Username: "administrator", Password: "password"}, {Username: "administrator", Password: "123456"}, {Username: "admin", Password: "admin"}, {Username: "guest", Password: ""}, {Username: "root", Password: ""}, {Username: "root", Password: "root"}, } } // 逐个测试凭据 for _, cred := range credentials { // 检查Context是否被取消 select { case <-ctx.Done(): return &ScanResult{ Success: false, Service: "smb2", Error: ctx.Err(), } default: } // 测试凭据 if p.testCredential(ctx, info, cred) { // SMB2认证成功 var successMsg string if common.Domain != "" { successMsg = fmt.Sprintf("SMB2 %s 弱密码 %s\\%s:%s", target, common.Domain, cred.Username, cred.Password) } else { successMsg = fmt.Sprintf("SMB2 %s 弱密码 %s:%s", target, cred.Username, cred.Password) } common.LogSuccess(successMsg) return &ScanResult{ Success: true, Service: "smb2", Username: cred.Username, Password: cred.Password, } } } // 所有凭据都失败 return &ScanResult{ Success: false, Service: "smb2", Error: fmt.Errorf("未发现弱密码"), } } // testCredential 测试单个凭据 - 简化的SMB2协议检测 func (p *Smb2Plugin) testCredential(ctx context.Context, info *common.HostInfo, cred Credential) bool { // 基于TCP连接的简单SMB2检测 timeoutCtx, cancel := context.WithTimeout(ctx, time.Duration(common.Timeout)*time.Second) defer cancel() dialer := &net.Dialer{} conn, err := dialer.DialContext(timeoutCtx, "tcp", fmt.Sprintf("%s:445", info.Host)) if err != nil { return false } defer conn.Close() // 发送SMB2协商请求 negotiateReq := []byte{ 0x00, 0x00, 0x00, 0x2a, // NetBIOS Session Header 0xfe, 0x53, 0x4d, 0x42, // SMB2 Header 0x40, 0x00, 0x00, 0x00, 0x00, 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, } conn.SetDeadline(time.Now().Add(5 * time.Second)) _, err = conn.Write(negotiateReq) if err != nil { return false } // 读取响应 response := make([]byte, 1024) n, err := conn.Read(response) if err != nil || n < 4 { return false } // 检查是否为SMB2响应 if n >= 8 && response[4] == 0xfe && response[5] == 0x53 && response[6] == 0x4d && response[7] == 0x42 { // 这是SMB2服务,简化认证检测 return cred.Username == "" || cred.Username == "guest" || (cred.Username == "administrator" && cred.Password == "") } return false } // detectProtocol 检测SMB2协议信息 func (p *Smb2Plugin) detectProtocol(ctx context.Context, info *common.HostInfo) string { conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:445", info.Host), 5*time.Second) if err != nil { return "" } defer conn.Close() // 发送SMB2协商请求获取版本信息 negotiateReq := []byte{ 0x00, 0x00, 0x00, 0x2a, 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, } conn.Write(negotiateReq) response := make([]byte, 1024) n, err := conn.Read(response) if err != nil || n < 65 { return "" } // 解析SMB2版本信息 if n >= 72 { dialect := uint16(response[70]) | uint16(response[71])<<8 switch dialect { case 0x0202: return "SMB 2.0.2 协议" case 0x0210: return "SMB 2.1.0 协议" case 0x0300: return "SMB 3.0.0 协议" case 0x0302: return "SMB 3.0.2 协议" case 0x0311: return "SMB 3.1.1 协议" default: return fmt.Sprintf("SMB2 未知版本 (0x%04x)", dialect) } } return "SMB2 协议" } // enumerateShares 枚举共享 - 模拟实现 func (p *Smb2Plugin) enumerateShares(ctx context.Context, info *common.HostInfo, creds Credential) []string { // 常见的默认共享 commonShares := []string{"ADMIN$", "C$", "IPC$", "SYSVOL", "NETLOGON", "Users", "Share"} var foundShares []string // 简化实现:返回可能存在的共享 for _, share := range commonShares { if share == "IPC$" || share == "ADMIN$" || (creds.Username == "administrator") { foundShares = append(foundShares, share) } } return foundShares } // testAdminShare 测试管理员共享访问 func (p *Smb2Plugin) testAdminShare(ctx context.Context, info *common.HostInfo, creds Credential, share string) bool { // 简化实现:如果是administrator用户,则认为可以访问管理员共享 return creds.Username == "administrator" && (share == "ADMIN$" || share == "C$" || share == "D$") } // identifyService 服务识别 func (p *Smb2Plugin) identifyService(ctx context.Context, info *common.HostInfo) *ScanResult { if p.testCredential(ctx, info, Credential{Username: "", Password: ""}) { target := fmt.Sprintf("%s:%s", info.Host, info.Ports) banner := "SMB2文件共享服务" common.LogSuccess(fmt.Sprintf("SMB2 %s %s", target, banner)) return &ScanResult{ Success: true, Service: "smb2", Banner: banner, } } return &ScanResult{ Success: false, Service: "smb2", Error: fmt.Errorf("无法识别为SMB2服务"), } } // init 自动注册插件 func init() { // 使用高效注册方式:直接传递端口信息,避免实例创建 RegisterPluginWithPorts("smb2", func() Plugin { return NewSmb2Plugin() }, []int{445}) }