diff options
Diffstat (limited to 'internal/proxy/intrude_test.go')
| -rw-r--r-- | internal/proxy/intrude_test.go | 118 |
1 files changed, 104 insertions, 14 deletions
diff --git a/internal/proxy/intrude_test.go b/internal/proxy/intrude_test.go index 6a0007f..59aa5a7 100644 --- a/internal/proxy/intrude_test.go +++ b/internal/proxy/intrude_test.go @@ -1,6 +1,7 @@ package proxy import ( + "fmt" "reflect" "testing" ) @@ -66,36 +67,35 @@ func TestParseMarkers(t *testing.T) { } } -func TestBuildRequest(t *testing.T) { +func TestBuildRequestValues(t *testing.T) { template := "GET /a/§1§/b/§2§/c/§3§ HTTP/1.1" tests := []struct { - active int - payload string - want string + values []string + want string }{ - {0, "PAYLOAD", "GET /a/PAYLOAD/b/2/c/3 HTTP/1.1"}, - {1, "PAYLOAD", "GET /a/1/b/PAYLOAD/c/3 HTTP/1.1"}, - {2, "PAYLOAD", "GET /a/1/b/2/c/PAYLOAD HTTP/1.1"}, + {[]string{"PAYLOAD", "2", "3"}, "GET /a/PAYLOAD/b/2/c/3 HTTP/1.1"}, + {[]string{"1", "PAYLOAD", "3"}, "GET /a/1/b/PAYLOAD/c/3 HTTP/1.1"}, + {[]string{"1", "2", "PAYLOAD"}, "GET /a/1/b/2/c/PAYLOAD HTTP/1.1"}, } for _, tt := range tests { - got, err := buildRequest([]byte(template), tt.active, tt.payload) + got, err := buildRequestValues([]byte(template), tt.values) if err != nil { - t.Fatalf("active=%d: unexpected error: %v", tt.active, err) + t.Fatalf("values=%v: unexpected error: %v", tt.values, err) } if string(got) != tt.want { - t.Errorf("active=%d: got %q, want %q", tt.active, got, tt.want) + t.Errorf("values=%v: got %q, want %q", tt.values, got, tt.want) } } } -func TestBuildRequestPayloadContainingMarkerChar(t *testing.T) { +func TestBuildRequestValuesPayloadContainingMarkerChar(t *testing.T) { // A payload that itself contains the marker character must not be - // reinterpreted as a marker on a later buildRequest call - each call - // re-splits the ORIGINAL template, not the previously built request. + // reinterpreted as a marker - buildRequestValues splits the ORIGINAL + // template, never the already-substituted result. template := "GET /§1§/§2§ HTTP/1.1" - got, err := buildRequest([]byte(template), 0, "§injected§") + got, err := buildRequestValues([]byte(template), []string{"§injected§", "2"}) if err != nil { t.Fatalf("unexpected error: %v", err) } @@ -105,6 +105,96 @@ func TestBuildRequestPayloadContainingMarkerChar(t *testing.T) { } } +func TestIntrudeValuesSniper(t *testing.T) { + positions := []IntrudePosition{{Index: 0, Base: "a"}, {Index: 1, Base: "b"}} + got, err := intrudeValues(Sniper, positions, [][]string{{"1", "2"}}) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + want := [][]string{ + {"1", "b"}, {"2", "b"}, // position 0 fuzzed, position 1 at base + {"a", "1"}, {"a", "2"}, // position 1 fuzzed, position 0 at base + } + if !reflect.DeepEqual(got, want) { + t.Errorf("got %v, want %v", got, want) + } +} + +func TestIntrudeValuesBatteringRam(t *testing.T) { + positions := []IntrudePosition{{Index: 0, Base: "a"}, {Index: 1, Base: "b"}} + got, err := intrudeValues(BatteringRam, positions, [][]string{{"1", "2"}}) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + // Same payload lands in every position at once, unlike Sniper. + want := [][]string{{"1", "1"}, {"2", "2"}} + if !reflect.DeepEqual(got, want) { + t.Errorf("got %v, want %v", got, want) + } +} + +func TestIntrudeValuesPitchfork(t *testing.T) { + positions := []IntrudePosition{{Index: 0, Base: "a"}, {Index: 1, Base: "b"}} + got, err := intrudeValues(Pitchfork, positions, [][]string{{"1", "2", "3"}, {"x", "y"}}) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + // Walks both sets in lockstep; stops at the shorter set's length (2), + // silently ignoring "3" from the longer one - Burp's own convention. + want := [][]string{{"1", "x"}, {"2", "y"}} + if !reflect.DeepEqual(got, want) { + t.Errorf("got %v, want %v", got, want) + } +} + +func TestIntrudeValuesPitchforkRequiresOneSetPerPosition(t *testing.T) { + positions := []IntrudePosition{{Index: 0, Base: "a"}, {Index: 1, Base: "b"}} + _, err := intrudeValues(Pitchfork, positions, [][]string{{"1", "2"}}) + if err == nil { + t.Fatal("expected error for one payload set across two positions") + } +} + +func TestIntrudeValuesClusterBomb(t *testing.T) { + positions := []IntrudePosition{{Index: 0, Base: "a"}, {Index: 1, Base: "b"}} + got, err := intrudeValues(ClusterBomb, positions, [][]string{{"1", "2"}, {"x", "y"}}) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + // Every combination - the rightmost (last) position cycles fastest. + want := [][]string{{"1", "x"}, {"1", "y"}, {"2", "x"}, {"2", "y"}} + if !reflect.DeepEqual(got, want) { + t.Errorf("got %v, want %v", got, want) + } +} + +func TestIntrudeValuesClusterBombOverCapRejected(t *testing.T) { + positions := []IntrudePosition{{Index: 0, Base: "a"}, {Index: 1, Base: "b"}, {Index: 2, Base: "c"}} + big := make([]string, 20) + for i := range big { + big[i] = fmt.Sprintf("v%d", i) + } + // 20 * 20 * 20 = 8000, comfortably over the 1000 cap. + _, err := intrudeValues(ClusterBomb, positions, [][]string{big, big, big}) + if err == nil { + t.Fatal("expected the request-count cap to reject this attack") + } +} + +func TestIntrudeValuesOverCapRejected(t *testing.T) { + positions := []IntrudePosition{{Index: 0, Base: "a"}} + big := make([]string, maxIntrudeRequests+1) + for i := range big { + big[i] = fmt.Sprintf("v%d", i) + } + if _, err := intrudeValues(Sniper, positions, [][]string{big}); err == nil { + t.Error("Sniper: expected the request-count cap to reject this attack") + } + if _, err := intrudeValues(BatteringRam, positions, [][]string{big}); err == nil { + t.Error("BatteringRam: expected the request-count cap to reject this attack") + } +} + func TestIntrudeRequestCount(t *testing.T) { positions, _, err := ParseMarkers([]byte("GET /§a§/§b§ HTTP/1.1")) if err != nil { |