● 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 / proxy / auth_digest.go
← Back to directory
138 lines · 3.3 KB
Raw
1package proxy
2
3import (
4 "crypto/md5"
5 "crypto/rand"
6 "encoding/hex"
7 "fmt"
8 "strings"
9)
10
11// DigestChallenge represents parsed fields from Proxy-Authenticate: Digest ...
12type DigestChallenge struct {
13 Realm string
14 Nonce string
15 QOP string
16 Algorithm string
17 Opaque string
18}
19
20// ParseDigestChallenge extracts key-value directives from a Digest challenge string.
21func ParseDigestChallenge(header string) DigestChallenge {
22 c := DigestChallenge{
23 Algorithm: "MD5",
24 }
25 header = strings.TrimSpace(header)
26 if strings.HasPrefix(strings.ToLower(header), "digest ") {
27 header = strings.TrimSpace(header[7:])
28 }
29
30 parts := splitDirectives(header)
31 for k, v := range parts {
32 switch strings.ToLower(k) {
33 case "realm":
34 c.Realm = v
35 case "nonce":
36 c.Nonce = v
37 case "qop":
38 c.QOP = v
39 case "algorithm":
40 c.Algorithm = v
41 case "opaque":
42 c.Opaque = v
43 }
44 }
45 return c
46}
47
48func splitDirectives(s string) map[string]string {
49 res := make(map[string]string)
50 for len(s) > 0 {
51 s = strings.TrimSpace(s)
52 commaIdx := strings.Index(s, ",")
53 equalIdx := strings.Index(s, "=")
54 if equalIdx == -1 {
55 break
56 }
57
58 key := strings.TrimSpace(s[:equalIdx])
59 rest := strings.TrimSpace(s[equalIdx+1:])
60
61 var val string
62 if strings.HasPrefix(rest, "\"") {
63 // Quoted string
64 quoteEnd := strings.Index(rest[1:], "\"")
65 if quoteEnd != -1 {
66 val = rest[1 : quoteEnd+1]
67 after := rest[quoteEnd+2:]
68 if nextComma := strings.Index(after, ","); nextComma != -1 {
69 s = after[nextComma+1:]
70 } else {
71 s = ""
72 }
73 } else {
74 val = strings.Trim(rest, "\"")
75 s = ""
76 }
77 } else {
78 // Unquoted token
79 if commaIdx != -1 && commaIdx > equalIdx {
80 val = strings.TrimSpace(rest[:commaIdx-equalIdx-1])
81 s = rest[commaIdx-equalIdx:]
82 if len(s) > 0 && s[0] == ',' {
83 s = s[1:]
84 }
85 } else {
86 val = rest
87 s = ""
88 }
89 }
90 res[key] = val
91 }
92 return res
93}
94
95func md5Hex(data string) string {
96 h := md5.Sum([]byte(data))
97 return hex.EncodeToString(h[:])
98}
99
100// BuildDigestAuthHeader computes the RFC 7616 Digest response for HTTP CONNECT.
101func BuildDigestAuthHeader(username, password, targetAddr string, ch DigestChallenge, nc int) string {
102 ncStr := fmt.Sprintf("%08x", nc)
103 cnonceBytes := make([]byte, 8)
104 _, _ = rand.Read(cnonceBytes)
105 cnonce := hex.EncodeToString(cnonceBytes)
106
107 ha1 := md5Hex(fmt.Sprintf("%s:%s:%s", username, ch.Realm, password))
108 ha2 := md5Hex(fmt.Sprintf("CONNECT:%s", targetAddr))
109
110 var response string
111 hasAuthQOP := strings.Contains(strings.ToLower(ch.QOP), "auth")
112
113 if hasAuthQOP {
114 response = md5Hex(fmt.Sprintf("%s:%s:%s:%s:auth:%s", ha1, ch.Nonce, ncStr, cnonce, ha2))
115 } else {
116 response = md5Hex(fmt.Sprintf("%s:%s:%s", ha1, ch.Nonce, ha2))
117 }
118
119 var sb strings.Builder
120 sb.WriteString("Digest ")
121 sb.WriteString(fmt.Sprintf("username=\"%s\", ", username))
122 sb.WriteString(fmt.Sprintf("realm=\"%s\", ", ch.Realm))
123 sb.WriteString(fmt.Sprintf("nonce=\"%s\", ", ch.Nonce))
124 sb.WriteString(fmt.Sprintf("uri=\"%s\", ", targetAddr))
125 sb.WriteString(fmt.Sprintf("response=\"%s\"", response))
126
127 if hasAuthQOP {
128 sb.WriteString(fmt.Sprintf(", qop=auth, nc=%s, cnonce=\"%s\"", ncStr, cnonce))
129 }
130 if ch.Opaque != "" {
131 sb.WriteString(fmt.Sprintf(", opaque=\"%s\"", ch.Opaque))
132 }
133 if ch.Algorithm != "" {
134 sb.WriteString(fmt.Sprintf(", algorithm=%s", ch.Algorithm))
135 }
136
137 return sb.String()
138}