From c4f391cb5a53653b12a949184eb5944d5ca56f35 Mon Sep 17 00:00:00 2001 From: Emmanuel Gautier Date: Mon, 12 Feb 2024 23:21:26 +0100 Subject: [PATCH] feat: add http trace method scan --- internal/auth/auth.go | 17 ++++++ scan/best_practices.go | 6 +- scan/best_practices/http_trace_method.go | 35 +++++++++++ scan/best_practices/http_trace_method_test.go | 59 +++++++++++++++++++ 4 files changed, 116 insertions(+), 1 deletion(-) create mode 100644 scan/best_practices/http_trace_method.go create mode 100644 scan/best_practices/http_trace_method_test.go diff --git a/internal/auth/auth.go b/internal/auth/auth.go index 1eddbae..dda7101 100644 --- a/internal/auth/auth.go +++ b/internal/auth/auth.go @@ -39,3 +39,20 @@ type Operation struct { SecuritySchemes []SecurityScheme } + +func (o Operation) Clone() Operation { + clonedHeaders := make(http.Header) + if o.Headers != nil { + clonedHeaders = o.Headers.Clone() + } + + clonedCookies := make([]http.Cookie, len(o.Cookies)) + copy(clonedCookies, o.Cookies) + + return Operation{ + Url: o.Url, + Method: o.Method, + Headers: &clonedHeaders, + Cookies: clonedCookies, + } +} diff --git a/scan/best_practices.go b/scan/best_practices.go index 66fd427..85196bf 100644 --- a/scan/best_practices.go +++ b/scan/best_practices.go @@ -8,6 +8,10 @@ func (s *Scan) WithHTTPHeadersBestPracticesScan() *Scan { return s.AddScanHandler(bestpractices.HTTPHeadersBestPracticesScanHandler) } +func (s *Scan) WithHTTPTraceMethodBestPracticesScan() *Scan { + return s.AddScanHandler(bestpractices.HTTPTraceMethodScanHandler) +} + func (s *Scan) WithAllBestPracticesScans() *Scan { - return s.WithHTTPHeadersBestPracticesScan() + return s.WithHTTPHeadersBestPracticesScan().WithHTTPTraceMethodBestPracticesScan() } diff --git a/scan/best_practices/http_trace_method.go b/scan/best_practices/http_trace_method.go new file mode 100644 index 0000000..954e679 --- /dev/null +++ b/scan/best_practices/http_trace_method.go @@ -0,0 +1,35 @@ +package bestpractices + +import ( + "github.com/cerberauth/vulnapi/internal/auth" + restapi "github.com/cerberauth/vulnapi/internal/rest_api" + "github.com/cerberauth/vulnapi/report" +) + +const ( + HTTPTraceMethodSeverityLevel = 1 + HTTPTraceMethodVulnerabilityName = "HTTP Trace Method enabled" + HTTPTraceMethodVulnerabilityDescription = "HTTP Trace method seems enabled for this request." +) + +func HTTPTraceMethodScanHandler(o *auth.Operation, ss auth.SecurityScheme) (*report.ScanReport, error) { + r := report.NewScanReport() + newOperation := o.Clone() + newOperation.Method = "TRACE" + + token := ss.GetValidValue().(string) + ss.SetAttackValue(token) + vsa := restapi.ScanRestAPI(&newOperation, ss) + r.AddScanAttempt(vsa).End() + + if vsa.Response.StatusCode < 300 { + r.AddVulnerabilityReport(&report.VulnerabilityReport{ + SeverityLevel: HTTPTraceMethodSeverityLevel, + Name: HTTPTraceMethodVulnerabilityName, + Description: HTTPTraceMethodVulnerabilityDescription, + Url: o.Url, + }) + } + + return r, nil +} diff --git a/scan/best_practices/http_trace_method_test.go b/scan/best_practices/http_trace_method_test.go new file mode 100644 index 0000000..463f8f9 --- /dev/null +++ b/scan/best_practices/http_trace_method_test.go @@ -0,0 +1,59 @@ +package bestpractices_test + +import ( + "testing" + + "github.com/cerberauth/vulnapi/internal/auth" + "github.com/cerberauth/vulnapi/report" + bestpractices "github.com/cerberauth/vulnapi/scan/best_practices" + "github.com/jarcoal/httpmock" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestHTTPTraceMethodScanHandler(t *testing.T) { + httpmock.Activate() + defer httpmock.DeactivateAndReset() + + token := "token" + securityScheme := auth.NewAuthorizationBearerSecurityScheme("default", &token) + o := auth.Operation{ + Method: "GET", + Url: "http://localhost:8080/", + } + + httpmock.RegisterResponder("TRACE", o.Url, httpmock.NewBytesResponder(405, nil)) + + report, err := bestpractices.HTTPTraceMethodScanHandler(&o, securityScheme) + + require.NoError(t, err) + assert.Equal(t, 1, httpmock.GetTotalCallCount()) + assert.False(t, report.HasVulnerabilityReport()) +} + +func TestHTTPTraceMethodWhenTraceIsEnabledScanHandler(t *testing.T) { + httpmock.Activate() + defer httpmock.DeactivateAndReset() + + token := "token" + securityScheme := auth.NewAuthorizationBearerSecurityScheme("default", &token) + o := auth.Operation{ + Method: "GET", + Url: "http://localhost:8080/", + } + vulnerabilityReport := report.VulnerabilityReport{ + SeverityLevel: bestpractices.HTTPTraceMethodSeverityLevel, + Name: bestpractices.HTTPTraceMethodVulnerabilityName, + Description: bestpractices.HTTPTraceMethodVulnerabilityDescription, + Url: o.Url, + } + + httpmock.RegisterResponder("TRACE", o.Url, httpmock.NewBytesResponder(204, nil)) + + report, err := bestpractices.HTTPTraceMethodScanHandler(&o, securityScheme) + + require.NoError(t, err) + assert.Equal(t, 1, httpmock.GetTotalCallCount()) + assert.True(t, report.HasVulnerabilityReport()) + assert.Equal(t, report.GetVulnerabilityReports()[0], &vulnerabilityReport) +}