● 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 / client / evasion / pipeline.go
← Back to directory
289 lines · 9.0 KB
Raw
1package evasion
2
3import (
4 "bytes"
5 "context"
6 "fmt"
7 "io"
8 "strings"
9
10 "github.com/shehzade/contraband/pkg/common"
11)
12
13type CompressionMethod string
14
15const (
16 CompressNone CompressionMethod = "none"
17 CompressZlib CompressionMethod = "zlib"
18 CompressGzip CompressionMethod = "gzip"
19 CompressZstd CompressionMethod = "zstd"
20)
21
22type CarrierType string
23
24const (
25 CarrierNone CarrierType = "raw"
26 CarrierBMP CarrierType = "bmp"
27 CarrierPNG CarrierType = "png"
28 CarrierWAV CarrierType = "wav"
29 CarrierJPEGZIP CarrierType = "jpeg-zip"
30 CarrierPDFJAR CarrierType = "pdf-jar"
31)
32
33type PipelineConfig struct {
34 Compression CompressionMethod
35 CompressLevel int // e.g. zlib.DefaultCompression (1-9)
36 Encrypt bool
37 Passphrase string // For PBKDF2 key derivation
38 RawKey []byte // 32-byte raw AES key (optional alternative to Passphrase)
39 SessionToken common.SessionToken
40 ChunkSize int // Default: 1MB (common.DefaultChunkSize)
41 Carrier CarrierType
42 Seed int64 // Deterministic seed for procedural carrier generation
43 Transforms string // Comma-separated transform chain (e.g. "zstd,aes_gcm,base64")
44}
45
46type Pipeline struct {
47 cfg PipelineConfig
48}
49
50func NewPipeline(cfg PipelineConfig) (*Pipeline, error) {
51 if cfg.ChunkSize <= 0 {
52 cfg.ChunkSize = common.DefaultChunkSize
53 }
54 if cfg.SessionToken.IsZero() {
55 tok, err := common.NewSessionToken()
56 if err != nil {
57 return nil, err
58 }
59 cfg.SessionToken = tok
60 }
61 return &Pipeline{cfg: cfg}, nil
62}
63
64// Transform takes raw payload bytes, compresses, encrypts, chunks with 32B ChunkHeader,
65// TransformStream applies compression and encryption to an io.Reader stream without chunking or framing,
66// returning an io.Reader suitable for single-layer wire transmission.
67func (p *Pipeline) TransformStream(ctx context.Context, reader io.Reader) (io.Reader, error) {
68 rawPayload, err := io.ReadAll(reader)
69 if err != nil {
70 return nil, fmt.Errorf("failed to read stream for evasion transform: %w", err)
71 }
72 transformed, err := p.TransformBytes(rawPayload)
73 if err != nil {
74 return nil, err
75 }
76 return bytes.NewReader(transformed), nil
77}
78
79// TransformBytes applies compression and encryption to the raw payload without chunking or framing.
80func (p *Pipeline) TransformBytes(rawPayload []byte) ([]byte, error) {
81 if p.cfg.Transforms != "" && p.cfg.Transforms != "raw" && p.cfg.Transforms != "none" {
82 return p.TransformChain(rawPayload)
83 }
84
85 // 1. Compression
86 compressed, err := Compress(rawPayload, p.cfg.Compression, p.cfg.CompressLevel)
87 if err != nil {
88 return nil, fmt.Errorf("compression failed: %w", err)
89 }
90
91 // 2. Encryption
92 var encrypted []byte
93 if p.cfg.Encrypt {
94 if p.cfg.Passphrase != "" {
95 encrypted, err = EncryptWithPassphrase(compressed, p.cfg.Passphrase)
96 } else if len(p.cfg.RawKey) == 32 {
97 encrypted, err = EncryptAESGCM(compressed, p.cfg.RawKey)
98 } else {
99 return nil, fmt.Errorf("encryption enabled but neither 32-byte key nor passphrase provided")
100 }
101 if err != nil {
102 return nil, fmt.Errorf("encryption failed: %w", err)
103 }
104 } else {
105 encrypted = compressed
106 }
107 return encrypted, nil
108}
109
110// Transform compresses, encrypts, chunks, adds 32-byte wire framing,
111// and embeds into the configured carrier format.
112func (p *Pipeline) Transform(ctx context.Context, rawPayload []byte) ([][]byte, error) {
113 transformed, err := p.TransformBytes(rawPayload)
114 if err != nil {
115 return nil, err
116 }
117
118 // Chunking & 32-byte Framing
119 framedChunks, err := ChunkPayload(transformed, p.cfg.SessionToken, p.cfg.ChunkSize)
120 if err != nil {
121 return nil, fmt.Errorf("chunking failed: %w", err)
122 }
123
124 // Carrier Embedding
125 if p.cfg.Carrier == CarrierNone || p.cfg.Carrier == "" {
126 return framedChunks, nil
127 }
128
129 embeddedCarriers := make([][]byte, len(framedChunks))
130 for i, chunk := range framedChunks {
131 carrierBytes, err := EmbedCarrier(chunk, p.cfg.Carrier, p.cfg.Seed+int64(i))
132 if err != nil {
133 return nil, fmt.Errorf("carrier embedding failed for chunk %d: %w", i+1, err)
134 }
135 embeddedCarriers[i] = carrierBytes
136 }
137
138 return embeddedCarriers, nil
139}
140
141// ReverseExtract takes carrier data, extracts chunk payload, verifies ChunkHeader,
142// decrypts, and decompresses back to original payload.
143func (p *Pipeline) ReverseExtract(ctx context.Context, carrierData []byte) ([]byte, *common.ChunkHeader, error) {
144 // 1. Carrier Extraction
145 extractedChunk, err := ExtractCarrier(carrierData, p.cfg.Carrier)
146 if err != nil {
147 return nil, nil, fmt.Errorf("carrier extraction failed: %w", err)
148 }
149
150 // 2. Parse 32-byte ChunkHeader
151 if len(extractedChunk) < common.ChunkHeaderSize {
152 return nil, nil, fmt.Errorf("extracted chunk too short for ChunkHeader (%d < %d)",
153 len(extractedChunk), common.ChunkHeaderSize)
154 }
155 var header common.ChunkHeader
156 if err := header.UnmarshalBinary(extractedChunk[:common.ChunkHeaderSize]); err != nil {
157 return nil, nil, fmt.Errorf("invalid chunk header: %w", err)
158 }
159
160 payloadData := extractedChunk[common.ChunkHeaderSize : common.ChunkHeaderSize+header.PayloadLen]
161
162 if p.cfg.Transforms != "" && p.cfg.Transforms != "raw" && p.cfg.Transforms != "none" {
163 decompressed, err := p.ReverseTransformChain(payloadData)
164 if err != nil {
165 return nil, nil, fmt.Errorf("reverse transform failed: %w", err)
166 }
167 return decompressed, &header, nil
168 }
169
170 // 3. Decryption
171 var decrypted []byte
172 if p.cfg.Encrypt {
173 if p.cfg.Passphrase != "" {
174 decrypted, err = DecryptWithPassphrase(payloadData, p.cfg.Passphrase)
175 } else if len(p.cfg.RawKey) == 32 {
176 decrypted, err = DecryptAESGCM(payloadData, p.cfg.RawKey)
177 } else {
178 return nil, nil, fmt.Errorf("decryption required but no valid key/passphrase available")
179 }
180 if err != nil {
181 return nil, nil, fmt.Errorf("decryption failed: %w", err)
182 }
183 } else {
184 decrypted = payloadData
185 }
186
187 // 4. Decompression
188 decompressed, err := Decompress(decrypted, p.cfg.Compression)
189 if err != nil {
190 return nil, nil, fmt.Errorf("decompression failed: %w", err)
191 }
192
193 return decompressed, &header, nil
194}
195
196// TransformChain applies the configured transform chain to data.
197func (p *Pipeline) TransformChain(data []byte) ([]byte, error) {
198 return ApplyTransformChain(data, p.cfg.Transforms, p.cfg.Passphrase, p.cfg.RawKey)
199}
200
201// ReverseTransformChain reverses the configured transform chain on data.
202func (p *Pipeline) ReverseTransformChain(data []byte) ([]byte, error) {
203 return ReverseTransformChain(data, p.cfg.Transforms, p.cfg.Passphrase, p.cfg.RawKey)
204}
205
206// ApplyTransformChain transforms payload sequentially according to comma-separated transforms string
207// (e.g. "zstd,aes_gcm,base64").
208func ApplyTransformChain(data []byte, transforms string, passphrase string, rawKey []byte) ([]byte, error) {
209 if transforms == "" || transforms == "raw" || transforms == "none" {
210 return data, nil
211 }
212 steps := strings.Split(transforms, ",")
213 cur := data
214 for _, rawStep := range steps {
215 step := strings.ToLower(strings.TrimSpace(rawStep))
216 if step == "" || step == "raw" || step == "none" {
217 continue
218 }
219 var err error
220 switch step {
221 case "zstd":
222 cur, err = CompressBytes(cur, CompressZstd)
223 case "gzip":
224 cur, err = CompressBytes(cur, CompressGzip)
225 case "zlib":
226 cur, err = CompressBytes(cur, CompressZlib)
227 case "aes", "aes_gcm", "aes-256-gcm", "encrypt":
228 if passphrase != "" {
229 cur, err = EncryptWithPassphrase(cur, passphrase)
230 } else if len(rawKey) == 32 {
231 cur, err = EncryptAESGCM(cur, rawKey)
232 } else {
233 return nil, fmt.Errorf("encryption step %q requires passphrase or 32-byte raw key", step)
234 }
235 case "base64", "b64":
236 cur = []byte(EncodeBase64(cur))
237 case "hex":
238 cur = []byte(EncodeHex(cur))
239 default:
240 return nil, fmt.Errorf("unsupported transform: %s", step)
241 }
242 if err != nil {
243 return nil, fmt.Errorf("transform step %q failed: %w", step, err)
244 }
245 }
246 return cur, nil
247}
248
249// ReverseTransformChain reverses a comma-separated transform chain in reverse order.
250func ReverseTransformChain(data []byte, transforms string, passphrase string, rawKey []byte) ([]byte, error) {
251 if transforms == "" || transforms == "raw" || transforms == "none" {
252 return data, nil
253 }
254 steps := strings.Split(transforms, ",")
255 cur := data
256 for i := len(steps) - 1; i >= 0; i-- {
257 step := strings.ToLower(strings.TrimSpace(steps[i]))
258 if step == "" || step == "raw" || step == "none" {
259 continue
260 }
261 var err error
262 switch step {
263 case "base64", "b64":
264 cur, err = DecodeBase64(string(cur))
265 case "hex":
266 cur, err = DecodeHex(string(cur))
267 case "aes", "aes_gcm", "aes-256-gcm", "encrypt":
268 if passphrase != "" {
269 cur, err = DecryptWithPassphrase(cur, passphrase)
270 } else if len(rawKey) == 32 {
271 cur, err = DecryptAESGCM(cur, rawKey)
272 } else {
273 return nil, fmt.Errorf("decryption step %q requires passphrase or 32-byte raw key", step)
274 }
275 case "zstd":
276 cur, err = DecompressBytes(cur, CompressZstd)
277 case "gzip":
278 cur, err = DecompressBytes(cur, CompressGzip)
279 case "zlib":
280 cur, err = DecompressBytes(cur, CompressZlib)
281 default:
282 return nil, fmt.Errorf("unsupported reverse transform: %s", step)
283 }
284 if err != nil {
285 return nil, fmt.Errorf("reverse transform step %q failed: %w", step, err)
286 }
287 }
288 return cur, nil
289}