---
title: "SMB Signing Is On. That Closed One Edge, Not the Graph."
description: "Signing is a per-protocol control, and NTLM over HTTP cannot be signed at all. The useful question is never whether signing is enabled — it is which relay edges are still open."
date: 2025-09-11
author: Sevban Dönmez (@jankesec)
canonical: https://jankesec.com/posts/ntlm-relay-triage/
---

## NTLM relay in 60 seconds

"SMB signing is enforced across the estate" is usually where the relay conversation ends.
It is a real improvement and it closes a real attack. It also gets treated as though it
settled the question, and it did not, because signing is not a property of the domain. It
is a property of one protocol at a time.

Relay is a graph problem. Signing removes edges. It does not remove the graph.

## What signing actually protects

An NTLM relay needs two things: authentication you can capture, and a destination that will
accept it without noticing it was replayed. Message signing breaks the second half — the
session is bound to the original exchange, so a relayed handshake fails.

The catch is that each protocol decides this for itself, and they have not converged:

**SMB** has had signing for a long time. Domain controllers require it by default. Member
servers and workstations historically did not, which is why relay lists were mostly other
people's laptops. Recent Windows builds have started enabling it on both client and server
by default, so the classic SMB-to-SMB edge really is closing — slowly, and only as fleets
turn over.

**LDAP** is the interesting one, because it has two separate controls that people conflate.
Signing covers plain LDAP on 389. Channel binding covers LDAPS on 636. Turning on one and
assuming it covered the other is common, and the gap is exactly where relayed
authentication lands.

**HTTP** cannot sign NTLM at all. There is no signing in the scheme. An HTTP endpoint that
accepts NTLM is a relay destination and always will be; the only protection is channel
binding on the TLS side.

That last line is why this survives hardening programmes. The work goes into SMB, which is
legible and has a GPO, and the protocol with no protection at all is the one nobody has a
checkbox for.

<figure class="diagram">
<svg viewBox="0 0 700 250" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Relay edges by protocol: SMB to SMB is closed by signing, while HTTP coercion relayed to LDAP stays open because NTLM over HTTP cannot be signed.">
<defs>
<marker id="rl-a" viewBox="0 0 8 8" refX="7" refY="4" markerWidth="7" markerHeight="7" orient="auto"><path d="M0,0 L8,4 L0,8 z" class="dg-arrow" /></marker>
<marker id="rl-c" viewBox="0 0 8 8" refX="7" refY="4" markerWidth="7" markerHeight="7" orient="auto"><path d="M0,0 L8,4 L0,8 z" class="dg-arrow-crit" /></marker>
</defs>
<rect x="0" y="86" width="176" height="72" rx="8" class="dg-box" />
<text x="16" y="112" class="dg-label">Coerced host</text>
<text x="16" y="132" class="dg-muted">AUTHENTICATION</text>
<text x="16" y="149" class="dg-muted">YOU CAN CAPTURE</text>
<line x1="178" y1="108" x2="292" y2="60" class="dg-line" marker-end="url(#rl-a)" />
<text x="196" y="76" class="dg-muted">OVER SMB</text>
<line x1="178" y1="136" x2="292" y2="188" class="dg-line-crit" marker-end="url(#rl-c)" />
<text x="192" y="176" class="dg-crit">OVER HTTP</text>
<rect x="300" y="24" width="400" height="66" rx="8" class="dg-box" />
<text x="316" y="48" class="dg-label">SMB target</text>
<text x="316" y="68" class="dg-mono">signing required</text>
<text x="316" y="84" class="dg-muted">RELAY FAILS — THE EDGE SIGNING ACTUALLY CLOSED</text>
<rect x="300" y="160" width="400" height="82" rx="8" class="dg-box-accent" />
<text x="316" y="184" class="dg-label">LDAP :389 / LDAPS :636</text>
<text x="316" y="204" class="dg-mono">signing not enforced · channel binding off</text>
<text x="316" y="220" class="dg-crit">RELAY SUCCEEDS</text>
<text x="316" y="236" class="dg-muted">NTLM OVER HTTP HAS NO SIGNING IN THE SCHEME</text>
</svg>
<figcaption>Signing is decided per protocol. Closing SMB removes one edge; the coercion simply moves to a transport that cannot sign, and lands on a destination whose own signing was deferred.</figcaption>
</figure>

## The asymmetry worth internalising

Relay is cross-protocol. You do not have to relay SMB to SMB.

The productive direction is inbound over a protocol that cannot sign, outbound to a
protocol whose signing is not enforced. Coerce authentication over HTTP, relay it to LDAP,
and SMB signing is not part of the story at any point.

Which makes the WebClient service the single most underrated item on an internal
assessment. It is what makes a Windows host speak WebDAV, it is off by default on servers
but frequently running on workstations, and it can often be started remotely. A host with
WebClient running is a host whose authentication you can obtain over HTTP — unsigned, by
design.

## Triage: three questions in order

The scan output everyone produces is a list of hosts without SMB signing. That is one edge
of one protocol. The three questions that actually shape the finding:

**1. What can I coerce, and over which protocol?** Not "can I coerce" — the coercion
primitives are well covered. Which transport the resulting authentication arrives on is what
determines everything downstream.

**2. Which destinations accept it unsigned?** Per protocol, not per host. LDAP and LDAPS are
separate answers.

**3. What does that destination let me do?** Relaying to LDAP as a workstation account is
not the same as relaying as a domain controller account. The identity you capture is the
ceiling on the outcome.

Enumeration for the first two is quick:

```
# Which hosts still permit unsigned SMB — the familiar list, and the least useful one
$ nxc smb 10.0.0.0/24 --gen-relay-list relay-targets.txt

# The question that usually gets skipped: is LDAP signing and channel binding enforced?
$ nxc ldap dc01.lab.local -u svc-audit -p '...' -M ldap-checker
LDAP-CHECKER  dc01.lab.local  LDAP signing NOT enforced
LDAP-CHECKER  dc01.lab.local  LDAPS channel binding is set to "never"

# Which hosts will speak WebDAV — i.e. hand you unsigned auth over HTTP
$ nxc smb 10.0.0.0/24 -u svc-audit -p '...' -M webdav
WEBDAV  10.0.0.51  WebClient service is running
```

Three lines, and the third one is the one that decides whether the first one mattered.

## What that turns into

With unsigned LDAP and an HTTP coercion source, the relay does not need SMB at all:

```
$ ntlmrelayx.py -t ldap://dc01.lab.local --delegate-access --no-smb-server -smb2support
[*] Servers started, waiting for connections
[*] HTTPD: Received connection from 10.0.0.51
[*] Authenticating against ldap://dc01.lab.local as LAB/WS02$ SUCCEED
[*] Delegation rights modified successfully!
[*] WS02$ can now impersonate users on WS02$ via S4U2Proxy
```

That last line should look familiar if you have read the delegation note — the relay's
output is an RBCD write. The two findings are the same finding wearing different clothes:
something that could write to a computer object, and a path to becoming that something.

## Evidence matrix

| Signal | What it proves | Negative control | Defender verification |
| --- | --- | --- | --- |
| Source accepts an authentication trigger over HTTP/WebClient | A coercible credential can leave the source over a relayable transport | Disable WebClient on the lab source and confirm the HTTP edge disappears | Inventory WebClient state and authentication-capable HTTP surfaces by host role |
| LDAP target does not require signing or channel binding for the tested path | The destination will accept relayed NTLM without binding it to the original channel | Enforce the relevant LDAP control and expect relay failure | Validate domain-controller policy and capture the reject event, not only the registry value |
| Controlled relay performs a reversible RBCD or equivalent write | Source and destination edges compose into an operational chain | Restore the attribute and repeat against a hardened target | Audit directory writes and correlate source coercion, relay host, and changed object |
| SMB-to-SMB relay fails while HTTP-to-LDAP succeeds | SMB signing closed one edge but not the relay class | Test each protocol pair rather than extrapolating from SMB | Maintain a protocol-pair matrix for sources, transports, and destinations |

## The pattern I keep seeing

Signing gets deployed as a project with an end date. It is scoped to SMB because SMB is
where the guidance was written, it gets rolled out, the ticket closes, and the estate is
recorded as remediated. LDAP signing gets deferred because it breaks legacy binds and
someone has to find them first. Channel binding gets deferred for the same reason. WebClient
never comes up at all, because it is not a security control and nobody inventories it.

Two years later the relay finding comes back, and it looks like a regression. It never was
one. The closed edge stayed closed; the report just described one edge as though it were the
whole graph.

## What to hand the defenders

**Report per protocol, not per estate.** SMB signing enforced, LDAP signing not enforced,
LDAPS channel binding off. Three states, three owners, three tickets. A single line saying
"NTLM relay is possible" gets closed by pointing at the SMB rollout.

**Name the coercion transport.** If the path ran over HTTP, say so explicitly, because it
changes which mitigation is relevant and pre-empts the "but signing is on" response before
it happens.

**WebClient is the cheap win.** It is not needed on the overwhelming majority of
workstations, disabling it removes the unsigned HTTP source entirely, and unlike LDAP
hardening it does not require hunting for legacy dependencies first.

**Then the durable one.** Channel binding and LDAP signing enforced, and a plan to retire
NTLM where it can be retired. That is slow work, which is precisely why it should be a
separate finding from the things that can be done this quarter.