● 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 / exfil / smb / smb_auth.go
← Back to directory
234 lines · 7.5 KB
Raw
1package smb
2
3import (
4 "crypto/hmac"
5 "crypto/md5"
6 "crypto/rand"
7 "encoding/binary"
8 "encoding/hex"
9 "fmt"
10 "strings"
11 "unicode/utf16"
12
13 "golang.org/x/crypto/md4"
14)
15
16func EncodeUTF16LE(s string) []byte {
17 runes := utf16.Encode([]rune(s))
18 b := make([]byte, len(runes)*2)
19 for i, r := range runes {
20 binary.LittleEndian.PutUint16(b[i*2:], r)
21 }
22 return b
23}
24
25func DecodeUTF16LE(b []byte) string {
26 if len(b)%2 != 0 {
27 b = b[:len(b)-1]
28 }
29 u16s := make([]uint16, len(b)/2)
30 for i := 0; i < len(u16s); i++ {
31 u16s[i] = binary.LittleEndian.Uint16(b[i*2:])
32 }
33 return string(utf16.Decode(u16s))
34}
35
36func ComputeNTHash(password string) []byte {
37 h := md4.New()
38 h.Write(EncodeUTF16LE(password))
39 return h.Sum(nil)
40}
41
42func ComputeNTLMv2Hash(username, password, domain string) []byte {
43 ntHash := ComputeNTHash(password)
44 mac := hmac.New(md5.New, ntHash)
45 mac.Write(EncodeUTF16LE(strings.ToUpper(username)))
46 mac.Write(EncodeUTF16LE(domain))
47 return mac.Sum(nil)
48}
49
50func BuildNTLMSSPType1() []byte {
51 b := make([]byte, 32)
52 copy(b[0:8], "NTLMSSP\x00")
53 binary.LittleEndian.PutUint32(b[8:12], 1) // Type 1 Negotiate
54 // Negotiate flags: Unicode, OEM, Request Target, NTLM, Extended Security, Negotiate 128
55 flags := uint32(0x00000001 | 0x00000002 | 0x00000004 | 0x00000200 | 0x00080000 | 0x20000000)
56 binary.LittleEndian.PutUint32(b[12:16], flags)
57 return b
58}
59
60func BuildNTLMSSPType2(serverChallenge [8]byte, targetName string) []byte {
61 targetBytes := EncodeUTF16LE(targetName)
62 b := make([]byte, 48+len(targetBytes))
63 copy(b[0:8], "NTLMSSP\x00")
64 binary.LittleEndian.PutUint32(b[8:12], 2) // Type 2 Challenge
65 binary.LittleEndian.PutUint16(b[12:14], uint16(len(targetBytes)))
66 binary.LittleEndian.PutUint16(b[14:16], uint16(len(targetBytes)))
67 binary.LittleEndian.PutUint32(b[16:20], 48)
68 // Flags
69 flags := uint32(0x00000001 | 0x00000004 | 0x00000200 | 0x00080000)
70 binary.LittleEndian.PutUint32(b[20:24], flags)
71 copy(b[24:32], serverChallenge[:])
72 copy(b[48:], targetBytes)
73 return b
74}
75
76func BuildNTLMSSPType3(username, domain, password string, serverChallenge [8]byte) []byte {
77 userBytes := EncodeUTF16LE(username)
78 domainBytes := EncodeUTF16LE(domain)
79
80 // Client challenge (8 bytes random)
81 var clientChallenge [8]byte
82 _, _ = rand.Read(clientChallenge[:])
83
84 // Time (8 bytes)
85 timeNow := timeToWindowsFileTime()
86
87 // Target info / AV pairs
88 avPairs := make([]byte, 0)
89 // MsvAvNbDomainName (type 2)
90 avPairs = append(avPairs, 2, 0)
91 avPairs = binary.LittleEndian.AppendUint16(avPairs, uint16(len(domainBytes)))
92 avPairs = append(avPairs, domainBytes...)
93 // MsvAvEOL (type 0)
94 avPairs = append(avPairs, 0, 0, 0, 0)
95
96 // NT Response / NetNTLMv2 blob
97 // Blob: 0x01010000, 0x00000000, timestamp(8), clientChallenge(8), 0x00000000, avPairs, 0x00000000
98 blob := make([]byte, 0, 32+len(avPairs))
99 blob = append(blob, 1, 1, 0, 0)
100 blob = append(blob, 0, 0, 0, 0)
101 blob = binary.LittleEndian.AppendUint64(blob, timeNow)
102 blob = append(blob, clientChallenge[:]...)
103 blob = append(blob, 0, 0, 0, 0)
104 blob = append(blob, avPairs...)
105 blob = append(blob, 0, 0, 0, 0)
106
107 // Compute HMAC-MD5 of serverChallenge + blob using NTLMv2Hash
108 ntlmv2Hash := ComputeNTLMv2Hash(username, password, domain)
109 mac := hmac.New(md5.New, ntlmv2Hash)
110 mac.Write(serverChallenge[:])
111 mac.Write(blob)
112 ntProof := mac.Sum(nil)
113
114 ntResponse := append(ntProof, blob...)
115 lmResponse := make([]byte, 24) // Empty/zeroed LM response
116
117 hdrLen := uint32(64)
118 domainOffset := hdrLen
119 userOffset := domainOffset + uint32(len(domainBytes))
120 lmOffset := userOffset + uint32(len(userBytes))
121 ntOffset := lmOffset + uint32(len(lmResponse))
122 totalLen := ntOffset + uint32(len(ntResponse))
123
124 b := make([]byte, totalLen)
125 copy(b[0:8], "NTLMSSP\x00")
126 binary.LittleEndian.PutUint32(b[8:12], 3) // Type 3 Authenticate
127
128 // LM Response fields
129 binary.LittleEndian.PutUint16(b[12:14], uint16(len(lmResponse)))
130 binary.LittleEndian.PutUint16(b[14:16], uint16(len(lmResponse)))
131 binary.LittleEndian.PutUint32(b[16:20], lmOffset)
132
133 // NT Response fields
134 binary.LittleEndian.PutUint16(b[20:22], uint16(len(ntResponse)))
135 binary.LittleEndian.PutUint16(b[22:24], uint16(len(ntResponse)))
136 binary.LittleEndian.PutUint32(b[24:28], ntOffset)
137
138 // Domain fields
139 binary.LittleEndian.PutUint16(b[28:30], uint16(len(domainBytes)))
140 binary.LittleEndian.PutUint16(b[30:32], uint16(len(domainBytes)))
141 binary.LittleEndian.PutUint32(b[32:36], domainOffset)
142
143 // User fields
144 binary.LittleEndian.PutUint16(b[36:38], uint16(len(userBytes)))
145 binary.LittleEndian.PutUint16(b[38:40], uint16(len(userBytes)))
146 binary.LittleEndian.PutUint32(b[40:44], userOffset)
147
148 // Session key (empty)
149 binary.LittleEndian.PutUint16(b[52:54], 0)
150 binary.LittleEndian.PutUint16(b[54:56], 0)
151 binary.LittleEndian.PutUint32(b[56:60], totalLen)
152
153 // Flags
154 flags := uint32(0x00000001 | 0x00000004 | 0x00000200 | 0x00080000)
155 binary.LittleEndian.PutUint32(b[60:64], flags)
156
157 copy(b[domainOffset:], domainBytes)
158 copy(b[userOffset:], userBytes)
159 copy(b[lmOffset:], lmResponse)
160 copy(b[ntOffset:], ntResponse)
161
162 return b
163}
164
165func timeToWindowsFileTime() uint64 {
166 // Windows file time is 100ns intervals since Jan 1, 1601 UTC
167 // Standard constant: 116444736000000000 100-ns intervals between 1601 and 1970
168 const intervalsTo1970 = 116444736000000000
169 return intervalsTo1970 + 10000000
170}
171
172func ParseNTLMSSPType2Challenge(data []byte) ([8]byte, string, error) {
173 var challenge [8]byte
174 if len(data) < 32 || string(data[:8]) != "NTLMSSP\x00" {
175 return challenge, "", fmt.Errorf("invalid NTLMSSP Type 2")
176 }
177 msgType := binary.LittleEndian.Uint32(data[8:12])
178 if msgType != 2 {
179 return challenge, "", fmt.Errorf("expected NTLMSSP Type 2, got %d", msgType)
180 }
181 targetLen := binary.LittleEndian.Uint16(data[12:14])
182 targetOffset := binary.LittleEndian.Uint32(data[16:20])
183 copy(challenge[:], data[24:32])
184
185 targetName := ""
186 if targetLen > 0 && int(targetOffset+uint32(targetLen)) <= len(data) {
187 targetName = DecodeUTF16LE(data[targetOffset : targetOffset+uint32(targetLen)])
188 }
189 return challenge, targetName, nil
190}
191
192func ParseNTLMSSPType3(data []byte, serverChallenge [8]byte) (string, string, string, string, error) {
193 if len(data) < 64 || string(data[:8]) != "NTLMSSP\x00" {
194 return "", "", "", "", fmt.Errorf("invalid NTLMSSP Type 3")
195 }
196 msgType := binary.LittleEndian.Uint32(data[8:12])
197 if msgType != 3 {
198 return "", "", "", "", fmt.Errorf("expected NTLMSSP Type 3, got %d", msgType)
199 }
200
201 ntLen := binary.LittleEndian.Uint16(data[20:22])
202 ntOffset := binary.LittleEndian.Uint32(data[24:28])
203
204 domainLen := binary.LittleEndian.Uint16(data[28:30])
205 domainOffset := binary.LittleEndian.Uint32(data[32:36])
206
207 userLen := binary.LittleEndian.Uint16(data[36:38])
208 userOffset := binary.LittleEndian.Uint32(data[40:44])
209
210 if int(userOffset+uint32(userLen)) > len(data) || int(ntOffset+uint32(ntLen)) > len(data) {
211 return "", "", "", "", fmt.Errorf("out of bounds offset in Type 3")
212 }
213
214 user := DecodeUTF16LE(data[userOffset : userOffset+uint32(userLen)])
215 domain := ""
216 if domainLen > 0 && int(domainOffset+uint32(domainLen)) <= len(data) {
217 domain = DecodeUTF16LE(data[domainOffset : domainOffset+uint32(domainLen)])
218 }
219
220 ntResponse := data[ntOffset : ntOffset+uint32(ntLen)]
221 if len(ntResponse) < 16 {
222 return "", "", "", "", fmt.Errorf("NT response too short: %d", len(ntResponse))
223 }
224
225 ntProof := ntResponse[:16]
226 blob := ntResponse[16:]
227
228 challengeHex := hex.EncodeToString(serverChallenge[:])
229 proofHex := hex.EncodeToString(ntProof)
230 blobHex := hex.EncodeToString(blob)
231
232 hashcatFormat := fmt.Sprintf("%s::%s:%s:%s:%s", user, domain, challengeHex, proofHex, blobHex)
233 return user, domain, hashcatFormat, challengeHex, nil
234}