package core import ( "context" "crypto/tls" "fmt" "net" "net/http" "regexp" "strconv" "strings" "time" "github.com/shadow1ng/fscan/common" ) // WebPortDetector Web端口检测器 type WebPortDetector struct { // 常见Web端口列表 commonWebPorts map[int]bool // HTTP检测超时时间 httpTimeout time.Duration // HTTP客户端 httpClient *http.Client // HTTPS客户端 httpsClient *http.Client } // NewWebPortDetector 创建Web端口检测器 func NewWebPortDetector() *WebPortDetector { timeout := 3 * time.Second // 定义常见Web端口(扩展列表) commonPorts := map[int]bool{ // 标准Web端口 80: true, // HTTP 443: true, // HTTPS 8080: true, // HTTP alternate 8443: true, // HTTPS alternate // 开发服务器端口 3000: true, // Node.js dev server 4000: true, // Ruby dev server 5000: true, // Python dev server 8000: true, // Development server 8888: true, // Common dev port 9000: true, // Common dev port 9090: true, // Common dev port // Web服务器alternate端口 8081: true, 8082: true, 8083: true, 8084: true, 8085: true, 8086: true, 8087: true, 8088: true, 8089: true, // 企业级Web端口 9080: true, // WebSphere 9443: true, // WebSphere SSL 7001: true, // WebLogic 7002: true, // WebLogic SSL // 应用服务器端口 8180: true, // Tomcat alternate 8181: true, // Tomcat alternate 8282: true, // Common alternate 8383: true, // Common alternate 8484: true, // Common alternate 8585: true, // Common alternate // 代理和负载均衡端口 3128: true, // Squid proxy 8008: true, // HTTP proxy 8118: true, // Privoxy // 监控和管理界面 9200: true, // Elasticsearch 5601: true, // Kibana 3001: true, // Grafana alternate 9091: true, // Prometheus } // 创建HTTP客户端 httpClient := &http.Client{ Timeout: timeout, Transport: &http.Transport{ DialContext: (&net.Dialer{ Timeout: timeout, }).DialContext, MaxIdleConns: 10, IdleConnTimeout: 30 * time.Second, DisableKeepAlives: true, MaxConnsPerHost: 5, }, CheckRedirect: func(req *http.Request, via []*http.Request) error { return http.ErrUseLastResponse // 不跟随重定向,减少检测时间 }, } // 创建HTTPS客户端(跳过证书验证) httpsClient := &http.Client{ Timeout: timeout, Transport: &http.Transport{ DialContext: (&net.Dialer{ Timeout: timeout, }).DialContext, TLSClientConfig: &tls.Config{ InsecureSkipVerify: true, }, MaxIdleConns: 10, IdleConnTimeout: 30 * time.Second, DisableKeepAlives: true, MaxConnsPerHost: 5, }, CheckRedirect: func(req *http.Request, via []*http.Request) error { return http.ErrUseLastResponse }, } return &WebPortDetector{ commonWebPorts: commonPorts, httpTimeout: timeout, httpClient: httpClient, httpsClient: httpsClient, } } // IsWebService 智能检测端口是否运行Web服务 func (w *WebPortDetector) IsWebService(host string, port int) bool { // 1. 快速路径:常见Web端口直接返回true if w.IsCommonWebPort(port) { common.LogDebug(fmt.Sprintf("端口 %d 是常见Web端口,启用Web插件", port)) return true } // 2. 智能路径:对非常见端口进行HTTP协议探测 common.LogDebug(fmt.Sprintf("对端口 %d 进行智能Web检测", port)) result := w.detectHTTPService(host, port) common.LogDebug(fmt.Sprintf("端口 %d 智能检测结果: %v", port, result)) return result } // IsCommonWebPort 检查是否为常见Web端口 func (w *WebPortDetector) IsCommonWebPort(port int) bool { return w.commonWebPorts[port] } // detectHTTPService 检测HTTP服务(真正的智能检测) func (w *WebPortDetector) detectHTTPService(host string, port int) bool { // 创建检测上下文,避免长时间阻塞 ctx, cancel := context.WithTimeout(context.Background(), w.httpTimeout) defer cancel() // 并发检测HTTP和HTTPS httpChan := make(chan bool, 1) httpsChan := make(chan bool, 1) // 检测HTTP go func() { httpChan <- w.tryHTTPConnection(ctx, host, port, "http") }() // 检测HTTPS(对于高端口常见) go func() { httpsChan <- w.tryHTTPConnection(ctx, host, port, "https") }() // 等待任一协议检测成功 select { case result := <-httpChan: if result { common.LogDebug(fmt.Sprintf("端口 %d 检测到HTTP服务", port)) return true } case <-ctx.Done(): return false } select { case result := <-httpsChan: if result { common.LogDebug(fmt.Sprintf("端口 %d 检测到HTTPS服务", port)) return true } case <-ctx.Done(): return false } return false } // tryHTTPConnection 尝试HTTP连接 func (w *WebPortDetector) tryHTTPConnection(ctx context.Context, host string, port int, protocol string) bool { // 构造URL var url string if (port == 80 && protocol == "http") || (port == 443 && protocol == "https") { url = fmt.Sprintf("%s://%s", protocol, host) } else { url = fmt.Sprintf("%s://%s:%d", protocol, host, port) } // 选择客户端 var client *http.Client if protocol == "https" { client = w.httpsClient } else { client = w.httpClient } // 发送HEAD请求,最小化网络开销 req, err := http.NewRequestWithContext(ctx, "HEAD", url, nil) if err != nil { return false } req.Header.Set("User-Agent", "fscan-web-detector/2.2") req.Header.Set("Accept", "*/*") resp, err := client.Do(req) if err != nil { // 检查错误类型,某些错误也表明是HTTP服务 return w.analyzeHTTPError(err) } defer resp.Body.Close() // 分析HTTP响应 return w.analyzeHTTPResponse(resp, url) } // analyzeHTTPError 分析HTTP错误,判断是否表明存在HTTP服务 func (w *WebPortDetector) analyzeHTTPError(err error) bool { errStr := strings.ToLower(err.Error()) // 先检查明确的非Web服务错误 nonWebErrors := []string{ "connection refused", "no such host", "network unreachable", "timeout", "deadline exceeded", "connection reset", "eof", } for _, nonWebErr := range nonWebErrors { if strings.Contains(errStr, nonWebErr) { return false } } // 这些错误表明连接到了HTTP服务器,只是协议或其他问题 webIndicators := []string{ "malformed http response", "unexpected http response", "ssl handshake", "certificate", "tls", "bad request", "method not allowed", "server gave http response", } for _, indicator := range webIndicators { if strings.Contains(errStr, indicator) { return true } } return false } // analyzeHTTPResponse 分析HTTP响应,判断是否为Web服务 func (w *WebPortDetector) analyzeHTTPResponse(resp *http.Response, url string) bool { // 任何HTTP状态码都表明这是Web服务 if resp.StatusCode > 0 { // 分析响应头获取更多信息 serverHeader := resp.Header.Get("Server") contentType := resp.Header.Get("Content-Type") // 记录检测到的Web服务器信息 if serverHeader != "" { common.LogDebug(fmt.Sprintf("检测到Web服务器: %s (Server: %s)", url, serverHeader)) } // 特殊处理:某些非Web服务也会返回HTTP响应 if w.isNonWebHTTPService(serverHeader, contentType) { common.LogDebug(fmt.Sprintf("端口返回HTTP响应但可能不是Web服务: %s", url)) return false } return true } return false } // isNonWebHTTPService 判断是否为非Web的HTTP服务 func (w *WebPortDetector) isNonWebHTTPService(serverHeader, contentType string) bool { // 某些服务使用HTTP协议但不是传统Web服务 nonWebServices := []string{ "docker", "kubernetes", "consul", "etcd", "vault", "nomad", } serverLower := strings.ToLower(serverHeader) for _, service := range nonWebServices { if strings.Contains(serverLower, service) { return true } } return false } // GetWebPortRange 获取可能的Web端口范围(用于高级扫描) func (w *WebPortDetector) GetWebPortRange() []int { // 返回扩展的Web端口列表,用于目标发现 var ports []int for port := range w.commonWebPorts { ports = append(ports, port) } // 添加一些常见的自定义端口范围 customRanges := []int{ 8090, 8091, 8092, 8093, 8094, 8095, 8096, 8097, 8098, 8099, 9001, 9002, 9003, 9004, 9005, 9006, 9007, 9008, 9009, 10000, 10001, 10002, 10003, 10004, 10005, } ports = append(ports, customRanges...) return ports } // IsWebPortByPattern 基于端口模式判断是否可能是Web端口 func (w *WebPortDetector) IsWebPortByPattern(port int) bool { portStr := strconv.Itoa(port) // Web端口的常见模式 webPatterns := []*regexp.Regexp{ regexp.MustCompile(`^80\d{2}$`), // 80xx regexp.MustCompile(`^90\d{2}$`), // 90xx regexp.MustCompile(`^300\d$`), // 300x regexp.MustCompile(`^[3-9]000$`), // x000 regexp.MustCompile(`^1[0-9]000$`), // 1x000 } for _, pattern := range webPatterns { if pattern.MatchString(portStr) { return true } } return false }