[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)
180 lines · 4.9 KB
| 1 | package traffic |
| 2 | |
| 3 | import ( |
| 4 | "context" |
| 5 | "crypto/rand" |
| 6 | "encoding/binary" |
| 7 | "fmt" |
| 8 | "math" |
| 9 | "sync" |
| 10 | "time" |
| 11 | |
| 12 | "github.com/shehzade/contraband/pkg/common" |
| 13 | ) |
| 14 | |
| 15 | // DistributionType identifies probability distribution for jitter calculations. |
| 16 | type DistributionType string |
| 17 | |
| 18 | const ( |
| 19 | DistributionUniform DistributionType = "uniform" |
| 20 | DistributionGaussian DistributionType = "gaussian" |
| 21 | ) |
| 22 | |
| 23 | // CadenceEngine generates delays and schedules pacing between chunks, probes, or sweeps. |
| 24 | type CadenceEngine struct { |
| 25 | mu sync.Mutex |
| 26 | profile string // burst, steady, jittered, office-hours, low-and-slow |
| 27 | intervalMin time.Duration // Minimum interval boundary |
| 28 | intervalMax time.Duration // Maximum interval boundary |
| 29 | jitterFactor float64 // 0.0 to 1.0 (or percentage 0 to 100) |
| 30 | distribution DistributionType // uniform or gaussian |
| 31 | packetGap time.Duration // Microsecond packet gap |
| 32 | |
| 33 | // Office-hours state machine |
| 34 | burstCounter int // Current consecutive chunk count |
| 35 | burstThreshold int // Chunks before next idle pause (e.g. 5–15) |
| 36 | inOfficeIdle bool |
| 37 | } |
| 38 | |
| 39 | // NewCadenceEngine initializes a CadenceEngine with given parameters. |
| 40 | func NewCadenceEngine(profile string, minDur, maxDur time.Duration, jitter float64, dist DistributionType) (*CadenceEngine, error) { |
| 41 | if profile == "" { |
| 42 | profile = common.CadenceSteady |
| 43 | } |
| 44 | |
| 45 | // Normalize jitter factor |
| 46 | if jitter > 1.0 { |
| 47 | jitter = jitter / 100.0 |
| 48 | } |
| 49 | if jitter < 0.0 { |
| 50 | jitter = 0.0 |
| 51 | } else if jitter > 1.0 { |
| 52 | jitter = 1.0 |
| 53 | } |
| 54 | |
| 55 | if minDur < 0 { |
| 56 | minDur = 0 |
| 57 | } |
| 58 | if maxDur < 0 { |
| 59 | maxDur = 0 |
| 60 | } |
| 61 | if maxDur > 0 && minDur > maxDur { |
| 62 | return nil, fmt.Errorf("interval_min (%v) cannot be greater than interval_max (%v)", minDur, maxDur) |
| 63 | } |
| 64 | |
| 65 | if dist == "" { |
| 66 | dist = DistributionGaussian |
| 67 | } |
| 68 | |
| 69 | return &CadenceEngine{ |
| 70 | profile: profile, |
| 71 | intervalMin: minDur, |
| 72 | intervalMax: maxDur, |
| 73 | jitterFactor: jitter, |
| 74 | distribution: dist, |
| 75 | burstThreshold: 8, |
| 76 | }, nil |
| 77 | } |
| 78 | |
| 79 | // NextDelay calculates the next pacing delay according to the configured cadence profile. |
| 80 | func (c *CadenceEngine) NextDelay() time.Duration { |
| 81 | c.mu.Lock() |
| 82 | defer c.mu.Unlock() |
| 83 | |
| 84 | switch c.profile { |
| 85 | case common.CadenceBurst: |
| 86 | return 0 |
| 87 | |
| 88 | case common.CadenceSteady: |
| 89 | return c.intervalMin |
| 90 | |
| 91 | case common.CadenceJittered: |
| 92 | return c.sampleInterval(c.intervalMin, c.intervalMax) |
| 93 | |
| 94 | case common.CadenceOfficeHours: |
| 95 | c.burstCounter++ |
| 96 | if c.burstCounter >= c.burstThreshold { |
| 97 | // Trigger realistic user idle think time (10s to 60s) |
| 98 | c.burstCounter = 0 |
| 99 | c.burstThreshold = 5 + int(c.randFloat64()*10.0) // 5 to 14 chunks |
| 100 | idleMin := 10 * time.Second |
| 101 | idleMax := 60 * time.Second |
| 102 | return c.sampleInterval(idleMin, idleMax) |
| 103 | } |
| 104 | // Active work interval: short inter-packet delay (50ms to 200ms) |
| 105 | return c.sampleInterval(50*time.Millisecond, 200*time.Millisecond) |
| 106 | |
| 107 | case common.CadenceLowAndSlow: |
| 108 | // Enforce long drip delays (e.g. 30s to 120s if not specified) |
| 109 | minD := c.intervalMin |
| 110 | maxD := c.intervalMax |
| 111 | if minD == 0 && maxD == 0 { |
| 112 | minD = 30 * time.Second |
| 113 | maxD = 120 * time.Second |
| 114 | } |
| 115 | return c.sampleInterval(minD, maxD) |
| 116 | |
| 117 | default: |
| 118 | return c.intervalMin |
| 119 | } |
| 120 | } |
| 121 | |
| 122 | // sampleInterval calculates a random delay between min and max using configured distribution. |
| 123 | func (c *CadenceEngine) sampleInterval(minD, maxD time.Duration) time.Duration { |
| 124 | if minD == maxD || maxD == 0 { |
| 125 | if c.jitterFactor > 0 { |
| 126 | // Apply jitter factor around base interval: [base*(1-J), base*(1+J)] |
| 127 | base := float64(minD) |
| 128 | halfRange := base * c.jitterFactor |
| 129 | low := math.Max(0, base-halfRange) |
| 130 | high := base + halfRange |
| 131 | return time.Duration(low + c.randFloat64()*(high-low)) |
| 132 | } |
| 133 | return minD |
| 134 | } |
| 135 | |
| 136 | if c.distribution == DistributionUniform { |
| 137 | delta := float64(maxD - minD) |
| 138 | return minD + time.Duration(c.randFloat64()*delta) |
| 139 | } |
| 140 | |
| 141 | // Gaussian distribution: Box-Muller transform |
| 142 | // Midpoint mu, standard deviation sigma = (max - min) / 6 |
| 143 | mu := float64(minD+maxD) / 2.0 |
| 144 | sigma := float64(maxD-minD) / 6.0 |
| 145 | |
| 146 | // Box-Muller transform |
| 147 | u1 := c.randFloat64() |
| 148 | if u1 <= 0.0 { |
| 149 | u1 = 1e-9 |
| 150 | } |
| 151 | u2 := c.randFloat64() |
| 152 | z0 := math.Sqrt(-2.0*math.Log(u1)) * math.Cos(2.0*math.Pi*u2) |
| 153 | |
| 154 | sampled := mu + z0*sigma |
| 155 | // Clamp to [minD, maxD] |
| 156 | clamped := math.Max(float64(minD), math.Min(float64(maxD), sampled)) |
| 157 | return time.Duration(clamped) |
| 158 | } |
| 159 | |
| 160 | // randFloat64 generates a cryptographically secure pseudo-random float in [0.0, 1.0). |
| 161 | func (c *CadenceEngine) randFloat64() float64 { |
| 162 | var buf [8]byte |
| 163 | if _, err := rand.Read(buf[:]); err != nil { |
| 164 | // Fallback to nanosecond epoch |
| 165 | return float64(time.Now().UnixNano()%1000000) / 1000000.0 |
| 166 | } |
| 167 | val := binary.BigEndian.Uint64(buf[:]) |
| 168 | return float64(val>>11) / float64(1<<53) |
| 169 | } |
| 170 | |
| 171 | // Sleep calculates NextDelay and blocks with context cancellation. |
| 172 | func (c *CadenceEngine) Sleep(ctx context.Context) error { |
| 173 | d := c.NextDelay() |
| 174 | return sleepContext(ctx, d) |
| 175 | } |
| 176 | |
| 177 | // SleepDuration executes context-aware sleep for an explicit duration. |
| 178 | func (c *CadenceEngine) SleepDuration(ctx context.Context, d time.Duration) error { |
| 179 | return sleepContext(ctx, d) |
| 180 | } |