init: 自 zomaintain/backend/rdplib 平移独立成库; module path 从上游 github.com/nakagami/grdp 改为 git.zeroonesoft.cn/golib/rdplib
This commit is contained in:
@@ -0,0 +1,530 @@
|
||||
package nla
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/md5"
|
||||
"crypto/rc4"
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"time"
|
||||
|
||||
"github.com/lunixbochs/struc"
|
||||
"git.zeroonesoft.cn/golib/rdplib/core"
|
||||
)
|
||||
|
||||
const (
|
||||
WINDOWS_MINOR_VERSION_0 = 0x00
|
||||
WINDOWS_MINOR_VERSION_1 = 0x01
|
||||
WINDOWS_MINOR_VERSION_2 = 0x02
|
||||
WINDOWS_MINOR_VERSION_3 = 0x03
|
||||
|
||||
WINDOWS_MAJOR_VERSION_5 = 0x05
|
||||
WINDOWS_MAJOR_VERSION_6 = 0x06
|
||||
NTLMSSP_REVISION_W2K3 = 0x0F
|
||||
)
|
||||
|
||||
const (
|
||||
MsvAvEOL = 0x0000
|
||||
MsvAvNbComputerName = 0x0001
|
||||
MsvAvNbDomainName = 0x0002
|
||||
MsvAvDnsComputerName = 0x0003
|
||||
MsvAvDnsDomainName = 0x0004
|
||||
MsvAvDnsTreeName = 0x0005
|
||||
MsvAvFlags = 0x0006
|
||||
MsvAvTimestamp = 0x0007
|
||||
MsvAvSingleHost = 0x0008
|
||||
MsvAvTargetName = 0x0009
|
||||
MsvChannelBindings = 0x000A
|
||||
)
|
||||
|
||||
type AVPair struct {
|
||||
Id uint16 `struc:"little"`
|
||||
Len uint16 `struc:"little,sizeof=Value"`
|
||||
Value []byte `struc:"little"`
|
||||
}
|
||||
|
||||
const (
|
||||
NTLMSSP_NEGOTIATE_56 = 0x80000000
|
||||
NTLMSSP_NEGOTIATE_KEY_EXCH = 0x40000000
|
||||
NTLMSSP_NEGOTIATE_128 = 0x20000000
|
||||
NTLMSSP_NEGOTIATE_VERSION = 0x02000000
|
||||
NTLMSSP_NEGOTIATE_TARGET_INFO = 0x00800000
|
||||
NTLMSSP_REQUEST_NON_NT_SESSION_KEY = 0x00400000
|
||||
NTLMSSP_NEGOTIATE_IDENTIFY = 0x00100000
|
||||
NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY = 0x00080000
|
||||
NTLMSSP_TARGET_TYPE_SERVER = 0x00020000
|
||||
NTLMSSP_TARGET_TYPE_DOMAIN = 0x00010000
|
||||
NTLMSSP_NEGOTIATE_ALWAYS_SIGN = 0x00008000
|
||||
NTLMSSP_NEGOTIATE_OEM_WORKSTATION_SUPPLIED = 0x00002000
|
||||
NTLMSSP_NEGOTIATE_OEM_DOMAIN_SUPPLIED = 0x00001000
|
||||
NTLMSSP_NEGOTIATE_NTLM = 0x00000200
|
||||
NTLMSSP_NEGOTIATE_LM_KEY = 0x00000080
|
||||
NTLMSSP_NEGOTIATE_DATAGRAM = 0x00000040
|
||||
NTLMSSP_NEGOTIATE_SEAL = 0x00000020
|
||||
NTLMSSP_NEGOTIATE_SIGN = 0x00000010
|
||||
NTLMSSP_REQUEST_TARGET = 0x00000004
|
||||
NTLM_NEGOTIATE_OEM = 0x00000002
|
||||
NTLMSSP_NEGOTIATE_UNICODE = 0x00000001
|
||||
)
|
||||
|
||||
type NVersion struct {
|
||||
ProductMajorVersion uint8 `struc:"little"`
|
||||
ProductMinorVersion uint8 `struc:"little"`
|
||||
ProductBuild uint16 `struc:"little"`
|
||||
Reserved [3]byte `struc:"little"`
|
||||
NTLMRevisionCurrent uint8 `struc:"little"`
|
||||
}
|
||||
|
||||
func NewNVersion() NVersion {
|
||||
return NVersion{
|
||||
ProductMajorVersion: WINDOWS_MAJOR_VERSION_6,
|
||||
ProductMinorVersion: WINDOWS_MINOR_VERSION_0,
|
||||
ProductBuild: 6002,
|
||||
NTLMRevisionCurrent: NTLMSSP_REVISION_W2K3,
|
||||
}
|
||||
}
|
||||
|
||||
type Message interface {
|
||||
Serialize() []byte
|
||||
}
|
||||
|
||||
type NegotiateMessage struct {
|
||||
Signature [8]byte `struc:"little"`
|
||||
MessageType uint32 `struc:"little"`
|
||||
NegotiateFlags uint32 `struc:"little"`
|
||||
DomainNameLen uint16 `struc:"little"`
|
||||
DomainNameMaxLen uint16 `struc:"little"`
|
||||
DomainNameBufferOffset uint32 `struc:"little"`
|
||||
WorkstationLen uint16 `struc:"little"`
|
||||
WorkstationMaxLen uint16 `struc:"little"`
|
||||
WorkstationBufferOffset uint32 `struc:"little"`
|
||||
Version NVersion `struc:"skip"`
|
||||
Payload [32]byte `struc:"skip"`
|
||||
}
|
||||
|
||||
func NewNegotiateMessage() *NegotiateMessage {
|
||||
return &NegotiateMessage{
|
||||
Signature: [8]byte{'N', 'T', 'L', 'M', 'S', 'S', 'P', 0x00},
|
||||
MessageType: 0x00000001,
|
||||
}
|
||||
}
|
||||
|
||||
func (m *NegotiateMessage) Serialize() []byte {
|
||||
if (m.NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION) != 0 {
|
||||
m.Version = NewNVersion()
|
||||
}
|
||||
buff := &bytes.Buffer{}
|
||||
struc.Pack(buff, m)
|
||||
|
||||
return buff.Bytes()
|
||||
}
|
||||
|
||||
type ChallengeMessage struct {
|
||||
totalLen int
|
||||
Signature []byte `struc:"[8]byte"`
|
||||
MessageType uint32 `struc:"little"`
|
||||
TargetNameLen uint16 `struc:"little"`
|
||||
TargetNameMaxLen uint16 `struc:"little"`
|
||||
TargetNameBufferOffset uint32 `struc:"little"`
|
||||
NegotiateFlags uint32 `struc:"little"`
|
||||
ServerChallenge [8]byte `struc:"little"`
|
||||
Reserved [8]byte `struc:"little"`
|
||||
TargetInfoLen uint16 `struc:"little"`
|
||||
TargetInfoMaxLen uint16 `struc:"little"`
|
||||
TargetInfoBufferOffset uint32 `struc:"little"`
|
||||
Version NVersion `struc:"skip"`
|
||||
Payload []byte `struc:"skip"`
|
||||
}
|
||||
|
||||
func (m *ChallengeMessage) Serialize() []byte {
|
||||
buff := &bytes.Buffer{}
|
||||
struc.Pack(buff, m)
|
||||
if (m.NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION) != 0 {
|
||||
struc.Pack(buff, m.Version)
|
||||
}
|
||||
buff.Write(m.Payload)
|
||||
return buff.Bytes()
|
||||
}
|
||||
|
||||
func NewChallengeMessage() *ChallengeMessage {
|
||||
return &ChallengeMessage{
|
||||
Signature: []byte{'N', 'T', 'L', 'M', 'S', 'S', 'P', 0x00},
|
||||
MessageType: 0x00000002,
|
||||
}
|
||||
}
|
||||
|
||||
// total len - payload len
|
||||
func (m *ChallengeMessage) BaseLen() uint32 {
|
||||
return uint32(m.totalLen - len(m.Payload))
|
||||
}
|
||||
|
||||
func (m *ChallengeMessage) getTargetInfo() []byte {
|
||||
if m.TargetInfoLen == 0 {
|
||||
return make([]byte, 0)
|
||||
}
|
||||
offset := m.BaseLen()
|
||||
start := m.TargetInfoBufferOffset - offset
|
||||
return m.Payload[start : start+uint32(m.TargetInfoLen)]
|
||||
}
|
||||
func (m *ChallengeMessage) getTargetName() []byte {
|
||||
if m.TargetNameLen == 0 {
|
||||
return make([]byte, 0)
|
||||
}
|
||||
offset := m.BaseLen()
|
||||
start := m.TargetNameBufferOffset - offset
|
||||
return m.Payload[start : start+uint32(m.TargetNameLen)]
|
||||
}
|
||||
func (m *ChallengeMessage) getTargetInfoTimestamp(data []byte) []byte {
|
||||
r := bytes.NewReader(data)
|
||||
for r.Len() > 0 {
|
||||
avPair := &AVPair{}
|
||||
struc.Unpack(r, avPair)
|
||||
if avPair.Id == MsvAvTimestamp {
|
||||
return avPair.Value
|
||||
}
|
||||
|
||||
if avPair.Id == MsvAvEOL {
|
||||
break
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type AuthenticateMessage struct {
|
||||
Signature [8]byte
|
||||
MessageType uint32 `struc:"little"`
|
||||
LmChallengeResponseLen uint16 `struc:"little"`
|
||||
LmChallengeResponseMaxLen uint16 `struc:"little"`
|
||||
LmChallengeResponseBufferOffset uint32 `struc:"little"`
|
||||
NtChallengeResponseLen uint16 `struc:"little"`
|
||||
NtChallengeResponseMaxLen uint16 `struc:"little"`
|
||||
NtChallengeResponseBufferOffset uint32 `struc:"little"`
|
||||
DomainNameLen uint16 `struc:"little"`
|
||||
DomainNameMaxLen uint16 `struc:"little"`
|
||||
DomainNameBufferOffset uint32 `struc:"little"`
|
||||
UserNameLen uint16 `struc:"little"`
|
||||
UserNameMaxLen uint16 `struc:"little"`
|
||||
UserNameBufferOffset uint32 `struc:"little"`
|
||||
WorkstationLen uint16 `struc:"little"`
|
||||
WorkstationMaxLen uint16 `struc:"little"`
|
||||
WorkstationBufferOffset uint32 `struc:"little"`
|
||||
EncryptedRandomSessionLen uint16 `struc:"little"`
|
||||
EncryptedRandomSessionMaxLen uint16 `struc:"little"`
|
||||
EncryptedRandomSessionBufferOffset uint32 `struc:"little"`
|
||||
NegotiateFlags uint32 `struc:"little"`
|
||||
Version NVersion `struc:"little"`
|
||||
MIC [16]byte `struc:"little"`
|
||||
Payload []byte `struc:"skip"`
|
||||
}
|
||||
|
||||
func (m *AuthenticateMessage) BaseLen() uint32 {
|
||||
return 88
|
||||
}
|
||||
|
||||
func NewAuthenticateMessage(negFlag uint32, domain, user, workstation []byte,
|
||||
lmchallResp, ntchallResp, enRandomSessKey []byte) *AuthenticateMessage {
|
||||
msg := &AuthenticateMessage{
|
||||
Signature: [8]byte{'N', 'T', 'L', 'M', 'S', 'S', 'P', 0x00},
|
||||
MessageType: 0x00000003,
|
||||
NegotiateFlags: negFlag,
|
||||
}
|
||||
payload := make([]byte, 0, len(lmchallResp)+len(ntchallResp)+len(domain)+len(user)+len(workstation)+len(enRandomSessKey))
|
||||
|
||||
msg.LmChallengeResponseLen = uint16(len(lmchallResp))
|
||||
msg.LmChallengeResponseMaxLen = msg.LmChallengeResponseLen
|
||||
msg.LmChallengeResponseBufferOffset = msg.BaseLen()
|
||||
payload = append(payload, lmchallResp...)
|
||||
|
||||
msg.NtChallengeResponseLen = uint16(len(ntchallResp))
|
||||
msg.NtChallengeResponseMaxLen = msg.NtChallengeResponseLen
|
||||
msg.NtChallengeResponseBufferOffset = msg.LmChallengeResponseBufferOffset + uint32(msg.LmChallengeResponseLen)
|
||||
payload = append(payload, ntchallResp...)
|
||||
|
||||
msg.DomainNameLen = uint16(len(domain))
|
||||
msg.DomainNameMaxLen = msg.DomainNameLen
|
||||
msg.DomainNameBufferOffset = msg.NtChallengeResponseBufferOffset + uint32(msg.NtChallengeResponseLen)
|
||||
payload = append(payload, domain...)
|
||||
|
||||
msg.UserNameLen = uint16(len(user))
|
||||
msg.UserNameMaxLen = msg.UserNameLen
|
||||
msg.UserNameBufferOffset = msg.DomainNameBufferOffset + uint32(msg.DomainNameLen)
|
||||
payload = append(payload, user...)
|
||||
|
||||
msg.WorkstationLen = uint16(len(workstation))
|
||||
msg.WorkstationMaxLen = msg.WorkstationLen
|
||||
msg.WorkstationBufferOffset = msg.UserNameBufferOffset + uint32(msg.UserNameLen)
|
||||
payload = append(payload, workstation...)
|
||||
|
||||
msg.EncryptedRandomSessionLen = uint16(len(enRandomSessKey))
|
||||
msg.EncryptedRandomSessionMaxLen = msg.EncryptedRandomSessionLen
|
||||
msg.EncryptedRandomSessionBufferOffset = msg.WorkstationBufferOffset + uint32(msg.WorkstationLen)
|
||||
payload = append(payload, enRandomSessKey...)
|
||||
|
||||
if (msg.NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION) != 0 {
|
||||
msg.Version = NewNVersion()
|
||||
}
|
||||
msg.Payload = payload
|
||||
|
||||
return msg
|
||||
}
|
||||
|
||||
func (m *AuthenticateMessage) Serialize() []byte {
|
||||
buff := &bytes.Buffer{}
|
||||
struc.Pack(buff, m)
|
||||
buff.Write(m.Payload)
|
||||
return buff.Bytes()
|
||||
}
|
||||
|
||||
type NTLMv2 struct {
|
||||
domain string
|
||||
user string
|
||||
password string
|
||||
respKeyNT []byte
|
||||
respKeyLM []byte
|
||||
negotiateMessage *NegotiateMessage
|
||||
challengeMessage *ChallengeMessage
|
||||
authenticateMessage *AuthenticateMessage
|
||||
enableUnicode bool
|
||||
}
|
||||
|
||||
func NewNTLMv2(domain, user, password string) *NTLMv2 {
|
||||
return &NTLMv2{
|
||||
domain: domain,
|
||||
user: user,
|
||||
password: password,
|
||||
respKeyNT: NTOWFv2(password, user, domain),
|
||||
respKeyLM: LMOWFv2(password, user, domain),
|
||||
}
|
||||
}
|
||||
|
||||
// generate first handshake messgae
|
||||
func (n *NTLMv2) GetNegotiateMessage() *NegotiateMessage {
|
||||
negoMsg := NewNegotiateMessage()
|
||||
negoMsg.NegotiateFlags = NTLMSSP_NEGOTIATE_KEY_EXCH |
|
||||
NTLMSSP_NEGOTIATE_128 |
|
||||
NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY |
|
||||
NTLMSSP_NEGOTIATE_ALWAYS_SIGN |
|
||||
NTLMSSP_NEGOTIATE_NTLM |
|
||||
NTLMSSP_NEGOTIATE_SEAL |
|
||||
NTLMSSP_NEGOTIATE_SIGN |
|
||||
NTLMSSP_REQUEST_TARGET |
|
||||
NTLMSSP_NEGOTIATE_UNICODE
|
||||
n.negotiateMessage = negoMsg
|
||||
return n.negotiateMessage
|
||||
}
|
||||
|
||||
// process NTLMv2 Authenticate hash
|
||||
func (n *NTLMv2) ComputeResponseV2(respKeyNT, respKeyLM, serverChallenge, clientChallenge,
|
||||
timestamp, serverInfo []byte) (ntChallResp, lmChallResp, SessBaseKey []byte) {
|
||||
|
||||
// Build the temp blob: 2+6+8+8+4+len(serverInfo) bytes
|
||||
temp := make([]byte, 0, 28+len(serverInfo))
|
||||
temp = append(temp, 0x01, 0x01) // Responser version, HiResponser version
|
||||
temp = append(temp, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)
|
||||
temp = append(temp, timestamp...)
|
||||
temp = append(temp, clientChallenge...)
|
||||
temp = append(temp, 0x00, 0x00, 0x00, 0x00)
|
||||
temp = append(temp, serverInfo...)
|
||||
|
||||
ntInput := make([]byte, 0, len(serverChallenge)+len(temp))
|
||||
ntInput = append(ntInput, serverChallenge...)
|
||||
ntInput = append(ntInput, temp...)
|
||||
ntProof := HMAC_MD5(respKeyNT, ntInput)
|
||||
|
||||
ntChallResp = make([]byte, 0, len(ntProof)+len(temp))
|
||||
ntChallResp = append(ntChallResp, ntProof...)
|
||||
ntChallResp = append(ntChallResp, temp...)
|
||||
|
||||
lmInput := make([]byte, 0, len(serverChallenge)+len(clientChallenge))
|
||||
lmInput = append(lmInput, serverChallenge...)
|
||||
lmInput = append(lmInput, clientChallenge...)
|
||||
lmChallResp = HMAC_MD5(respKeyLM, lmInput)
|
||||
lmChallResp = append(lmChallResp, clientChallenge...)
|
||||
|
||||
SessBaseKey = HMAC_MD5(respKeyNT, ntProof)
|
||||
return
|
||||
}
|
||||
|
||||
func MIC(exportedSessionKey []byte, negotiateMessage, challengeMessage, authenticateMessage Message) []byte {
|
||||
neg := negotiateMessage.Serialize()
|
||||
chal := challengeMessage.Serialize()
|
||||
auth := authenticateMessage.Serialize()
|
||||
data := make([]byte, 0, len(neg)+len(chal)+len(auth))
|
||||
data = append(data, neg...)
|
||||
data = append(data, chal...)
|
||||
data = append(data, auth...)
|
||||
return HMAC_MD5(exportedSessionKey, data)
|
||||
}
|
||||
|
||||
func concat(bs ...[]byte) []byte {
|
||||
return bytes.Join(bs, nil)
|
||||
}
|
||||
|
||||
var (
|
||||
clientSigning = concat([]byte("session key to client-to-server signing key magic constant"), []byte{0x00})
|
||||
serverSigning = concat([]byte("session key to server-to-client signing key magic constant"), []byte{0x00})
|
||||
clientSealing = concat([]byte("session key to client-to-server sealing key magic constant"), []byte{0x00})
|
||||
serverSealing = concat([]byte("session key to server-to-client sealing key magic constant"), []byte{0x00})
|
||||
)
|
||||
|
||||
func (n *NTLMv2) GetAuthenticateMessage(s []byte) (*AuthenticateMessage, *NTLMv2Security) {
|
||||
slog.Debug("GetAuthenticateMessage", "s", s)
|
||||
|
||||
challengeMsg := &ChallengeMessage{totalLen: len(s)}
|
||||
r := bytes.NewReader(s)
|
||||
err := struc.Unpack(r, challengeMsg)
|
||||
if err != nil {
|
||||
slog.Error("GetAuthenticateMessage", "err", err)
|
||||
return nil, nil
|
||||
}
|
||||
if challengeMsg.NegotiateFlags&NTLMSSP_NEGOTIATE_VERSION != 0 {
|
||||
version := NVersion{}
|
||||
err := struc.Unpack(r, &version)
|
||||
if err != nil {
|
||||
slog.Error("GetAuthenticateMessage", "err", err)
|
||||
return nil, nil
|
||||
}
|
||||
challengeMsg.Version = version
|
||||
}
|
||||
challengeMsg.Payload, _ = core.ReadBytes(r.Len(), r)
|
||||
n.challengeMessage = challengeMsg
|
||||
slog.Debug("GetAuthenticateMessage", "challengeMsg", challengeMsg)
|
||||
|
||||
serverName := challengeMsg.getTargetName()
|
||||
serverInfo := challengeMsg.getTargetInfo()
|
||||
timestamp := challengeMsg.getTargetInfoTimestamp(serverInfo)
|
||||
computeMIC := false
|
||||
if timestamp == nil {
|
||||
ft := uint64(time.Now().UnixNano()) / 100
|
||||
ft += 116444736000000000 // add time between unix & windows offset
|
||||
timestamp = make([]byte, 8)
|
||||
binary.LittleEndian.PutUint64(timestamp, ft)
|
||||
} else {
|
||||
computeMIC = true
|
||||
}
|
||||
slog.Debug("GetAuthenticateMessage", "serverName", core.UnicodeDecode(serverName))
|
||||
serverChallenge := challengeMsg.ServerChallenge[:]
|
||||
clientChallenge := core.Random(8)
|
||||
ntChallengeResponse, lmChallengeResponse, SessionBaseKey := n.ComputeResponseV2(
|
||||
n.respKeyNT, n.respKeyLM, serverChallenge, clientChallenge, timestamp, serverInfo)
|
||||
|
||||
exchangeKey := SessionBaseKey
|
||||
exportedSessionKey := core.Random(16)
|
||||
EncryptedRandomSessionKey := make([]byte, len(exportedSessionKey))
|
||||
rc, _ := rc4.NewCipher(exchangeKey)
|
||||
rc.XORKeyStream(EncryptedRandomSessionKey, exportedSessionKey)
|
||||
|
||||
if challengeMsg.NegotiateFlags&NTLMSSP_NEGOTIATE_UNICODE != 0 {
|
||||
n.enableUnicode = true
|
||||
}
|
||||
slog.Debug(fmt.Sprintf("user: %s, password:********", n.user))
|
||||
domain, user, _ := n.GetEncodedCredentials()
|
||||
|
||||
n.authenticateMessage = NewAuthenticateMessage(challengeMsg.NegotiateFlags,
|
||||
domain, user, []byte(""), lmChallengeResponse, ntChallengeResponse, EncryptedRandomSessionKey)
|
||||
|
||||
if computeMIC {
|
||||
copy(n.authenticateMessage.MIC[:], MIC(exportedSessionKey, n.negotiateMessage, n.challengeMessage, n.authenticateMessage)[:16])
|
||||
}
|
||||
|
||||
md := md5.New()
|
||||
//ClientSigningKey
|
||||
a := concat(exportedSessionKey, clientSigning)
|
||||
md.Write(a)
|
||||
ClientSigningKey := md.Sum(nil)
|
||||
//ServerSigningKey
|
||||
md.Reset()
|
||||
a = concat(exportedSessionKey, serverSigning)
|
||||
md.Write(a)
|
||||
ServerSigningKey := md.Sum(nil)
|
||||
//ClientSealingKey
|
||||
md.Reset()
|
||||
a = concat(exportedSessionKey, clientSealing)
|
||||
md.Write(a)
|
||||
ClientSealingKey := md.Sum(nil)
|
||||
//ServerSealingKey
|
||||
md.Reset()
|
||||
a = concat(exportedSessionKey, serverSealing)
|
||||
md.Write(a)
|
||||
ServerSealingKey := md.Sum(nil)
|
||||
|
||||
slog.Debug(fmt.Sprintf("ClientSigningKey:%s", hex.EncodeToString(ClientSigningKey)))
|
||||
slog.Debug(fmt.Sprintf("ServerSigningKey:%s", hex.EncodeToString(ServerSigningKey)))
|
||||
slog.Debug(fmt.Sprintf("ClientSealingKey:%s", hex.EncodeToString(ClientSealingKey)))
|
||||
slog.Debug(fmt.Sprintf("ServerSealingKey:%s", hex.EncodeToString(ServerSealingKey)))
|
||||
|
||||
encryptRC4, _ := rc4.NewCipher(ClientSealingKey)
|
||||
decryptRC4, _ := rc4.NewCipher(ServerSealingKey)
|
||||
|
||||
ntlmSec := &NTLMv2Security{encryptRC4, decryptRC4, ClientSigningKey, ServerSigningKey, 0}
|
||||
|
||||
return n.authenticateMessage, ntlmSec
|
||||
}
|
||||
|
||||
func (n *NTLMv2) GetEncodedCredentials() ([]byte, []byte, []byte) {
|
||||
if n.enableUnicode {
|
||||
return core.UnicodeEncode(n.domain), core.UnicodeEncode(n.user), core.UnicodeEncode(n.password)
|
||||
}
|
||||
return []byte(n.domain), []byte(n.user), []byte(n.password)
|
||||
}
|
||||
|
||||
type NTLMv2Security struct {
|
||||
EncryptRC4 *rc4.Cipher
|
||||
DecryptRC4 *rc4.Cipher
|
||||
SigningKey []byte
|
||||
VerifyKey []byte
|
||||
SeqNum uint32
|
||||
}
|
||||
|
||||
func (n *NTLMv2Security) GssEncrypt(s []byte) []byte {
|
||||
p := make([]byte, len(s))
|
||||
n.EncryptRC4.XORKeyStream(p, s)
|
||||
|
||||
// HMAC input: SeqNum(4) + plaintext
|
||||
sigInput := make([]byte, 4+len(s))
|
||||
binary.LittleEndian.PutUint32(sigInput, n.SeqNum)
|
||||
copy(sigInput[4:], s)
|
||||
s1 := HMAC_MD5(n.SigningKey, sigInput)[:8]
|
||||
|
||||
checksum := make([]byte, 8)
|
||||
n.EncryptRC4.XORKeyStream(checksum, s1)
|
||||
|
||||
// Output: version(4) + checksum(8) + SeqNum(4) + encrypted(len(p))
|
||||
out := make([]byte, 16+len(p))
|
||||
binary.LittleEndian.PutUint32(out[0:], 0x00000001)
|
||||
copy(out[4:], checksum)
|
||||
binary.LittleEndian.PutUint32(out[12:], n.SeqNum)
|
||||
copy(out[16:], p)
|
||||
|
||||
n.SeqNum++
|
||||
return out
|
||||
}
|
||||
|
||||
func (n *NTLMv2Security) GssDecrypt(s []byte) []byte {
|
||||
if len(s) < 16 {
|
||||
return nil
|
||||
}
|
||||
// s[0:4] = version (ignored), s[4:12] = checksum, s[12:16] = seqNum, s[16:] = data
|
||||
checksum := s[4:12]
|
||||
seqNum := binary.LittleEndian.Uint32(s[12:16])
|
||||
data := s[16:]
|
||||
|
||||
p := make([]byte, len(data))
|
||||
n.DecryptRC4.XORKeyStream(p, data)
|
||||
|
||||
check := make([]byte, 8)
|
||||
n.DecryptRC4.XORKeyStream(check, checksum)
|
||||
|
||||
// HMAC input: seqNum(4) + decrypted(len(p))
|
||||
verifyInput := make([]byte, 4+len(p))
|
||||
binary.LittleEndian.PutUint32(verifyInput, seqNum)
|
||||
copy(verifyInput[4:], p)
|
||||
verify := HMAC_MD5(n.VerifyKey, verifyInput)[:8]
|
||||
|
||||
if !bytes.Equal(verify, check) {
|
||||
return nil
|
||||
}
|
||||
return p
|
||||
}
|
||||
Reference in New Issue
Block a user