Skip to content

Bug: CoerceAndRelay edges not created when RestrictSendingNTLMTraffic registry key is absent (default Windows config) #2534

Description

@chryzsh

Description:

CoerceAndRelay edges (LDAP, LDAPS, ADCS, SMB) are never created when the RestrictSendingNTLMTraffic registry key does not exist on target computers, which is the default on every Windows machine where the "Network security: Restrict NTLM: Outgoing NTLM traffic to remote servers" policy has never been configured.

This is a regression introduced by #1787. Before that PR, the NTLMRegistryInfo fields were non-pointer uint types, so an absent registry key defaulted to 0, and 0 == 2 evaluated to false (not restricted), which is correct. After #1787 changed the fields to *uint pointers to support nullable values, an absent key becomes nil, which causes the ingestion to set RestrictOutboundNTLM to nil on the graph node. The analysis in PostNTLM (packages/go/analysis/ad/ntlm.go:96-101) then fails to read the property as a boolean and skips the computer entirely (fail-closed), excluding it from UnprotectedComputersCache and blocking all four edge types.

Per Microsoft's documentation:

"This state of being not defined is the same as Allow all, and the device will allow all NTLM authentication requests when the policy is deployed."

Since most environments never configure this policy, this effectively prevents CoerceAndRelay edges from being created in the majority of deployments.

Are you intending to fix this bug?

Yes. I can submit a PR, but the guidelines says to create an issue first. I'd like to get your take on this before I submit the PR.

Component(s) Affected:

  • API

Steps to Reproduce:

  1. Collect data with SharpHound (--collectionmethods all) as Domain Admin against an environment where the RestrictSendingNTLMTraffic registry key has never been configured (the Windows default)
  2. Ingest data into BloodHound CE with ntlm_post_processing feature flag enabled
  3. Verify prerequisites are met: SMB signing disabled on some computers, WebClient running, LDAP signing not enforced on DCs, vulnerable ADCS templates
  4. Run the built-in "All coerce and NTLM relay edges" query or:
    MATCH p = (n:Base)-[:CoerceAndRelayNTLMToLDAP|CoerceAndRelayNTLMToLDAPS|CoerceAndRelayNTLMToADCS|CoerceAndRelayNTLMToSMB]->(:Base) RETURN p LIMIT 500
    
  5. Zero results returned despite all prerequisites being met

Expected Behavior:

When NTLMRegistryData.Collected is true and RestrictSendingNtlmTraffic is nil (key absent), RestrictOutboundNTLM should be set to false (not restricted), reflecting the Windows default of "Allow all." CoerceAndRelay edges should be created for computers meeting the other prerequisites.

Actual Behavior:

RestrictOutboundNTLM is set to nil in packages/go/ein/ad.go:110. The analysis in packages/go/analysis/ad/ntlm.go:96-101 fails to read the property as a boolean, treats the computer as protected (fail-closed), and excludes it from UnprotectedComputersCache. No CoerceAndRelay edges are created for any of the four types.

The registry key simply does not exist on a default Windows Server 2025 installation:

C:\>reg query HKLM\SYSTEM\CurrentControlSet\Control\Lsa\MSV1_0

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa\MSV1_0
    Auth132    REG_SZ    IISSUBA
    NtlmMinClientSec    REG_DWORD    0x20000000
    NtlmMinServerSec    REG_DWORD    0x20000000
    Auth2    REG_SZ    RASSFM

SharpHound correctly reports this as NTLMRegistryData.Collected: true with RestrictSendingNtlmTraffic: null.

Environment Information:

BloodHound: CE v8.7.0 (current main)

Collector: SharpHound 2.11.0

OS: Windows Server 2025 Standard (target DC/computers), Ubuntu 25.10 (BH host)

Docker: Docker Compose deployment

Potential Solution (optional):

In packages/go/ein/ad.go, change line 110 from:

itemProps[ad.RestrictOutboundNTLM.String()] = nil

to:

itemProps[ad.RestrictOutboundNTLM.String()] = false

This only affects the case where registry data was successfully collected (Collected=true) but the key is absent. Unreachable computers where collection failed remain unaffected (property stays unset, analysis still skips them with fail-closed behavior preserved).

I was also worried there may a scenario where the key is absent but NTLM is restricted. However, all I've found is that absence of the key is definitively "Allow All" per Microsoft. The only way to restrict is to explicitly create the key with value 1 or 2. The nil default in #1787 was arguably a bit conservative for this specific property. It makes sense for other registry values where absence of the key might be ambiguous, but RestrictSendingNTLMTraffic has a documented default.

Note that #1440 proposed a more comprehensive approach with a UI-configurable default value, but was closed in favor of #1787.

Related Issues:

Contributor Checklist:

  • I have searched the issue tracker to ensure this bug hasn't been reported before or is not already being
    addressed.
  • I have provided clear steps to reproduce the issue.
  • I have included relevant environment information details.
  • I have attached necessary supporting documents.
  • I have checked that any JSON files I am attempting to upload to BloodHound are valid.

Disclosure: I used LLM/AI to assist me in tracing down the issue in the code. I've reviewed the proposed fix, understand everything about it, done my own research on this issue, and feel confident about sharing this.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingtriageThis issue requires triaging

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions