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

- 创建通用SMB框架,包含抽象接口、凭据管理和并发扫描引擎 - SMB2插件代码量从492行减少到159行,减少68%代码量 - 统一错误分类和处理机制,提高代码可维护性 - 支持密码和哈希两种认证方式,保持向后兼容性 - 模块化设计便于单元测试和功能扩展
146 lines
3.0 KiB
Go
146 lines
3.0 KiB
Go
package smb2
|
||
|
||
import (
|
||
"context"
|
||
"fmt"
|
||
"os"
|
||
"time"
|
||
|
||
"github.com/shadow1ng/fscan/plugins/legacy/smb/common"
|
||
fscanCommon "github.com/shadow1ng/fscan/common"
|
||
"github.com/hirochachacha/go-smb2"
|
||
)
|
||
|
||
// Smb2Connector SMB2连接器实现
|
||
type Smb2Connector struct{}
|
||
|
||
// NewSmb2Connector 创建SMB2连接器
|
||
func NewSmb2Connector() *Smb2Connector {
|
||
return &Smb2Connector{}
|
||
}
|
||
|
||
// Connect 建立SMB2连接并进行认证
|
||
func (c *Smb2Connector) Connect(ctx context.Context, target *common.TargetInfo,
|
||
cred *common.Credential) (*common.ConnectionResult, error) {
|
||
|
||
// 建立TCP连接,使用socks代理支持
|
||
conn, err := fscanCommon.WrapperTcpWithTimeout("tcp",
|
||
fmt.Sprintf("%s:%d", target.Host, target.Port),
|
||
time.Duration(fscanCommon.Timeout)*time.Second)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("连接失败: %v", err)
|
||
}
|
||
defer conn.Close()
|
||
|
||
// 配置NTLM认证
|
||
initiator := smb2.NTLMInitiator{
|
||
User: cred.Username,
|
||
Domain: target.Domain,
|
||
}
|
||
|
||
// 设置认证方式(哈希或密码)
|
||
if cred.IsHash && len(cred.Hash) > 0 {
|
||
initiator.Hash = cred.Hash
|
||
} else {
|
||
initiator.Password = cred.Password
|
||
}
|
||
|
||
// 创建SMB2会话
|
||
dialer := &smb2.Dialer{
|
||
Initiator: &initiator,
|
||
}
|
||
|
||
// 建立会话
|
||
session, err := dialer.Dial(conn)
|
||
if err != nil {
|
||
classifiedError := common.ClassifySmbError(err)
|
||
return &common.ConnectionResult{
|
||
Success: false,
|
||
Error: classifiedError,
|
||
}, nil
|
||
}
|
||
defer session.Logoff()
|
||
|
||
// 检查上下文是否已取消
|
||
select {
|
||
case <-ctx.Done():
|
||
return &common.ConnectionResult{
|
||
Success: false,
|
||
Error: ctx.Err(),
|
||
}, nil
|
||
default:
|
||
}
|
||
|
||
// 获取共享列表
|
||
shares, err := session.ListSharenames()
|
||
if err != nil {
|
||
return &common.ConnectionResult{
|
||
Success: false,
|
||
Error: fmt.Errorf("获取共享列表失败: %v", err),
|
||
}, nil
|
||
}
|
||
|
||
// 检查上下文是否已取消
|
||
select {
|
||
case <-ctx.Done():
|
||
return &common.ConnectionResult{
|
||
Success: false,
|
||
Error: ctx.Err(),
|
||
Shares: shares,
|
||
}, nil
|
||
default:
|
||
}
|
||
|
||
// 尝试验证管理员权限
|
||
hasAdminAccess := c.validateAdminAccess(ctx, session)
|
||
|
||
return &common.ConnectionResult{
|
||
Success: true,
|
||
Shares: shares,
|
||
HasAdminAccess: hasAdminAccess,
|
||
}, nil
|
||
}
|
||
|
||
// GetProtocolName 获取协议名称
|
||
func (c *Smb2Connector) GetProtocolName() string {
|
||
return "SMB2"
|
||
}
|
||
|
||
// GetDefaultPort 获取默认端口
|
||
func (c *Smb2Connector) GetDefaultPort() int {
|
||
return 445
|
||
}
|
||
|
||
// validateAdminAccess 验证管理员权限
|
||
func (c *Smb2Connector) validateAdminAccess(ctx context.Context, session *smb2.Session) bool {
|
||
// 检查上下文
|
||
select {
|
||
case <-ctx.Done():
|
||
return false
|
||
default:
|
||
}
|
||
|
||
// 尝试挂载C$共享
|
||
fs, err := session.Mount("C$")
|
||
if err != nil {
|
||
return false
|
||
}
|
||
defer fs.Umount()
|
||
|
||
// 检查上下文
|
||
select {
|
||
case <-ctx.Done():
|
||
return false
|
||
default:
|
||
}
|
||
|
||
// 尝试读取系统文件以验证权限
|
||
path := `Windows\win.ini`
|
||
f, err := fs.OpenFile(path, os.O_RDONLY, 0666)
|
||
if err != nil {
|
||
return false
|
||
}
|
||
defer f.Close()
|
||
|
||
return true
|
||
} |