[SHEHZADE//ABDULLAH ANSARI]
Red Team Consultant @ Google | MS in Cybersecurity @ NYU
● 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)
271 lines · 11.5 KB
| 1 | package main |
| 2 | |
| 3 | import ( |
| 4 | "context" |
| 5 | "fmt" |
| 6 | "math/rand" |
| 7 | "os" |
| 8 | "strconv" |
| 9 | "strings" |
| 10 | |
| 11 | "github.com/shehzade/contraband/pkg/client" |
| 12 | "github.com/shehzade/contraband/pkg/common" |
| 13 | "github.com/shehzade/contraband/pkg/dryrun" |
| 14 | "github.com/spf13/cobra" |
| 15 | ) |
| 16 | |
| 17 | var ( |
| 18 | flagConfig string |
| 19 | flagVerbose bool |
| 20 | flagJSON bool |
| 21 | flagSeed int64 |
| 22 | flagDryRun bool |
| 23 | |
| 24 | clientServer string |
| 25 | clientPorts string |
| 26 | clientProtocols string |
| 27 | clientDLP string |
| 28 | clientCarrierType string |
| 29 | clientSize string |
| 30 | clientChunkSize string |
| 31 | clientThreads int |
| 32 | clientThroughput string |
| 33 | clientCadence string |
| 34 | clientIntervalMin string |
| 35 | clientIntervalMax string |
| 36 | clientTransforms string |
| 37 | clientPassphrase string |
| 38 | clientStego string |
| 39 | clientTLSFingerprint string |
| 40 | clientProxy string |
| 41 | clientProxyAuth string |
| 42 | clientConcurrency int |
| 43 | clientRateLimit int |
| 44 | clientOutputJSON string |
| 45 | clientOutputHTML string |
| 46 | clientOutputSARIF string |
| 47 | clientDestroy bool |
| 48 | clientSavePayload bool |
| 49 | clientEvidenceDir string |
| 50 | clientRateAlias string |
| 51 | clientSyncReport string |
| 52 | clientZone string |
| 53 | clientNoColor bool |
| 54 | |
| 55 | loadedConfig *common.ProfileConfig |
| 56 | appLogger common.Logger |
| 57 | ) |
| 58 | |
| 59 | var rootCmd = &cobra.Command{ |
| 60 | Use: "contraband-client", |
| 61 | Short: "Contraband Standalone Egress Assessment Client", |
| 62 | Long: `Dedicated standalone client binary for automated network egress assessment, |
| 63 | multi-protocol data smuggling, corporate proxy traversal, synthetic DLP verification, |
| 64 | and forensic reporting.`, |
| 65 | SilenceUsage: true, |
| 66 | SilenceErrors: true, |
| 67 | PersistentPreRunE: func(cmd *cobra.Command, args []string) error { |
| 68 | appLogger = common.GetLogger() |
| 69 | if flagJSON { |
| 70 | appLogger.SetMode(common.LogModeJSON) |
| 71 | } |
| 72 | if flagVerbose { |
| 73 | appLogger.SetLevel(common.LogLevelDebug) |
| 74 | } |
| 75 | if flagSeed != 0 { |
| 76 | rand.Seed(flagSeed) |
| 77 | appLogger.Debug(fmt.Sprintf("Initialized deterministic random seed: %d", flagSeed)) |
| 78 | } |
| 79 | if flagConfig != "" { |
| 80 | cfg, err := common.LoadProfileConfig(flagConfig) |
| 81 | if err != nil { |
| 82 | return fmt.Errorf("failed to load configuration profile: %w", err) |
| 83 | } |
| 84 | loadedConfig = cfg |
| 85 | } |
| 86 | return nil |
| 87 | }, |
| 88 | RunE: runClientStandalone, |
| 89 | } |
| 90 | |
| 91 | func init() { |
| 92 | rootCmd.PersistentFlags().StringVarP(&flagConfig, "config", "c", "", "Path to YAML/JSON configuration profile") |
| 93 | rootCmd.PersistentFlags().BoolVarP(&flagVerbose, "verbose", "v", false, "Enable verbose debug logging") |
| 94 | rootCmd.PersistentFlags().BoolVar(&flagJSON, "json", false, "Output results in structured JSON format") |
| 95 | rootCmd.PersistentFlags().Int64Var(&flagSeed, "seed", 0, "Deterministic random seed for reproducible runs") |
| 96 | rootCmd.PersistentFlags().BoolVar(&flagDryRun, "dry-run", false, "Pre-flight validation without transmitting live network traffic") |
| 97 | |
| 98 | rootCmd.Flags().StringVarP(&clientServer, "server", "s", "127.0.0.1", "Target server IP or hostname") |
| 99 | rootCmd.Flags().StringVarP(&clientPorts, "ports", "p", "quick", "Port preset: 'quick', 'standard', 'full', or comma/range") |
| 100 | rootCmd.Flags().StringVar(&clientProtocols, "protocols", "all", "Protocols: 'all', category, or comma-separated list") |
| 101 | rootCmd.Flags().StringVar(&clientDLP, "dlp", "all", "Synthetic DLP payloads: 'all', 'pci', 'pii', 'hipaa', 'secrets'") |
| 102 | rootCmd.Flags().StringVar(&clientCarrierType, "carrier-type", "csv", "Procedural text carrier: 'csv', 'json', 'sql', 'email', 'syslog'") |
| 103 | rootCmd.Flags().StringVar(&clientSize, "size", "1MB", "Data volume to exfiltrate ('100KB', '50MB', '2GB')") |
| 104 | rootCmd.Flags().StringVar(&clientChunkSize, "chunk-size", "1MB", "Transfer chunk size ('64KB', '1MB')") |
| 105 | rootCmd.Flags().IntVar(&clientThreads, "threads", 4, "Concurrent worker streams (1–64)") |
| 106 | rootCmd.Flags().StringVar(&clientThroughput, "throughput", "unlimited", "Outbound rate limit ('100KB/s', '5MB/s', 'unlimited')") |
| 107 | rootCmd.Flags().StringVar(&clientCadence, "cadence", "steady", "Traffic pacing: 'burst', 'steady', 'jittered', 'office-hours'") |
| 108 | rootCmd.Flags().StringVar(&clientIntervalMin, "interval-min", "500ms", "Minimum jitter sleep interval") |
| 109 | rootCmd.Flags().StringVar(&clientIntervalMax, "interval-max", "4s", "Maximum jitter sleep interval") |
| 110 | rootCmd.Flags().StringVar(&clientTransforms, "transforms", "raw", "Evasion chain: comma-separated 'raw', 'base64', 'hex', 'gzip', 'zstd', 'aes_gcm'") |
| 111 | rootCmd.Flags().StringVar(&clientPassphrase, "passphrase", "", "Passphrase for AES-256-GCM encryption") |
| 112 | rootCmd.Flags().StringVar(&clientStego, "stego", "none", "Steganographic carrier: 'none', 'bmp', 'png', 'wav', 'jpeg-zip', 'pdf-jar'") |
| 113 | rootCmd.Flags().StringVar(&clientTLSFingerprint, "tls-fingerprint", "chrome", "uTLS Client Hello: 'chrome', 'edge', 'safari', 'firefox', 'curl', 'default'") |
| 114 | rootCmd.Flags().StringVar(&clientProxy, "proxy", "auto", "Proxy traversal: 'auto', 'direct', or URL") |
| 115 | rootCmd.Flags().StringVar(&clientProxyAuth, "proxy-auth", "basic", "Proxy authentication: 'basic', 'ntlm', 'negotiate'") |
| 116 | rootCmd.Flags().IntVar(&clientConcurrency, "concurrency", 100, "Sweep worker pool concurrency") |
| 117 | rootCmd.Flags().IntVar(&clientRateLimit, "rate-limit", 500, "Maximum sweep packets per second") |
| 118 | rootCmd.Flags().StringVar(&clientOutputJSON, "output-json", "", "Destination file for full JSON results") |
| 119 | rootCmd.Flags().StringVar(&clientOutputHTML, "output-html", "", "Destination file for interactive zero-CDN HTML dashboard") |
| 120 | rootCmd.Flags().StringVar(&clientOutputSARIF, "output-sarif", "", "Destination file for SARIF v2.1.0 report") |
| 121 | rootCmd.Flags().StringVar(&clientSyncReport, "sync-report", "", "URL of live dashboard server to synchronize report to (e.g. http://10.0.0.1:8080)") |
| 122 | rootCmd.Flags().StringVar(&clientZone, "zone", "corporate-default", "Network zone or segment identifier for multi-zone telemetry") |
| 123 | rootCmd.Flags().BoolVar(&clientNoColor, "no-color", false, "Disable ANSI color formatting in terminal output") |
| 124 | rootCmd.Flags().BoolVar(&clientDestroy, "destroy", false, "Auto-cleanup uploaded objects in cloud storage") |
| 125 | rootCmd.Flags().BoolVar(&clientSavePayload, "save-payload", false, "Physically retain full exfiltrated payload on client disk") |
| 126 | rootCmd.Flags().StringVar(&clientEvidenceDir, "evidence-dir", "evidence", "Directory to store reconstructed exfiltration payloads") |
| 127 | |
| 128 | // Operator flag aliases |
| 129 | rootCmd.Flags().StringVar(&clientServer, "target", "127.0.0.1", "Alias for --server") |
| 130 | rootCmd.Flags().StringVar(&clientRateAlias, "rate", "", "Alias for --throughput (e.g. '10MB/s') or --rate-limit (e.g. 500)") |
| 131 | rootCmd.Flags().StringVar(&clientThroughput, "bandwidth", "unlimited", "Alias for --throughput") |
| 132 | rootCmd.Flags().StringVar(&clientTLSFingerprint, "utls", "chrome", "Alias for --tls-fingerprint") |
| 133 | rootCmd.Flags().StringVar(&clientIntervalMax, "jitter", "4s", "Alias for --interval-max") |
| 134 | rootCmd.Flags().StringVar(&clientIntervalMax, "timing-jitter", "4s", "Alias for --interval-max") |
| 135 | rootCmd.Flags().StringVar(&clientOutputJSON, "output", "", "Alias for --output-json") |
| 136 | rootCmd.Flags().StringVar(&clientEvidenceDir, "report-dir", "evidence", "Alias for --evidence-dir") |
| 137 | |
| 138 | rootCmd.AddCommand(&cobra.Command{ |
| 139 | Use: "version", |
| 140 | Short: "Print client version", |
| 141 | Run: func(cmd *cobra.Command, args []string) { |
| 142 | fmt.Printf("Contraband Client v%s (Pure Go, CGO_ENABLED=0)\n", common.Version) |
| 143 | }, |
| 144 | }) |
| 145 | } |
| 146 | |
| 147 | func runClientStandalone(cmd *cobra.Command, args []string) error { |
| 148 | cfg := client.DefaultClientConfig() |
| 149 | |
| 150 | cfg.Server = clientServer |
| 151 | cfg.Ports = clientPorts |
| 152 | cfg.Protocols = clientProtocols |
| 153 | cfg.DLPPayloads = clientDLP |
| 154 | cfg.CarrierType = clientCarrierType |
| 155 | cfg.Size = clientSize |
| 156 | cfg.ChunkSize = clientChunkSize |
| 157 | cfg.Threads = clientThreads |
| 158 | cfg.Throughput = clientThroughput |
| 159 | cfg.Cadence = clientCadence |
| 160 | cfg.IntervalMin = clientIntervalMin |
| 161 | cfg.IntervalMax = clientIntervalMax |
| 162 | cfg.Transforms = clientTransforms |
| 163 | cfg.Passphrase = clientPassphrase |
| 164 | cfg.Stego = clientStego |
| 165 | cfg.TLSFingerprint = clientTLSFingerprint |
| 166 | cfg.Proxy = clientProxy |
| 167 | cfg.ProxyAuth = clientProxyAuth |
| 168 | cfg.Concurrency = clientConcurrency |
| 169 | cfg.RateLimit = clientRateLimit |
| 170 | cfg.OutputJSON = clientOutputJSON |
| 171 | cfg.OutputHTML = clientOutputHTML |
| 172 | cfg.OutputSARIF = clientOutputSARIF |
| 173 | cfg.SyncReport = clientSyncReport |
| 174 | cfg.Zone = clientZone |
| 175 | cfg.NoColor = clientNoColor |
| 176 | cfg.Destroy = clientDestroy |
| 177 | cfg.SavePayload = clientSavePayload |
| 178 | cfg.EvidenceDir = clientEvidenceDir |
| 179 | cfg.Seed = flagSeed |
| 180 | |
| 181 | // CLI alias overrides |
| 182 | if cmd.Flags().Changed("target") { |
| 183 | cfg.Server = clientServer |
| 184 | } |
| 185 | if cmd.Flags().Changed("rate") { |
| 186 | if val, err := strconv.Atoi(clientRateAlias); err == nil { |
| 187 | cfg.RateLimit = val |
| 188 | clientRateLimit = val |
| 189 | } else if strings.HasSuffix(strings.ToLower(clientRateAlias), "/s") || strings.HasSuffix(strings.ToLower(clientRateAlias), "/sec") || strings.ToLower(clientRateAlias) == "unlimited" { |
| 190 | if _, err := common.ParseBandwidthRate(clientRateAlias); err != nil { |
| 191 | return fmt.Errorf("invalid bandwidth rate %q: %w", clientRateAlias, err) |
| 192 | } |
| 193 | cfg.Throughput = clientRateAlias |
| 194 | clientThroughput = clientRateAlias |
| 195 | } else { |
| 196 | return fmt.Errorf("invalid argument %q for \"--rate\" flag: strconv.ParseInt: parsing %q: invalid syntax", clientRateAlias, clientRateAlias) |
| 197 | } |
| 198 | } |
| 199 | if cmd.Flags().Changed("bandwidth") { |
| 200 | cfg.Throughput = clientThroughput |
| 201 | } |
| 202 | if cmd.Flags().Changed("utls") { |
| 203 | cfg.TLSFingerprint = clientTLSFingerprint |
| 204 | } |
| 205 | if cmd.Flags().Changed("jitter") || cmd.Flags().Changed("timing-jitter") { |
| 206 | cfg.IntervalMax = clientIntervalMax |
| 207 | } |
| 208 | if cmd.Flags().Changed("output") { |
| 209 | cfg.OutputJSON = clientOutputJSON |
| 210 | } |
| 211 | if cmd.Flags().Changed("report-dir") { |
| 212 | cfg.EvidenceDir = clientEvidenceDir |
| 213 | } |
| 214 | |
| 215 | if loadedConfig != nil { |
| 216 | if loadedConfig.Target.Server != "" && !cmd.Flags().Changed("server") && !cmd.Flags().Changed("target") { |
| 217 | cfg.Server = loadedConfig.Target.Server |
| 218 | } |
| 219 | if loadedConfig.Target.Ports != "" && !cmd.Flags().Changed("ports") { |
| 220 | cfg.Ports = loadedConfig.Target.Ports |
| 221 | } |
| 222 | if len(loadedConfig.Target.Protocols) > 0 && !cmd.Flags().Changed("protocols") { |
| 223 | cfg.Protocols = strings.Join(loadedConfig.Target.Protocols, ",") |
| 224 | } |
| 225 | if len(loadedConfig.DLP.Payloads) > 0 && !cmd.Flags().Changed("dlp") { |
| 226 | cfg.DLPPayloads = strings.Join(loadedConfig.DLP.Payloads, ",") |
| 227 | } |
| 228 | if loadedConfig.DLP.CarrierType != "" && !cmd.Flags().Changed("carrier-type") { |
| 229 | cfg.CarrierType = loadedConfig.DLP.CarrierType |
| 230 | } |
| 231 | if loadedConfig.DLP.Size != "" && !cmd.Flags().Changed("size") { |
| 232 | cfg.Size = loadedConfig.DLP.Size |
| 233 | } |
| 234 | if loadedConfig.Traffic.Threads > 0 && !cmd.Flags().Changed("threads") { |
| 235 | cfg.Threads = loadedConfig.Traffic.Threads |
| 236 | } |
| 237 | if loadedConfig.Traffic.Throughput != "" && !cmd.Flags().Changed("throughput") && !cmd.Flags().Changed("bandwidth") && !cmd.Flags().Changed("rate") { |
| 238 | cfg.Throughput = loadedConfig.Traffic.Throughput |
| 239 | } |
| 240 | if loadedConfig.Sweep.RateLimit > 0 && !cmd.Flags().Changed("rate-limit") && !cmd.Flags().Changed("rate") { |
| 241 | cfg.RateLimit = loadedConfig.Sweep.RateLimit |
| 242 | } |
| 243 | if loadedConfig.Traffic.Cadence != "" && !cmd.Flags().Changed("cadence") { |
| 244 | cfg.Cadence = loadedConfig.Traffic.Cadence |
| 245 | } |
| 246 | if loadedConfig.Proxy.Mode != "" && !cmd.Flags().Changed("proxy") { |
| 247 | cfg.Proxy = loadedConfig.Proxy.Mode |
| 248 | } |
| 249 | if loadedConfig.Output.JSONPath != "" && !cmd.Flags().Changed("output-json") && !cmd.Flags().Changed("output") { |
| 250 | cfg.OutputJSON = loadedConfig.Output.JSONPath |
| 251 | } |
| 252 | if loadedConfig.Output.HTMLPath != "" && !cmd.Flags().Changed("output-html") { |
| 253 | cfg.OutputHTML = loadedConfig.Output.HTMLPath |
| 254 | } |
| 255 | } |
| 256 | |
| 257 | if flagDryRun { |
| 258 | return dryrun.RunClientDryRun(cfg) |
| 259 | } |
| 260 | |
| 261 | ctx := context.Background() |
| 262 | _, err := client.RunAssessment(ctx, cfg) |
| 263 | return err |
| 264 | } |
| 265 | |
| 266 | func main() { |
| 267 | if err := rootCmd.Execute(); err != nil { |
| 268 | fmt.Fprintf(os.Stderr, "Error: %v\n", err) |
| 269 | os.Exit(1) |
| 270 | } |
| 271 | } |