internal/honeypot/rdp/credssp_test.go

package rdp

import (
	"encoding/binary"
	"testing"
)

func TestParseNTLMType3(t *testing.T) {
	// Construct a minimal NTLM Type 3 message
	// Offsets:
	// NtResponse: 20
	// Domain: 28
	// User: 36
	// Workstation: 44

	data := make([]byte, 200)
	copy(data[0:8], NTLMSSP_SIGNATURE)
	binary.LittleEndian.PutUint32(data[8:12], 3) // Type 3

	// Helper to set field
	setField := func(offset int, payload []byte, startOffset int) {
		length := len(payload)
		binary.LittleEndian.PutUint16(data[offset:offset+2], uint16(length))
		binary.LittleEndian.PutUint16(data[offset+2:offset+4], uint16(length))
		binary.LittleEndian.PutUint32(data[offset+4:offset+8], uint32(startOffset))
		copy(data[startOffset:startOffset+length], payload)
	}

	// Payload data (UTF-16LE)
	uName := []byte{'u', 0, 's', 0, 'e', 0, 'r', 0}
	dom := []byte{'D', 0, 'O', 0, 'M', 0, 'A', 0, 'I', 0, 'N', 0}
	work := []byte{'H', 0, 'O', 0, 'S', 0, 'T', 0}
	hash := make([]byte, 64)
	for i := 0; i < 64; i++ {
		hash[i] = byte(i)
	}

	setField(36, uName, 64)
	setField(28, dom, 64+len(uName))
	setField(44, work, 64+len(uName)+len(dom))
	setField(20, hash, 64+len(uName)+len(dom)+len(work))

	username, domain, workstation, ntlmHash := parseNTLMType3(data)

	if username != "user" {
		t.Errorf("expected username 'user', got '%s'", username)
	}
	if domain != "DOMAIN" {
		t.Errorf("expected domain 'DOMAIN', got '%s'", domain)
	}
	if workstation != "HOST" {
		t.Errorf("expected workstation 'HOST', got '%s'", workstation)
	}

	// Expected hash is hex of first 16 bytes of hash payload
	expectedHash := "000102030405060708090a0b0c0d0e0f"
	if ntlmHash != expectedHash {
		t.Errorf("expected ntlmHash '%s', got '%s'", expectedHash, ntlmHash)
	}
}