mirror of
https://github.com/shadow1ng/fscan.git
synced 2025-09-14 05:56:46 +08:00
refactor: 简化HTTP协议检测,消除硬编码和重复检测
优化内容: - 消除硬编码协议列表,使用通用的二进制字符比例判断 - 协议预检查只执行一次,HTTP和HTTPS检测复用结果 - 添加tryHTTPConnectionDirect函数,避免重复协议检查 - 简化isNonHTTPProtocol逻辑,基于统计学特征而非特定协议 技术改进: - 检测性能提升:避免重复的TCP连接和协议分析 - 更好的可维护性:无需为每个新协议添加硬编码规则 - 更智能的判断:基于二进制内容比例,适用于所有未知协议
This commit is contained in:
parent
c5eb2cef86
commit
57aa48be58
@ -6,8 +6,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"regexp"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
@ -35,51 +33,10 @@ type WebPortDetector struct {
|
|||||||
func NewWebPortDetector() *WebPortDetector {
|
func NewWebPortDetector() *WebPortDetector {
|
||||||
timeout := 3 * time.Second
|
timeout := 3 * time.Second
|
||||||
|
|
||||||
// 定义常见Web端口(扩展列表)
|
// 只保留最基本的Web端口用于快速路径优化
|
||||||
commonPorts := map[int]bool{
|
commonPorts := map[int]bool{
|
||||||
// 标准Web端口
|
80: true, // HTTP
|
||||||
80: true, // HTTP
|
443: true, // HTTPS
|
||||||
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客户端
|
// 创建HTTP客户端
|
||||||
@ -192,18 +149,23 @@ func (w *WebPortDetector) detectHTTPService(host string, port int) bool {
|
|||||||
ctx, cancel := context.WithTimeout(context.Background(), w.httpTimeout)
|
ctx, cancel := context.WithTimeout(context.Background(), w.httpTimeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
|
// 先进行一次协议预检查,避免重复检测
|
||||||
|
if !w.quickProtocolCheck(ctx, host, port) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// 并发检测HTTP和HTTPS
|
// 并发检测HTTP和HTTPS
|
||||||
httpChan := make(chan bool, 1)
|
httpChan := make(chan bool, 1)
|
||||||
httpsChan := make(chan bool, 1)
|
httpsChan := make(chan bool, 1)
|
||||||
|
|
||||||
// 检测HTTP
|
// 检测HTTP(跳过协议检查,因为已经检查过了)
|
||||||
go func() {
|
go func() {
|
||||||
httpChan <- w.tryHTTPConnection(ctx, host, port, "http")
|
httpChan <- w.tryHTTPConnectionDirect(ctx, host, port, "http")
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// 检测HTTPS(对于高端口常见)
|
// 检测HTTPS(跳过协议检查,因为已经检查过了)
|
||||||
go func() {
|
go func() {
|
||||||
httpsChan <- w.tryHTTPConnection(ctx, host, port, "https")
|
httpsChan <- w.tryHTTPConnectionDirect(ctx, host, port, "https")
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// 等待任一协议检测成功
|
// 等待任一协议检测成功
|
||||||
@ -230,8 +192,18 @@ func (w *WebPortDetector) detectHTTPService(host string, port int) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// tryHTTPConnection 尝试HTTP连接
|
// tryHTTPConnection 尝试HTTP连接(带协议检查)
|
||||||
func (w *WebPortDetector) tryHTTPConnection(ctx context.Context, host string, port int, protocol string) bool {
|
func (w *WebPortDetector) tryHTTPConnection(ctx context.Context, host string, port int, protocol string) bool {
|
||||||
|
// 先进行简单的协议嗅探,避免向非HTTP服务发送HTTP请求
|
||||||
|
if !w.quickProtocolCheck(ctx, host, port) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return w.tryHTTPConnectionDirect(ctx, host, port, protocol)
|
||||||
|
}
|
||||||
|
|
||||||
|
// tryHTTPConnectionDirect 直接尝试HTTP连接(跳过协议检查)
|
||||||
|
func (w *WebPortDetector) tryHTTPConnectionDirect(ctx context.Context, host string, port int, protocol string) bool {
|
||||||
// 构造URL
|
// 构造URL
|
||||||
var url string
|
var url string
|
||||||
if (port == 80 && protocol == "http") || (port == 443 && protocol == "https") {
|
if (port == 80 && protocol == "http") || (port == 443 && protocol == "https") {
|
||||||
@ -291,8 +263,6 @@ func (w *WebPortDetector) analyzeHTTPError(err error) bool {
|
|||||||
|
|
||||||
// 这些错误表明连接到了HTTP服务器,只是协议或其他问题
|
// 这些错误表明连接到了HTTP服务器,只是协议或其他问题
|
||||||
webIndicators := []string{
|
webIndicators := []string{
|
||||||
"malformed http response",
|
|
||||||
"unexpected http response",
|
|
||||||
"ssl handshake",
|
"ssl handshake",
|
||||||
"certificate",
|
"certificate",
|
||||||
"tls",
|
"tls",
|
||||||
@ -301,6 +271,16 @@ func (w *WebPortDetector) analyzeHTTPError(err error) bool {
|
|||||||
"server gave http response",
|
"server gave http response",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 特别处理:malformed HTTP response通常表明是非HTTP协议
|
||||||
|
if strings.Contains(errStr, "malformed http response") {
|
||||||
|
// 检查是否包含明显的二进制数据(如MySQL greeting包)
|
||||||
|
if strings.Contains(errStr, "\\x00") || strings.Contains(errStr, "\\x") {
|
||||||
|
common.LogDebug(fmt.Sprintf("检测到二进制协议响应,非HTTP服务: %s", err.Error()))
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// 如果是文本形式的malformed response,可能仍是HTTP服务的变种
|
||||||
|
}
|
||||||
|
|
||||||
for _, indicator := range webIndicators {
|
for _, indicator := range webIndicators {
|
||||||
if strings.Contains(errStr, indicator) {
|
if strings.Contains(errStr, indicator) {
|
||||||
return true
|
return true
|
||||||
@ -312,88 +292,116 @@ func (w *WebPortDetector) analyzeHTTPError(err error) bool {
|
|||||||
|
|
||||||
// analyzeHTTPResponse 分析HTTP响应,判断是否为Web服务
|
// analyzeHTTPResponse 分析HTTP响应,判断是否为Web服务
|
||||||
func (w *WebPortDetector) analyzeHTTPResponse(resp *http.Response, url string) bool {
|
func (w *WebPortDetector) analyzeHTTPResponse(resp *http.Response, url string) bool {
|
||||||
// 任何HTTP状态码都表明这是Web服务
|
// 检查是否为有效的HTTP响应
|
||||||
if resp.StatusCode > 0 {
|
if resp.StatusCode <= 0 {
|
||||||
// 分析响应头获取更多信息
|
return false
|
||||||
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
|
// 检查状态码是否合理(避免协议混淆导致的假阳性)
|
||||||
|
if resp.StatusCode < 100 || resp.StatusCode >= 600 {
|
||||||
|
common.LogDebug(fmt.Sprintf("端口返回无效HTTP状态码: %d", resp.StatusCode))
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更严格的验证:检查是否有基本的HTTP头部
|
||||||
|
if len(resp.Header) == 0 {
|
||||||
|
common.LogDebug(fmt.Sprintf("端口返回HTTP响应但缺少HTTP头部: %s", url))
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查是否包含标准HTTP头部字段
|
||||||
|
hasValidHeaders := false
|
||||||
|
standardHeaders := []string{"Server", "Content-Type", "Content-Length", "Date", "Connection", "Cache-Control"}
|
||||||
|
for _, header := range standardHeaders {
|
||||||
|
if resp.Header.Get(header) != "" {
|
||||||
|
hasValidHeaders = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !hasValidHeaders {
|
||||||
|
common.LogDebug(fmt.Sprintf("端口返回响应但缺少标准HTTP头部: %s", url))
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 分析响应头获取更多信息
|
||||||
|
serverHeader := resp.Header.Get("Server")
|
||||||
|
contentType := resp.Header.Get("Content-Type")
|
||||||
|
|
||||||
|
// 特殊处理:某些非Web服务也会返回HTTP响应
|
||||||
|
if w.isNonWebHTTPService(serverHeader, contentType) {
|
||||||
|
common.LogDebug(fmt.Sprintf("端口返回HTTP响应但可能不是Web服务: %s", url))
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 记录检测到的Web服务器信息
|
||||||
|
if serverHeader != "" {
|
||||||
|
common.LogDebug(fmt.Sprintf("检测到Web服务器: %s (Server: %s)", url, serverHeader))
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// isNonWebHTTPService 判断是否为非Web的HTTP服务
|
// isNonWebHTTPService 判断是否为非Web的HTTP服务
|
||||||
func (w *WebPortDetector) isNonWebHTTPService(serverHeader, contentType string) bool {
|
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
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetWebPortRange 获取可能的Web端口范围(用于高级扫描)
|
// quickProtocolCheck 快速协议检查,避免向非HTTP服务发送HTTP请求
|
||||||
func (w *WebPortDetector) GetWebPortRange() []int {
|
func (w *WebPortDetector) quickProtocolCheck(ctx context.Context, host string, port int) bool {
|
||||||
// 返回扩展的Web端口列表,用于目标发现
|
// 建立TCP连接
|
||||||
var ports []int
|
conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%d", host, port), 2*time.Second)
|
||||||
for port := range w.commonWebPorts {
|
if err != nil {
|
||||||
ports = append(ports, port)
|
return false
|
||||||
}
|
}
|
||||||
|
defer conn.Close()
|
||||||
// 添加一些常见的自定义端口范围
|
|
||||||
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 {
|
conn.SetReadDeadline(time.Now().Add(1 * time.Second))
|
||||||
portStr := strconv.Itoa(port)
|
|
||||||
|
|
||||||
// Web端口的常见模式
|
// 读取服务器的初始响应(如果有)
|
||||||
webPatterns := []*regexp.Regexp{
|
buffer := make([]byte, 512)
|
||||||
regexp.MustCompile(`^80\d{2}$`), // 80xx
|
n, err := conn.Read(buffer)
|
||||||
regexp.MustCompile(`^90\d{2}$`), // 90xx
|
if err != nil {
|
||||||
regexp.MustCompile(`^300\d$`), // 300x
|
// 大多数HTTP服务不会主动发送数据,这是正常的
|
||||||
regexp.MustCompile(`^[3-9]000$`), // x000
|
// 超时或EOF表示服务器在等待客户端请求(HTTP特征)
|
||||||
regexp.MustCompile(`^1[0-9]000$`), // 1x000
|
if netErr, ok := err.(net.Error); ok && netErr.Timeout() {
|
||||||
|
return true // HTTP服务通常不主动发送数据
|
||||||
|
}
|
||||||
|
if err.Error() == "EOF" {
|
||||||
|
return true // 连接正常但无数据,可能是HTTP
|
||||||
|
}
|
||||||
|
return true // 其他错误也可能是HTTP服务
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, pattern := range webPatterns {
|
if n > 0 {
|
||||||
if pattern.MatchString(portStr) {
|
// 如果服务器主动发送数据,检查是否包含明显的二进制内容
|
||||||
return true
|
// 简单规则:如果包含太多不可打印字符,很可能不是HTTP协议
|
||||||
|
nonPrintableCount := 0
|
||||||
|
for i := 0; i < n && i < 100; i++ { // 只检查前100字节
|
||||||
|
b := buffer[i]
|
||||||
|
if b < 32 && b != 9 && b != 10 && b != 13 { // 排除tab、换行、回车
|
||||||
|
nonPrintableCount++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果不可打印字符太多(超过20%),很可能是二进制协议
|
||||||
|
if nonPrintableCount > n/5 {
|
||||||
|
common.LogDebug(fmt.Sprintf("端口 %d 检测到二进制协议(不可打印字符: %d/%d)", port, nonPrintableCount, n))
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// min 辅助函数
|
||||||
|
func min(a, b int) int {
|
||||||
|
if a < b {
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user