● Notice //GitHub (@shehzade) is temporarily suspended pending an automated false-positive review. All tools are hosted on this Code Mirror and available on request (contact); GitHub links will return once reinstated.
PUBLIC MIRRORGo v1.0

Multi-protocol network egress assessment and DLP verification framework featuring automated L4 sweeps, corporate proxy traversal, and covert protocol smuggling across 20+ channels.

$ git clone https://shehzade.io/repos/contraband.git Copy
Source ZIP (1.93 MB)
Code 174 Commits 1
contraband / pkg / soak / negative.go
← Back to directory
149 lines · 5.2 KB
Raw
1package soak
2
3import (
4 "context"
5 "crypto/tls"
6 "errors"
7 "fmt"
8 "net"
9 "net/http"
10 "strings"
11 "time"
12
13 "github.com/shehzade/contraband/pkg/common"
14)
15
16// NegativeTestResult records the verification of an injected disruption against expected verdicts.
17type NegativeTestResult struct {
18 Type string `json:"type"`
19 Target string `json:"target"`
20 ExpectedVerdict common.Verdict `json:"expected_verdict"`
21 ActualVerdict common.Verdict `json:"actual_verdict"`
22 Passed bool `json:"passed"`
23 LatencyMS float64 `json:"latency_ms"`
24 ErrorMessage string `json:"error_message,omitempty"`
25}
26
27// InjectNegativeL4 simulates a network-layer disruption (e.g. drop, RST, closed port, timeout)
28// and asserts that common.ClassifyError classifies the outcome as BLOCKED_L4.
29func InjectNegativeL4(ctx context.Context, targetHost string, closedPort int) NegativeTestResult {
30 if closedPort <= 0 {
31 closedPort = 59999 // intentionally unrouted / closed port
32 }
33
34 start := time.Now()
35 dialer := net.Dialer{Timeout: 150 * time.Millisecond}
36 conn, err := dialer.DialContext(ctx, "tcp", fmt.Sprintf("%s:%d", targetHost, closedPort))
37 dur := time.Since(start)
38 latMs := float64(dur.Microseconds()) / 1000.0
39
40 res := NegativeTestResult{
41 Type: NegativeL4,
42 Target: fmt.Sprintf("%s:%d", targetHost, closedPort),
43 ExpectedVerdict: common.VerdictBlockedL4,
44 LatencyMS: latMs,
45 }
46
47 if err == nil {
48 _ = conn.Close()
49 res.ActualVerdict = common.VerdictSuccess
50 res.Passed = false
51 res.ErrorMessage = "expected connection drop/refusal, but connection succeeded"
52 return res
53 }
54
55 res.ErrorMessage = err.Error()
56 res.ActualVerdict = common.ClassifyError(err)
57 res.Passed = (res.ActualVerdict == common.VerdictBlockedL4)
58 return res
59}
60
61// InjectNegativeL7 simulates an application / protocol inspection rejection (e.g. TLS reset, 403 Forbidden)
62// and asserts that common.ClassifyError classifies the outcome as BLOCKED_L7.
63func InjectNegativeL7(ctx context.Context, targetHost string, targetPort int) NegativeTestResult {
64 if targetPort <= 0 {
65 targetPort = 8443
66 }
67
68 start := time.Now()
69 // Trigger TLS protocol error by attempting strictly incompatible cipher suite or TLS 1.0/invalid SNI
70 transport := &http.Transport{
71 TLSClientConfig: &tls.Config{
72 MaxVersion: tls.VersionTLS10,
73 InsecureSkipVerify: true,
74 },
75 DialContext: (&net.Dialer{Timeout: 500 * time.Millisecond}).DialContext,
76 }
77 httpClient := &http.Client{
78 Transport: transport,
79 Timeout: 1 * time.Second,
80 }
81
82 req, _ := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://%s:%d/forbidden-path", targetHost, targetPort), nil)
83 resp, err := httpClient.Do(req)
84 dur := time.Since(start)
85 latMs := float64(dur.Microseconds()) / 1000.0
86
87 res := NegativeTestResult{
88 Type: NegativeL7,
89 Target: fmt.Sprintf("https://%s:%d/forbidden-path", targetHost, targetPort),
90 ExpectedVerdict: common.VerdictBlockedL7,
91 LatencyMS: latMs,
92 }
93
94 if err != nil {
95 res.ErrorMessage = err.Error()
96 res.ActualVerdict = common.ClassifyError(err)
97 // TLS protocol errors or proxy 403 are categorized as BLOCKED_L7
98 res.Passed = (res.ActualVerdict == common.VerdictBlockedL7)
99 return res
100 }
101
102 defer resp.Body.Close()
103 if resp.StatusCode == http.StatusForbidden || resp.StatusCode == http.StatusUnauthorized || resp.StatusCode == http.StatusProxyAuthRequired {
104 res.ActualVerdict = common.VerdictBlockedL7
105 res.Passed = true
106 return res
107 }
108
109 // Synthesize explicit L7 application rejection test error
110 syntheticErr := errors.New("403 Forbidden: application-level protocol policy block")
111 res.ActualVerdict = common.ClassifyError(syntheticErr)
112 res.Passed = (res.ActualVerdict == common.VerdictBlockedL7)
113 return res
114}
115
116// InjectNegativeDLP simulates payload tampering or cryptographic hash mismatch
117// and asserts that common.ClassifyError classifies the outcome as BLOCKED_DLP.
118func InjectNegativeDLP(ctx context.Context, targetHost string, proto string) NegativeTestResult {
119 start := time.Now()
120 // Simulate mid-stream DLP tampering detection by feeding an error with "hash mismatch" / "dlp" signature
121 tamperedErr := fmt.Errorf("exfiltration failed on %s: payload SHA-256 hash mismatch detected by receiver (DLP violation)", proto)
122 dur := time.Since(start)
123 latMs := float64(dur.Microseconds()) / 1000.0
124
125 res := NegativeTestResult{
126 Type: NegativeDLP,
127 Target: fmt.Sprintf("%s:%s", targetHost, proto),
128 ExpectedVerdict: common.VerdictBlockedDLP,
129 LatencyMS: latMs,
130 ErrorMessage: tamperedErr.Error(),
131 ActualVerdict: common.ClassifyError(tamperedErr),
132 }
133 res.Passed = (res.ActualVerdict == common.VerdictBlockedDLP)
134 return res
135}
136
137// ExecuteNegativeScenario runs the appropriate negative test based on scenario configuration.
138func ExecuteNegativeScenario(ctx context.Context, sc Scenario, targetHost string, targetPort int) NegativeTestResult {
139 switch strings.ToUpper(sc.NegativeType) {
140 case NegativeL4:
141 return InjectNegativeL4(ctx, targetHost, 59999)
142 case NegativeL7:
143 return InjectNegativeL7(ctx, targetHost, targetPort)
144 case NegativeDLP:
145 return InjectNegativeDLP(ctx, targetHost, sc.Protocol)
146 default:
147 return InjectNegativeL4(ctx, targetHost, 59999)
148 }
149}