Compare commits

..

No commits in common. "v2.0.2" and "main" have entirely different histories.
v2.0.2 ... main

24 changed files with 205 additions and 436 deletions

57
.gitignore vendored
View File

@ -5,60 +5,3 @@ fscan.exe
fscan
makefile
fscanapi.csv
# IDE files / IDE 文件
.vscode/
.cursor/
.cursorrules
.claude/
# Local development files / 本地开发文件
*.local
*.tmp
*.temp
.env
.env.local
.env.development
.env.test
.env.production
# OS files / 操作系统文件
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db
desktop.ini
# Logs / 日志文件
*.log
logs/
log/
# Test coverage / 测试覆盖率
coverage.txt
coverage.html
*.cover
# Build artifacts / 构建产物
dist/
build/
bin/
*.exe
*.dll
*.so
*.dylib
# Go specific / Go 相关
vendor/
*.test
*.prof
*.mem
*.cpu
__debug_bin*
# Local development tools / 本地开发工具
.air.toml
air_tmp/

View File

@ -957,6 +957,8 @@ var (
ShowScanPlan bool // 是否显示扫描计划详情
SlowLogOutput bool // 是否启用慢速日志输出
Language string // 界面语言设置
ApiAddr string // API地址
SecretKey string // 加密密钥
)
var (

View File

@ -152,7 +152,8 @@ func Flag(Info *HostInfo) {
// ═════════════════════════════════════════════════
flag.StringVar(&Shellcode, "sc", "", GetText("flag_shellcode"))
flag.StringVar(&Language, "lang", "zh", GetText("flag_language"))
flag.StringVar(&ApiAddr, "api", "", GetText("flag_api"))
flag.StringVar(&SecretKey, "secret", "", GetText("flag_api_key"))
// 解析命令行参数
parseCommandLineArgs()
@ -160,6 +161,95 @@ func Flag(Info *HostInfo) {
SetLanguage()
}
// FlagFormRemote 解析远程扫描的命令行参数
func FlagFromRemote(info *HostInfo, argString string) error {
if strings.TrimSpace(argString) == "" {
return fmt.Errorf("参数为空")
}
args, err := parseEnvironmentArgs(argString)
if err != nil {
return fmt.Errorf("远程参数解析失败: %v", err)
}
// 创建一个新的 FlagSet 用于远程参数解析,避免污染主命令行
fs := flag.NewFlagSet("remote", flag.ContinueOnError)
// 注册需要的远程 flag注意使用 fs 而非 flag 包的全局变量
fs.StringVar(&info.Host, "h", "", GetText("flag_host"))
fs.StringVar(&ExcludeHosts, "eh", "", GetText("flag_exclude_hosts"))
fs.StringVar(&Ports, "p", MainPorts, GetText("flag_ports"))
fs.StringVar(&ExcludePorts, "ep", "", GetText("flag_exclude_ports"))
fs.StringVar(&HostsFile, "hf", "", GetText("flag_hosts_file"))
fs.StringVar(&PortsFile, "pf", "", GetText("flag_ports_file"))
fs.StringVar(&ScanMode, "m", "all", GetText("flag_scan_mode"))
fs.IntVar(&ThreadNum, "t", 10, GetText("flag_thread_num"))
fs.Int64Var(&Timeout, "time", 3, GetText("flag_timeout"))
fs.IntVar(&ModuleThreadNum, "mt", 10, GetText("flag_module_thread_num"))
fs.Int64Var(&GlobalTimeout, "gt", 180, GetText("flag_global_timeout"))
fs.IntVar(&LiveTop, "top", 10, GetText("flag_live_top"))
fs.BoolVar(&DisablePing, "np", false, GetText("flag_disable_ping"))
fs.BoolVar(&UsePing, "ping", false, GetText("flag_use_ping"))
fs.BoolVar(&EnableFingerprint, "fingerprint", false, GetText("flag_enable_fingerprint"))
fs.BoolVar(&LocalMode, "local", false, GetText("flag_local_mode"))
fs.StringVar(&Username, "user", "", GetText("flag_username"))
fs.StringVar(&Password, "pwd", "", GetText("flag_password"))
fs.StringVar(&AddUsers, "usera", "", GetText("flag_add_users"))
fs.StringVar(&AddPasswords, "pwda", "", GetText("flag_add_passwords"))
fs.StringVar(&UsersFile, "userf", "", GetText("flag_users_file"))
fs.StringVar(&PasswordsFile, "pwdf", "", GetText("flag_passwords_file"))
fs.StringVar(&HashFile, "hashf", "", GetText("flag_hash_file"))
fs.StringVar(&HashValue, "hash", "", GetText("flag_hash_value"))
fs.StringVar(&Domain, "domain", "", GetText("flag_domain"))
fs.StringVar(&SshKeyPath, "sshkey", "", GetText("flag_ssh_key"))
fs.StringVar(&TargetURL, "u", "", GetText("flag_target_url"))
fs.StringVar(&URLsFile, "uf", "", GetText("flag_urls_file"))
fs.StringVar(&Cookie, "cookie", "", GetText("flag_cookie"))
fs.Int64Var(&WebTimeout, "wt", 5, GetText("flag_web_timeout"))
fs.StringVar(&HttpProxy, "proxy", "", GetText("flag_http_proxy"))
fs.StringVar(&Socks5Proxy, "socks5", "", GetText("flag_socks5_proxy"))
fs.StringVar(&PocPath, "pocpath", "", GetText("flag_poc_path"))
fs.StringVar(&Pocinfo.PocName, "pocname", "", GetText("flag_poc_name"))
fs.BoolVar(&PocFull, "full", false, GetText("flag_poc_full"))
fs.BoolVar(&DnsLog, "dns", false, GetText("flag_dns_log"))
fs.IntVar(&PocNum, "num", 20, GetText("flag_poc_num"))
fs.BoolVar(&DisablePocScan, "nopoc", false, GetText("flag_no_poc"))
fs.StringVar(&RedisFile, "rf", "", GetText("flag_redis_file"))
fs.StringVar(&RedisShell, "rs", "", GetText("flag_redis_shell"))
fs.BoolVar(&DisableRedis, "noredis", false, GetText("flag_disable_redis"))
fs.StringVar(&RedisWritePath, "rwp", "", GetText("flag_redis_write_path"))
fs.StringVar(&RedisWriteContent, "rwc", "", GetText("flag_redis_write_content"))
fs.StringVar(&RedisWriteFile, "rwf", "", GetText("flag_redis_write_file"))
fs.BoolVar(&DisableBrute, "nobr", false, GetText("flag_disable_brute"))
fs.IntVar(&MaxRetries, "retry", 3, GetText("flag_max_retries"))
fs.StringVar(&Outputfile, "o", "result.txt", GetText("flag_output_file"))
fs.StringVar(&OutputFormat, "f", "txt", GetText("flag_output_format"))
fs.BoolVar(&DisableSave, "no", false, GetText("flag_disable_save"))
fs.BoolVar(&Silent, "silent", false, GetText("flag_silent_mode"))
fs.BoolVar(&NoColor, "nocolor", false, GetText("flag_no_color"))
fs.StringVar(&LogLevel, "log", LogLevelSuccess, GetText("flag_log_level"))
fs.BoolVar(&ShowProgress, "pg", false, GetText("flag_show_progress"))
fs.BoolVar(&ShowScanPlan, "sp", false, GetText("flag_show_scan_plan"))
fs.BoolVar(&SlowLogOutput, "slow", false, GetText("flag_slow_log_output"))
fs.StringVar(&Shellcode, "sc", "", GetText("flag_shellcode"))
fs.StringVar(&Language, "lang", "zh", GetText("flag_language"))
// 开始解析远程传入的参数
if err := fs.Parse(args); err != nil {
return fmt.Errorf("远程参数解析失败: %v", err)
}
return nil
}
// parseCommandLineArgs 处理来自环境变量和命令行的参数
func parseCommandLineArgs() {
// 首先检查环境变量中的参数

View File

@ -67,6 +67,18 @@ func InitOutput() error {
return fmt.Errorf(GetText("output_create_dir_failed", err))
}
if ApiAddr != "" {
OutputFormat = "csv"
Outputfile = filepath.Join(dir, "fscanapi.csv")
Num = 0
End = 0
if _, err := os.Stat(Outputfile); err == nil {
if err := os.Remove(Outputfile); err != nil {
return fmt.Errorf(GetText("output_file_remove_failed", err))
}
}
}
manager := &OutputManager{
outputPath: Outputfile,
outputFormat: OutputFormat,

View File

@ -108,8 +108,9 @@ func parsePasswords() {
if Password != "" {
passes := strings.Split(Password, ",")
for _, pass := range passes {
// 保留空密码,因为空口令是重要的安全测试场景
pwdList = append(pwdList, pass)
if pass != "" {
pwdList = append(pwdList, pass)
}
}
Passwords = pwdList
LogBase(GetText("load_passwords", len(pwdList)))
@ -124,8 +125,9 @@ func parsePasswords() {
}
for _, pass := range passes {
// 保留空密码,用户可能在文件中故意添加空行来测试空口令
pwdList = append(pwdList, pass)
if pass != "" {
pwdList = append(pwdList, pass)
}
}
Passwords = pwdList
LogBase(GetText("load_passwords_from_file", len(passes)))

View File

@ -1,16 +1,13 @@
package Common
import (
"context"
"crypto/tls"
"errors"
"fmt"
"golang.org/x/net/proxy"
"net"
"net/url"
"strings"
"time"
"golang.org/x/net/proxy"
)
// WrapperTcpWithTimeout 创建一个带超时的TCP连接
@ -19,12 +16,6 @@ func WrapperTcpWithTimeout(network, address string, timeout time.Duration) (net.
return WrapperTCP(network, address, d)
}
// WrapperTcpWithContext 创建一个带上下文的TCP连接
func WrapperTcpWithContext(ctx context.Context, network, address string) (net.Conn, error) {
d := &net.Dialer{}
return WrapperTCPWithContext(ctx, network, address, d)
}
// WrapperTCP 根据配置创建TCP连接
func WrapperTCP(network, address string, forward *net.Dialer) (net.Conn, error) {
// 直连模式
@ -50,54 +41,6 @@ func WrapperTCP(network, address string, forward *net.Dialer) (net.Conn, error)
return conn, nil
}
// WrapperTCPWithContext 根据配置创建支持上下文的TCP连接
func WrapperTCPWithContext(ctx context.Context, network, address string, forward *net.Dialer) (net.Conn, error) {
// 直连模式
if Socks5Proxy == "" {
conn, err := forward.DialContext(ctx, network, address)
if err != nil {
return nil, fmt.Errorf(GetText("tcp_conn_failed"), err)
}
return conn, nil
}
// Socks5代理模式
dialer, err := Socks5Dialer(forward)
if err != nil {
return nil, fmt.Errorf(GetText("socks5_create_failed"), err)
}
// 创建一个结果通道来处理连接和取消
connChan := make(chan struct {
conn net.Conn
err error
}, 1)
go func() {
conn, err := dialer.Dial(network, address)
select {
case <-ctx.Done():
if conn != nil {
conn.Close()
}
case connChan <- struct {
conn net.Conn
err error
}{conn, err}:
}
}()
select {
case <-ctx.Done():
return nil, ctx.Err()
case result := <-connChan:
if result.err != nil {
return nil, fmt.Errorf(GetText("socks5_conn_failed"), result.err)
}
return result.conn, nil
}
}
// Socks5Dialer 创建Socks5代理拨号器
func Socks5Dialer(forward *net.Dialer) (proxy.Dialer, error) {
// 解析代理URL
@ -133,59 +76,3 @@ func Socks5Dialer(forward *net.Dialer) (proxy.Dialer, error) {
return dialer, nil
}
// WrapperTlsWithContext 创建一个通过代理的TLS连接
func WrapperTlsWithContext(ctx context.Context, network, address string, tlsConfig *tls.Config) (net.Conn, error) {
// 直连模式
if Socks5Proxy == "" {
dialer := &net.Dialer{}
tcpConn, err := dialer.DialContext(ctx, network, address)
if err != nil {
return nil, fmt.Errorf("直连TCP连接失败: %v", err)
}
// 在TCP连接上进行TLS握手
tlsConn := tls.Client(tcpConn, tlsConfig)
// 使用ctx的deadline设置TLS握手超时
if deadline, ok := ctx.Deadline(); ok {
tlsConn.SetDeadline(deadline)
}
if err := tlsConn.Handshake(); err != nil {
tcpConn.Close()
return nil, fmt.Errorf("TLS握手失败: %v", err)
}
// 清除deadline让上层代码自己管理超时
tlsConn.SetDeadline(time.Time{})
return tlsConn, nil
}
// Socks5代理模式
// 首先通过代理建立到目标的TCP连接
tcpConn, err := WrapperTcpWithContext(ctx, network, address)
if err != nil {
return nil, fmt.Errorf("通过代理建立TCP连接失败: %v", err)
}
// 在TCP连接上进行TLS握手
tlsConn := tls.Client(tcpConn, tlsConfig)
// 使用ctx的deadline设置TLS握手超时
if deadline, ok := ctx.Deadline(); ok {
tlsConn.SetDeadline(deadline)
}
if err := tlsConn.Handshake(); err != nil {
tcpConn.Close()
return nil, fmt.Errorf("TLS握手失败: %v", err)
}
// 清除deadline让上层代码自己管理超时
tlsConn.SetDeadline(time.Time{})
return tlsConn, nil
}

View File

@ -407,8 +407,8 @@ func (i *Info) Write(msg []byte) error {
_, err := i.Conn.Write(msg)
if err != nil && strings.Contains(err.Error(), "close") {
i.Conn.Close()
// 连接关闭时重试 - 支持SOCKS5代理
i.Conn, err = Common.WrapperTcpWithTimeout("tcp", fmt.Sprintf("%s:%d", i.Address, i.Port), time.Duration(6)*time.Second)
// 连接关闭时重试
i.Conn, err = net.DialTimeout("tcp4", fmt.Sprintf("%s:%d", i.Address, i.Port), time.Duration(6)*time.Second)
if err == nil {
i.Conn.SetWriteDeadline(time.Now().Add(time.Second * time.Duration(WrTimeout)))
_, err = i.Conn.Write(msg)

View File

@ -6,6 +6,7 @@ import (
"github.com/shadow1ng/fscan/Common"
"golang.org/x/sync/errgroup"
"golang.org/x/sync/semaphore"
"net"
"strings"
"sync"
"sync/atomic"
@ -52,8 +53,8 @@ func EnhancedPortScan(hosts []string, ports string, timeout int64) []string {
g.Go(func() error {
defer sem.Release(1)
// 连接测试 - 支持SOCKS5代理
conn, err := Common.WrapperTcpWithTimeout("tcp", addr, to)
// 连接测试
conn, err := net.DialTimeout("tcp", addr, to)
if err != nil {
return nil
}

View File

@ -3,11 +3,11 @@ package Plugins
import (
"context"
"fmt"
"github.com/shadow1ng/fscan/Common"
"net"
"strings"
"sync"
"time"
"github.com/shadow1ng/fscan/Common"
)
// ActiveMQCredential 表示一个ActiveMQ凭据
@ -213,7 +213,8 @@ func ActiveMQConn(ctx context.Context, info *Common.HostInfo, user string, pass
addr := fmt.Sprintf("%s:%v", info.Host, info.Ports)
// 使用上下文创建带超时的连接
conn, err := Common.WrapperTcpWithTimeout("tcp", addr, time.Duration(Common.Timeout)*time.Second)
dialer := &net.Dialer{Timeout: time.Duration(Common.Timeout) * time.Second}
conn, err := dialer.DialContext(ctx, "tcp", addr)
if err != nil {
return false, err
}

View File

@ -3,29 +3,14 @@ package Plugins
import (
"context"
"fmt"
"net"
"github.com/gocql/gocql"
"github.com/shadow1ng/fscan/Common"
"strconv"
"strings"
"sync"
"time"
"github.com/gocql/gocql"
"github.com/shadow1ng/fscan/Common"
)
// CassandraProxyDialer 实现gocql.Dialer接口支持代理连接
type CassandraProxyDialer struct {
timeout time.Duration
}
func (d *CassandraProxyDialer) DialContext(ctx context.Context, network, addr string) (net.Conn, error) {
host, port, err := net.SplitHostPort(addr)
if err != nil {
return nil, err
}
return Common.WrapperTcpWithContext(ctx, network, fmt.Sprintf("%s:%s", host, port))
}
// CassandraCredential 表示一个Cassandra凭据
type CassandraCredential struct {
Username string
@ -238,13 +223,6 @@ func CassandraConn(ctx context.Context, info *Common.HostInfo, user string, pass
cluster.ProtoVersion = 4
cluster.Consistency = gocql.One
// 如果配置了代理设置自定义Dialer
if Common.Socks5Proxy != "" {
cluster.Dialer = &CassandraProxyDialer{
timeout: timeout,
}
}
if user != "" || pass != "" {
cluster.Authenticator = gocql.PasswordAuthenticator{
Username: user,

View File

@ -5,13 +5,12 @@ import (
"context"
"crypto/tls"
"fmt"
"github.com/shadow1ng/fscan/Common"
"io"
"net"
"strings"
"sync"
"time"
"github.com/shadow1ng/fscan/Common"
)
// IMAPCredential 表示一个IMAP凭据
@ -212,7 +211,8 @@ func IMAPConn(ctx context.Context, info *Common.HostInfo, user string, pass stri
// 在协程中尝试连接
go func() {
// 先尝试普通连接
conn, err := Common.WrapperTcpWithContext(ctx, "tcp", addr)
dialer := &net.Dialer{Timeout: timeout}
conn, err := dialer.DialContext(ctx, "tcp", addr)
if err == nil {
flag, authErr := tryIMAPAuth(conn, user, pass, timeout)
conn.Close()
@ -232,16 +232,14 @@ func IMAPConn(ctx context.Context, info *Common.HostInfo, user string, pass stri
tlsConfig := &tls.Config{
InsecureSkipVerify: true,
}
// 使用支持代理的TLS连接
tlsConn, tlsErr := Common.WrapperTlsWithContext(ctx, "tcp", addr, tlsConfig)
tlsConn, tlsErr := tls.DialWithDialer(dialer, "tcp", addr, tlsConfig)
if tlsErr != nil {
select {
case <-ctx.Done():
case resultChan <- struct {
success bool
err error
}{false, fmt.Errorf("TLS连接失败: %v", tlsErr)}:
}{false, fmt.Errorf("连接失败: %v", tlsErr)}:
}
return
}

View File

@ -3,12 +3,12 @@ package Plugins
import (
"context"
"fmt"
"github.com/go-ldap/ldap/v3"
"github.com/shadow1ng/fscan/Common"
"net"
"strings"
"sync"
"time"
"github.com/go-ldap/ldap/v3"
"github.com/shadow1ng/fscan/Common"
)
// LDAPCredential 表示一个LDAP凭据
@ -210,8 +210,13 @@ func tryLDAPCredential(ctx context.Context, info *Common.HostInfo, credential LD
func LDAPConn(ctx context.Context, info *Common.HostInfo, user string, pass string) (bool, error) {
address := fmt.Sprintf("%s:%s", info.Host, info.Ports)
// 创建拨号器并设置超时
dialer := &net.Dialer{
Timeout: time.Duration(Common.Timeout) * time.Second,
}
// 使用上下文控制的拨号过程
conn, err := Common.WrapperTcpWithContext(ctx, "tcp", address)
conn, err := dialer.DialContext(ctx, "tcp", address)
if err != nil {
return false, err
}

View File

@ -4,25 +4,13 @@ import (
"context"
"database/sql"
"fmt"
"net"
_ "github.com/denisenkom/go-mssqldb"
"github.com/shadow1ng/fscan/Common"
"strings"
"sync"
"time"
mssql "github.com/denisenkom/go-mssqldb"
"github.com/shadow1ng/fscan/Common"
)
// MSSQLProxyDialer 自定义dialer结构体
type MSSQLProxyDialer struct {
timeout time.Duration
}
// DialContext 实现mssql.Dialer接口支持socks代理
func (d *MSSQLProxyDialer) DialContext(ctx context.Context, network, addr string) (net.Conn, error) {
return Common.WrapperTcpWithContext(ctx, network, addr)
}
// MssqlCredential 表示一个MSSQL凭据
type MssqlCredential struct {
Username string
@ -217,58 +205,7 @@ func MssqlConn(ctx context.Context, info *Common.HostInfo, user string, pass str
host, username, password, port,
)
// 检查是否需要使用socks代理
if Common.Socks5Proxy != "" {
// 使用自定义dialer创建连接器
connector, err := mssql.NewConnector(connStr)
if err != nil {
return false, err
}
// 设置自定义dialer
connector.Dialer = &MSSQLProxyDialer{
timeout: time.Duration(Common.Timeout) * time.Millisecond,
}
// 使用连接器创建数据库连接
db := sql.OpenDB(connector)
defer db.Close()
// 设置连接参数
db.SetConnMaxLifetime(timeout)
db.SetConnMaxIdleTime(timeout)
db.SetMaxIdleConns(0)
db.SetMaxOpenConns(1)
// 通过上下文执行ping操作以支持超时控制
pingCtx, pingCancel := context.WithTimeout(ctx, timeout)
defer pingCancel()
errChan := make(chan error, 1)
go func() {
errChan <- db.PingContext(pingCtx)
}()
// 等待ping结果或者超时
select {
case err := <-errChan:
if err != nil {
return false, err
}
return true, nil
case <-ctx.Done():
// 全局超时或取消
return false, ctx.Err()
case <-pingCtx.Done():
if pingCtx.Err() == context.DeadlineExceeded {
// 单个连接超时
return false, fmt.Errorf("连接超时")
}
return false, pingCtx.Err()
}
}
// 使用标准连接方式
// 建立数据库连接
db, err := sql.Open("mssql", connStr)
if err != nil {
return false, err

View File

@ -4,9 +4,9 @@ import (
"context"
"encoding/binary"
"fmt"
"time"
"github.com/shadow1ng/fscan/Common"
"net"
"time"
)
// ModbusScanResult 表示 Modbus 扫描结果
@ -77,7 +77,8 @@ func tryModbusScan(ctx context.Context, info *Common.HostInfo, timeoutSeconds in
// 在协程中执行扫描
go func() {
// 尝试建立连接
conn, err := Common.WrapperTcpWithContext(connCtx, "tcp", target)
var d net.Dialer
conn, err := d.DialContext(connCtx, "tcp", target)
if err != nil {
select {
case <-connCtx.Done():

View File

@ -3,11 +3,11 @@ package Plugins
import (
"context"
"fmt"
"github.com/shadow1ng/fscan/Common"
"io"
"net"
"strings"
"time"
"github.com/shadow1ng/fscan/Common"
)
// MongodbScan 执行MongoDB未授权扫描
@ -112,8 +112,13 @@ func MongodbUnauth(ctx context.Context, info *Common.HostInfo) (bool, error) {
func checkMongoAuth(ctx context.Context, address string, packet []byte) (string, error) {
Common.LogDebug(fmt.Sprintf("建立MongoDB连接: %s", address))
// 创建连接超时上下文
connCtx, cancel := context.WithTimeout(ctx, time.Duration(Common.Timeout)*time.Second)
defer cancel()
// 使用带超时的连接
conn, err := Common.WrapperTcpWithTimeout("tcp", address, time.Duration(Common.Timeout)*time.Second)
var d net.Dialer
conn, err := d.DialContext(connCtx, "tcp", address)
if err != nil {
return "", fmt.Errorf("连接失败: %v", err)
}

View File

@ -4,38 +4,13 @@ import (
"context"
"database/sql"
"fmt"
"net"
_ "github.com/go-sql-driver/mysql"
"github.com/shadow1ng/fscan/Common"
"strings"
"sync"
"time"
"github.com/go-sql-driver/mysql"
"github.com/shadow1ng/fscan/Common"
)
// MySQLProxyDialer 自定义dialer结构体
type MySQLProxyDialer struct {
timeout time.Duration
}
// Dial 实现mysql.Dialer接口支持socks代理
func (d *MySQLProxyDialer) Dial(ctx context.Context, addr string) (net.Conn, error) {
return Common.WrapperTcpWithContext(ctx, "tcp", addr)
}
// registerMySQLDialer 注册MySQL自定义dialer
func registerMySQLDialer() {
// 创建自定义dialer
dialer := &MySQLProxyDialer{
timeout: time.Duration(Common.Timeout) * time.Millisecond,
}
// 注册自定义dialer到go-sql-driver/mysql
mysql.RegisterDialContext("tcp-proxy", func(ctx context.Context, addr string) (net.Conn, error) {
return dialer.Dial(ctx, addr)
})
}
// MySQLCredential 表示一个MySQL凭据
type MySQLCredential struct {
Username string
@ -229,24 +204,11 @@ func MysqlConn(ctx context.Context, info *Common.HostInfo, user string, pass str
host, port, username, password := info.Host, info.Ports, user, pass
timeout := time.Duration(Common.Timeout) * time.Second
// 检查是否需要使用socks代理
var connStr string
if Common.Socks5Proxy != "" {
// 注册自定义dialer
registerMySQLDialer()
// 使用自定义网络类型的连接字符串
connStr = fmt.Sprintf(
"%v:%v@tcp-proxy(%v:%v)/mysql?charset=utf8&timeout=%v",
username, password, host, port, timeout,
)
} else {
// 标准连接字符串
connStr = fmt.Sprintf(
"%v:%v@tcp(%v:%v)/mysql?charset=utf8&timeout=%v",
username, password, host, port, timeout,
)
}
// 构造连接字符串,包含超时设置
connStr := fmt.Sprintf(
"%v:%v@tcp(%v:%v)/mysql?charset=utf8&timeout=%v",
username, password, host, port, timeout,
)
// 创建结果通道
resultChan := make(chan struct {

View File

@ -3,12 +3,11 @@ package Plugins
import (
"context"
"fmt"
"github.com/neo4j/neo4j-go-driver/v4/neo4j"
"github.com/shadow1ng/fscan/Common"
"strings"
"sync"
"time"
"github.com/neo4j/neo4j-go-driver/v4/neo4j"
"github.com/shadow1ng/fscan/Common"
)
// Neo4jCredential 表示一个Neo4j凭据
@ -269,9 +268,6 @@ func Neo4jConn(info *Common.HostInfo, user string, pass string) (bool, error) {
config := func(c *neo4j.Config) {
c.SocketConnectTimeout = timeout
c.ConnectionAcquisitionTimeout = timeout
// 注意Neo4j驱动可能不支持代理配置
// 如果需要代理支持,可能需要使用更底层的连接方式
}
var driver neo4j.Driver

View File

@ -5,12 +5,11 @@ import (
"context"
"crypto/tls"
"fmt"
"github.com/shadow1ng/fscan/Common"
"net"
"strings"
"sync"
"time"
"github.com/shadow1ng/fscan/Common"
)
// POP3Credential 表示一个POP3凭据
@ -256,7 +255,12 @@ func POP3Conn(ctx context.Context, info *Common.HostInfo, user string, pass stri
// 在协程中尝试连接,支持取消
go func() {
// 首先尝试普通连接
conn, err := Common.WrapperTcpWithTimeout("tcp", addr, timeout)
dialer := &net.Dialer{
Timeout: timeout,
// 增加KeepAlive设置可能有助于处理一些服务器的限制
KeepAlive: 30 * time.Second,
}
conn, err := dialer.DialContext(ctx, "tcp", addr)
if err == nil {
flag, authErr := tryPOP3Auth(conn, user, pass, timeout)
conn.Close()
@ -283,10 +287,7 @@ func POP3Conn(ctx context.Context, info *Common.HostInfo, user string, pass stri
tlsConfig := &tls.Config{
InsecureSkipVerify: true,
}
// 对于TLS连接暂时使用标准dialer
// TODO: 实现通过socks代理的TLS连接
tempDialer := &net.Dialer{Timeout: timeout}
tlsConn, tlsErr := tls.DialWithDialer(tempDialer, "tcp", addr, tlsConfig)
tlsConn, tlsErr := tls.DialWithDialer(dialer, "tcp", addr, tlsConfig)
if tlsErr != nil {
select {
case <-ctx.Done():

View File

@ -3,32 +3,14 @@ package Plugins
import (
"context"
"database/sql"
"database/sql/driver"
"fmt"
"net"
_ "github.com/lib/pq"
"github.com/shadow1ng/fscan/Common"
"strings"
"sync"
"time"
"github.com/lib/pq"
"github.com/shadow1ng/fscan/Common"
)
// PostgresProxyDialer 自定义dialer结构体
type PostgresProxyDialer struct {
timeout time.Duration
}
// Dial 实现pq.Dialer接口支持socks代理
func (d *PostgresProxyDialer) Dial(network, address string) (net.Conn, error) {
return Common.WrapperTcpWithTimeout(network, address, d.timeout)
}
// DialTimeout 实现具有超时的连接
func (d *PostgresProxyDialer) DialTimeout(network, address string, timeout time.Duration) (net.Conn, error) {
return Common.WrapperTcpWithTimeout(network, address, timeout)
}
// PostgresCredential 表示一个PostgreSQL凭据
type PostgresCredential struct {
Username string
@ -220,41 +202,7 @@ func PostgresConn(ctx context.Context, info *Common.HostInfo, user string, pass
user, pass, info.Host, info.Ports, Common.Timeout/1000, // 转换为秒
)
// 检查是否需要使用socks代理
if Common.Socks5Proxy != "" {
// 使用自定义dialer通过socks代理连接
dialer := &PostgresProxyDialer{
timeout: time.Duration(Common.Timeout) * time.Millisecond,
}
// 使用pq.DialOpen通过自定义dialer建立连接
conn, err := pq.DialOpen(dialer, connStr)
if err != nil {
return false, err
}
defer conn.Close()
// 转换为sql.DB进行测试
db := sql.OpenDB(&postgresConnector{conn: conn})
defer db.Close()
// 使用上下文测试连接
err = db.PingContext(ctx)
if err != nil {
return false, err
}
// 简单查询测试权限
var version string
err = db.QueryRowContext(ctx, "SELECT version()").Scan(&version)
if err != nil {
return false, err
}
return true, nil
}
// 使用标准连接方式
// 建立数据库连接
db, err := sql.Open("postgres", connStr)
if err != nil {
return false, err
@ -282,19 +230,6 @@ func PostgresConn(ctx context.Context, info *Common.HostInfo, user string, pass
return true, nil
}
// postgresConnector 封装driver.Conn为sql.driver.Connector
type postgresConnector struct {
conn driver.Conn
}
func (c *postgresConnector) Connect(ctx context.Context) (driver.Conn, error) {
return c.conn, nil
}
func (c *postgresConnector) Driver() driver.Driver {
return &pq.Driver{}
}
// savePostgresResult 保存PostgreSQL扫描结果
func savePostgresResult(info *Common.HostInfo, target string, credential PostgresCredential) {
successMsg := fmt.Sprintf("PostgreSQL服务 %s 成功爆破 用户名: %v 密码: %v",

View File

@ -3,11 +3,11 @@ package Plugins
import (
"context"
"fmt"
"github.com/shadow1ng/fscan/Common"
"net"
"strings"
"sync"
"time"
"github.com/shadow1ng/fscan/Common"
)
// RsyncCredential 表示一个Rsync凭据
@ -212,8 +212,13 @@ func RsyncConn(ctx context.Context, info *Common.HostInfo, user string, pass str
host, port := info.Host, info.Ports
timeout := time.Duration(Common.Timeout) * time.Second
// 设置带有上下文的拨号器
dialer := &net.Dialer{
Timeout: timeout,
}
// 建立连接
conn, err := Common.WrapperTcpWithTimeout("tcp", fmt.Sprintf("%s:%s", host, port), timeout)
conn, err := dialer.DialContext(ctx, "tcp", fmt.Sprintf("%s:%s", host, port))
if err != nil {
return false, "", err
}
@ -330,11 +335,13 @@ func RsyncConn(ctx context.Context, info *Common.HostInfo, user string, pass str
}
// 5. 为每个模块创建新连接尝试认证
authConn, err := Common.WrapperTcpWithTimeout(host, port, timeout)
authConn, err := dialer.DialContext(ctx, "tcp", fmt.Sprintf("%s:%s", host, port))
if err != nil {
continue
}
defer authConn.Close() // 重复初始握手
defer authConn.Close()
// 重复初始握手
authConn.SetReadDeadline(time.Now().Add(timeout))
_, err = authConn.Read(buffer)
if err != nil {

View File

@ -3,13 +3,13 @@ package Plugins
import (
"context"
"fmt"
"github.com/shadow1ng/fscan/Common"
"net"
"os"
"strings"
"sync"
"time"
"github.com/shadow1ng/fscan/Common"
"github.com/hirochachacha/go-smb2"
)
@ -316,8 +316,9 @@ func trySmb2Credential(ctx context.Context, info *Common.HostInfo, credential Sm
// Smb2Con 尝试SMB2连接并进行认证检查共享访问权限
func Smb2Con(ctx context.Context, info *Common.HostInfo, user string, pass string, hash []byte, hasprint bool) (flag bool, err error, shares []string) {
// 建立TCP连接使用socks代理支持
conn, err := Common.WrapperTcpWithTimeout("tcp", fmt.Sprintf("%s:445", info.Host), time.Duration(Common.Timeout)*time.Second)
// 建立TCP连接使用上下文提供的超时控制
var d net.Dialer
conn, err := d.DialContext(ctx, "tcp", fmt.Sprintf("%s:445", info.Host))
if err != nil {
return false, fmt.Errorf("连接失败: %v", err), nil
}

View File

@ -3,12 +3,12 @@ package Plugins
import (
"context"
"fmt"
"github.com/shadow1ng/fscan/Common"
"net"
"net/smtp"
"strings"
"sync"
"time"
"github.com/shadow1ng/fscan/Common"
)
// SmtpCredential 表示一个SMTP凭据
@ -253,7 +253,11 @@ func SmtpConn(info *Common.HostInfo, user string, pass string, timeoutSeconds in
addr := fmt.Sprintf("%s:%s", host, port)
// 设置连接超时
conn, err := Common.WrapperTcpWithTimeout("tcp", addr, timeout)
dialer := &net.Dialer{
Timeout: timeout,
}
conn, err := dialer.Dial("tcp", addr)
if err != nil {
return false, err
}

View File

@ -5,13 +5,12 @@ import (
"context"
"errors"
"fmt"
"github.com/shadow1ng/fscan/Common"
"net"
"regexp"
"strings"
"sync"
"time"
"github.com/shadow1ng/fscan/Common"
)
// TelnetCredential 表示一个Telnet凭据
@ -249,8 +248,9 @@ func tryTelnetCredential(ctx context.Context, info *Common.HostInfo, credential
// telnetConnWithContext 带上下文的Telnet连接尝试
func telnetConnWithContext(ctx context.Context, info *Common.HostInfo, user, pass string) (bool, error) {
// 创建TCP连接(使用支持context的socks代理)
conn, err := Common.WrapperTcpWithContext(ctx, "tcp", fmt.Sprintf("%s:%s", info.Host, info.Ports))
// 创建TCP连接(使用上下文控制)
var d net.Dialer
conn, err := d.DialContext(ctx, "tcp", fmt.Sprintf("%s:%s", info.Host, info.Ports))
if err != nil {
return false, err
}

View File

@ -3,11 +3,11 @@ package Plugins
import (
"context"
"fmt"
"sync"
"time"
"github.com/mitchellh/go-vnc"
"github.com/shadow1ng/fscan/Common"
"net"
"sync"
"time"
)
// VncCredential 表示VNC凭据
@ -190,7 +190,8 @@ func VncConn(ctx context.Context, info *Common.HostInfo, pass string) (bool, err
timeout := time.Duration(Common.Timeout) * time.Second
// 使用带上下文的TCP连接
conn, err := Common.WrapperTcpWithTimeout("tcp", fmt.Sprintf("%s:%s", Host, Port), timeout)
var d net.Dialer
conn, err := d.DialContext(ctx, "tcp", fmt.Sprintf("%s:%s", Host, Port))
if err != nil {
return false, err
}