mirror of
https://github.com/shadow1ng/fscan.git
synced 2026-09-22 03:10:42 +08:00
tighten scan session and HTTP paths
This commit is contained in:
+7
-7
@@ -504,7 +504,7 @@ func scanSinglePort(ctx context.Context, host string, port int, addr string, ada
|
|||||||
serviceInfo, _ := scanner.SmartIdentify()
|
serviceInfo, _ := scanner.SmartIdentify()
|
||||||
|
|
||||||
// 步骤4:处理结果
|
// 步骤4:处理结果
|
||||||
processServiceResult(host, port, addr, serviceInfo, config, session)
|
processServiceResult(ctx, host, port, addr, serviceInfo, config, session)
|
||||||
}
|
}
|
||||||
|
|
||||||
// handleConnectionFailure 处理连接失败
|
// handleConnectionFailure 处理连接失败
|
||||||
@@ -651,10 +651,10 @@ func saveOpenPort(session *common.ScanSession, host string, port int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// processServiceResult 处理服务识别结果
|
// processServiceResult 处理服务识别结果
|
||||||
func processServiceResult(host string, port int, addr string, serviceInfo *ServiceInfo, config *common.Config, session *common.ScanSession) {
|
func processServiceResult(ctx context.Context, host string, port int, addr string, serviceInfo *ServiceInfo, config *common.Config, session *common.ScanSession) {
|
||||||
if serviceInfo == nil {
|
if serviceInfo == nil {
|
||||||
// 服务识别失败,尝试 HTTP 回退探测
|
// 服务识别失败,尝试 HTTP 回退探测
|
||||||
if !tryHTTPFallbackDetection(host, port, addr, config, session) {
|
if !tryHTTPFallbackDetection(ctx, host, port, addr, config, session) {
|
||||||
session.LogInfo(i18n.Tr("port_open", addr))
|
session.LogInfo(i18n.Tr("port_open", addr))
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
@@ -714,10 +714,10 @@ func buildServiceDetails(port int, info *ServiceInfo) map[string]interface{} {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// tryHTTPFallbackDetection 尝试HTTP回退探测,返回是否成功识别为HTTP服务
|
// tryHTTPFallbackDetection 尝试HTTP回退探测,返回是否成功识别为HTTP服务
|
||||||
func tryHTTPFallbackDetection(host string, port int, addr string, config *common.Config, session *common.ScanSession) bool {
|
func tryHTTPFallbackDetection(ctx context.Context, host string, port int, addr string, config *common.Config, session *common.ScanSession) bool {
|
||||||
// 使用WebDetection进行HTTP协议探测
|
// 使用WebDetection进行HTTP协议探测
|
||||||
webDetector := GetWebPortDetector()
|
webDetector := GetWebPortDetector()
|
||||||
if !webDetector.DetectHTTPServiceOnly(host, port, config, session) {
|
if !webDetector.DetectHTTPServiceOnlyContext(ctx, host, port, config, session) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -806,7 +806,7 @@ func probeSubnets(ctx context.Context, hosts []string, timeout time.Duration, se
|
|||||||
limiter <- struct{}{}
|
limiter <- struct{}{}
|
||||||
go func(pfx, addr string) {
|
go func(pfx, addr string) {
|
||||||
defer func() { <-limiter; wg.Done() }()
|
defer func() { <-limiter; wg.Done() }()
|
||||||
conn, err := net.DialTimeout("tcp", addr, subnetProbeTimeout)
|
conn, err := session.DialTCP(ctx, "tcp", addr, subnetProbeTimeout)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
_ = conn.Close()
|
_ = conn.Close()
|
||||||
aliveSubnets.Store(pfx, true)
|
aliveSubnets.Store(pfx, true)
|
||||||
@@ -844,7 +844,7 @@ func probeSubnets(ctx context.Context, hosts []string, timeout time.Duration, se
|
|||||||
|
|
||||||
go func(pfx, h string, p int) {
|
go func(pfx, h string, p int) {
|
||||||
defer func() { <-limiter; wg.Done() }()
|
defer func() { <-limiter; wg.Done() }()
|
||||||
conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%d", h, p), subnetProbeTimeout)
|
conn, err := session.DialTCP(ctx, "tcp", fmt.Sprintf("%s:%d", h, p), subnetProbeTimeout)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
_ = conn.Close()
|
_ = conn.Close()
|
||||||
aliveSubnets.Store(pfx, true)
|
aliveSubnets.Store(pfx, true)
|
||||||
|
|||||||
+23
-9
@@ -28,8 +28,12 @@ func GetWebPortDetector() *WebPortDetector {
|
|||||||
// 策略:TLS握手优先(快速且准确),失败后尝试GM TLS,最后HTTP
|
// 策略:TLS握手优先(快速且准确),失败后尝试GM TLS,最后HTTP
|
||||||
// 返回: "https", "https-gm", "http", 或 "" (都不是Web服务)
|
// 返回: "https", "https-gm", "http", 或 "" (都不是Web服务)
|
||||||
func DetectHTTPScheme(host string, port int, config *common.Config, session *common.ScanSession) string {
|
func DetectHTTPScheme(host string, port int, config *common.Config, session *common.ScanSession) string {
|
||||||
|
return DetectHTTPSchemeContext(context.Background(), host, port, config, session)
|
||||||
|
}
|
||||||
|
|
||||||
|
func DetectHTTPSchemeContext(ctx context.Context, host string, port int, config *common.Config, session *common.ScanSession) string {
|
||||||
// 优化:先快速检测 TCP 连通性
|
// 优化:先快速检测 TCP 连通性
|
||||||
if !isPortReachable(host, port, config, session) {
|
if !isPortReachable(ctx, host, port, config, session) {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,7 +76,13 @@ func DetectHTTPScheme(host string, port int, config *common.Config, session *com
|
|||||||
|
|
||||||
// 使用HEAD请求(更轻量)
|
// 使用HEAD请求(更轻量)
|
||||||
httpURL := fmt.Sprintf("http://%s", addr)
|
httpURL := fmt.Sprintf("http://%s", addr)
|
||||||
resp, err := client.Head(httpURL)
|
req, err := http.NewRequestWithContext(ctx, "HEAD", httpURL, nil)
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36")
|
||||||
|
req.Header.Set("Accept", "*/*")
|
||||||
|
resp, err := session.HTTPDo(client, req)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
_ = resp.Body.Close()
|
_ = resp.Body.Close()
|
||||||
return "http"
|
return "http"
|
||||||
@@ -127,21 +137,25 @@ func createHTTPClient(config *common.Config, session *common.ScanSession) *http.
|
|||||||
|
|
||||||
// DetectHTTPServiceOnly HTTP协议检测 - 保持API兼容,简化实现
|
// DetectHTTPServiceOnly HTTP协议检测 - 保持API兼容,简化实现
|
||||||
func (w *WebPortDetector) DetectHTTPServiceOnly(host string, port int, config *common.Config, session *common.ScanSession) bool {
|
func (w *WebPortDetector) DetectHTTPServiceOnly(host string, port int, config *common.Config, session *common.ScanSession) bool {
|
||||||
|
return w.DetectHTTPServiceOnlyContext(context.Background(), host, port, config, session)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *WebPortDetector) DetectHTTPServiceOnlyContext(ctx context.Context, host string, port int, config *common.Config, session *common.ScanSession) bool {
|
||||||
// 优化:先快速检测 TCP 连通性,避免在不可达端口上浪费双倍超时时间
|
// 优化:先快速检测 TCP 连通性,避免在不可达端口上浪费双倍超时时间
|
||||||
// 对于不存在的端口,这可以将检测时间从 2×timeout 减少到 1×timeout
|
// 对于不存在的端口,这可以将检测时间从 2×timeout 减少到 1×timeout
|
||||||
if !isPortReachable(host, port, config, session) {
|
if !isPortReachable(ctx, host, port, config, session) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
client := createHTTPClient(config, session)
|
client := createHTTPClient(config, session)
|
||||||
|
|
||||||
// 尝试HTTP
|
// 尝试HTTP
|
||||||
if w.tryHTTP(client, session, host, port, "http") {
|
if w.tryHTTP(ctx, client, session, host, port, "http") {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// 尝试HTTPS
|
// 尝试HTTPS
|
||||||
if w.tryHTTP(client, session, host, port, "https") {
|
if w.tryHTTP(ctx, client, session, host, port, "https") {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -150,11 +164,11 @@ func (w *WebPortDetector) DetectHTTPServiceOnly(host string, port int, config *c
|
|||||||
|
|
||||||
// isPortReachable 快速检测端口是否可达(TCP 连接测试)
|
// isPortReachable 快速检测端口是否可达(TCP 连接测试)
|
||||||
// 用于在 HTTP/HTTPS 检测前过滤不可达端口,避免双重超时
|
// 用于在 HTTP/HTTPS 检测前过滤不可达端口,避免双重超时
|
||||||
func isPortReachable(host string, port int, config *common.Config, session *common.ScanSession) bool {
|
func isPortReachable(ctx context.Context, host string, port int, config *common.Config, session *common.ScanSession) bool {
|
||||||
timeout := config.Network.WebTimeout
|
timeout := config.Network.WebTimeout
|
||||||
addr := net.JoinHostPort(host, strconv.Itoa(port))
|
addr := net.JoinHostPort(host, strconv.Itoa(port))
|
||||||
|
|
||||||
conn, err := session.DialTCP(context.Background(), "tcp", addr, timeout)
|
conn, err := session.DialTCP(ctx, "tcp", addr, timeout)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@@ -163,7 +177,7 @@ func isPortReachable(host string, port int, config *common.Config, session *comm
|
|||||||
}
|
}
|
||||||
|
|
||||||
// tryHTTP 尝试HTTP请求 - 简化的核心逻辑
|
// tryHTTP 尝试HTTP请求 - 简化的核心逻辑
|
||||||
func (w *WebPortDetector) tryHTTP(client *http.Client, session *common.ScanSession, host string, port int, protocol string) bool {
|
func (w *WebPortDetector) tryHTTP(ctx context.Context, client *http.Client, session *common.ScanSession, 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") {
|
||||||
@@ -173,7 +187,7 @@ func (w *WebPortDetector) tryHTTP(client *http.Client, session *common.ScanSessi
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 发送HEAD请求
|
// 发送HEAD请求
|
||||||
req, err := http.NewRequest("HEAD", url, nil)
|
req, err := http.NewRequestWithContext(ctx, "HEAD", url, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package core
|
package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
@@ -32,6 +33,26 @@ web_scanner_test.go - WebScanner核心逻辑测试
|
|||||||
缓存操作需要验证并发安全性。"
|
缓存操作需要验证并发安全性。"
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
func TestDetectHTTPServiceOnlyContextHonorsCancellation(t *testing.T) {
|
||||||
|
cfg := common.GetGlobalConfig()
|
||||||
|
oldTimeout := cfg.Network.WebTimeout
|
||||||
|
cfg.Network.WebTimeout = 2 * time.Second
|
||||||
|
defer func() { cfg.Network.WebTimeout = oldTimeout }()
|
||||||
|
|
||||||
|
session := common.NewScanSession(cfg, common.NewState(), common.GetFlagVars())
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
cancel()
|
||||||
|
|
||||||
|
start := time.Now()
|
||||||
|
detected := GetWebPortDetector().DetectHTTPServiceOnlyContext(ctx, "203.0.113.1", 80, cfg, session)
|
||||||
|
if detected {
|
||||||
|
t.Fatal("canceled web detection should not report a service")
|
||||||
|
}
|
||||||
|
if elapsed := time.Since(start); elapsed > 200*time.Millisecond {
|
||||||
|
t.Fatalf("canceled web detection took %s", elapsed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
// 核心逻辑测试:Web服务识别
|
// 核心逻辑测试:Web服务识别
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
|
|||||||
@@ -27,15 +27,14 @@ func NewElasticsearchPlugin() *ElasticsearchPlugin {
|
|||||||
|
|
||||||
func (p *ElasticsearchPlugin) Scan(ctx context.Context, info *common.HostInfo, session *common.ScanSession) *ScanResult {
|
func (p *ElasticsearchPlugin) Scan(ctx context.Context, info *common.HostInfo, session *common.ScanSession) *ScanResult {
|
||||||
config := session.Config
|
config := session.Config
|
||||||
state := session.State
|
|
||||||
target := info.Target()
|
target := info.Target()
|
||||||
|
|
||||||
if config.DisableBrute {
|
if config.DisableBrute {
|
||||||
return p.identifyService(ctx, info, config, state)
|
return p.identifyService(ctx, info, session)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 首先检测未授权访问
|
// 首先检测未授权访问
|
||||||
if p.testCredential(ctx, info, Credential{Username: "", Password: ""}, config, state) {
|
if p.testCredential(ctx, info, Credential{Username: "", Password: ""}, session) {
|
||||||
common.LogVuln(i18n.Tr("elasticsearch_unauth", target))
|
common.LogVuln(i18n.Tr("elasticsearch_unauth", target))
|
||||||
return &ScanResult{
|
return &ScanResult{
|
||||||
Success: true,
|
Success: true,
|
||||||
@@ -56,7 +55,7 @@ func (p *ElasticsearchPlugin) Scan(ctx context.Context, info *common.HostInfo, s
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, cred := range credentials {
|
for _, cred := range credentials {
|
||||||
if p.testCredential(ctx, info, cred, config, state) {
|
if p.testCredential(ctx, info, cred, session) {
|
||||||
common.LogVuln(i18n.Tr("elasticsearch_credential", target, cred.Username, cred.Password))
|
common.LogVuln(i18n.Tr("elasticsearch_credential", target, cred.Username, cred.Password))
|
||||||
return &ScanResult{
|
return &ScanResult{
|
||||||
Success: true,
|
Success: true,
|
||||||
@@ -75,7 +74,8 @@ func (p *ElasticsearchPlugin) Scan(ctx context.Context, info *common.HostInfo, s
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *ElasticsearchPlugin) testCredential(ctx context.Context, info *common.HostInfo, cred Credential, config *common.Config, state *common.State) bool {
|
func (p *ElasticsearchPlugin) testCredential(ctx context.Context, info *common.HostInfo, cred Credential, session *common.ScanSession) bool {
|
||||||
|
config := session.Config
|
||||||
client := &http.Client{
|
client := &http.Client{
|
||||||
Timeout: config.Timeout,
|
Timeout: config.Timeout,
|
||||||
Transport: &http.Transport{
|
Transport: &http.Transport{
|
||||||
@@ -100,12 +100,10 @@ func (p *ElasticsearchPlugin) testCredential(ctx context.Context, info *common.H
|
|||||||
req.Header.Set("Authorization", "Basic "+auth)
|
req.Header.Set("Authorization", "Basic "+auth)
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := client.Do(req)
|
resp, err := session.HTTPDo(client, req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
state.IncrementTCPFailedPacketCount()
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
state.IncrementTCPSuccessPacketCount()
|
|
||||||
defer func() { _ = resp.Body.Close() }()
|
defer func() { _ = resp.Body.Close() }()
|
||||||
|
|
||||||
if resp.StatusCode == 200 {
|
if resp.StatusCode == 200 {
|
||||||
@@ -121,15 +119,15 @@ func (p *ElasticsearchPlugin) testCredential(ctx context.Context, info *common.H
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *ElasticsearchPlugin) identifyService(ctx context.Context, info *common.HostInfo, config *common.Config, state *common.State) *ScanResult {
|
func (p *ElasticsearchPlugin) identifyService(ctx context.Context, info *common.HostInfo, session *common.ScanSession) *ScanResult {
|
||||||
target := info.Target()
|
target := info.Target()
|
||||||
|
|
||||||
if p.testCredential(ctx, info, Credential{Username: "", Password: ""}, config, state) {
|
if p.testCredential(ctx, info, Credential{Username: "", Password: ""}, session) {
|
||||||
banner := "Elasticsearch"
|
banner := "Elasticsearch"
|
||||||
common.LogSuccess(i18n.Tr("elasticsearch_service", target, banner))
|
common.LogSuccess(i18n.Tr("elasticsearch_service", target, banner))
|
||||||
return &ScanResult{
|
return &ScanResult{
|
||||||
Success: true,
|
Success: true,
|
||||||
Type: plugins.ResultTypeService,
|
Type: plugins.ResultTypeService,
|
||||||
Service: "elasticsearch",
|
Service: "elasticsearch",
|
||||||
Banner: banner,
|
Banner: banner,
|
||||||
}
|
}
|
||||||
|
|||||||
+14
-18
@@ -27,15 +27,14 @@ func NewNeo4jPlugin() *Neo4jPlugin {
|
|||||||
|
|
||||||
func (p *Neo4jPlugin) Scan(ctx context.Context, info *common.HostInfo, session *common.ScanSession) *ScanResult {
|
func (p *Neo4jPlugin) Scan(ctx context.Context, info *common.HostInfo, session *common.ScanSession) *ScanResult {
|
||||||
config := session.Config
|
config := session.Config
|
||||||
state := session.State
|
|
||||||
target := info.Target()
|
target := info.Target()
|
||||||
|
|
||||||
if config.DisableBrute {
|
if config.DisableBrute {
|
||||||
return p.identifyService(ctx, info, config, state)
|
return p.identifyService(ctx, info, session)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 先测试未授权访问
|
// 先测试未授权访问
|
||||||
if result := p.testUnauthorizedAccess(ctx, info, config, state); result != nil && result.Success {
|
if result := p.testUnauthorizedAccess(ctx, info, session); result != nil && result.Success {
|
||||||
common.LogVuln(i18n.Tr("neo4j_unauth", target))
|
common.LogVuln(i18n.Tr("neo4j_unauth", target))
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
@@ -50,7 +49,7 @@ func (p *Neo4jPlugin) Scan(ctx context.Context, info *common.HostInfo, session *
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 使用公共框架进行并发凭据测试
|
// 使用公共框架进行并发凭据测试
|
||||||
authFn := p.createAuthFunc(info, config, state)
|
authFn := p.createAuthFunc(info, session)
|
||||||
testConfig := DefaultConcurrentTestConfigWithTarget(config, info)
|
testConfig := DefaultConcurrentTestConfigWithTarget(config, info)
|
||||||
|
|
||||||
result := TestCredentialsConcurrently(ctx, credentials, authFn, "neo4j", testConfig)
|
result := TestCredentialsConcurrently(ctx, credentials, authFn, "neo4j", testConfig)
|
||||||
@@ -63,14 +62,15 @@ func (p *Neo4jPlugin) Scan(ctx context.Context, info *common.HostInfo, session *
|
|||||||
}
|
}
|
||||||
|
|
||||||
// createAuthFunc 创建Neo4j认证函数
|
// createAuthFunc 创建Neo4j认证函数
|
||||||
func (p *Neo4jPlugin) createAuthFunc(info *common.HostInfo, config *common.Config, state *common.State) AuthFunc {
|
func (p *Neo4jPlugin) createAuthFunc(info *common.HostInfo, session *common.ScanSession) AuthFunc {
|
||||||
return func(ctx context.Context, cred Credential) *AuthResult {
|
return func(ctx context.Context, cred Credential) *AuthResult {
|
||||||
return p.doNeo4jAuth(ctx, info, cred, config, state)
|
return p.doNeo4jAuth(ctx, info, cred, session)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// doNeo4jAuth 执行Neo4j认证
|
// doNeo4jAuth 执行Neo4j认证
|
||||||
func (p *Neo4jPlugin) doNeo4jAuth(ctx context.Context, info *common.HostInfo, cred Credential, config *common.Config, state *common.State) *AuthResult {
|
func (p *Neo4jPlugin) doNeo4jAuth(ctx context.Context, info *common.HostInfo, cred Credential, session *common.ScanSession) *AuthResult {
|
||||||
|
config := session.Config
|
||||||
baseURL := fmt.Sprintf("http://%s:%d", info.Host, info.Port)
|
baseURL := fmt.Sprintf("http://%s:%d", info.Host, info.Port)
|
||||||
|
|
||||||
client := &http.Client{Timeout: config.Timeout}
|
client := &http.Client{Timeout: config.Timeout}
|
||||||
@@ -87,16 +87,14 @@ func (p *Neo4jPlugin) doNeo4jAuth(ctx context.Context, info *common.HostInfo, cr
|
|||||||
req.SetBasicAuth(cred.Username, cred.Password)
|
req.SetBasicAuth(cred.Username, cred.Password)
|
||||||
req.Header.Set("Content-Type", "application/json")
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
resp, err := client.Do(req)
|
resp, err := session.HTTPDo(client, req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
state.IncrementTCPFailedPacketCount()
|
|
||||||
return &AuthResult{
|
return &AuthResult{
|
||||||
Success: false,
|
Success: false,
|
||||||
ErrorType: classifyNeo4jErrorType(err),
|
ErrorType: classifyNeo4jErrorType(err),
|
||||||
Error: err,
|
Error: err,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
state.IncrementTCPSuccessPacketCount()
|
|
||||||
defer func() { _ = resp.Body.Close() }()
|
defer func() { _ = resp.Body.Close() }()
|
||||||
|
|
||||||
if resp.StatusCode == 200 {
|
if resp.StatusCode == 200 {
|
||||||
@@ -147,7 +145,8 @@ func classifyNeo4jErrorType(err error) ErrorType {
|
|||||||
return ClassifyError(err, neo4jAuthErrors, CommonNetworkErrors)
|
return ClassifyError(err, neo4jAuthErrors, CommonNetworkErrors)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Neo4jPlugin) testUnauthorizedAccess(ctx context.Context, info *common.HostInfo, config *common.Config, state *common.State) *ScanResult {
|
func (p *Neo4jPlugin) testUnauthorizedAccess(ctx context.Context, info *common.HostInfo, session *common.ScanSession) *ScanResult {
|
||||||
|
config := session.Config
|
||||||
baseURL := fmt.Sprintf("http://%s:%d", info.Host, info.Port)
|
baseURL := fmt.Sprintf("http://%s:%d", info.Host, info.Port)
|
||||||
|
|
||||||
client := &http.Client{Timeout: config.Timeout}
|
client := &http.Client{Timeout: config.Timeout}
|
||||||
@@ -157,12 +156,10 @@ func (p *Neo4jPlugin) testUnauthorizedAccess(ctx context.Context, info *common.H
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := client.Do(req)
|
resp, err := session.HTTPDo(client, req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
state.IncrementTCPFailedPacketCount()
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
state.IncrementTCPSuccessPacketCount()
|
|
||||||
defer func() { _ = resp.Body.Close() }()
|
defer func() { _ = resp.Body.Close() }()
|
||||||
|
|
||||||
if resp.StatusCode == 200 {
|
if resp.StatusCode == 200 {
|
||||||
@@ -177,7 +174,8 @@ func (p *Neo4jPlugin) testUnauthorizedAccess(ctx context.Context, info *common.H
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Neo4jPlugin) identifyService(ctx context.Context, info *common.HostInfo, config *common.Config, state *common.State) *ScanResult {
|
func (p *Neo4jPlugin) identifyService(ctx context.Context, info *common.HostInfo, session *common.ScanSession) *ScanResult {
|
||||||
|
config := session.Config
|
||||||
target := info.Target()
|
target := info.Target()
|
||||||
baseURL := fmt.Sprintf("http://%s:%d", info.Host, info.Port)
|
baseURL := fmt.Sprintf("http://%s:%d", info.Host, info.Port)
|
||||||
|
|
||||||
@@ -192,16 +190,14 @@ func (p *Neo4jPlugin) identifyService(ctx context.Context, info *common.HostInfo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := client.Do(req)
|
resp, err := session.HTTPDo(client, req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
state.IncrementTCPFailedPacketCount()
|
|
||||||
return &ScanResult{
|
return &ScanResult{
|
||||||
Success: false,
|
Success: false,
|
||||||
Service: "neo4j",
|
Service: "neo4j",
|
||||||
Error: err,
|
Error: err,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
state.IncrementTCPSuccessPacketCount()
|
|
||||||
defer func() { _ = resp.Body.Close() }()
|
defer func() { _ = resp.Body.Close() }()
|
||||||
|
|
||||||
var banner string
|
var banner string
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ func NewRabbitMQPlugin() *RabbitMQPlugin {
|
|||||||
|
|
||||||
func (p *RabbitMQPlugin) Scan(ctx context.Context, info *common.HostInfo, session *common.ScanSession) *ScanResult {
|
func (p *RabbitMQPlugin) Scan(ctx context.Context, info *common.HostInfo, session *common.ScanSession) *ScanResult {
|
||||||
config := session.Config
|
config := session.Config
|
||||||
state := session.State
|
|
||||||
target := info.Target()
|
target := info.Target()
|
||||||
|
|
||||||
if config.DisableBrute {
|
if config.DisableBrute {
|
||||||
@@ -36,7 +35,7 @@ func (p *RabbitMQPlugin) Scan(ctx context.Context, info *common.HostInfo, sessio
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 先检测未授权访问
|
// 先检测未授权访问
|
||||||
if result := p.testUnauthorizedAccess(ctx, info, config, state); result != nil && result.Success {
|
if result := p.testUnauthorizedAccess(ctx, info, session); result != nil && result.Success {
|
||||||
common.LogSuccess(i18n.Tr("rabbitmq_service", target, result.Banner))
|
common.LogSuccess(i18n.Tr("rabbitmq_service", target, result.Banner))
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
@@ -51,7 +50,7 @@ func (p *RabbitMQPlugin) Scan(ctx context.Context, info *common.HostInfo, sessio
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 使用公共框架进行并发凭据测试
|
// 使用公共框架进行并发凭据测试
|
||||||
authFn := p.createAuthFunc(info, config, state)
|
authFn := p.createAuthFunc(info, session)
|
||||||
testConfig := DefaultConcurrentTestConfigWithTarget(config, info)
|
testConfig := DefaultConcurrentTestConfigWithTarget(config, info)
|
||||||
|
|
||||||
result := TestCredentialsConcurrently(ctx, credentials, authFn, "rabbitmq", testConfig)
|
result := TestCredentialsConcurrently(ctx, credentials, authFn, "rabbitmq", testConfig)
|
||||||
@@ -64,14 +63,15 @@ func (p *RabbitMQPlugin) Scan(ctx context.Context, info *common.HostInfo, sessio
|
|||||||
}
|
}
|
||||||
|
|
||||||
// createAuthFunc 创建RabbitMQ认证函数
|
// createAuthFunc 创建RabbitMQ认证函数
|
||||||
func (p *RabbitMQPlugin) createAuthFunc(info *common.HostInfo, config *common.Config, state *common.State) AuthFunc {
|
func (p *RabbitMQPlugin) createAuthFunc(info *common.HostInfo, session *common.ScanSession) AuthFunc {
|
||||||
return func(ctx context.Context, cred Credential) *AuthResult {
|
return func(ctx context.Context, cred Credential) *AuthResult {
|
||||||
return p.doRabbitMQAuth(ctx, info, cred, config, state)
|
return p.doRabbitMQAuth(ctx, info, cred, session)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// doRabbitMQAuth 执行RabbitMQ认证
|
// doRabbitMQAuth 执行RabbitMQ认证
|
||||||
func (p *RabbitMQPlugin) doRabbitMQAuth(ctx context.Context, info *common.HostInfo, cred Credential, config *common.Config, state *common.State) *AuthResult {
|
func (p *RabbitMQPlugin) doRabbitMQAuth(ctx context.Context, info *common.HostInfo, cred Credential, session *common.ScanSession) *AuthResult {
|
||||||
|
config := session.Config
|
||||||
// 对于AMQP端口,使用HTTP管理接口
|
// 对于AMQP端口,使用HTTP管理接口
|
||||||
port := info.Port
|
port := info.Port
|
||||||
if port == 5672 || port == 5671 {
|
if port == 5672 || port == 5671 {
|
||||||
@@ -96,16 +96,14 @@ func (p *RabbitMQPlugin) doRabbitMQAuth(ctx context.Context, info *common.HostIn
|
|||||||
req.SetBasicAuth(cred.Username, cred.Password)
|
req.SetBasicAuth(cred.Username, cred.Password)
|
||||||
req.Header.Set("Content-Type", "application/json")
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
resp, err := client.Do(req)
|
resp, err := session.HTTPDo(client, req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
state.IncrementTCPFailedPacketCount()
|
|
||||||
return &AuthResult{
|
return &AuthResult{
|
||||||
Success: false,
|
Success: false,
|
||||||
ErrorType: classifyRabbitMQErrorType(err),
|
ErrorType: classifyRabbitMQErrorType(err),
|
||||||
Error: err,
|
Error: err,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
state.IncrementTCPSuccessPacketCount()
|
|
||||||
defer func() { _ = resp.Body.Close() }()
|
defer func() { _ = resp.Body.Close() }()
|
||||||
|
|
||||||
if resp.StatusCode == 200 {
|
if resp.StatusCode == 200 {
|
||||||
@@ -157,7 +155,8 @@ func classifyRabbitMQErrorType(err error) ErrorType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// testUnauthorizedAccess 测试RabbitMQ未授权访问
|
// testUnauthorizedAccess 测试RabbitMQ未授权访问
|
||||||
func (p *RabbitMQPlugin) testUnauthorizedAccess(ctx context.Context, info *common.HostInfo, config *common.Config, state *common.State) *ScanResult {
|
func (p *RabbitMQPlugin) testUnauthorizedAccess(ctx context.Context, info *common.HostInfo, session *common.ScanSession) *ScanResult {
|
||||||
|
config := session.Config
|
||||||
port := info.Port
|
port := info.Port
|
||||||
if port == 5672 || port == 5671 {
|
if port == 5672 || port == 5671 {
|
||||||
port = 15672
|
port = 15672
|
||||||
@@ -172,11 +171,9 @@ func (p *RabbitMQPlugin) testUnauthorizedAccess(ctx context.Context, info *commo
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := client.Do(req)
|
resp, err := session.HTTPDo(client, req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
state.IncrementTCPFailedPacketCount()
|
|
||||||
} else {
|
} else {
|
||||||
state.IncrementTCPSuccessPacketCount()
|
|
||||||
defer func() { _ = resp.Body.Close() }()
|
defer func() { _ = resp.Body.Close() }()
|
||||||
|
|
||||||
if resp.StatusCode == 200 {
|
if resp.StatusCode == 200 {
|
||||||
@@ -193,7 +190,7 @@ func (p *RabbitMQPlugin) testUnauthorizedAccess(ctx context.Context, info *commo
|
|||||||
guestReq, err := http.NewRequestWithContext(ctx, "GET", baseURL+"/api/overview", nil)
|
guestReq, err := http.NewRequestWithContext(ctx, "GET", baseURL+"/api/overview", nil)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
guestReq.SetBasicAuth("guest", "guest")
|
guestReq.SetBasicAuth("guest", "guest")
|
||||||
guestResp, guestErr := client.Do(guestReq)
|
guestResp, guestErr := session.HTTPDo(client, guestReq)
|
||||||
if guestErr == nil {
|
if guestErr == nil {
|
||||||
defer func() { _ = guestResp.Body.Close() }()
|
defer func() { _ = guestResp.Body.Close() }()
|
||||||
if guestResp.StatusCode == 200 {
|
if guestResp.StatusCode == 200 {
|
||||||
@@ -263,7 +260,6 @@ func (p *RabbitMQPlugin) identifyService(ctx context.Context, info *common.HostI
|
|||||||
|
|
||||||
func (p *RabbitMQPlugin) testManagementInterface(ctx context.Context, info *common.HostInfo, session *common.ScanSession) *ScanResult {
|
func (p *RabbitMQPlugin) testManagementInterface(ctx context.Context, info *common.HostInfo, session *common.ScanSession) *ScanResult {
|
||||||
config := session.Config
|
config := session.Config
|
||||||
state := session.State
|
|
||||||
target := info.Target()
|
target := info.Target()
|
||||||
baseURL := fmt.Sprintf("http://%s:%d", info.Host, info.Port)
|
baseURL := fmt.Sprintf("http://%s:%d", info.Host, info.Port)
|
||||||
|
|
||||||
@@ -278,16 +274,14 @@ func (p *RabbitMQPlugin) testManagementInterface(ctx context.Context, info *comm
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := client.Do(req)
|
resp, err := session.HTTPDo(client, req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
state.IncrementTCPFailedPacketCount()
|
|
||||||
return &ScanResult{
|
return &ScanResult{
|
||||||
Success: false,
|
Success: false,
|
||||||
Service: "rabbitmq",
|
Service: "rabbitmq",
|
||||||
Error: err,
|
Error: err,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
state.IncrementTCPSuccessPacketCount()
|
|
||||||
defer func() { _ = resp.Body.Close() }()
|
defer func() { _ = resp.Body.Close() }()
|
||||||
|
|
||||||
if resp.StatusCode == 200 || resp.StatusCode == 401 {
|
if resp.StatusCode == 200 || resp.StatusCode == 401 {
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ func (p *WebTitlePlugin) Scan(ctx context.Context, info *common.HostInfo, sessio
|
|||||||
|
|
||||||
func (p *WebTitlePlugin) getWebTitle(ctx context.Context, info *common.HostInfo, config *common.Config, session *common.ScanSession) (string, int, int, string, []string, string, error) {
|
func (p *WebTitlePlugin) getWebTitle(ctx context.Context, info *common.HostInfo, config *common.Config, session *common.ScanSession) (string, int, int, string, []string, string, error) {
|
||||||
// 智能协议检测
|
// 智能协议检测
|
||||||
protocol := p.detectProtocol(info, config, session)
|
protocol := p.detectProtocol(ctx, info, config, session)
|
||||||
isGM := false
|
isGM := false
|
||||||
urlScheme := protocol
|
urlScheme := protocol
|
||||||
if protocol == "https-gm" {
|
if protocol == "https-gm" {
|
||||||
@@ -250,7 +250,7 @@ func (p *WebTitlePlugin) formatHeaders(headers http.Header) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// detectProtocol 智能检测HTTP/HTTPS协议(基于服务识别和主动探测)
|
// detectProtocol 智能检测HTTP/HTTPS协议(基于服务识别和主动探测)
|
||||||
func (p *WebTitlePlugin) detectProtocol(info *common.HostInfo, config *common.Config, session *common.ScanSession) string {
|
func (p *WebTitlePlugin) detectProtocol(ctx context.Context, info *common.HostInfo, config *common.Config, session *common.ScanSession) string {
|
||||||
host := info.Host
|
host := info.Host
|
||||||
port := info.Port
|
port := info.Port
|
||||||
|
|
||||||
@@ -277,7 +277,7 @@ func (p *WebTitlePlugin) detectProtocol(info *common.HostInfo, config *common.Co
|
|||||||
|
|
||||||
// 第三优先级:主动协议检测(TLS握手)
|
// 第三优先级:主动协议检测(TLS握手)
|
||||||
// 对于-u模式或服务名为普通"http"的情况,进行主动检测确认
|
// 对于-u模式或服务名为普通"http"的情况,进行主动检测确认
|
||||||
detected := core.DetectHTTPScheme(host, port, config, session)
|
detected := core.DetectHTTPSchemeContext(ctx, host, port, config, session)
|
||||||
if detected != "" {
|
if detected != "" {
|
||||||
// 缓存检测结果(避免重复检测)
|
// 缓存检测结果(避免重复检测)
|
||||||
if exists {
|
if exists {
|
||||||
|
|||||||
Reference in New Issue
Block a user