package ntlmssp import ( "bytes" "crypto/rand" "encoding/base64" "encoding/binary" "errors" "time" ) type authenicateMessage struct { LmChallengeResponse []byte NtChallengeResponse []byte TargetName string UserName string // only set if negotiateFlag_NTLMSSP_NEGOTIATE_KEY_EXCH EncryptedRandomSessionKey []byte NegotiateFlags negotiateFlags MIC []byte } type authenticateMessageFields struct { messageHeader LmChallengeResponse varField NtChallengeResponse varField TargetName varField UserName varField Workstation varField _ [8]byte NegotiateFlags negotiateFlags } func (m authenicateMessage) MarshalBinary() ([]byte, error) { if !m.NegotiateFlags.Has(negotiateFlagNTLMSSPNEGOTIATEUNICODE) { return nil, errors.New("Only unicode is supported") } target, user := toUnicode(m.TargetName), toUnicode(m.UserName) workstation := toUnicode("go-ntlmssp") ptr := binary.Size(&authenticateMessageFields{}) f := authenticateMessageFields{ messageHeader: newMessageHeader(3), NegotiateFlags: m.NegotiateFlags, LmChallengeResponse: newVarField(&ptr, len(m.LmChallengeResponse)), NtChallengeResponse: newVarField(&ptr, len(m.NtChallengeResponse)), TargetName: newVarField(&ptr, len(target)), UserName: newVarField(&ptr, len(user)), Workstation: newVarField(&ptr, len(workstation)), } f.NegotiateFlags.Unset(negotiateFlagNTLMSSPNEGOTIATEVERSION) b := bytes.Buffer{} if err := binary.Write(&b, binary.LittleEndian, &f); err != nil { return nil, err } if err := binary.Write(&b, binary.LittleEndian, &m.LmChallengeResponse); err != nil { return nil, err } if err := binary.Write(&b, binary.LittleEndian, &m.NtChallengeResponse); err != nil { return nil, err } if err := binary.Write(&b, binary.LittleEndian, &target); err != nil { return nil, err } if err := binary.Write(&b, binary.LittleEndian, &user); err != nil { return nil, err } if err := binary.Write(&b, binary.LittleEndian, &workstation); err != nil { return nil, err } return b.Bytes(), nil } // ProcessChallenge crafts an AUTHENTICATE message in response to the CHALLENGE message // that was received from the server func ProcessChallenge(challengeMessageData []byte, user, password string) ([]byte, error) { if user == "" && password == "" { PrintDebug("User %s, Pass Length %d", user, len(password)) return nil, errors.New("Anonymous authentication not supported") } // debugging PrintDebug("Received NTLM Type 2 Challenge: %s", base64.StdEncoding.EncodeToString(challengeMessageData)) DecodeNTLMMessage(challengeMessageData) var cm challengeMessage if err := cm.UnmarshalBinary(challengeMessageData); err != nil { PrintDebug("Failed to unmarshal challenge data") return nil, err } if cm.NegotiateFlags.Has(negotiateFlagNTLMSSPNEGOTIATELMKEY) { PrintDebug("server requested NTLM v1") return nil, errors.New("Only NTLM v2 is supported, but server requested v1 (NTLMSSP_NEGOTIATE_LM_KEY)") } if cm.NegotiateFlags.Has(negotiateFlagNTLMSSPNEGOTIATEKEYEXCH) { PrintDebug("Key exchange requested but not supported") return nil, errors.New("Key exchange requested but not supported (NTLMSSP_NEGOTIATE_KEY_EXCH)") } am := authenicateMessage{ UserName: user, TargetName: cm.TargetName, NegotiateFlags: cm.NegotiateFlags, } timestamp := cm.TargetInfo[avIDMsvAvTimestamp] if timestamp == nil { // no time sent, take current time ft := uint64(time.Now().UnixNano()) / 100 ft += 116444736000000000 // add time between unix & windows offset timestamp = make([]byte, 8) binary.LittleEndian.PutUint64(timestamp, ft) } clientChallenge := make([]byte, 8) rand.Reader.Read(clientChallenge) ntlmV2Hash := getNtlmV2Hash(password, user, cm.TargetName) PrintDebug("NTLM V2 hash '%s'", base64.StdEncoding.EncodeToString(ntlmV2Hash)) am.NtChallengeResponse = computeNtlmV2Response(ntlmV2Hash, cm.ServerChallenge[:], clientChallenge, timestamp, cm.TargetInfoRaw) if cm.TargetInfoRaw == nil { am.LmChallengeResponse = computeLmV2Response(ntlmV2Hash, cm.ServerChallenge[:], clientChallenge) } PrintDebug("Challenge response: NT %s; LM %s", am.NtChallengeResponse, am.LmChallengeResponse) return am.MarshalBinary() }