Odyssey

Challenge Lab (Hard) - by Ryan Yager

The following post by 0xb0b is licensed under CC BY 4.0arrow-up-right


Scenario

Objective / Scope

You are a member of the Hack Smarter Red Team and have been assigned to perform a black-box penetration test against a client's critical infrastructure. There are three machines in scope: one Linux web server and two Windows enterprise hosts.

The client’s environment is currently in a degraded state due to ongoing migration efforts; the Domain Controllers are experiencing synchronization failures. Consequently, standard automated LDAP enumeration tools (such as BloodHound) are expected to fail or return unreliable data. The client wants to assess if an attacker can thrive in this "broken" environment where standard administrative tools are malfunctioning.

Note From Ryan Yager

Odyssey was built off a recent engagement that I had where the DC's were not syncing correctly. This caused a lot of problems during the engagement. We also had to go through a proxy, which made tools like LDAP very hard to use. Your normal tools may fail... can you think outside the box?

Summary

chevron-rightSummaryhashtag

In Odyssey we enumerate a multi-host Active Directory environment consisting of a domain controller, a Windows workstation, and a Linux web server. Exploiting a Server-Side Template Injection (SSTI) vulnerability on the web portal, we achieve remote code execution and gain a reverse shell as www-data, pivoting to root on the web server through exposed SSH keys. Discovering credentials within configuration files, we authenticate to the internal workstation as ghill_sa, a local Backup Operator. Using registry hive extraction and offline hash dumping, we escalate to local Administrator on WKST-01 and uncover additional credentials for domain users. Pivoting into the domain, we leverage bbarkinson's privileges to create a controlled machine account, enumerate with SharpHound the domain, and identify a GenericWrite misconfiguration on the Finance GPO. Abusing this through pyGPOAbuse, we add bbarkinson to the Domain Admins group, authenticate to the domain controller, and retrieve the final flag.

Recon

In our initial reconnaissance phase, we perform a port scan on every available machine and manually probe the services available.

DC-01

We use rustscan -b 500 -a 10.176.156 -- -sC -sV -Pn to enumerate all TCP ports on the DC-01 machine, piping the discovered results into Nmap which runs default NSE scripts -sC, service and version detection -sV, and treats the host as online without ICMP echo -Pn.

A batch size of 500 trades speed for stability, the default 1500 balances both, while much larger sizes increase throughput but risk missed responses and instability.

Our RustScan of 10.1.176.156 identified a Windows domain controller named DC01.hsm.local in the hsm.local domain. Exposed services include DNS 53, Kerberos 88/464, multiple MSRPC endpoints 135, 593, 49664+, SMB 139/445, LDAP and LDAPS 389/636/3268/3269 tied to Active Directory, RDP 3389, WinRM 5985, and .NET Remoting 9389. This indicates a fully integrated Windows AD environment where LDAP/LDAPS and Kerberos provide authentication, SMB and RPC enable remote management, and RDP/WinRM serve as remote access points.

WKST-01

We use rustscan -b 500 -a 10.1.196.123 -- -sC -sV -Pn to enumerate all TCP ports on the WKST-01 machine, piping the discovered results into Nmap which runs default NSE scripts -sC, service and version detection -sV, and treats the host as online without ICMP echo -Pn.

A batch size of 500 trades speed for stability, the default 1500 balances both, while much larger sizes increase throughput but risk missed responses and instability.

On the WKST-01 host we were able to identify SMB 139/445, RDP 3389 and some MSRPC endpoints 49669+.

SMB

We try to access the SMB service anonymously, but without success. At least we are able to identify a Windows 11 / Server 2025 Build 26100 system.

Web-01

We use rustscan -b 500 -a 10.1.237.199 -- -sC -sV -Pn to enumerate all TCP ports on the Web-01 machine, piping the discovered results into Nmap which runs default NSE scripts -sC, service and version detection -sV, and treats the host as online without ICMP echo -Pn.

A batch size of 500 trades speed for stability, the default 1500 balances both, while much larger sizes increase throughput but risk missed responses and instability.

Here we only have SSH open on port 22 and a web server running on port 5000. The host is a Linux machine.

We visit the website hosted on port 5000 and have the Odyssey Portal in front of us. We are asked to enter our template, prefilled is the command ping.

At the /support page we are able to identify an email address.

Shell as ghill_sa on Web 01

We stay at the initial page enumerated, the index page and test the input for different types of attacks like command injection, SSTI injection and so on. We start with a simple character.

The character gets rendered, no issues yet.

Next, we test for Server Side Template Injection (SSTI).

With {{7*7}} (a common SSTI test payload) we can check if the application evaluates it and returns 49. If so it means that user input is being rendered by a template engine like Jinja2 without proper sanitization.

We see it gets evaluated. So we have an SSTI with which we might get an RCE.

We look for a simple Jinja2 SSTI payload and the first resource I like to refer to is the one by Ingo Kleiber. We try the following payload...

... and we see it gets evaluated. We are www-data.

Next, we adapt the payload to spawn a reverse shell using busybox. We set up a listener using Penelope before. We enter the payload...

... and receive a reverse shell.

Shell as root on Web01

While enumerating the targets, we notice the processing of ssh key files in .bash_history. We look for these.

The user cannot be determined from the authorized_keys file. Nevertheless, we copy the key to our system.

We adjust the permissions on the key and then attempt to log in as root to Web-01 via SSH, which is successful. We find the first flag in /root/user.txt on Web-01.

Shell as ghill_sa on WKST-01

In the cronjobs, we find an entry that writes /etc/update.conf to \dc01.hsm.local\share.

If we examine the /etc/update.conf file, we find the credentials of the user ghill_sa.

We try those credentials using RDP on WKST-01 and are able to successfully log in. We are now ghill_sa on WKST-01. But no flags yet.

If we try to authenticate against smb and rdp using Netexec like follows it will fail.

With --local-auth we force Netexec to authenticate against the local SAM database of WKST-01 instead of attempting domain authentication, which could fail recalling the issue in the scenario. The following command works because the credentials are valid locally on the workstation, not in the domain context.

Access as Administrator on WKST-01

On WKST-01 as ghill_sa we see we are in the Backup Operators group.

As we are part of Backup Operators, we have permission to copy the SAM, SYSTEM, and SECURITY hives.

We might fail doing it locally, but we can back these up remotely using Impackets reg.py.

We now use reg.py to back up the hives directly to our share. To do this, we use smbserver.py to start the service.

We use backup to save all three of the aforementioned hives to the share.

circle-info

This can lead to timeouts and possibly corrupt the files. Alternatively, the backups can be stored on the system locally using reg.py, for example in C:\Users\ghill_sa\Desktop, and then exfiltrated using any means of your choice.

Once we have successfully extracted the hives, we can use secretsdump.py to extract the hashes from SAM and SYSTEM.

We are able to authenticate as the local Administrator using local-auth.

From there we are able to use smbclient to explore the C: drive and get the second flag.

Obfuscation of SharpHound

circle-exclamation

We clone the SharpHound repository and the Codeception repository:

Next, we need to compile Codeception first:

Open the solution with Visual Studio. Make sure it's set to Release, and Build.

Once Codecepticon has been compiled, all the required files will be under the Release directory.

We can either generate the command using the CommandLineGenerator.html or use the provided command of the blog. We'll use the command from the blog with slight adjustments to the path and profile regarding SharpHound:

--module csharp Defines the module we want to use, current options are: csharp, powershell, vba, and sign.

--action obfuscate Define the action - specific to the module selected above.

--path "C:\code\Rubeus\Rubeus.sln" Path to the solution you are targeting.

--map-file "C:\code\Rubeus\Obfuscated-Mapping.html" This output file will contain a mapping between original and obfuscated values - something we will refer to further down this post. As long as you are using the final obfuscated executable, you need this file in your life - do not delete it.

--profile sharphound Define a profile (from a pre-existing supported list). Just to clarify, this does not mean that Codecepticon only supports applications that have a profile. A profile is just extra tweaks that Codecepticon performs in order to add some final touches that are specific to that tool.

--rename all Rename everything - namespaces, classes, enums, etc.

--rename-method markov Set the identifier renaming method to markov which is auto-generation of "words that look English, but aren't". This is for helping with keeping the entropy of the executable lower.

--markov-min-length 3 --markov-max-length 9 All auto-generated words will be between 3 and 9 characters.

--markov-min-words 3 --markov-max-words 4 Each obfuscated identifier will be a combination of 3 to 4 auto-generated words.

--string-rewrite Define that we also want to rewrite strings.

--string-rewrite-method file Define the string rewrite method as file. This means that all strings will be taken out of the executable and saved in an external file, that has to be present during execution (in order to load the strings during runtime). This was implemented to minimise the risk of AV/EDRs detection - can't scan what isn't there, right?

--string-rewrite-extfile "C:\code\Rubeus\debug.log" Specify the location of the external file where all the strings will be saved in.

After running the command the SharpHound project has been modified.

Furthermore we get an Obfuscated-Mapping.html file that provides information about the obfuscated parameters. Since these are also obfuscated.

Now open the SharpHound project in Visual Studio Code and compile the obfuscated version. We receive the binary (called SymmeAntsFome.exe in this case ) and a debug.log. The debug.log is required to rewrite the strings back to the original values, this file is required to run the obfuscated binary.

From the Obfuscated-Mapping.html we identify that collectionmethods resolves to FlablersEngriesToxicate

Unfortunately we did not find the All parameter passed to collectionmethods but we can identify it be reviewing the original and modified source and find it. In this case: EllyJokenoidMultist

So the following command...

...translates to:

If we haven't already done so, we start our SMB server...

... and mount the share on WKST-01

Next, we copy the obfuscated Sharphound.exe and the debug.log to the machine.

And run it. We see its unable to get the current domain.

After further testing, we have determined that WKST-01 cannot reach the domain controller via name resolution. We therefore need to update this in the network configuration.

For now we generate the Hosts file with the user bbarkinson, which we have also received the hash from the SAM and SYSTEM hive dump.

Shell as Administrator on WKST-01

To change the network configuration on WKST-01, we need an interactive session as admin. We already have the hash. Using NetExec, we can execute commands on the system. We simply change the local admin password as follows:

After issuing the command, we test whether we can authenticate with the new password set. We are successful.

We log in via RDP as Administrator.

Now that we are administrators, we can disable Microsoft Defender.

Now we can also use the non-obfuscated version of SharpHound without any problems. This makes things easier.

In Control Panel -> Network and Internet -> Network and Sharing Center

we change the Adapter configuration Adapter -> Properties -> Internet Protocol Version 4 to set the preferred DNS server to the IP of the domain controller DC-01.

Now we are able to reach out to the domain controller, but can't authenticate as the local Administrator.

Shell as LOCAL SYSTEM (WKST-01$)

As a local administrator we can spawn a NT Authority System shell via the Sysinternals PsExec.

This will allow us to authenticate agains the domain controller, because we will become LOCAL SYSTEM / WKST-01$. With that we have the permission to perform LDAP queries and have sufficient rights for the SharpHound collection. We did it already in Hoppers Origins: https://0xb0b.gitbook.io/writeups/tryhackme/2025/advent-of-cyber-25-side-quest/hoppers-origins#bloodhound-enumeration-2arrow-up-right

circle-info

Run PowerShell as Administrator to be able to use PsExec.exe

We spawn the powershell session as NT Authroity System usin PsExec.

circle-info

In my RDP session, I had an error where my keystrokes were not recognized in the newly spawned PowerShell terminal. If this is the case, the commands can also simply be copied into the terminal.

Now we are able to run SharpHound.exe and collect the data.

We transfer the files to our attacker machine and ingest the data to BloodHound.

We see that the user bbarkinson has a GenericWrite permission over the FINANCE POLICY.

This allows us to update this GPO to execute command on behalf of an Administrator:

Access as bbarkinson on DC-01

We see we can authenticate ourselves to the domain controller using bbarkinson's hash.

Shell as bbarkinson (added to Domain Admins) on DC-01

We are recalling the machine Sysco where we already abused such permissions over a GPO.

With pyGPOabuse we can update an existing GPO and add or modfiy users and groups:

pyGPOabuse, update an existing GPO - add a local admin pygpoabuse 'domain'/'user':'password' -gpo-id "12345677-ABCD-9876-ABCD-123456789012"

Our idea is now to change the password of bbarkinson and add that user to the Domain Admins group. We do that with the following command:

After a short duration we are able to authenticate as bbarkinson.

We are able to RDP into the domain controller as bbarkinson, whom is now Domain Admin and we can read the final flag at C:\Users\Administrator\Desktop\root.txt.

Last updated

Was this helpful?