● 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
← Back to directory
307 lines · 11.1 KB
Raw
1package main
2
3import (
4 "context"
5 "fmt"
6 "math/rand"
7 "os"
8 "os/signal"
9 "strconv"
10 "strings"
11 "syscall"
12 "time"
13
14 "github.com/shehzade/contraband/pkg/common"
15 "github.com/shehzade/contraband/pkg/dryrun"
16 "github.com/shehzade/contraband/pkg/server"
17 "github.com/shehzade/contraband/pkg/server/listeners"
18 "github.com/shehzade/contraband/pkg/server/multiplexer"
19 "github.com/spf13/cobra"
20)
21
22var (
23 flagConfig string
24 flagVerbose bool
25 flagJSON bool
26 flagSeed int64
27 flagDryRun bool
28
29 serverBind string
30 serverDomain string
31 serverPorts string
32 serverProtocols string
33 serverSMBMode string
34 serverCert string
35 serverKey string
36 serverEnableEBPF bool
37 serverDB string
38 serverSavePayload bool
39 serverEvidenceDir string
40 serverTCP bool
41 serverUDP bool
42 serverBanner string
43 serverPortAlias string
44 serverDashboard string
45
46 loadedConfig *common.ProfileConfig
47 appLogger common.Logger
48)
49
50var rootCmd = &cobra.Command{
51 Use: "contraband-server",
52 Short: "Contraband Standalone Reflection Server & Session Ledger",
53 Long: `Dedicated standalone server binary for multi-port reflection,
54multi-protocol listener coordination, and session ledger reassembly.`,
55 SilenceUsage: true,
56 SilenceErrors: true,
57 PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
58 appLogger = common.GetLogger()
59 if flagJSON {
60 appLogger.SetMode(common.LogModeJSON)
61 }
62 if flagVerbose {
63 appLogger.SetLevel(common.LogLevelDebug)
64 }
65 if flagSeed != 0 {
66 rand.Seed(flagSeed)
67 appLogger.Debug(fmt.Sprintf("Initialized deterministic random seed: %d", flagSeed))
68 }
69 if flagConfig != "" {
70 cfg, err := common.LoadProfileConfig(flagConfig)
71 if err != nil {
72 return fmt.Errorf("failed to load configuration profile: %w", err)
73 }
74 loadedConfig = cfg
75 }
76 return nil
77 },
78 RunE: runServerStandalone,
79}
80
81func init() {
82 rootCmd.PersistentFlags().StringVarP(&flagConfig, "config", "c", "", "Path to YAML/JSON configuration profile")
83 rootCmd.PersistentFlags().BoolVarP(&flagVerbose, "verbose", "v", false, "Enable verbose debug logging")
84 rootCmd.PersistentFlags().BoolVar(&flagJSON, "json", false, "Output results in structured JSON format")
85 rootCmd.PersistentFlags().Int64Var(&flagSeed, "seed", 0, "Deterministic random seed for reproducible runs")
86 rootCmd.PersistentFlags().BoolVar(&flagDryRun, "dry-run", false, "Pre-flight validation without transmitting live network traffic")
87
88 rootCmd.Flags().StringVarP(&serverBind, "bind", "b", "0.0.0.0", "Server IP interface to bind listeners")
89 rootCmd.Flags().StringVarP(&serverDomain, "domain", "d", "contraband.example.com", "Authoritative domain for DNS tunneling reassembly")
90 rootCmd.Flags().StringVarP(&serverPorts, "ports", "p", "quick", "Port matrix for multiplexer: 'quick', 'standard', or ranges")
91 rootCmd.Flags().StringVar(&serverPortAlias, "port", "", "Alias for --ports")
92 rootCmd.Flags().StringVar(&serverProtocols, "protocols", "all", "Server protocol listeners to activate ('all', 'web', 'dns', etc.)")
93 rootCmd.Flags().StringVar(&serverSMBMode, "smb-mode", "hash-capture", "SMB server operational mode: 'hash-capture' or 'file-transfer'")
94 rootCmd.Flags().StringVar(&serverCert, "cert", "", "Custom TLS certificate path (auto-generates dynamic CA if omitted)")
95 rootCmd.Flags().StringVar(&serverKey, "key", "", "Custom TLS private key path")
96 rootCmd.Flags().BoolVar(&serverEnableEBPF, "enable-ebpf", false, "Enable Linux eBPF/nftables full-spectrum port redirection (requires root)")
97 rootCmd.Flags().StringVar(&serverDB, "db", ":memory:", "SQLite database path for session ledger (:memory: for in-memory)")
98 rootCmd.Flags().BoolVar(&serverSavePayload, "save-payload", false, "Physically reconstruct and save received payload files to disk")
99 rootCmd.Flags().StringVar(&serverEvidenceDir, "evidence-dir", "evidence", "Destination directory for physical session payloads")
100 rootCmd.Flags().BoolVar(&serverTCP, "tcp", true, "Enable TCP reflection in port multiplexer")
101 rootCmd.Flags().BoolVar(&serverUDP, "udp", true, "Enable UDP reflection in port multiplexer")
102 rootCmd.Flags().StringVar(&serverBanner, "banner", "", "Custom JSON reflection banner format string")
103 rootCmd.Flags().StringVar(&serverDashboard, "dashboard", "", "Address to bind live command center dashboard and report server (e.g. :8080)")
104 rootCmd.Flags().StringVar(&serverDashboard, "live-dashboard", "", "Alias for --dashboard")
105
106 rootCmd.AddCommand(&cobra.Command{
107 Use: "version",
108 Short: "Print server version",
109 Run: func(cmd *cobra.Command, args []string) {
110 fmt.Printf("Contraband Server v%s (Pure Go, CGO_ENABLED=0)\n", common.Version)
111 },
112 })
113}
114
115func runServerStandalone(cmd *cobra.Command, args []string) error {
116 if serverPortAlias != "" && (serverPorts == "quick" || serverPorts == "") {
117 serverPorts = serverPortAlias
118 }
119
120 cfg := server.DefaultServerConfig()
121 cfg.BindIP = serverBind
122 cfg.Domain = serverDomain
123 cfg.Ports = serverPorts
124 cfg.Protocols = serverProtocols
125 cfg.SMBMode = serverSMBMode
126 cfg.CertPath = serverCert
127 cfg.KeyPath = serverKey
128 cfg.EnableEBPF = serverEnableEBPF
129 cfg.DBPath = serverDB
130 cfg.SavePayload = serverSavePayload
131 cfg.EvidenceDir = serverEvidenceDir
132 cfg.DashboardAddr = serverDashboard
133 cfg.EnableTCP = serverTCP
134 cfg.EnableUDP = serverUDP
135 cfg.Banner = serverBanner
136
137 if flagDryRun {
138 return dryrun.RunServerDryRun(cfg)
139 }
140
141 engine, err := server.NewServerEngine(cfg)
142 if err != nil {
143 return fmt.Errorf("failed to initialize server engine: %w", err)
144 }
145
146 requestedPorts, err := common.ParsePortsField(cfg.Ports)
147 if err != nil {
148 return fmt.Errorf("invalid ports specification %q: %w", cfg.Ports, err)
149 }
150
151 registry := listeners.NewListenerRegistry(appLogger)
152 logger := common.GetLogger()
153 tlsConfig := engine.GetTLSConfig()
154 sshHostKey := engine.GetSSHHostKey()
155
156 protoLower := strings.ToLower(cfg.Protocols)
157 isCustomPorts := cfg.Ports != "quick" && cfg.Ports != "standard" && cfg.Ports != "full"
158
159 dashPort := 0
160 if cfg.DashboardAddr != "" {
161 parts := strings.Split(cfg.DashboardAddr, ":")
162 if len(parts) > 1 {
163 if dp, err := strconv.Atoi(parts[len(parts)-1]); err == nil {
164 dashPort = dp
165 }
166 }
167 }
168
169 if protoLower != "none" && protoLower != "reflector" {
170 if isCustomPorts {
171 hasPort := func(target int) bool {
172 for _, p := range requestedPorts {
173 if p == target {
174 return true
175 }
176 }
177 return false
178 }
179 if protoLower == "all" || strings.Contains(protoLower, "web") || strings.Contains(protoLower, "http") {
180 var webPorts []int
181 for _, p := range requestedPorts {
182 if (p == 8443 || p == 9443 || p == 10443 || p == 80 || p == 443 || p == 8080) && p != dashPort {
183 webPorts = append(webPorts, p)
184 }
185 }
186 if len(webPorts) > 0 {
187 _ = registry.Register(listeners.NewHTTPServerListener(cfg.BindIP, webPorts, webPorts[0], tlsConfig))
188 }
189 }
190 if (protoLower == "all" || strings.Contains(protoLower, "dns")) && hasPort(53) {
191 _ = registry.Register(listeners.NewDNSServerListener(cfg.BindIP, 53, 53, 853, 8443, tlsConfig))
192 }
193 } else {
194 defaultWebPorts := []int{80, 443, 8080}
195 if dashPort > 0 {
196 var filtered []int
197 for _, p := range defaultWebPorts {
198 if p != dashPort {
199 filtered = append(filtered, p)
200 }
201 }
202 defaultWebPorts = filtered
203 }
204
205 dohPort := 8443
206 for _, p := range defaultWebPorts {
207 if p == dohPort {
208 dohPort = 8444
209 break
210 }
211 }
212
213 if err := registry.Register(listeners.NewHTTPServerListener(cfg.BindIP, defaultWebPorts, 443, tlsConfig)); err != nil {
214 logger.Warn(fmt.Sprintf("Failed to register HTTPServerListener: %v", err))
215 }
216 if err := registry.Register(listeners.NewGRPCServerListener(cfg.BindIP, []int{50051}, tlsConfig)); err != nil {
217 logger.Warn(fmt.Sprintf("Failed to register GRPCServerListener: %v", err))
218 }
219 if err := registry.Register(listeners.NewDNSServerListener(cfg.BindIP, 53, 53, 853, dohPort, tlsConfig)); err != nil {
220 logger.Warn(fmt.Sprintf("Failed to register DNSServerListener: %v", err))
221 }
222 if err := registry.Register(listeners.NewFTPServerListener(cfg.BindIP, 21, 990, tlsConfig)); err != nil {
223 logger.Warn(fmt.Sprintf("Failed to register FTPServerListener: %v", err))
224 }
225 if err := registry.Register(listeners.NewSFTPServerListener(cfg.BindIP, 22, sshHostKey)); err != nil {
226 logger.Warn(fmt.Sprintf("Failed to register SFTPServerListener: %v", err))
227 }
228 if err := registry.Register(listeners.NewSMTPServerListener(cfg.BindIP, 25, 465, 587, tlsConfig)); err != nil {
229 logger.Warn(fmt.Sprintf("Failed to register SMTPServerListener: %v", err))
230 }
231 if err := registry.Register(listeners.NewSMBServerListener(cfg.BindIP, 445, cfg.SMBMode)); err != nil {
232 logger.Warn(fmt.Sprintf("Failed to register SMBServerListener: %v", err))
233 }
234 if err := registry.Register(listeners.NewSMBQUICServerListener(cfg.BindIP, 4430, tlsConfig)); err != nil {
235 logger.Warn(fmt.Sprintf("Failed to register SMBQUICServerListener: %v", err))
236 }
237 if err := registry.Register(listeners.NewICMPServerListener(cfg.BindIP)); err != nil {
238 logger.Warn(fmt.Sprintf("Failed to register ICMPServerListener: %v", err))
239 }
240 if err := registry.Register(listeners.NewNTPServerListener(cfg.BindIP, 123)); err != nil {
241 logger.Warn(fmt.Sprintf("Failed to register NTPServerListener: %v", err))
242 }
243 if err := registry.Register(listeners.NewSyslogServerListener(cfg.BindIP, 514, 5140, 6514, tlsConfig)); err != nil {
244 logger.Warn(fmt.Sprintf("Failed to register SyslogServerListener: %v", err))
245 }
246 if err := registry.Register(listeners.NewMQTTServerListener(cfg.BindIP, 1883, 8883, tlsConfig)); err != nil {
247 logger.Warn(fmt.Sprintf("Failed to register MQTTServerListener: %v", err))
248 }
249 }
250 }
251
252 for _, l := range registry.GetAllListeners() {
253 engine.RegisterListener(l)
254 }
255
256 multiplexPorts := registry.FilterMultiplexPorts(requestedPorts)
257 if len(multiplexPorts) > 0 {
258 portStrList := make([]string, len(multiplexPorts))
259 for i, p := range multiplexPorts {
260 portStrList[i] = fmt.Sprintf("%d", p)
261 }
262 muxPorts := strings.Join(portStrList, ",")
263
264 muxCfg := multiplexer.MultiplexerConfig{
265 BindIP: cfg.BindIP,
266 Ports: muxPorts,
267 EnableTCP: cfg.EnableTCP,
268 EnableUDP: cfg.EnableUDP,
269 EnableEBPF: cfg.EnableEBPF,
270 PeekTimeout: cfg.PeekTimeout,
271 SocketTimeout: cfg.SocketTimeout,
272 }
273 mux, err := multiplexer.NewUserspaceMultiplexer(muxCfg, engine.GetLedger(), appLogger)
274 if err != nil {
275 return fmt.Errorf("failed to create userspace multiplexer: %w", err)
276 }
277 engine.SetMultiplexer(mux)
278 }
279
280 ctx, cancel := context.WithCancel(context.Background())
281 defer cancel()
282
283 sigChan := make(chan os.Signal, 1)
284 signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM, syscall.SIGINT)
285 go func() {
286 <-sigChan
287 appLogger.Info("Shutdown signal received, gracefully terminating server...")
288 cancel()
289 }()
290
291 if err := engine.Start(ctx); err != nil {
292 return fmt.Errorf("failed to start server: %w", err)
293 }
294
295 <-ctx.Done()
296
297 shutdownCtx, shutdownCancel := context.WithTimeout(context.Background(), 5*time.Second)
298 defer shutdownCancel()
299 return engine.Shutdown(shutdownCtx)
300}
301
302func main() {
303 if err := rootCmd.Execute(); err != nil {
304 fmt.Fprintf(os.Stderr, "Error: %v\n", err)
305 os.Exit(1)
306 }
307}