● 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 / stego_png.go
← Back to directory
258 lines · 6.5 KB
Raw
1package evasion
2
3import (
4 "bytes"
5 "encoding/binary"
6 "errors"
7 "fmt"
8 "hash/crc32"
9 "image"
10 "image/color"
11 "image/draw"
12 "image/png"
13 "math/rand"
14)
15
16var (
17 PNGSignature = []byte{0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A}
18 ChunkTypeCtRB = []byte("ctRB")
19)
20
21// GenerateProceduralPNG creates a dynamic, procedural RGBA PNG image with unique entropy.
22func GenerateProceduralPNG(width, height int, seed int64) ([]byte, error) {
23 img := image.NewRGBA(image.Rect(0, 0, width, height))
24 rng := rand.New(rand.NewSource(seed))
25
26 baseR := uint8(rng.Intn(128) + 64)
27 baseG := uint8(rng.Intn(128) + 64)
28 baseB := uint8(rng.Intn(128) + 64)
29
30 for y := 0; y < height; y++ {
31 for x := 0; x < width; x++ {
32 c := color.RGBA{
33 R: uint8((int(baseR) + x*128/width) % 256),
34 G: uint8((int(baseG) + y*128/height) % 256),
35 B: uint8((int(baseB) + (x+y)*64/width) % 256),
36 A: 255,
37 }
38 img.SetRGBA(x, y, c)
39 }
40 }
41
42 var buf bytes.Buffer
43 enc := png.Encoder{CompressionLevel: png.DefaultCompression}
44 if err := enc.Encode(&buf, img); err != nil {
45 return nil, err
46 }
47 return buf.Bytes(), nil
48}
49
50// EmbedPNGAncillary embeds payload into an ancillary ctRB PNG chunk.
51func EmbedPNGAncillary(pngData []byte, payload []byte) ([]byte, error) {
52 if len(pngData) < 8 || !bytes.Equal(pngData[:8], PNGSignature) {
53 return nil, errors.New("invalid PNG signature")
54 }
55
56 // Build ctRB chunk: [Length 4B][Type 4B][Data][CRC 4B]
57 chunkLen := uint32(len(payload))
58 crcPayload := append(ChunkTypeCtRB, payload...)
59 crcVal := crc32.ChecksumIEEE(crcPayload)
60
61 var chunk bytes.Buffer
62 binary.Write(&chunk, binary.BigEndian, chunkLen)
63 chunk.Write(ChunkTypeCtRB)
64 chunk.Write(payload)
65 binary.Write(&chunk, binary.BigEndian, crcVal)
66
67 // Insert chunk right after IHDR chunk
68 offset := 8
69 ihdrEnd := -1
70 for offset < len(pngData) {
71 length := binary.BigEndian.Uint32(pngData[offset : offset+4])
72 chunkType := string(pngData[offset+4 : offset+8])
73 nextOffset := offset + 8 + int(length) + 4
74 if chunkType == "IHDR" {
75 ihdrEnd = nextOffset
76 break
77 }
78 offset = nextOffset
79 }
80
81 if ihdrEnd == -1 || ihdrEnd > len(pngData) {
82 return nil, errors.New("could not locate IHDR chunk in PNG")
83 }
84
85 var out bytes.Buffer
86 out.Write(pngData[:ihdrEnd])
87 out.Write(chunk.Bytes())
88 out.Write(pngData[ihdrEnd:])
89 return out.Bytes(), nil
90}
91
92// ExtractPNGAncillary extracts payload from ctRB ancillary chunk.
93func ExtractPNGAncillary(pngData []byte) ([]byte, error) {
94 if len(pngData) < 8 || !bytes.Equal(pngData[:8], PNGSignature) {
95 return nil, errors.New("invalid PNG signature")
96 }
97
98 offset := 8
99 for offset+8 <= len(pngData) {
100 length := binary.BigEndian.Uint32(pngData[offset : offset+4])
101 chunkType := pngData[offset+4 : offset+8]
102 dataStart := offset + 8
103 dataEnd := dataStart + int(length)
104 crcEnd := dataEnd + 4
105
106 if crcEnd > len(pngData) {
107 return nil, errors.New("malformed PNG: chunk extends beyond file length")
108 }
109
110 if bytes.Equal(chunkType, ChunkTypeCtRB) {
111 payload := pngData[dataStart:dataEnd]
112 crcVal := binary.BigEndian.Uint32(pngData[dataEnd:crcEnd])
113 computedCRC := crc32.ChecksumIEEE(pngData[offset+4 : dataEnd])
114 if crcVal != computedCRC {
115 return nil, errors.New("CRC mismatch in ctRB chunk")
116 }
117 return payload, nil
118 }
119 offset = crcEnd
120 }
121
122 return nil, errors.New("no ctRB ancillary chunk found in PNG")
123}
124
125// EmbedPNGLSB embeds payload into RGB pixel LSBs of a PNG.
126func EmbedPNGLSB(pngData []byte, payload []byte) ([]byte, error) {
127 srcImg, err := png.Decode(bytes.NewReader(pngData))
128 if err != nil {
129 return nil, fmt.Errorf("failed to decode PNG: %w", err)
130 }
131
132 bounds := srcImg.Bounds()
133 width := bounds.Dx()
134 height := bounds.Dy()
135
136 rgba := image.NewRGBA(bounds)
137 draw.Draw(rgba, bounds, srcImg, bounds.Min, draw.Src)
138
139 usableChannels := width * height * 3 // R, G, B
140 maxCapacity := (usableChannels / 8) - 4
141 if len(payload) > maxCapacity {
142 return nil, fmt.Errorf("payload (%d bytes) exceeds PNG LSB capacity (%d bytes)", len(payload), maxCapacity)
143 }
144
145 framed := make([]byte, 4+len(payload))
146 binary.BigEndian.PutUint32(framed[0:4], uint32(len(payload)))
147 copy(framed[4:], payload)
148
149 totalBits := len(framed) * 8
150 bitIdx := 0
151
152outerPixel:
153 for y := 0; y < height; y++ {
154 for x := 0; x < width; x++ {
155 c := rgba.RGBAAt(x, y)
156
157 // Red channel
158 if bitIdx < totalBits {
159 bit := (framed[bitIdx/8] >> (7 - (bitIdx % 8))) & 0x01
160 c.R = (c.R & 0xFE) | bit
161 bitIdx++
162 }
163 // Green channel
164 if bitIdx < totalBits {
165 bit := (framed[bitIdx/8] >> (7 - (bitIdx % 8))) & 0x01
166 c.G = (c.G & 0xFE) | bit
167 bitIdx++
168 }
169 // Blue channel
170 if bitIdx < totalBits {
171 bit := (framed[bitIdx/8] >> (7 - (bitIdx % 8))) & 0x01
172 c.B = (c.B & 0xFE) | bit
173 bitIdx++
174 }
175
176 rgba.SetRGBA(x, y, c)
177 if bitIdx >= totalBits {
178 break outerPixel
179 }
180 }
181 }
182
183 var buf bytes.Buffer
184 enc := png.Encoder{CompressionLevel: png.DefaultCompression}
185 if err := enc.Encode(&buf, rgba); err != nil {
186 return nil, err
187 }
188 return buf.Bytes(), nil
189}
190
191// ExtractPNGLSB extracts payload embedded via EmbedPNGLSB.
192func ExtractPNGLSB(pngData []byte) ([]byte, error) {
193 srcImg, err := png.Decode(bytes.NewReader(pngData))
194 if err != nil {
195 return nil, fmt.Errorf("failed to decode PNG: %w", err)
196 }
197
198 bounds := srcImg.Bounds()
199 width := bounds.Dx()
200 height := bounds.Dy()
201
202 rgba, ok := srcImg.(*image.RGBA)
203 if !ok {
204 rgba = image.NewRGBA(bounds)
205 draw.Draw(rgba, bounds, srcImg, bounds.Min, draw.Src)
206 }
207
208 // Extract first 32 bits (4 bytes) to read length header
209 var lengthBuf [4]byte
210 bitIdx := 0
211
212 for y := 0; y < height && bitIdx < 32; y++ {
213 for x := 0; x < width && bitIdx < 32; x++ {
214 c := rgba.RGBAAt(x, y)
215 for _, channelVal := range []uint8{c.R, c.G, c.B} {
216 if bitIdx >= 32 {
217 break
218 }
219 bit := channelVal & 0x01
220 lengthBuf[bitIdx/8] |= (bit << (7 - (bitIdx % 8)))
221 bitIdx++
222 }
223 }
224 }
225
226 payloadLen := int(binary.BigEndian.Uint32(lengthBuf[:]))
227 usableChannels := width * height * 3
228 maxCapacity := (usableChannels / 8) - 4
229 if payloadLen <= 0 || payloadLen > maxCapacity {
230 return nil, fmt.Errorf("invalid extracted PNG LSB payload length %d (capacity: %d)", payloadLen, maxCapacity)
231 }
232
233 payload := make([]byte, payloadLen)
234 totalBits := (4 + payloadLen) * 8
235 currBit := 0
236
237outerExtract:
238 for y := 0; y < height; y++ {
239 for x := 0; x < width; x++ {
240 c := rgba.RGBAAt(x, y)
241 for _, channelVal := range []uint8{c.R, c.G, c.B} {
242 if currBit < 32 {
243 currBit++
244 continue
245 }
246 if currBit >= totalBits {
247 break outerExtract
248 }
249 bit := channelVal & 0x01
250 targetBit := currBit - 32
251 payload[targetBit/8] |= (bit << (7 - (targetBit % 8)))
252 currBit++
253 }
254 }
255 }
256
257 return payload, nil
258}