package telnetmini
import (
"bytes"
"encoding/binary"
"fmt"
)
type NTLMInfoResponse struct {
TargetName string
NetBIOSDomainName string
NetBIOSComputerName string
DNSDomainName string
DNSComputerName string
DNSTreeName string
ProductVersion string
Timestamp uint64
}
func ParseNTLMResponse(data []byte) (*NTLMInfoResponse, error) {
ntlmStart := bytes.Index(data, []byte("NTLMSSP"))
if ntlmStart == -1 {
return nil, fmt.Errorf("NTLMSSP signature not found in response")
}
ntlmEnd := bytes.Index(data[ntlmStart:], []byte{0xFF, 0xF0})
if ntlmEnd == -1 {
return nil, fmt.Errorf("NTLM response not properly terminated with Sub-option End")
}
ntlmData := data[ntlmStart : ntlmStart+ntlmEnd]
if len(ntlmData) < 12 {
return nil, fmt.Errorf("NTLM response too short")
}
messageType := binary.LittleEndian.Uint32(ntlmData[8:12])
if messageType != 2 {
return nil, fmt.Errorf("expected NTLM challenge message, got type %d", messageType)
}
targetNameLen := binary.LittleEndian.Uint16(ntlmData[12:14])
targetNameOffset := binary.LittleEndian.Uint32(ntlmData[16:20])
targetInfoLen := binary.LittleEndian.Uint16(ntlmData[40:42])
targetInfoOffset := binary.LittleEndian.Uint32(ntlmData[44:48])
var targetName string
if targetNameLen > 0 && int(targetNameOffset) < len(ntlmData) {
end := int(targetNameOffset) + int(targetNameLen)
if end <= len(ntlmData) {
targetName = string(ntlmData[targetNameOffset:end])
}
}
var ntlmInfo NTLMInfoResponse
ntlmInfo.TargetName = targetName
if targetInfoLen > 0 && int(targetInfoOffset) < len(ntlmData) {
end := int(targetInfoOffset) + int(targetInfoLen)
if end <= len(ntlmData) {
parseTargetInfo(ntlmData[targetInfoOffset:end], &ntlmInfo)
}
}
return &ntlmInfo, nil
}
func CalculateTimestampSkew(ntlmTimestamp uint64) int64 {
if ntlmTimestamp == 0 {
return 0
}
unixTimestamp := int64(ntlmTimestamp/10000000) - 11644473600
return unixTimestamp
}
func parseTargetInfo(data []byte, info *NTLMInfoResponse) {
for i := 0; i < len(data)-4; {
if i+4 > len(data) {
break
}
infoType := binary.LittleEndian.Uint16(data[i : i+2])
infoLen := binary.LittleEndian.Uint16(data[i+2 : i+4])
if i+4+int(infoLen) > len(data) {
break
}
infoData := data[i+4 : i+4+int(infoLen)]
switch infoType {
case 1:
if len(infoData) > 0 {
info.NetBIOSComputerName = string(infoData)
}
case 2:
if len(infoData) > 0 {
info.NetBIOSDomainName = string(infoData)
}
case 3:
if len(infoData) > 0 {
info.DNSComputerName = string(infoData)
}
case 4:
if len(infoData) > 0 {
info.DNSDomainName = string(infoData)
}
case 5:
if len(infoData) > 0 {
info.DNSTreeName = string(infoData)
}
case 6:
if len(infoData) >= 8 {
info.Timestamp = binary.LittleEndian.Uint64(infoData)
}
case 7:
case 8:
if len(infoData) > 0 {
info.TargetName = string(infoData)
}
case 9:
case 10:
case 11:
if len(infoData) >= 8 {
major := uint8(infoData[0])
minor := uint8(infoData[1])
build := binary.LittleEndian.Uint16(infoData[2:4])
info.ProductVersion = fmt.Sprintf("%d.%d.%d", major, minor, build)
}
}
i += 4 + int(infoLen)
}
}
func CreateNTLMNegotiateBlob() []byte {
var buf bytes.Buffer
buf.WriteString("NTLMSSP")
buf.WriteByte(0x00)
messageTypeBytes := make([]byte, 4)
binary.LittleEndian.PutUint32(messageTypeBytes, 1)
buf.Write(messageTypeBytes)
flags := uint32(0x00000001 +
0x00000002 +
0x00000004 +
0x00000200 +
0x00008000 +
0x00080000 +
0x20000000 +
0x80000000)
flagsBytes := make([]byte, 4)
binary.LittleEndian.PutUint32(flagsBytes, flags)
buf.Write(flagsBytes)
domainNameLenBytes := make([]byte, 2)
binary.LittleEndian.PutUint16(domainNameLenBytes, 0)
buf.Write(domainNameLenBytes)
domainNameMaxLenBytes := make([]byte, 2)
binary.LittleEndian.PutUint16(domainNameMaxLenBytes, 0)
buf.Write(domainNameMaxLenBytes)
domainNameOffsetBytes := make([]byte, 4)
binary.LittleEndian.PutUint32(domainNameOffsetBytes, 0)
buf.Write(domainNameOffsetBytes)
workstationNameLenBytes := make([]byte, 2)
binary.LittleEndian.PutUint16(workstationNameLenBytes, 0)
buf.Write(workstationNameLenBytes)
workstationNameMaxLenBytes := make([]byte, 2)
binary.LittleEndian.PutUint16(workstationNameMaxLenBytes, 0)
buf.Write(workstationNameMaxLenBytes)
workstationNameOffsetBytes := make([]byte, 4)
binary.LittleEndian.PutUint32(workstationNameOffsetBytes, 0)
buf.Write(workstationNameOffsetBytes)
buf.Write(make([]byte, 8))
return buf.Bytes()
}
func CreateTNAPLoginPacket() []byte {
var buf bytes.Buffer
buf.WriteByte(0x01)
buf.WriteByte(0x00)
ntlmBlob := CreateNTLMNegotiateBlob()
data := buf.Bytes()
data[1] = byte(len(ntlmBlob))
buf.Write(ntlmBlob)
return buf.Bytes()
}