Generated from Manticore v1.1.6 • 388 packages. View on pkg.go.dev

msdrsr

import "github.com/TheManticoreProject/Manticore/network/dcerpc/ms-protocols/ms-drsr"

Package msdrsr implements high-level MS-DRSR (Directory Replication Service Remote Protocol) workflows over the drsuapi DCE/RPC interface (e3514235-4b06-11d1-ab04-00c04fc2dcd2 v4.0), carried over ncacn_ip_tcp.

Unlike the named-pipe protocols (ms-srvs, ms-rrp), drsuapi has no fixed endpoint: the server listens on a dynamic TCP port that the client resolves through the endpoint mapper on TCP/135. A Client therefore owns its own transport lifecycle — it resolves the endpoint, dials it, authenticates with NTLM at packet-privacy level (drsuapi requires sign+seal), binds the drsuapi abstract syntax, and performs the IDL_DRSBind capability handshake — rather than borrowing an established SMB session.

This file covers the connection lifecycle (Connect/Close, i.e. IDL_DRSBind / IDL_DRSUnbind and the DRS_EXTENSIONS negotiation). The replication workflows (DRSCrackNames, DRSGetNCChanges, DCSync) build on the bound handle in later files.

References:

  • [MS-DRSR] 4.1.3 IDL_DRSBind, 4.1.25 IDL_DRSUnbind, 5.39 DRS_EXTENSIONS_INT
  • [MS-RPCE] 2.1.1.1 ncacn_ip_tcp; endpoint mapper ept_map ([C706] Appendix O)

Index

Constants

DRS_VERIFY_DSNAMES is the IDL_DRSVerifyNames flag selecting verification by DSNAME (the names are distinguished names) ([MS-DRSR] 4.1.26.2).

const DRS_VERIFY_DSNAMES = 0

DefaultTimeout bounds the endpoint-mapper and drsuapi TCP dials and reads when the caller does not set one with SetTimeout.

const DefaultTimeout = 10 * time.Second

type AccountSecrets

AccountSecrets holds the credential material decrypted from one replicated object. NTHash/LMHash are the current 16-byte hashes (valid only when HasNT/HasLM is set); the history slices are previous hashes as the DC returns them. SupplementalCredentials retains the transport-decrypted source blob; parsed keys, cleartext, and WDigest hashes are exposed in the adjacent fields.

type AccountSecrets struct {
    DN             string
    SAMAccountName string
    SID            []byte
    RID            uint32
    NTHash         [16]byte
    LMHash         [16]byte
    HasNT          bool
    HasLM          bool
    IsDeleted      bool
    NTHistory      [][16]byte
    LMHistory      [][16]byte

    SupplementalCredentials []byte
    KerberosKeys            []KerberosKey
    CleartextPassword       string
    CleartextPasswordRaw    []byte
    WDigestHashes           [][16]byte
}

type Client

Client is an MS-DRSR client over ncacn_ip_tcp. The zero value is not usable; build one with New. It is not safe for concurrent use.

type Client struct {
    // contains filtered or unexported fields
}

func New

func New(host string, creds *credentials.Credentials) *Client

New returns an MS-DRSR client for the given host (IP or hostname) and credentials. By default the drsuapi TCP endpoint is resolved through the endpoint mapper on TCP/135; call SetPort to skip resolution and dial a known port directly.

func (*Client) Close

func (c *Client) Close() error

Close unbinds the drsuapi handle (IDL_DRSUnbind) and tears down the transport. It is safe to call on a Client that never connected.

func (*Client) Connect

func (c *Client) Connect() error

Connect resolves the drsuapi endpoint (unless a port was set), dials it over ncacn_ip_tcp, authenticates with NTLM at packet-privacy level, binds the drsuapi abstract syntax, and performs the IDL_DRSBind capability handshake. On success the negotiated context handle and the server’s extensions are available via Handle and ServerExtensions, and the connection is ready for replication calls.

The endpoint resolution, NTLM packet-privacy setup (drsuapi rejects lower levels), and bind are handled by an msproto.TCPBinder; only the drsuapi-specific IDL_DRSBind capability handshake lives here.

func (*Client) CrackNames

func (c *Client) CrackNames(formatOffered, formatDesired uint32, names ...string) ([]CrackedName, error)

CrackNames translates names from one DS_NAME_FORMAT to another via IDL_DRSCrackNames (opnum 12), e.g. a “DOMAIN\user” account name (DS_NT4_ACCOUNT_NAME) to the object’s GUID (DS_UNIQUE_ID_NAME). It returns one CrackedName per input name, in order. A per-name failure is reported in that entry’s Status, not as a Go error; the error return is reserved for transport/RPC failures.

func (*Client) DCSync

func (c *Client) DCSync(name string, formatOffered uint32) (*AccountSecrets, error)

DCSync performs a full DCSync of a single account against the connected DC: it resolves the account name (given in the offered DS_NAME_FORMAT) to its objectGUID with IDL_DRSCrackNames, replicates the object with IDL_DRSGetNCChanges (EXOP_REPL_OBJ), and decrypts its secrets. It is the one-call form of ResolveToGUID + ReplicateSingleObject + DecryptSecrets. The Client must already be connected.

func (*Client) DCSyncAll

func (c *Client) DCSyncAll(ncDN string) ([]*AccountSecrets, error)

DCSyncAll replicates the whole naming context and decrypts the secrets of every security principal in it — a full-domain credential dump (the secretsdump “-just-dc” equivalent). ncDN is the NC distinguished name, e.g. “DC=lab,DC=local”.

func (*Client) DCSyncByAccount

func (c *Client) DCSyncByAccount(domainUser string) (*AccountSecrets, error)

DCSyncByAccount DCSyncs an account identified by its NT4 name (“DOMAIN\\user”).

func (*Client) DCSyncByDN

func (c *Client) DCSyncByDN(dn string) (*AccountSecrets, error)

DCSyncByDN DCSyncs an account identified by its distinguished name (e.g. “CN=Administrator,CN=Users,DC=lab,DC=local”). This is the most portable form: unlike the NT4 “DOMAIN\\user” format it needs no NetBIOS domain name.

func (*Client) DCSyncByUPN

func (c *Client) DCSyncByUPN(upn string) (*AccountSecrets, error)

DCSyncByUPN DCSyncs an account identified by its user principal name (“user@domain”).

func (*Client) DecryptSecrets

func (c *Client) DecryptSecrets(res *ReplicationResult) ([]*AccountSecrets, error)

DecryptSecrets decrypts the secret attributes of every replicated object that carries an objectSid (i.e. a security principal), using the connection’s NTLM session key. It must be called on the same Client whose ReplicateSingleObject (or full-NC call) produced the result, because decryption is keyed by that connection’s session key.

func (*Client) DomainControllerInfo

func (c *Client) DomainControllerInfo(domainFQDN string) ([]DCInfo, error)

DomainControllerInfo calls IDL_DRSDomainControllerInfo (opnum 16) at InfoLevel 2 for the given domain (its FQDN, e.g. “lab.local”), returning one DCInfo per DC. It is the source of a DC’s NtdsDsaObjectGuid, which a strict server or full-NC replication wants as the source DSA in IDL_DRSGetNCChanges.

func (*Client) GetMemberships

func (c *Client) GetMemberships(sids [][]byte, opType drsrtypes.REVERSE_MEMBERSHIP_OPERATION_TYPE, limitingDomainNC string) ([]MembershipGroup, error)

GetMemberships computes the reverse group memberships of the given objects (addressed by binary SID — the basis reverse membership operates on) via IDL_DRSGetMemberships (opnum 9). opType selects which memberships to compute (e.g. drsrtypes.RevMembGetGroupsForUser). limitingDomainNC, if non-empty, bounds the search to that domain NC. It is read-only.

func (*Client) GetMemberships2

func (c *Client) GetMemberships2(sidSets [][][]byte, opType drsrtypes.REVERSE_MEMBERSHIP_OPERATION_TYPE, limitingDomainNC string) ([]MembershipGroup, error)

GetMemberships2 issues a batch of reverse-membership requests in one call via IDL_DRSGetMemberships2 (opnum 21), one request per SID set, returning the groups from each reply concatenated.

func (*Client) GetNT4ChangeLog

func (c *Client) GetNT4ChangeLog(restart []byte, preferredMaxLength uint32) (*NT4ChangeLog, error)

GetNT4ChangeLog calls IDL_DRSGetNT4ChangeLog (opnum 11). restart is the opaque cursor from a previous call (nil to start); preferredMaxLength bounds the returned log size.

func (*Client) GetObjectExistence

func (c *Client) GetObjectExistence(ncDN string, start guid.GUID, count uint32, upToDateVector *drsrtypes.UPTODATE_VECTOR_V1_EXT, digest [16]byte) (*ObjectExistenceResult, error)

GetObjectExistence calls IDL_DRSGetObjectExistence (opnum 23) for a range in a naming context. start must identify the first object in the client’s sequence and count is the maximum number of objects in that sequence. upToDateVector is the merged client/server replication state used to exclude objects that have not reached both replicas. The digest is the MD5 digest of the client’s GUID sequence; a mismatch makes the server return its sequence for reconciliation.

func (*Client) GetReplInfo

func (c *Client) GetReplInfo(infoType uint32, objectDN string, sourceDSA guid.GUID) (*drsrtypes.DRS_MSG_GETREPLINFO_REPLY, error)

GetReplInfo calls IDL_DRSGetReplInfo (opnum 19) for the given DS_REPL_INFO_TYPE and returns the raw reply union; the caller reads the arm matching infoType (e.g. reply.PCursors for DS_REPL_INFO_CURSORS_FOR_NC). objectDN is the target NC or object DN for the chosen info type; sourceDSA may be the zero GUID. This is a read-only recon call. For the common info types prefer the friendly helpers (ReplicationCursors, …).

func (*Client) Handle

func (c *Client) Handle() drsrtypes.DRS_HANDLE

Handle returns the drsuapi context handle established by Connect. It is the null handle until Connect succeeds.

func (*Client) Interface

func (c *Client) Interface() syntax.SyntaxID

Interface reports the DCE/RPC abstract syntax MS-DRSR speaks (drsuapi v4.0).

func (*Client) IsConnected

func (c *Client) IsConnected() bool

IsConnected reports whether Connect has succeeded and Close has not yet run.

func (*Client) QuerySitesByCost

func (c *Client) QuerySitesByCost(fromSite string, toSites []string) ([]SiteCost, error)

QuerySitesByCost calls IDL_DRSQuerySitesByCost (opnum 24) to compute the replication cost from fromSite to each of toSites (site names). It is read-only.

func (*Client) RPC

func (c *Client) RPC() *dcerpcclient.Client

RPC exposes the underlying bound DCE/RPC client so replication call wrappers can issue drsuapi methods against the established handle. It is nil until Connect succeeds.

func (*Client) ReadNgcKey

func (c *Client) ReadNgcKey(accountDN string) (key []byte, retVal uint32, err error)

ReadNgcKey reads the NGC (Windows Hello for Business) protector key of an account via IDL_DRSReadNgcKey (opnum 30). account is the account’s distinguished name. It returns the raw key blob (empty when the account has no key) and the server’s RetVal: 0 means a key was returned; a non-zero code (e.g. 0x200A ERROR_DS_NO_ATTRIBUTE_OR_VALUE) means no key is present. err is only set for transport/RPC failures. Read-only.

func (*Client) ReplicateNC

func (c *Client) ReplicateNC(ncDN string) (*ReplicationResult, error)

ReplicateNC replicates an entire naming context (every object in ncDN, e.g. “DC=lab,DC=local”) by paging IDL_DRSGetNCChanges, and returns the accumulated objects. Unlike ReplicateSingleObject (EXOP_REPL_OBJ), this issues no extended op and pages the whole NC: each page’s reply carries fMoreData and a usnvecTo cursor that drives the next request until the cycle completes ([MS-DRSR] 4.5.1).

Attribute values are returned still encrypted; pass the result to DecryptSecrets (the envelope is identical to the single-object path). The prefix table is taken from the first page — the source DC’s table is stable within a replication cycle.

func (*Client) ReplicateSingleObject

func (c *Client) ReplicateSingleObject(objectGUID guid.GUID) (*ReplicationResult, error)

ReplicateSingleObject replicates exactly one object, identified by its objectGUID, via IDL_DRSGetNCChanges (opnum 3) with the EXOP_REPL_OBJ extended operation — the core of DCSync. It issues a V8 request and expects the V6 reply negotiated at bind. Attribute values are returned as received (secrets remain encrypted).

uuidDsaObjDest/uuidInvocIdSrc are left as the NULL GUID: for EXOP_REPL_OBJ the server does not require the caller’s source-DSA GUID. (Full-NC replication and strict servers want the real source DSA GUID from IDL_DRSDomainControllerInfo; that is a later step.)

func (*Client) ReplicationConnectFailures

func (c *Client) ReplicationConnectFailures() ([]ReplFailure, error)

ReplicationConnectFailures returns KCC connection failures (DS_REPL_INFO_KCC_DSA_CONNECT_FAILURES).

func (*Client) ReplicationCursors

func (c *Client) ReplicationCursors(ncDN string) ([]ReplCursor, error)

ReplicationCursors returns the replication cursors for a naming context (the DC’s up-to-dateness vector) via IDL_DRSGetReplInfo (DS_REPL_INFO_CURSORS_FOR_NC). ncDN is the NC distinguished name, e.g. “DC=lab,DC=local”.

func (*Client) ReplicationLinkFailures

func (c *Client) ReplicationLinkFailures() ([]ReplFailure, error)

ReplicationLinkFailures returns KCC link failures (DS_REPL_INFO_KCC_DSA_LINK_FAILURES).

func (*Client) ReplicationNeighbors

func (c *Client) ReplicationNeighbors(ncDN string) ([]ReplNeighbor, error)

ReplicationNeighbors returns the replication neighbors (repsFrom) of a naming context via IDL_DRSGetReplInfo (DS_REPL_INFO_NEIGHBORS). ncDN is the NC distinguished name.

func (*Client) ReplicationPendingOps

func (c *Client) ReplicationPendingOps() ([]ReplPendingOp, error)

ReplicationPendingOps returns the DC’s pending replication operations via IDL_DRSGetReplInfo (DS_REPL_INFO_PENDING_OPS).

func (*Client) ResolveToGUID

func (c *Client) ResolveToGUID(accountName string, formatOffered uint32) (guid.GUID, error)

ResolveToGUID resolves a single account name (in the given offered format, e.g. drsrtypes.DS_NT4_ACCOUNT_NAME for “DOMAIN\\user”) to its objectGUID by cracking it to DS_UNIQUE_ID_NAME and parsing the “{guid}” result.

func (*Client) ServerExtensions

func (c *Client) ServerExtensions() *drsrtypes.DRS_EXTENSIONS_INT

ServerExtensions returns the capability structure the server returned at IDL_DRSBind, or nil if not connected or the server returned none. Callers inspect its DwFlags to confirm negotiated features (e.g. STRONG_ENCRYPTION before requesting secrets).

func (*Client) SessionKey

func (c *Client) SessionKey() []byte

SessionKey returns the NTLM session key negotiated for this connection, used to decrypt replicated secrets in IDL_DRSGetNCChanges. It is nil until Connect succeeds.

func (*Client) SetPort

func (c *Client) SetPort(port int)

SetPort overrides endpoint-mapper resolution and dials drsuapi on the given TCP port directly. It must be called before Connect.

func (*Client) SetReplicationProgress

func (c *Client) SetReplicationProgress(fn func(objects int))

SetReplicationProgress registers a callback invoked once per page during full-NC replication (ReplicateNC, and therefore DCSyncAll) with the cumulative number of objects received so far. The naming context’s total object count is not known in advance, so callers should render the value as a running count, not a percentage. Pass nil to disable. Set it before starting a replication call; it is not safe to change while one is in progress.

func (*Client) SetSourceDSA

func (c *Client) SetSourceDSA(g guid.GUID)

SetSourceDSA sets the source DSA objectGUID sent as uuidDsaObjDest/uuidInvocIdSrc in subsequent IDL_DRSGetNCChanges calls. Single-object EXOP_REPL_OBJ replication works with the NULL GUID (the default), but a strict server or full-NC replication wants the real source DSA GUID, obtained from DomainControllerInfo.

func (*Client) SetTimeout

func (c *Client) SetTimeout(d time.Duration)

SetTimeout bounds each TCP dial and read. It must be called before Connect.

func (*Client) VerifyNames

func (c *Client) VerifyNames(dns []string) ([]VerifiedName, error)

VerifyNames checks whether the given distinguished names resolve to objects on the DC via IDL_DRSVerifyNames (opnum 8), returning one result per input in order. It requests no attributes (existence only). This is read-only.

type CrackedName

CrackedName is one resolved entry from CrackNames: the per-item status (drsrtypes.DS_NAME_* — DS_NAME_NO_ERROR means resolved), the crossRef domain, and the translated name in the requested format.

type CrackedName struct {
    Status uint32
    Domain string
    Name   string
}

type DCInfo

DCInfo is one domain-controller entry from IDL_DRSDomainControllerInfo (InfoLevel 2). NtdsDsaObjectGuid is the DC’s NTDS DSA objectGUID — the value used as the source DSA in replication requests.

type DCInfo struct {
    NetbiosName       string
    DNSHostName       string
    SiteName          string
    NtdsDsaObjectGuid guid.GUID
    ServerObjectGuid  guid.GUID
    IsPDC             bool
    IsGC              bool
    DSEnabled         bool
}

type KerberosKey

KerberosKey is one key extracted from Primary:Kerberos or Primary:Kerberos-Newer-Keys. KeyType is the Kerberos encryption type, Value is the raw key, Salt is the default UTF-16 salt, and IterationCount is zero for legacy records.

type KerberosKey struct {
    KeyType        uint32
    Value          []byte
    Salt           string
    IterationCount uint32
    Category       KerberosKeyCategory
}

type KerberosKeyCategory

KerberosKeyCategory identifies the KERB_STORED_CREDENTIAL[_NEW] array from which a key was extracted.

type KerberosKeyCategory string
const (
    KerberosKeyCurrent KerberosKeyCategory = "current"
    KerberosKeyService KerberosKeyCategory = "service"
    KerberosKeyOld     KerberosKeyCategory = "old"
    KerberosKeyOlder   KerberosKeyCategory = "older"
)

type MembershipGroup

MembershipGroup is one group returned by GetMemberships: the group’s distinguished name and objectGUID, plus its per-entry attribute value (e.g. SE_GROUP flags).

type MembershipGroup struct {
    DN        string
    GUID      guid.GUID
    Attribute uint32
}

type NT4ChangeLog

NT4ChangeLog is the result of IDL_DRSGetNT4ChangeLog: the opaque change-log and restart blobs (legacy NT4 BDC synchronization, [MS-DRSR] 4.1.9) and the server’s NTSTATUS. The blobs are returned verbatim (legacy NT4 SAM delta stream). Read-only. Modern DCs return ERROR_NOT_SUPPORTED.

type NT4ChangeLog struct {
    Restart        []byte
    Log            []byte
    ActualNTStatus uint32
}

type ObjectExistenceResult

ObjectExistenceResult reports whether the server’s digest matches the supplied digest. When it does not match, GUIDs contains the server’s object sequence for the requested range after applying the supplied up-to-date vector.

type ObjectExistenceResult struct {
    DigestMatches bool
    GUIDs         []guid.GUID
}

type ReplCursor

ReplCursor is one replication cursor: the up-to-dateness of a source DSA’s changes as known to the queried DC.

type ReplCursor struct {
    SourceDSAInvocationID guid.GUID
    UpToDateUSN           int64
}

type ReplFailure

ReplFailure is one KCC connect/link failure record.

type ReplFailure struct {
    DsaDN       string
    DsaObjGuid  guid.GUID
    NumFailures uint32
    LastResult  uint32
}

type ReplNeighbor

ReplNeighbor is one replication source (a repsFrom) of a naming context.

type ReplNeighbor struct {
    NamingContext         string
    SourceDsaDN           string
    SourceDsaAddress      string
    SourceDsaObjGuid      guid.GUID
    SourceDsaInvocationID guid.GUID
    ReplicaFlags          uint32
    LastSyncResult        uint32
    ConsecutiveFailures   uint32
}

type ReplPendingOp

ReplPendingOp is one queued replication operation on the DC.

type ReplPendingOp struct {
    SerialNumber  uint32
    Priority      uint32
    OpType        uint32
    NamingContext string
    DsaDN         string
    DsaAddress    string
}

type ReplicatedAttribute

ReplicatedAttribute is one attribute of a replicated object: its ATTRTYP (an OID compressed against the reply’s prefix table) and its raw value(s). Secret attributes (unicodePwd, ntPwdHistory, supplementalCredentials, …) are returned still encrypted; decryption with the session key and PEK is Phase 4.

type ReplicatedAttribute struct {
    AttrType uint32
    Values   [][]byte
}

type ReplicatedObject

ReplicatedObject is one object returned by IDL_DRSGetNCChanges: its identity (GUID and, when present, distinguished name) and its attributes (values still encrypted).

type ReplicatedObject struct {
    GUID       guid.GUID
    DN         string
    Attributes []ReplicatedAttribute
}

type ReplicationResult

ReplicationResult holds the objects from a GetNCChanges reply plus the context a later decryption pass needs: the source prefix table (to map each ATTRTYP to its OID) and the raw V6 reply (which carries the PEK list and uptodate vectors).

type ReplicationResult struct {
    Objects     []ReplicatedObject
    PrefixTable drsrtypes.SCHEMA_PREFIX_TABLE
    Reply       *drsrtypes.DRS_MSG_GETCHGREPLY_V6
}

type SiteCost

SiteCost is the replication cost from the source site to one target site (DwErrorCode is 0 on success; DwCost is the computed cost, 0xFFFFFFFF when unreachable).

type SiteCost struct {
    ToSite    string
    ErrorCode uint32
    Cost      uint32
}

type SupplementalCredentialsInfo

SupplementalCredentialsInfo is the parsed credential material in an MS-SAMR USER_PROPERTIES blob.

type SupplementalCredentialsInfo struct {
    KerberosKeys         []KerberosKey
    CleartextPassword    string
    CleartextPasswordRaw []byte
    WDigestHashes        [][16]byte
}

func ParseSupplementalCredentials

func ParseSupplementalCredentials(blob []byte) (*SupplementalCredentialsInfo, error)

ParseSupplementalCredentials parses an MS-SAMR USER_PROPERTIES blob. Unknown properties are ignored; recognized properties are bounds-checked and malformed data is rejected.

type VerifiedName

VerifiedName is the result of verifying one object name: the resolved GUID (zero if the object was not found) and its returned distinguished name.

type VerifiedName struct {
    Input string
    DN    string
    GUID  guid.GUID
    Found bool
}