HTB Fries — Complete Walkthrough (Hard/Windows)
The provided <email redacted> / D4LE11maan!! credentials worked on pgAdmin (form-encoded POST to /authenticate/login). pgAdmin 9.1.0 is vulnerable to Python eval() injection in the query tool download endpoint. Exploitation flow: 1. Login → get CSRF
Scenario
HTB Fries — Complete Walkthrough (Hard/Windows) attack path
The provided / D4LE11maan!! credentials worked on pgAdmin (form-encoded POST to /authenticate/login). pgAdmin 9.1.0 is vulnerable to Python eval() injection in the query tool download endpoint. Exploitation flow: 1. Login → get CSRF
Objective
Machine walkthrough focused on Machines evidence, validation, and reusable operator lessons.
Walkthrough flow
Phase 2: Initial Foothold — pgAdmin
Phase 3: Credential Discovery via Gitea
Phase 4: SSH to Docker Host
Phase 5: NFS GID Spoofing → Docker TLS Certs
Phase 6: Docker TLS → PWM Config
Source coverage
High source coverage
Status: complete. This article is generated from 7 sanitized Markdown sources and keeps raw flags, credentials, keys, cookies, and reusable secrets out of the rendered blog.
High confidence: the page is reconstructed from a primary walkthrough plus multiple supporting notes or evidence sources. Treat the chain as source-backed, while still checking the listed source files for sensitive values.
- <TARGET>-Fries/walkthrough.md
- HTB/<TARGET>-Fries/notes.md
- HTB/<TARGET>-Fries/attack-map.md
- HTB/<TARGET>-Fries/session-resume.md
- HTB/<TARGET>-Fries/custom-exploit-notes.md
- HTB/<TARGET>-Fries/dead-ends.md
- HTB/_knowledge/exports/ctf-lightrag-latest-203412/documents/machine__<TARGET>-Fries__notes.md.b02a6e77f5.md
Technical Walkthrough
HTB Fries — Complete Walkthrough (Hard/Windows)
Machine Info
- Name: Fries
- Difficulty: Hard
- OS: Windows (DC) + Linux (Docker Host)
- IP: <TARGET>
- Domain: fries.htb
- Creators: ruycr4ft, kavigihan
- Date: 2026-05-10
Flags
- User:
<hash redacted>—/root/user.txton Linux Docker host - Root:
<hash redacted>—C:\Users\Administrator\Desktop\root.txton DC01
Phase 2: Initial Foothold — pgAdmin <secret redacted>
pgAdmin Authentication
The provided <email redacted> / D4LE11maan!! credentials worked on pgAdmin (form-encoded POST to /authenticate/login).
<secret redacted>: Authenticated RCE
pgAdmin 9.1.0 is vulnerable to Python eval() injection in the query tool download endpoint.
Exploitation flow:
- Login → get CSRF token
- Register attacker-controlled PostgreSQL server (Pwnbox)
- Connect pgAdmin to it
- Initialize SQL editor session (
/sqleditor/initialize/sqleditor/{trans_id}/{sgid}/{sid}/{did}) - Execute any SQL query via
/sqleditor/query_tool/start/{trans_id} - Exploit: POST to
/sqleditor/query_tool/download/{trans_id}withquery_commitedcontaining Python code
Key finding: eval() executes but the download endpoint discards the return value and serves cached SQL results. Blind RCE confirmed via reverse shell callback.
Shell on pgAdmin Container
uid=5050(pgadmin) gid=0(root)at <TARGET>
Phase 3: Credential Discovery via Gitea
Gitea Access
dale / D4LE11maan!! authenticated to Gitea. Found private repo dale/fries.htb.
Git History Secret Leak
Commit 3e8ca66 ("gitignore update") deleted .env which was present in initial commit be59cce:
<secret redacted>=postgresql://root:PsqLR00tpaSS11@<TARGET>:5432/ps_dbLesson: Always check git history for removed sensitive files. .gitignore addition ≠ secret removal from history.
Phase 4: SSH to Docker Host
Password Reuse
<secret redacted>=<redacted> (from pgAdmin container env) worked for SSH:
ssh svc@<TARGET> # password: <redacted>Host Identity
- Hostname:
web - IP: <TARGET> (proxy) + <TARGET> (Docker bridge gateway)
- Docker host running all containers
Phase 5: NFS GID Spoofing → Docker TLS Certs
NFS Export Discovery
/srv/web.fries.htb *(rw,no_subtree_check,insecure)Contains certs/ directory restricted to GID 59605603 (infra managers — AD group).
Python NFS Client with GID Spoofing
Wrote a raw Python NFS v3 client (using socket + struct) that:
- Connects to mountd (port 52995) with <secret redacted> spoofing GID=59605603
- MOUNTs the export to get root filehandle
- LOOKUPs
certs/directory - READs all certificate files
Extracted: ca.pem, ca-key.pem, server-cert.pem, server-key.pem
Lesson: NFS <secret redacted> trusts whatever UID/GID the client claims. With no_subtree_check and insecure, any client can spoof credentials. Root squash only blocks UID 0, not arbitrary GID spoofing.
Phase 6: Docker TLS → PWM Config
Authz-Broker Policy
/var/lib/authz-broker/policy.json:
- CN=fries → no policy (denied)
- CN=svc → container_list, container_logs
- CN=sysadm → container (readonly)
- CN=root → all
Client Certificate Generation
Used the CA key to sign custom certs:
- CN=sysadm → listed containers
- CN=root → full Docker access (exec, cp, run)
LDAP Passback Attack
- Modified PWM's LDAP URL from
ldaps://dc01.fries.htb:636toldap://<TARGET>:9389 - Started nc listener on port 9389 (on host as svc)
- Triggered PWM login from localhost:8443
- Captured plaintext LDAP bind:
svc_infra / m6tneOMAh5p0wQ0d
Phase 7: AD Exploitation
gMSA Password Read
crackmapexec ldap <TARGET> -u svc_infra -p m6tneOMAh5p0wQ0d --gmsa
→ gMSA_CA_prod$ NTLM: <hash redacted>User Flag
Used Docker (CN=root cert) to mount host filesystem and read /root/user.txt.
ADCS ESC6 + LDAPS Authentication
- Enabled <secret redacted> via ICertAdminD2 DCOM (ManageCA right)
- Requested certificate as svc_infra with
Usertemplate + UPN=<email redacted> - Authenticated via LDAPS (Schannel bypasses SID-based strong mapping check)
- Added svc_infra to Domain Admins via LDAP shell
- Read root.txt via wmiexec as Domain Admin
Dead Ends & Failures
| Attempt | Result | Lesson |
|---|---|---|
| Brute-force PWM config password (~35 attempts) | All failed, lockout triggered | Don't guess; find the credential through the intended path |
Tomcat ..;/ path traversal | Blocked by PWM security filter | Modern PWM validates path segments |
| d.cooper password against AD | Wrong by design | HTB provides creds for non-AD services |
| Docker container breakout (PG/pgAdmin) | No caps, no socket, seccomp=2 | Containers were properly hardened |
| Gitea admin Git hooks | Blocked by app.ini <secret redacted>=false | DB changes don't override app config |
| certutil -setreg for ESC6 | Requires local admin elevation | Use DCOM ICertAdmin2 interface instead |
| Kerberos auth with forged cert | SID mismatch (strong mapping) | Use LDAPS/Schannel which bypasses SID check |
| DCSync as WEB$ machine account | <secret redacted> | Machine account lacks replication rights |
Tools Used
- nmap, ffuf, gobuster, crackmapexec, certipy, impacket suite
- Custom Python: NFS GID spoof client, <secret redacted> exploit, DCOM ESC6 enabler
- evil-winrm, hashcat, netcat
Source-Backed Dossier
The sections below are merged from companion Markdown notes for the same case. They are rendered after sanitization so the article stays precise without publishing raw flags, credentials, or target-specific secrets.
Notes
Scope
- Target: <TARGET>
- Domain: fries.htb
- OS: Windows (AD likely)
- Difficulty: Hard
- Creators: ruycr4ft, kavigihan
Connection
- Pwnbox: <TARGET> (profex0r)
- Credentials: see
loot/credentials.md
Evidence Ledger
| Timestamp | Command | Output File | Finding | Confidence | Next |
|---|---|---|---|---|---|
| 2026-05-10 | vhost fuzzing | enum/vhosts-* | Found db-mgmt05.fries.htb and code.fries.htb | High | Test web credentials |
| 2026-05-10 | pgAdmin login + <secret redacted> | exploits/cve_2025_2945.py | Authenticated RCE achieved as pgadmin in container | High | Enumerate pgAdmin/Gitea/Postgres secrets |
Attack Map
Network Topology (Two-Host Model)
[Pwnbox <TARGET>] --VPN--> [<TARGET>]
|
+---------------+---------------+
| |
[Linux Proxy <TARGET>] [DC01 <TARGET>]
- Ubuntu 22.04 - Windows Server 2019
- nginx 1.18.0 - Domain: fries.htb
- Flask (port 80) - Kerberos, LDAP, SMB
- Tomcat 9.0.99 (port 443) - DNS, WinRM, ADWS
- SSH (port 22) - SMB signing required
- PWM app (config mode) - Guest disabled
- pgAdmin 9.1.0
- Gitea 1.22.6Service Map
| Port | Service | Host | Notes |
|---|---|---|---|
| 22 | SSH (OpenSSH 8.9p1 Ubuntu) | Proxy | Password auth enabled |
| 2049 | NFS | Proxy | Public research suggests /srv/web.fries.htb export; validate live |
| 53 | DNS (Simple DNS Plus) | DC01 | Zone transfer denied |
| 80 | HTTP (nginx→Flask) | Proxy | Static restaurant site (/about, /menu only) |
| 88 | Kerberos | DC01 | Standard |
| 135/445 | SMB | DC01 | Signing required, null auth ok but no share access |
| 389/636 | LDAP/LDAPS | DC01 | Requires bind, rootDSE accessible |
| 443 | HTTPS (nginx→Tomcat/PWM) | Proxy | PWM in open configuration mode |
| 464 | kpasswd | DC01 | Standard |
| 5985 | WinRM | DC01 | Requires auth |
| 9389 | ADWS | DC01 | Standard |
Web Vhosts
| Vhost | App | Result |
|---|---|---|
| db-mgmt05.fries.htb | pgAdmin 9.1.0 | Authenticated RCE via <secret redacted>; shell as pgadmin in container |
| code.fries.htb | Gitea 1.22.6 | Valid dale session; private repo dale/fries.htb |
| pwm.fries.htb | PWM v2.0.8 | Config mode; requires config unlock material |
| web.fries.htb / fries.htb | Flask/nginx | Static public restaurant site from external view |
Docker Network
| IP | Service |
|---|---|
| <TARGET> | Docker host |
| <TARGET> | Flask :5000 |
| <TARGET> | PostgreSQL :5432, superuser + RCE |
| <TARGET> | pgAdmin :80, RCE |
| <TARGET> | Gitea :22, dale admin |
| <TARGET> | PWM :8443 |
AD Users (Confirmed via Kerberos)
- administrator
- d.cooper
- web
- svc_infra
- gMSA_CA_prod$ (gMSA, WinRM validated by operator)
PWM State
- Open configuration mode (LDAP broken)
- Config unlock material: UNKNOWN (guessing stopped per harness)
- LDAP bind: svc_infra → ldaps://dc01.fries.htb:636
- LDAPS failure reason: certificate mismatch (server cert subject empty, not in trust store)
- svc_infra bind material: <secret redacted> (stored in config, TLS fails before bind tested)
- REST API: all user-facing endpoints return 5053 (app unavailable)
- Reference pages accessible: environment.jsp, rest.jsp, settings.jsp
- Tomcat version: 9.0.99
Sensitive Values (see loot/credentials.md)
- d.cooper → <secret redacted> everywhere (provided value wrong on this instance, not expired)
- svc_infra → bind value unknown, stored in PWM config XML
Ranked Hypotheses
| # | Hypothesis | Confidence | Status |
|---|---|---|---|
| 1 | svc SSH using recovered pgAdmin admin value | HIGH | NEXT |
| 2 | NFS export access to Docker TLS cert material | HIGH | AFTER SSH |
| 3 | Docker TLS authz CN bypass to access Docker API | HIGH | AFTER CERTS |
| 4 | Docker/PWM config volume read through Docker API | HIGH | AFTER API |
| 5 | Extract or coerce PWM svc_infra bind material | HIGH | AFTER CONFIG |
| 6 | Linux/NFS/Docker host search for user.txt | HIGH | NEXT FOR USER |
| 7 | gMSA ADCS ESC6+ESC16 direct issuance | HIGH | NEXT FOR ROOT |
| 8 | Certificate Manager Deny ACE removal | LOW | FALLBACK ONLY |
Flag State
- User flag captured from
/root/user.txton the Linux Docker host through Docker volume access as root. Raw value is stored only inloot/. - Root flag pending. Current root path is ADCS after gMSA WinRM; ESC7
ManageCAis confirmed, but Certificate Manager issuance is blocked by an explicit Deny ACE.
Exhausted Vectors
- d.cooper password (35+ variants, Kerberos/NTLM/SSH)
- PWM config brute force (~35 <password redacted>, lockout-limited)
- Flask SSTI (no injection points)
- Nginx alias/path traversal on port 80
- Tomcat ..;/ bypass (caught by PWM security filter)
- SMB/LDAP/RPC anonymous enumeration
- Vhost/DNS subdomain brute
- File extension fuzzing on port 80
- RID cycling (access denied)
- HTTP request smuggling (partial, no useful result)
- HTTPS catch-all produces false positive 200s for all non-/pwm/ paths
Session Resume
Completion State: <secret redacted>
Current Checkpoint
- Reset completed; current target IP is <TARGET>. Prior target IP was <TARGET>.
- Full TCP scan complete and shows a split external surface: Ubuntu proxy services plus exposed DC services.
- AD confirmed: DC01.fries.htb, fries.htb domain.
- Provided d.cooper credential consistently fails across Kerberos/NTLM/SAMR before and after fresh reset. Treat it as intentionally invalid for AD/SMB/SSH, not stale machine state.
- Vhosts discovered: db-mgmt05.fries.htb runs pgAdmin 9.1.0; code.fries.htb runs Gitea 1.22.6.
- Provided d.cooper web credential works in pgAdmin.
- <secret redacted> yielded authenticated RCE as pgadmin in container <TARGET>.
- Docker network mapped: host .1, Flask .2:5000, PostgreSQL .3:5432, pgAdmin .4:80, Gitea .5:22, PWM .6:8443.
- Gitea access obtained as dale; private repo dale/fries.htb contains Flask source.
- Gitea commit history leaked PostgreSQL superuser DSN.
- PostgreSQL superuser access achieved;
COPY FROM PROGRAMgives RCE as the PostgreSQL service user in container <TARGET>. - Dale promoted to Gitea admin via database modification; only one Gitea repo was found.
- Container breakout triage completed: no Docker socket/API, no host mounts, no useful capabilities, seccomp enforced. Gitea admin RCE is blocked by app.ini despite DB flag; Actions disabled.
- PWM v2.0.8 remains in configuration mode; svc_infra bind value still not extracted.
- WinRM is reachable from the container to DC, but no valid AD credential is known.
- Update from active operator:
svc_infrawas recovered, gMSAgMSA_CA_prod$was read, and WinRM to DC01 is working as the gMSA. - User flag captured from
/root/user.txton the Linux Docker host through Docker volume access as root. Raw flag is stored only underloot/. - Root path is ADCS. ESC7
ManageCAis confirmed, but Certificate Manager path is blocked by an explicit Deny ACE. Treat removal of Deny ACE as a risky fallback unless safer ManageCA paths are exhausted.
Ranked Hypotheses
| # | Hypothesis | Evidence | Missing Proof | Cheapest Validation | Confidence | Status |
|---|---|---|---|---|---|---|
| 1 | User flag is on Linux/NFS/Docker side, not Windows desktops | Live capture from /root/user.txt on Linux Docker host | None | Save sanitized path and raw flag only in loot/ | SOLVED | DONE |
| 2 | ESC6+ESC16 direct issuance avoids Certificate Manager Deny | gMSA has DC WinRM; ESC7 present; Deny is on Certificate Manager path | CA flags/template state and enrollable template | Use Certify/Certipy read-only enumeration, then enable ESC6/ESC16 only if ManageCA is confirmed | HIGH | NEXT |
| 3 | Deny ACE only blocks officer/issue-request path, not ManageCA CA flag changes | ESC7 separates ManageCA from ManageCertificates | certipy find/Certify ACL evidence | Confirm gMSA retains ManageCA despite Deny | HIGH | NEXT |
| 4 | SubCA/issue-request path is blocked by Certificate Manager Deny | Operator reports explicit Deny ACE | Request/issue error evidence | Avoid this path unless Deny can be safely removed | MEDIUM | HOLD |
| 5 | Removing Deny ACE or changing CA security descriptor is possible but risky | ManageCA may manage CA security; gMSA has DC execution | WriteDACL/ManageCA proof plus rollback | Only after snapshot/evaluator gate | LOW | FALLBACK |
Closed Branches
- Provided d.cooper credential — WRONG password on this instance (<secret redacted> / <secret redacted> / SAMR <secret redacted>), not expired.
- Anonymous LDAP/SMB — access denied
- Guest account — disabled
- Port 80 SSTI — no injection points found
- Vhost fuzzing — only web/pwm/dc01, no unique content
- HTTPS catch-all — returns 200 with redirect for all paths (false positive)
- ASREPRoast — d.cooper doesn't have <secret redacted>
- Generic container breakout from pgAdmin/PostgreSQL — no Docker socket/API, no host mounts, no useful capabilities, seccomp enforced.
- Gitea admin to RCE — DB flag alone is insufficient; app.ini blocks hooks and Actions are disabled.
Last RAG Tags: NONE (not queried yet)
Evaluator Decision
SPLIT <secret redacted>. For user flag, stop searching Windows desktops and search Linux/NFS/Docker host filesystem. For root, do not remove Deny ACE first. Validate whether gMSA ManageCA can enable ESC6+ESC16 and use a directly enrollable client-auth template with Administrator UPN + SID.
Blocker
User flag is captured. DC WinRM exists as gMSA, but Domain Admin/root is not achieved.
Next Checkpoint
After read-only ADCS enumeration as gMSA and validation of the safest root path.
Notes
<secret redacted> pgAdmin RCE
- Target: db-mgmt05.fries.htb, pgAdmin 9.1.0.
- Authenticated as d.cooper web user.
- RCE achieved through Query Tool download endpoint.
- Shell context: uid=5050(pgadmin), container IP <TARGET>.
- Current exploit scripts live under
exploits/; preserve working payload and exact command/output before further edits.
Post-Foothold Priorities
- Validate Linux host SSH as
svcusing the recovered pgAdmin admin value. - From Linux host, validate NFS export
/srv/web.fries.htband access to Docker TLS cert material undercerts/. - Validate Docker TLS on localhost/gateway :2376 with read-only
_ping,containers/json, andvolumes. - If authz blocks current cert identity, inspect policy/cert materials and generate a client cert with an allowed CN only after recording an evaluator gate.
- Use Docker API read-only archive/cp to extract PWM
/config/PwmConfiguration.xml. - Extract/decrypt/crack PWM config material or perform controlled LDAP passback to recover
svc_infra. - Use validated AD credentials through WinRM/LDAP to progress to user/root.
PostgreSQL RCE
- PostgreSQL superuser DSN recovered from Gitea commit history.
COPY FROM PROGRAMgives command execution in PostgreSQL container <TARGET>.- Preserve exact SQL payloads and command outputs under
enum/orexploits/.
Docker TLS / PWM Config Research Path
- Public Fries-specific research indicates the intended next bridge is not generic container breakout, but SSH to the Linux host as
svc, NFS access to web files/certs, Docker TLS authz identity manipulation, then Docker API read of PWM/config. - Keep Docker interaction read-only until PWM config path is proven.
Dead Ends
Credential Guessing / Spraying
- Provided d.cooper credential fails consistently via Kerberos, NTLM, and SAMR before and after fresh reset. Treat as intentionally wrong for AD/SMB/SSH.
- PWM config unlock guessing stopped. Lockout behavior plus weak evidence makes this poor signal and high overhead.
- Internal PWM unlock guessing remains low priority even after container foothold. There are higher-signal credential stores now.
- After PostgreSQL RCE, do not test discovered web/database values against AD repeatedly. They are service-scoped unless live AD auth proves otherwise.
Anonymous Windows Enumeration
- Anonymous LDAP/SMB/RPC did not produce share, RID, or directory access beyond basic service/domain confirmation.
- Guest account unavailable.
- ASREPRoast did not identify a usable no-preauth user.
HTTP / HTTPS Broad Enumeration
- Port 80 Flask app reported static with no useful parameters or SSTI surface.
- HTTPS catch-all creates false-positive 200s for arbitrary paths; future fuzzing must baseline length/body, not status alone.
- Tomcat traversal attempts reportedly hit PWM security filtering; keep exact payload/output artifacts before considering this fully closed.
- Docker API and registry probes reported unreachable; verify artifacts are mirrored before closing the Docker branch.
- Direct PWM config-manager probing is low value: PWM v2.0.8 source shows config-manager routes require config-manager authentication in <secret redacted> mode.
Container Breakout
- PostgreSQL and pgAdmin containers: no Docker socket/API, no host filesystem mounts, no useful Linux capabilities, seccomp enforced.
- Gitea admin to RCE:
allow_git_hookdatabase flag alone is insufficient because app.ini blocks hooks; Actions are disabled. Revisit only if filesystem access to Gitea config or another Gitea execution primitive appears.
Notes
Scope
- Target: <TARGET>
- Domain: fries.htb
- OS: Windows (AD likely)
- Difficulty: Hard
- Creators: ruycr4ft, kavigihan
Connection
- Pwnbox: <TARGET> (<<secret redacted>>)
- Credentials: see
loot/credentials.md
Evidence Ledger
| Timestamp | Command | Output File | Finding | Confidence | Next |
|---|---|---|---|---|---|
| 2026-05-10 | vhost fuzzing | enum/vhosts-* | Found db-mgmt05.fries.htb and code.fries.htb | High | Test web credentials |
| 2026-05-10 | pgAdmin login + <secret redacted> | exploits/cve_2025_2945.py | Authenticated RCE achieved as pgadmin in container | High | Enumerate pgAdmin/Gitea/Postgres secrets |