Documentation
¶
Overview ¶
Package transport is declared here for nmf.go; see nns.go for the package-level doc comment.
Package transport implements the NNS (.NET NegotiateStream) and NMF (.NET Message Framing) protocol layers used by Active Directory Web Services (ADWS) on port 9389.
Protocol stack (bottom to top):
TCP Socket (net.tcp://dc:9389) NNS - .NET NegotiateStream (SPNEGO/NTLM/Kerberos authentication + message protection) NMF - .NET Message Framing (record boundaries + binary XML encoding negotiation) SOAP/XML (application layer)
Package transport - PKINIT support using RedTeamPentesting/adauth's pkinit package.
Performs a PKINIT AS exchange (RFC 4556) to obtain a Kerberos TGT from an RSA certificate, writes it to a temporary CCache file, and hands it off to the NNS layer via the existing CredentialCCache path.
Index ¶
- Constants
- func DecodeNBFS(input []byte) (string, error)
- func DecodeNBFSE(input []byte) (string, error)
- func DialADWS(ctx context.Context, host string, port int, opts ResolverOptions) (net.Conn, error)
- func DiscoverDC(ctx context.Context, domain string, opts ResolverOptions) (string, error)
- func EncodeNBFS(input string) ([]byte, error)
- func EncodeNBFSE(input string) ([]byte, error)
- func LoadPEM(certFile, keyFile string) (*rsa.PrivateKey, *x509.Certificate, error)
- func LoadPFX(pfxFile, password string) (*rsa.PrivateKey, *x509.Certificate, error)
- func PKINITAuthenticate(ctx context.Context, username, domain, kdcHost string, cert *x509.Certificate, ...) (tempCCachePath string, err error)
- func ResolveIPToFQDN(ctx context.Context, host string, opts ResolverOptions) (string, error)
- type CredentialType
- type NBFSECodec
- type NMFConnection
- type NNSConnection
- func NewNNSConnection(conn net.Conn, domain, username, password, targetSPN string, useKerberos bool, ...) *NNSConnection
- func NewNNSConnectionAnonymous(conn net.Conn, targetSPN string, useKerberos bool, ...) *NNSConnection
- func NewNNSConnectionWithAESKey(conn net.Conn, domain, username, aesKey, targetSPN string, ...) *NNSConnection
- func NewNNSConnectionWithCCache(conn net.Conn, domain, username, ccachePath, targetSPN string, ...) *NNSConnection
- func NewNNSConnectionWithNTHash(conn net.Conn, domain, username, ntHash, targetSPN string, useKerberos bool, ...) *NNSConnection
- func NewNNSConnectionWithPKINIT(conn net.Conn, domain, username string, cert *x509.Certificate, ...) *NNSConnection
- func (nns *NNSConnection) Authenticate() error
- func (nns *NNSConnection) Close() error
- func (nns *NNSConnection) IsAuthenticated() bool
- func (nns *NNSConnection) Recv(buf []byte) (int, error)
- func (nns *NNSConnection) Send(data []byte) error
- func (nns *NNSConnection) WithResolverOptions(opts ResolverOptions) *NNSConnection
- type ProtectionLevel
- type ResolverOptions
Constants ¶
const ( ModeSingleton uint8 = 0x01 ModeDuplex uint8 = 0x02 // Most common for ADWS ModeSimplex uint8 = 0x03 ModeDuplexSession uint8 = 0x04 )
Connection modes (from [MC-NMF] 2.2.3.1.2)
const ( // SOAP 1.1 encodings EncodingSOAP11UTF8 uint8 = 0x00 EncodingSOAP11UTF16 uint8 = 0x01 EncodingSOAP11UTF16LE uint8 = 0x02 // SOAP 1.2 encodings; ADWS requires EncodingSOAP12BinaryDict (0x08) EncodingSOAP12UTF8 uint8 = 0x03 EncodingSOAP12UTF16 uint8 = 0x04 EncodingSOAP12UTF16LE uint8 = 0x05 EncodingSOAP12MTOM uint8 = 0x06 EncodingSOAP12Binary uint8 = 0x07 // Binary XML (NBFS) EncodingSOAP12BinaryDict uint8 = 0x08 // Most efficient - used by ADWS )
Known encodings (from [MC-NMF] 2.2.3.4.1)
const ( RecordTypeVersion uint8 = 0x00 // Version Record RecordTypeMode uint8 = 0x01 // Mode Record RecordTypeVia uint8 = 0x02 // Via Record RecordTypeEncoding uint8 = 0x03 // Known Encoding Record RecordTypeExtEncoding uint8 = 0x04 // Extensible Encoding Record RecordTypeUnsizedEnv uint8 = 0x05 // Unsized Envelope Record RecordTypeSizedEnv uint8 = 0x06 // Sized Envelope Record RecordTypeEnd uint8 = 0x07 // End Record RecordTypeFault uint8 = 0x08 // Fault Record RecordTypeUpgradeReq uint8 = 0x09 // Upgrade Request Record RecordTypeUpgradeRes uint8 = 0x0A // Upgrade Response Record RecordTypePreambleAck uint8 = 0x0B // Preamble Ack Record (was incorrectly 0x01) RecordTypePreambleEnd uint8 = 0x0C // Preamble End Record (was incorrectly 0x02) )
NMF Record Types (from [MC-NMF] 2.2.1 Record Types table)
const ( MessageIDInProgress byte = 0x16 // HandshakeInProgress: Continue negotiation MessageIDDone byte = 0x14 // HandshakeDone: Authentication completed successfully MessageIDError byte = 0x15 // HandshakeError: Authentication failed )
MessageID constants for NNS handshake/data (per MS-NNS 2.2.1)
Variables ¶
This section is empty.
Functions ¶
func DecodeNBFS ¶
DecodeNBFS decodes NBFX/NBFS records to XML (no StringTable prefix).
func DecodeNBFSE ¶
func DialADWS ¶ added in v1.1.0
DialADWS dials a TCP connection to the ADWS port on host and returns the raw net.Conn. The returned connection should be passed to NewNNSConnection (or one of its variants) and then to NewNMFConnection.
Name resolution uses the resolver built from opts, so a custom DNS server and DNS-over-TCP are honoured consistently for both discovery and dialling.
If port is 0, the default ADWS port 9389 is used.
func DiscoverDC ¶ added in v1.1.0
DiscoverDC finds a domain controller for domain by querying SRV records.
It queries (in order):
_ldap._tcp.<domain> _kerberos._tcp.<domain>
The target of the first record (sorted by priority then weight, as returned by net.Resolver.LookupSRV) is returned. An error is returned when no SRV records are found for either service.
opts controls which DNS server and transport protocol are used for the lookup.
func EncodeNBFS ¶
EncodeNBFS encodes XML using NBFX records with the NBFS static dictionary (no StringTable). This matches the [MC-NBFS] SOAP Data Structure framing (without [MC-NBFSE] extension).
func EncodeNBFSE ¶
func LoadPEM ¶
func LoadPEM(certFile, keyFile string) (*rsa.PrivateKey, *x509.Certificate, error)
LoadPEM loads an RSA private key and certificate from separate PEM files.
func LoadPFX ¶
func LoadPFX(pfxFile, password string) (*rsa.PrivateKey, *x509.Certificate, error)
LoadPFX decodes a PKCS#12 (.pfx/.p12) file and returns the RSA private key and certificate. It delegates to adauth.DecodePFX which correctly handles the Microsoft-specific cert/chain ordering quirk in pkcs12.DecodeChain.
func PKINITAuthenticate ¶
func PKINITAuthenticate(ctx context.Context, username, domain, kdcHost string, cert *x509.Certificate, key *rsa.PrivateKey, opts ResolverOptions) (tempCCachePath string, err error)
PKINITAuthenticate performs a PKINIT AS exchange against kdcHost:88, returning the TGT as a temporary ccache file path. The caller must delete the file. opts controls name resolution and TCP/UDP transport used to reach the KDC.
func ResolveIPToFQDN ¶ added in v1.1.0
ResolveIPToFQDN returns the FQDN for host by performing a reverse DNS (PTR) lookup when host is an IP address, and returns host unchanged otherwise.
The first hostname returned by the PTR lookup is used. A trailing dot (canonical DNS form) is stripped automatically. If the lookup fails the error is returned so the caller can decide whether to fall back to the raw IP.
Types ¶
type CredentialType ¶
type CredentialType int
CredentialType defines the type of credential provided for authentication.
const ( // Password-based authentication CredentialPassword CredentialType = iota // NTLM hash authentication (hex-encoded MD4 of password) CredentialNTHash // Kerberos credential cache (ccache file path or from KRB5CCNAME env) CredentialCCache // Anonymous authentication (empty credentials) CredentialAnonymous // PKINIT certificate-based Kerberos pre-authentication (RFC 4556) CredentialClientCert // Kerberos AES session key (hex-encoded AES-128 or AES-256 key) CredentialAESKey )
type NBFSECodec ¶
type NBFSECodec struct {
// contains filtered or unexported fields
}
NBFSECodec maintains [MC-NBFSE] StringTable mappings across multiple documents.
[MC-NBFSE] 2.1: the first StringTable entry has ID 1, and each subsequent String is assigned the next-higher odd number. A consumer MUST maintain this mapping until there are no further documents to process.
This codec is used by the transport layer to decode a sequence of SOAP envelopes over a single NMF/NNS connection.
func NewNBFSECodec ¶
func NewNBFSECodec() *NBFSECodec
type NMFConnection ¶
type NMFConnection struct {
// contains filtered or unexported fields
}
NMFConnection represents a .NET Message Framing protocol connection.
NMF provides message boundaries and encoding negotiation on top of NNS.
References:
- [MC-NMF]: .NET Message Framing Protocol
- Used by WCF (Windows Communication Foundation)
func NewNMFConnection ¶
func NewNMFConnection(transport *NNSConnection, fqdn string, port int) *NMFConnection
NewNMFConnection creates an NMF connection over the given NNS transport. Authentication is performed later when Connect is called.
func (*NMFConnection) Connect ¶
func (nmf *NMFConnection) Connect(resource string) error
Connect establishes the NMF connection to the specified ADWS resource endpoint.
Connection sequence (per MC-NMF spec):
- Send NMF Preamble on raw TCP (pre-auth)
- Send Upgrade Request on raw TCP (pre-auth)
- Receive Upgrade Response on raw TCP (pre-auth)
- Perform NNS authentication handshake on raw TCP (handshake messages unprotected per MS-NNS 2.2.1)
- Send Preamble End via NNS transport (post-auth, protected)
- Receive Preamble Ack via NNS transport (post-auth, protected)
Endpoints:
- Windows/Enumeration - LDAP queries (Enumerate/Pull)
- Windows/Resource - Get/Put operations
- Windows/ResourceFactory - Create objects
- Windows/AccountManagement - Account operations
- Windows/TopologyManagement - DC operations
func (*NMFConnection) Recv ¶
func (nmf *NMFConnection) Recv() (string, error)
Recv receives a SOAP message from an NMF SizedEnvelope record.
func (*NMFConnection) Send ¶
func (nmf *NMFConnection) Send(soapMessage string) error
Send sends a SOAP message encapsulated in an NMF SizedEnvelope record.
SizedEnvelope format:
[0] RecordType (0x06) [1:.] EncodedSize (variable-length integer) [...] SOAP payload (binary XML when using SOAP12Binary/Dict)
type NNSConnection ¶
type NNSConnection struct {
// contains filtered or unexported fields
}
NNSConnection represents a .NET NegotiateStream connection with SPNEGO/NTLM/Kerberos authentication and message signing/sealing capabilities.
References:
- [MS-NNS]: .NET NegotiateStream Protocol
- Uses go-msrpc/ssp for GSS-API authentication (SPNEGO, NTLM, Kerberos)
func NewNNSConnection ¶
func NewNNSConnection(conn net.Conn, domain, username, password, targetSPN string, useKerberos bool, protectionLevel ProtectionLevel) *NNSConnection
NewNNSConnection creates an NNS connection over an established TCP socket using password credentials. If targetSPN is empty, the SPN is derived as "host/<domain>" during the authentication handshake.
func NewNNSConnectionAnonymous ¶
func NewNNSConnectionAnonymous(conn net.Conn, targetSPN string, useKerberos bool, protectionLevel ProtectionLevel) *NNSConnection
NewNNSConnectionAnonymous creates a new NNS connection using anonymous authentication.
func NewNNSConnectionWithAESKey ¶
func NewNNSConnectionWithAESKey(conn net.Conn, domain, username, aesKey, targetSPN string, protectionLevel ProtectionLevel) *NNSConnection
NewNNSConnectionWithAESKey creates a new NNS connection using a Kerberos AES session key (overpass-the-hash with AES; works even when RC4-HMAC is disabled on the DC). aesKey must be a hex-encoded 16-byte (AES-128) or 32-byte (AES-256) key.
func NewNNSConnectionWithCCache ¶
func NewNNSConnectionWithCCache(conn net.Conn, domain, username, ccachePath, targetSPN string, protectionLevel ProtectionLevel) *NNSConnection
NewNNSConnectionWithCCache creates a new NNS connection using Kerberos credential cache. ccachePath can be empty to use the KRB5CCNAME environment variable.
func NewNNSConnectionWithNTHash ¶
func NewNNSConnectionWithNTHash(conn net.Conn, domain, username, ntHash, targetSPN string, useKerberos bool, protectionLevel ProtectionLevel) *NNSConnection
NewNNSConnectionWithNTHash creates a new NNS connection using NTLM hash authentication.
func NewNNSConnectionWithPKINIT ¶
func NewNNSConnectionWithPKINIT(conn net.Conn, domain, username string, cert *x509.Certificate, key *rsa.PrivateKey, targetSPN string, protectionLevel ProtectionLevel) *NNSConnection
NewNNSConnectionWithPKINIT creates a new NNS connection using PKINIT certificate-based Kerberos pre-authentication (RFC 4556 / MS-PKCA). The cert and key must be RSA.
func (*NNSConnection) Authenticate ¶
func (nns *NNSConnection) Authenticate() error
Authenticate performs authentication handshake over NNS protocol using GSS-API.
Authentication Logic:
- Always use SPNEGO
- If useKerberos = false: SPNEGO with NTLM-only
- If useKerberos = true: SPNEGO with Kerberos-only
Flow:
- Initialize GSS-API security context with credentials and mechanisms
- Client → Server: InitSecContext() → HandshakeInProgress
- Server → Client: AcceptSecContext() → HandshakeInProgress or HandshakeDone
- [Optional] Client → Server: Continue until HandshakeDone
Returns error if authentication fails.
func (*NNSConnection) Close ¶
func (nns *NNSConnection) Close() error
Close closes the underlying connection.
func (*NNSConnection) IsAuthenticated ¶
func (nns *NNSConnection) IsAuthenticated() bool
IsAuthenticated returns whether the connection has completed authentication.
func (*NNSConnection) Recv ¶
func (nns *NNSConnection) Recv(buf []byte) (int, error)
Recv receives data with optional signature verification and unsealing using GSS-API Unwrap.
After authentication, NNS Data Messages have the format:
[0:4] PayloadSize (uint32, little-endian) [4:.] Payload (signed/encrypted data)
Uses GSS_Unwrap from the negotiated security mechanism.
func (*NNSConnection) Send ¶
func (nns *NNSConnection) Send(data []byte) error
Send sends data with optional signing and sealing using GSS-API Wrap.
After authentication, NNS Data Messages have the format:
[0:4] PayloadSize (uint32, little-endian) - size of wrapped payload [4:.] Payload (signed/encrypted data from GSS_Wrap)
Uses GSS_Wrap from the negotiated security mechanism.
func (*NNSConnection) WithResolverOptions ¶ added in v1.1.0
func (nns *NNSConnection) WithResolverOptions(opts ResolverOptions) *NNSConnection
WithResolverOptions sets the ResolverOptions used for all KDC TCP connections made by this NNSConnection (Kerberos AS/TGS exchanges and PKINIT). It returns the receiver so calls can be chained onto any New* constructor.
type ProtectionLevel ¶
type ProtectionLevel int
ProtectionLevel defines the message protection requested during NNS negotiation. It controls whether GSS-API wraps messages with signing only or with encryption.
const ( // ProtectionNone - No message protection requested (still performs the normal NNS/SPNEGO handshake). ProtectionNone ProtectionLevel = iota // ProtectionSign - Message integrity (uses SPNEGO, can negotiate Kerberos or NTLM) ProtectionSign // ProtectionEncryptAndSign - Message confidentiality and integrity (uses SPNEGO) ProtectionEncryptAndSign )
type ResolverOptions ¶ added in v1.1.0
type ResolverOptions struct {
// NameServer is an optional custom DNS server address (host or host:port).
// If empty, the system resolver is used.
NameServer string
// UseTCP forces DNS queries to be sent over TCP instead of UDP.
// Useful when UDP DNS is blocked, when large SRV responses are expected,
// or when DNS-over-TCP is required by policy.
// When NameServer is empty and UseTCP is true, the system-selected server
// is still contacted but via TCP.
UseTCP bool
}
ResolverOptions configures DNS resolution behaviour used for DC discovery and for dialling the ADWS TCP connection.