[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)
234 lines · 7.5 KB
| 1 | package smb |
| 2 | |
| 3 | import ( |
| 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 | |
| 16 | func 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 | |
| 25 | func 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 | |
| 36 | func ComputeNTHash(password string) []byte { |
| 37 | h := md4.New() |
| 38 | h.Write(EncodeUTF16LE(password)) |
| 39 | return h.Sum(nil) |
| 40 | } |
| 41 | |
| 42 | func 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 | |
| 50 | func 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 | |
| 60 | func 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 | |
| 76 | func 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 | |
| 165 | func 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 | |
| 172 | func 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 | |
| 192 | func 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 | } |