<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://simondotsh.com/feed.xml" rel="self" type="application/atom+xml" /><link href="https://simondotsh.com/" rel="alternate" type="text/html" /><updated>2026-01-04T16:13:00+00:00</updated><id>https://simondotsh.com/feed.xml</id><title type="html">simondotsh’s infosec blog</title><entry><title type="html">What I Seek Out of a Pentester</title><link href="https://simondotsh.com/infosec/2026/01/04/what-i-seek-pentester.html" rel="alternate" type="text/html" title="What I Seek Out of a Pentester" /><published>2026-01-04T05:00:00+00:00</published><updated>2026-01-04T05:00:00+00:00</updated><id>https://simondotsh.com/infosec/2026/01/04/what-i-seek-pentester</id><content type="html" xml:base="https://simondotsh.com/infosec/2026/01/04/what-i-seek-pentester.html"><![CDATA[<p>So you are an aspiring professional penetration tester (pentester)? That’s great! It can be a rewarding field for one who cannot get enough of intricate technologies and making them behave unexpectedly.</p>

<p>After more than five years of pentesting, I moved onto a management role and this led to meeting even more individuals who desire to transition into pentesting. Almost every time, I was asked “what can I work on to guarantee I will get a job?” The hard truth is nothing will result in a guarantee as I believe there are far more candidates than available opportunities, and the competition gets stronger by the day. Obviously, this is not the kind of answer I like to provide; I do not wish to discourage anyone. Instead, I prefer to focus the discussion on what can make a candidate stand out. Spoiler: there is much more than the technical aspect.</p>

<p>This post contains what makes a difference to me when I am considering a potential candidate working full-time to improve the security of a single large organization, along with some recommendations.</p>

<h2 id="ideal-candidate">Ideal Candidate</h2>
<p>The following are some of the most critical skill sets and mindsets I believe essential to have success in the role.</p>

<h3 id="in-depth-knowledge-of-computer-science">In-Depth Knowledge of Computer Science</h3>
<p>Let’s get the technical part out of the way first since we cannot avoid it.</p>

<p>Take a web application pentest as an example. You will be given a limited timeframe to review its security controls and then present your findings to the software development team. By the end of the pentest, you are expected to know about the application practically as much as the developers themselves, and are going to let them know what they could have done better. That, in itself, is no small task and is quite difficult without knowing much about software development.</p>

<p>In the previous example, does this mean you must have five years of experience in development? Not necessarily but surely, it helps; however everyone learns differently. I have seen some pentesters who only worked as a developer in a three-month internship and it was enough to lay the groundwork to become ridiculously strong at application security. Without a doubt, they spent a considerable amount of personal time learning more about the topic in a self-taught way to get to where they are now. On the other hand, if I look at my own path, I do not think I would have been able to easily transition into pentesting without my four years of full-stack development as they were a critical learning experience. It all depends.</p>

<p>Something else to consider is how you are bound to have new technology you have never heard of thrown at you in a “here, tell me all I have done wrong” kind of way. Then, you will need to become an expert of the technology quickly in order to be confident about what you looked into. This is much easier to achieve when you can rely on your knowledge of the field since you will be able to identify similarities with other technologies you know of.</p>

<h3 id="excellent-documentation">Excellent Documentation</h3>
<p>Now you have found some shiny vulnerabilities, the most important part remains: communication. All the necessary information must be documented in a report to ensure the remediation team gets everything they need to proceed. Precise, but straight to the point to captivate your audience. I cannot stress this enough as any missing or inaccurate piece of information may cause confusion leading to incomplete remediation. Therefore, take a step back, impersonate your audience, and think of what information would be useful to you as the remediator.</p>

<p>Proper reporting also contains a criticality suggestion often guiding how urgent remediation must be done. A common mistake is to have a tendency to blow out of proportion how critical a finding is. This does not help an organization and may mobilize the remediation team on a finding that could have been at the bottom of the list. I get it, you want your findings to be remediated but if any new finding of yours suddenly becomes your talk of the hour, you will lose some credibility to your peers. Again, take a step back, consider the entire context and act accordingly.</p>

<h3 id="focusing-on-value">Focusing on Value</h3>
<p>This one is a favorite of mine. If I hand you five days to work on a project of your choosing, will I be happy of my investment?</p>

<p>A large organization can get a lot of value out of such exercises as pentesters often have good intuition regarding what may be broken, or what could improve their team. Unfortunately, it can also lead to rabbit holes where a lot of time is spent and not much comes out of it; however, I believe it is unfair to expect impactful results every time, and I will never discredit an activity where the motivation had sound reasoning.</p>

<p>As an example, consider the case where a pentester decided to look for some XSS. After a few days, they tell you all about the multiple XSS they found on a web application. Further investigation reveals that the application is only accessible internally, does not host any sensitive information, and is no longer used by any user. In short, the impact is extremely limited. Meanwhile, the pentester has never tested publicly-accessible web applications, cannot tell much about the posture of the organization’s Active Directory/Entra, and has no idea of the effectiveness of “Domain Admins before lunch” type of techniques. That would certainly make me question the investment of time.</p>

<p>In short, consider the big picture before working on a project. It does not mean your idea is wrong, but there are possibly other activities that should be prioritized first. Perhaps you could work on creating a backlog of significant exercises with your team, and challenge yourselves on the priorities.</p>

<h3 id="key-resource-in-special-projects">Key Resource in Special Projects</h3>
<p>Whether it is a new large-scale project or an incident response event, the two often share the common factor of having to work without an exact course of action. What I mean by this is you may be brought into these to contribute as the key offensive security expert, and you will have to deal with imprecision. Also, they usually come with an aggressive deadline, so no time to mess around.</p>

<p>These special projects get easier over time as experience pays dividends, but even if it is your first time, you need to be up for the challenge no matter how much it takes you out of your comfort zone. Understand your client, think of what you would want out of yourself if you were in their shoes and plan accordingly. Whatever you will be doing will be much better than freezing and reconsidering your own expertise because it is much different than what you are used to.</p>

<p>As a manager, it is extremely appeasing to know no matter what comes, your experts will be ready to answer.</p>

<h3 id="peers-feedback--knowledge-sharing">Peers Feedback &amp; Knowledge Sharing</h3>
<p>Everyone can improve, no matter how long they have been in the game. You noticed a situation where your colleague could have done something differently to possibly have a better result? Wonderful! Now, make sure to have an open-minded discussion with them. Maybe there was a variable at play you were unaware of, or maybe you are right it could have been better. All that matters is as professionals, you are expected to have these healthy discussions where everyone can greatly benefit.</p>

<p>Similarly, make sure to be on the lookout for topics that could be shared with your colleagues. Whether it is a new technique, a workflow to be more efficient or a primer on a technology most people are not comfortable with, these will be worth sharing. You want to avoid at all costs a case where every member of the team individually spent some time researching the same subject. How about one member does it and makes a presentation to the team?</p>

<h3 id="scope--time-estimates">Scope &amp; Time Estimates</h3>
<p>Most pentests assigned to you will contain two pieces of vital information: the scope (what must be tested) and an estimate of how long the pentest is going to take. Failure to follow the former implies your report will paint possibly a much different picture than reality. Indeed, if you spent time validating the security controls of a different scope, your report might end up claiming the expected scope is clean. In the same vein, if a pentest ends up taking twice as long as initially defined, another pentest will have to be put on hold in the meantime. As mentioned in “Focusing on the Valuable”, it does not mean the extra time you have spent was a waste, but it might not have been the right moment to look into the additional parts.</p>

<p>Making sure to have full understanding of the scope and doing some time checks along the way helps to deliver quality.</p>

<h2 id="my-recommendations">My Recommendations</h2>
<p>As briefly mentioned in the introduction, there is no way to guide with the guarantee of achieving your goal. Nonetheless, here are a few pointers I believe are important to take into account.</p>

<h3 id="contribute-to-a-community">Contribute to a Community</h3>
<p>There are plenty of information security communities out there under different forms. Depending on your location and preferences, some may be a better fit but no matter which one you decide to take part of, the outcome is likely to be positive. It can be of benefit both on the technical aspect and to make contacts who may help you get an opportunity eventually. Make sure to bring a positive attitude and to help wherever you can.</p>

<p>Personally, I got my first job in the field through a contact from a community I spent a lot of time with. Usually, it makes the interview process more straightforward, and that is desirable.</p>

<h3 id="do-not-overdo-certifications">Do Not Overdo Certifications</h3>
<p>Some people got to the point where they can spell out the entire alphabet with the certification acronyms they have. When I read a curriculum vitae, it does not make much of a difference to me whether someone has three or twenty certifications. Does it mean they are useless? Absolutely not as they are a great way to show you have been working on your goal. If you are having fun catching ‘em all, keep going but do not count on them to be the key differentiator.</p>

<h3 id="be-good-with-code-and-web-apps">Be Good With Code and Web Apps</h3>
<p>The idea here is there are different areas of expertise in pentesting itself, but some are more in need. I think it would be a missing opportunity to not be comfortable with web application pentesting as there is usually a bigger demand there, hopefully including code review for better coverage. Even if you are unsure which area is going to be your favorite, it seems adequate to begin with where the most opportunities are going to be.</p>

<h3 id="do-not-blindly-use-toolsai">Do Not Blindly Use Tools/AI</h3>
<p>This one can be summarized by understanding what you are doing and why you are doing it. Some seem to be under the impression pentesting is about building an arsenal of tools and then just blindly throwing everything at your target without much consideration. While this can work out in certain scenarios, especially low-hanging fruits, it will never be as effective as someone who understands what those tools do and why they are using them.</p>

<p>Recently, a friend overheard a nearby team at a capture-the-flag event celebrating the flag they had just obtained. When a participant quizzed his teammate on how they managed to solve the challenge, they replied they had no idea because they simply let their AI agent solve it. While any means to an end within the bounds of rules is fair play during a competition, I cannot help but think it would have been much more beneficial for this team of students to focus on the learning experience instead of automagically solving challenges. Enjoy the journey, not just the destination.</p>

<h3 id="selling-yourself-the-why-you-edition">Selling Yourself: The “Why You?” Edition</h3>
<p>There is more material than ever to learn about pentesting, and surely this results in more people looking for professional opportunities. Time needs to be spent on marketing yourself to successfully stand out of the pack. Consider the section “Ideal Candidate”. How can you show through your actions and accomplishments you possess the characteristics of an ideal candidate? The popular idiom “actions speak louder than words” certainly applies here as it will improve confidence in your abilities to your interlocutor. Feel free to ask your community to review your curriculum vitae and see what kind of impression it makes on them to help you out.</p>

<h2 id="closing-words">Closing Words</h2>
<p>I had the pleasure of meeting many accomplished pentesters and it does not take long to feel how passionate they are. Thus, it is not surprising in any way to find them doing research or getting involved in a community after a long day of work. I am in the belief that anyone willing to dedicate themselves this much into a discipline shall get the opportunity to be an excellent asset for an organization. While I cannot provide a clear path to follow, I hope these thoughts will be helpful in forging your own way.</p>]]></content><author><name></name></author><category term="infosec" /><summary type="html"><![CDATA[So you are an aspiring professional penetration tester (pentester)? That’s great! It can be a rewarding field for one who cannot get enough of intricate technologies and making them behave unexpectedly.]]></summary></entry><entry><title type="html">An Azure Tale of VPN, Conditional Access and MFA Bypass</title><link href="https://simondotsh.com/infosec/2023/08/15/azure-tale-vpn-ca-mfa-bypass.html" rel="alternate" type="text/html" title="An Azure Tale of VPN, Conditional Access and MFA Bypass" /><published>2023-08-15T05:00:00+00:00</published><updated>2023-08-15T05:00:00+00:00</updated><id>https://simondotsh.com/infosec/2023/08/15/azure-tale-vpn-ca-mfa-bypass</id><content type="html" xml:base="https://simondotsh.com/infosec/2023/08/15/azure-tale-vpn-ca-mfa-bypass.html"><![CDATA[<p>Cloud service providers (CSP) enable organizations to deploy modern solutions with impressive swiftness. Part of this is due to providers’ responsibility of tackling harder problems both on a functional and security level, while consumers are in charge of minimal configuration. With this in mind, one has to consider that CSPs face the challenge of designing security controls with an adequate threshold of effectiveness compatible with the desired user experience. Thus, when relying on these controls, consumers must ensure to be aware of potential weaknesses to avoid wildly claiming “if you manage to bypass this, you’ve broken Azure.”</p>

<p>Recently, I reviewed an implementation of a virtual private network (VPN) server using Azure AD (now Entra ID) as its identity provider (idP), allowing to easily enforce controls such as multi-factor authentication (MFA). The main objective of the assessment was straightforward: manage to authenticate on the VPN without solving an MFA challenge.</p>

<p>This post does not introduce novel or undocumented techniques, but aims to present a methodology to approach a similar problem along with mitigation opportunities.</p>

<h2 id="basis">Basis</h2>
<p>Before diving into the analysis, crucial elements will be described.</p>

<h3 id="setup">Setup</h3>
<p>In order to reproduce a simplified test environment, the following components were configured:</p>

<ul>
  <li><a href="https://developer.microsoft.com/en-us/microsoft-365/dev-program">Microsoft 365 E5 sandbox</a> with an Azure AD tenant.</li>
  <li>Cisco ASA v9.7+ device running on a private network with the VPN server AnyConnect.</li>
  <li>Cisco AnyConnect enterprise application to achieve SAML-based authentication with Azure AD on the ASA.</li>
  <li>Azure AD conditional access policies applying to various applications and identities.</li>
  <li>Azure AD user with access to the Cisco AnyConnect enterprise application.</li>
  <li>Azure AD-joined Windows 10 device managed by Intune with the Cisco AnyConnect client v4.6+.</li>
</ul>

<h3 id="the-analysts-privileges">The Analyst’s Privileges</h3>
<p>In my experience, when it comes to these kinds of assessments or any penetration test, hiding technical details to an analyst accomplishes no good. In fact, it limits the depth at which vulnerabilities may be found, and enables a malicious actor to exploit what was difficultly done in a limited time frame.</p>

<p>As such, the investigation will be conducted with <a href="https://learn.microsoft.com/en-us/azure/active-directory/roles/permissions-reference#global-reader">Global Reader</a> privileges.</p>

<h3 id="expected-authentication-flow">Expected Authentication Flow</h3>
<p>When connecting to the VPN server on the Windows device, the user is prompted to authenticate:</p>
<figure>
  <img src="/assets/img/azure-tale-vpn-ca-mfa-bypass/anyconnect-auth01.png" width="70%" />
  <figcaption>The integrated Internet Explorer 11 browser has trouble with rudimentary CSS.</figcaption>
</figure>

<p>Since <code class="language-plaintext highlighter-rouge">user@weakest.cloud</code> has logged into Windows, they have obtained a primary refresh token (PRT) claiming satisfaction for the first factor requirement (you can read about this process <a href="https://learn.microsoft.com/en-us/azure/active-directory/devices/concept-primary-refresh-token#browser-sso-using-prt">here</a>), and can therefore proceed to solving the second factor:</p>

<p><img src="/assets/img/azure-tale-vpn-ca-mfa-bypass/anyconnect-auth02.png" width="70%" /></p>

<p>Once this challenge succeeds, the VPN server allows the connection:</p>

<p><img src="/assets/img/azure-tale-vpn-ca-mfa-bypass/anyconnect-auth03.png" width="70%" /></p>

<h2 id="analysis">Analysis</h2>
<p>As previously stated, the goal is to bypass MFA, but the context matters significantly. For instance, depending on your threat model, an ill-intended actor with local or physical access to an Azure-joined device managing to circumvent MFA is likely to be less concerning than achieving the deed with a set of phished credentials, simply due to the vast difference in exposure. This must be regarded when looking for potential vectors.</p>

<h3 id="sign-in-logs">Sign-in Logs</h3>
<p>In a scenario where the interaction between an identity and an application must be inspected, it makes sense to review the user’s sign-in logs and visualize which conditional access policies were applied after authenticating on the application.</p>

<p><img src="/assets/img/azure-tale-vpn-ca-mfa-bypass/sign-in-logs-cap.png" /></p>

<p>Pay attention to the <code class="language-plaintext highlighter-rouge">Result</code> column and consider the following: except the policy stating <code class="language-plaintext highlighter-rouge">Not Applied</code>, controls in the other two must be satisfied; failure to meet one of these yields access denied. For a primer on conditional access, refer to this <a href="https://danielchronlund.com/2018/11/23/how-multiple-conditional-access-policies-are-applied/">article</a>.</p>

<p>Put into words, we can conclude that when authenticating on the AnyConnect application, <code class="language-plaintext highlighter-rouge">user@weakest.cloud</code> must solve an MFA challenge and originate from a device deemed as compliant by Intune due to the policies <code class="language-plaintext highlighter-rouge">CA200-AnyConnectUsers-AppProtection-AnyConnect-WindowsmacOSLinux-MFA</code> and <code class="language-plaintext highlighter-rouge">CA001-Global-AppProtection-AllApps-AnyPlatform-Compliant</code> respectively.</p>

<p>Now, onto reading the pair’s configuration.</p>

<h3 id="mfa-policy">MFA Policy</h3>
<p><code class="language-plaintext highlighter-rouge">CA200-AnyConnectUsers-AppProtection-AnyConnect-WindowsmacOSLinux-MFA</code> follows the nomenclature <a href="https://learn.microsoft.com/en-us/azure/architecture/guide/security/conditional-access-framework">suggested by Microsoft</a> which, in return, allows to quickly get an idea of how it is configured, but should be validated concretely nonetheless.</p>

<p><img src="/assets/img/azure-tale-vpn-ca-mfa-bypass/mfa-cap.png" width="40%" /></p>

<p>The following can already be seen: it applies to specific users without any exclusion, targets a single application (the AnyConnect enterprise application since it applied to us when connecting), and one category of conditions is used.</p>

<p>Let’s look into the users first:</p>

<p><img src="/assets/img/azure-tale-vpn-ca-mfa-bypass/mfa-cap-users.png" width="40%" /></p>

<p>To facilitate delegation, only the security group named <code class="language-plaintext highlighter-rouge">AnyConnect Users</code> appears to handle access to the application, but must be confirmed.</p>

<p>What about the condition?</p>

<p><img src="/assets/img/azure-tale-vpn-ca-mfa-bypass/mfa-cap-devices.png" width="30%" /></p>

<p>The policy applies only to users connecting from Windows, macOS and Linux. Does this mean a connection from a mobile device would not trigger an MFA challenge? This will be validated later on.</p>

<h3 id="compliance-policy">Compliance Policy</h3>
<p><code class="language-plaintext highlighter-rouge">CA001-Global-AppProtection-AllApps-AnyPlatform-Compliant</code> is much simpler:</p>

<p><img src="/assets/img/azure-tale-vpn-ca-mfa-bypass/compliance-cap.png" width="40%" /></p>

<p>All users must authenticate from compliant devices to be permitted on applications where Azure AD handles access. Some exceptions exist, like <a href="https://learn.microsoft.com/en-us/azure/active-directory/conditional-access/howto-conditional-access-policy-compliant-device#create-a-conditional-access-policy">Microsoft Intune Enrollment</a>, where its critical feature of enrolling devices would no longer be possible.</p>

<p>So, the AnyConnect client must initiate a connection from a compliant device to avoid denial. This implies possessing a device managed (enrolled) by Intune and meeting compliance prerequisites; mere device registration would not do the trick.</p>

<h2 id="validations">Validations</h2>
<p>This section serves to validate some hypotheses crafted from the previous analysis:</p>

<ul>
  <li>Users other than <code class="language-plaintext highlighter-rouge">AnyConnect Users</code> may have access to the AnyConnect enterprise application.</li>
  <li>Only a specific set of device platforms may be governed by a policy enforcing MFA.</li>
  <li><code class="language-plaintext highlighter-rouge">user@weakest.cloud</code> may be able to enroll mobile devices.</li>
</ul>

<h3 id="enterprise-application-access">Enterprise Application Access</h3>
<p>As only members of the group <code class="language-plaintext highlighter-rouge">AnyConnect Users</code> are covered by the MFA policy, two questions arise: do users need explicit access to the application and if so, can more identities access it? These can be easily checked through the Azure portal.</p>

<p>After browsing to the enterprise application, in the properties, the <code class="language-plaintext highlighter-rouge">Assignment required?</code> entry dictates whether explicit access to the application is needed:</p>

<p><img src="/assets/img/azure-tale-vpn-ca-mfa-bypass/app-assignment-required.png" width="70%" /></p>

<p>This is the desired value, otherwise anyone not a member of the group <code class="language-plaintext highlighter-rouge">AnyConnect Users</code> would get access to the application without MFA, unless another policy would save the day.</p>

<p>To answer the last question, the section <code class="language-plaintext highlighter-rouge">Users and groups</code> lists who can access this application:</p>

<p><img src="/assets/img/azure-tale-vpn-ca-mfa-bypass/app-assigned-users.png" width="70%" /></p>

<p>This looks good: only expected users are authorized to connect. With that being said, further checkups could be done such as looking into owners and other principals able to control the application, but is left out of this exercise.</p>

<h3 id="device-platforms-without-mfa">Device Platforms Without MFA</h3>
<p>The <code class="language-plaintext highlighter-rouge">What If</code> feature of conditional access policies is a convenient way to conclude what policies apply when certain parameters are selected. As an example, when setting the test user and the AnyConnect application on a Windows device, the two aforementioned policies are reported as applying:</p>

<p><img src="/assets/img/azure-tale-vpn-ca-mfa-bypass/what-if-anyconnect-config.png" width="70%" /></p>

<figure>
  <img src="/assets/img/azure-tale-vpn-ca-mfa-bypass/what-if-anyconnect-results.png" />
  <figcaption>The Azure portal also has trouble with rudimentary CSS.</figcaption>
</figure>

<p>What if the device is iOS instead?</p>

<p><img src="/assets/img/azure-tale-vpn-ca-mfa-bypass/what-if-anyconnect-ios-results.png" /></p>

<p>As previously guessed, MFA is not required to connect to AnyConnect on a mobile device, but it must be enrolled and compliant. A stroll into the compliance policies of the Intune admin center lets on that compliance will not be an issue.</p>

<h3 id="mobile-device-enrollment">Mobile Device Enrollment</h3>
<p>To validate if the user can enroll a device, the same strategy with the <code class="language-plaintext highlighter-rouge">What If</code> feature is used, but will target the Microsoft Intune Enrollment application instead:</p>

<p><img src="/assets/img/azure-tale-vpn-ca-mfa-bypass/what-if-enrollment-ios-results.png" /></p>

<p>The policy <code class="language-plaintext highlighter-rouge">CA002-Global-AppProtection-IntuneEnrollment-AnyPlatformExceptWindows-Block</code> blocks enrollment of any mobile device, but as its name states, allows all users to enroll Windows devices. Further checks reveal that out of the various ways to enforce MFA, none would apply during enrollment.</p>

<h2 id="chaining-findings">Chaining Findings</h2>
<p>Operations in the last section confirmed these next statements:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">user@weakest.cloud</code> can enroll Windows devices without MFA.</li>
  <li>iOS devices are not subject to MFA when authenticating on AnyConnect.</li>
</ul>

<p>Now is the time to trick Azure AD to let us enroll an iOS device and authenticate on AnyConnect without MFA.</p>

<h3 id="my-ipad-is-a-windows-device">My iPad Is A Windows Device</h3>
<p>A critical piece of information about conditional access policies filtering on device platforms must be known: they rely on data sent by clients in the HTTP request upon authenticating, and can be altered except when a token with a device ID claim is involved. In the latter case, this claim is used to match the device platform in Azure AD, so no alteration is possible (thanks to <a href="https://twitter.com/_dirkjan">Dirk-jan</a> for confirming this); however, no such claim is needed during the enrollment of an iPad device.</p>

<p>Before proxying and modifying the key request, the Company Portal application is installed. After authenticating with <code class="language-plaintext highlighter-rouge">user@weakest.cloud</code> and attempting enrollment of the iPad device, the inability is witnessed concretely:</p>

<p><img src="/assets/img/azure-tale-vpn-ca-mfa-bypass/ipad-enrollment-denied.png" width="90%" /></p>

<p>After going back one step in the process, this screen is reached:</p>

<p><img src="/assets/img/azure-tale-vpn-ca-mfa-bypass/ipad-enrollment-before-proxy.png" width="90%" /></p>

<p>At this point, the iPad is configured to use a proxy server, and requests sent by the client are captured. Once the button <code class="language-plaintext highlighter-rouge">Continue</code> is pressed, eventually a request to <code class="language-plaintext highlighter-rouge">login.microsoft.com</code> will be sent. Two parts of it must be tweaked: the <code class="language-plaintext highlighter-rouge">GET</code> parameter <code class="language-plaintext highlighter-rouge">x-client-SKU=MSAL.iOS</code> has to be removed, and the <code class="language-plaintext highlighter-rouge">User-Agent</code> header needs to match one from a Windows device, for instance <code class="language-plaintext highlighter-rouge">Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36 Edge/18.19045</code>.</p>

<figure>
  <img src="/assets/img/azure-tale-vpn-ca-mfa-bypass/ipad-enrollment-capturing.png" />
  <figcaption>For the curious, this Montréal-based Burp-alternative software is named <a href="https://caido.io/">Caido</a>.</figcaption>
</figure>

<p>Once the request is forwarded, the user is prompted to download a configuration profile, confirming the policy conditions have been met:</p>

<p><img src="/assets/img/azure-tale-vpn-ca-mfa-bypass/ipad-enrollment-success.png" /></p>

<p>Then, the rest of the process has to be completed normally, which leads to having a compliant device as can be seen in Azure AD:</p>

<p><img src="/assets/img/azure-tale-vpn-ca-mfa-bypass/ipad-compliant.png" /></p>

<h3 id="authenticating-on-vpn">Authenticating on VPN</h3>
<p>All that is left to gain access to the internal network is to install Cisco Secure Client on the iPad and connect to the VPN server:</p>

<p><img src="/assets/img/azure-tale-vpn-ca-mfa-bypass/ipad-anyconnect-connected.png" /></p>

<p>Similarly to the presented authentication flow, the client was asked to authenticate but with a significant difference: no MFA was demanded. The sign-in logs reflect this:</p>

<p><img src="/assets/img/azure-tale-vpn-ca-mfa-bypass/logs-no-mfa01.png" width="70%" /></p>

<p><img src="/assets/img/azure-tale-vpn-ca-mfa-bypass/logs-no-mfa02.png" width="80%" /></p>

<h3 id="what-about-the-windows-device">What About the Windows Device?</h3>
<p>The main reason why this bypass was possible is due to the policy enforcing MFA on the VPN applying only to Windows, macOS and Linux. What if from our Windows device, we also change the user agent on authentication to state a different uncovered OS?</p>

<p>In the current setup, as far as I know this cannot be done, simply because the device must be compliant, and compliance validation implies supplying a token with a device ID claim. As mentioned before, this ID is also used to match the device platform, so the user agent is ignored. On the iPad, the user agent was modified while enrolling, not during authentication on the VPN.</p>

<h2 id="mitigation">Mitigation</h2>
<p>In this demonstration, the environment would benefit from adjusting conditional access policies and device platform restrictions.</p>

<h3 id="do-not-scope-on-specific-platforms">Do Not Scope on Specific Platforms</h3>
<p>Instead of forcing MFA on precise platforms, the weak policy should instead include <code class="language-plaintext highlighter-rouge">Any device</code> without exclusions to ensure unknown and unexpected platforms would also be prompted to solve the challenge.</p>

<h3 id="no-mfa-no-enrollment">No MFA No Enrollment</h3>
<p>When taking the big picture into account, this control neutralizes the biggest threat: compromised credentials used to access cloud applications. Currently, the configuration allows an attacker with <code class="language-plaintext highlighter-rouge">user@weakest.cloud</code>’s password to enroll their own device, then browse any application where the only condition is having a compliant device. In the case where MFA is required to enroll, it would instead force the compromission of a device to achieve the same, which definitely requires more effort.</p>

<h3 id="using-device-platform-restrictions">Using Device Platform Restrictions</h3>
<p>While I have not fully investigated the complete bulletproofness of this control, Intune offers the possibility to restrict the enrollment of specific platforms for desired principals instead of relying on an easily-bypassable conditional access policy. See the <a href="https://learn.microsoft.com/en-us/mem/intune/enrollment/create-device-platform-restrictions">documentation</a> for more information.</p>

<h2 id="conclusion">Conclusion</h2>
<p>Using Azure AD as the idP for cloud and on-premises software is convenient and powerful, but does not avoid complexity especially in large tenants. Conditional access policies can quickly become a mess of loosely scoped conditions and exclusions leading to gaps when a different context is considered. Assessment of these controls in accordance to the identified threats definitely benefit organizations to assure full coverage.</p>]]></content><author><name></name></author><category term="infosec" /><summary type="html"><![CDATA[Cloud service providers (CSP) enable organizations to deploy modern solutions with impressive swiftness. Part of this is due to providers’ responsibility of tackling harder problems both on a functional and security level, while consumers are in charge of minimal configuration. With this in mind, one has to consider that CSPs face the challenge of designing security controls with an adequate threshold of effectiveness compatible with the desired user experience. Thus, when relying on these controls, consumers must ensure to be aware of potential weaknesses to avoid wildly claiming “if you manage to bypass this, you’ve broken Azure.”]]></summary></entry><entry><title type="html">Assessing Standalone Managed Service Accounts</title><link href="https://simondotsh.com/infosec/2022/12/12/assessing-smsa.html" rel="alternate" type="text/html" title="Assessing Standalone Managed Service Accounts" /><published>2022-12-12T05:00:00+00:00</published><updated>2022-12-12T05:00:00+00:00</updated><id>https://simondotsh.com/infosec/2022/12/12/assessing-smsa</id><content type="html" xml:base="https://simondotsh.com/infosec/2022/12/12/assessing-smsa.html"><![CDATA[<p>Managed Service Accounts (MSA) offer an identity with automatic password management to run applications such as services. They come in two flavors: <a href="https://learn.microsoft.com/en-us/windows-server/identity/ad-ds/manage/understand-service-accounts#standalone-managed-service-accounts">Standalone Managed Service Accounts</a> (sMSA) and <a href="https://learn.microsoft.com/en-us/windows-server/identity/ad-ds/manage/understand-service-accounts#group-managed-service-accounts">Group Managed Service Accounts</a> (gMSA). The former can only be installed (used) on a single host, as opposed to the latter whose password can be retrieved by a multitude of configured principals, allowing usage on multiple hosts.</p>

<p>While this post also mentions gMSA for comparison purposes, it aims to document an assessment activity for the standalone version, since it appears to receive slightly less attention despite being the first iteration of the concept.</p>

<p>For more details about MSA, refer to <a href="https://twitter.com/SteveSyfuhs">Steve Syfuhs</a>’ <a href="https://syfuhs.net/how-managed-service-accounts-in-active-directory-work">excellent post</a>.</p>

<h2 id="a-weakness-to-look-for">A Weakness to Look For</h2>
<p>The problem lies in what the sMSA and gMSA can do on a domain. Microsoft specifically mention to enforce the least privileged model:</p>

<figure>
  <img src="/assets/img/assessing-smsa/smsa-security-issues.png" />
  <figcaption>Source: <a href="https://learn.microsoft.com/en-us/azure/active-directory/fundamentals/service-accounts-standalone-managed#assess-the-security-posture-of-smsas">https://learn.microsoft.com/en-us/azure/active-directory/fundamentals/service-accounts-standalone-managed</a></figcaption>
</figure>

<p>Why? Let us take the drastic example of an sMSA member of the <code class="language-plaintext highlighter-rouge">Domain Admins</code> group. This implies that anybody with control over the computer object where it is installed or with administrative privileges on it could retrieve the credentials of the account, leading to compromising <code class="language-plaintext highlighter-rouge">Domain Admins</code> privileges.</p>

<p>The same issue applies to gMSA, but with a potentially higher exposure. Indeed, the <a href="https://learn.microsoft.com/en-us/windows/win32/adschema/a-msds-groupmsamembership">msDS-GroupMSAMembership</a> attribute of a gMSA states the principals that can read its password. If any of these principals were to get compromised, the malicious actor would be awarded with the gMSA’s privileges on the domain.</p>

<h2 id="reconnaissance">Reconnaissance</h2>
<h3 id="enumerating-smsa">Enumerating sMSA</h3>
<p>sMSA are typically located in a container named <code class="language-plaintext highlighter-rouge">Managed Service Accounts</code> at the root of the domain, e.g. <code class="language-plaintext highlighter-rouge">CN=Managed Service Accounts,DC=contoso,DC=com</code>; however, they can be moved elsewhere and also share the location with gMSA.</p>

<p>The most straightforward way to find them is by filtering on their object class <a href="https://learn.microsoft.com/en-us/windows/win32/adschema/c-msds-managedserviceaccount">msDS-ManagedServiceAccount</a> in an LDAP query:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>net ads search -k -S $SERVER '(objectClass=msDS-ManagedServiceAccount)' sAMAccountName
</code></pre></div></div>
<p><img src="/assets/img/assessing-smsa/enumerating-smsa.png" /></p>

<p>gMSA have the object class <a href="https://learn.microsoft.com/en-us/windows/win32/adschema/c-msds-groupmanagedserviceaccount">msDS-GroupManagedServiceAccount</a> instead.</p>

<h3 id="identifying-where-smsa-are-installed">Identifying Where sMSA Are Installed</h3>
<p>sMSA hold an attribute named <a href="https://learn.microsoft.com/en-us/windows/win32/adschema/a-msds-hostserviceaccountbl">msDS-HostServiceAccountBL</a>. The <code class="language-plaintext highlighter-rouge">BL</code> part stands for “back link”, since it is a <a href="https://learn.microsoft.com/en-us/windows/win32/ad/linked-attributes">linked attribute</a>. This attribute contains the distinguished name of the computer where it is installed. Then, by querying the attributes of that computer object, the forward link <a href="https://learn.microsoft.com/en-us/windows/win32/adschema/a-msds-hostserviceaccount">msDS-HostServiceAccount</a> can be found, which in return stores the distinguished name of the sMSA.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>net ads search -k -S $SERVER '(sAMAccountName=$SMSA_SAMACCOUNTNAME)' msDS-HostServiceAccountBL
net ads search -k -S $SERVER '(sAMAccountName=$COMPUTER_SAMACCOUNTNAME)' msDS-HostServiceAccount
</code></pre></div></div>

<p><img src="/assets/img/assessing-smsa/smsa-installed.png" /></p>

<h3 id="visualizing-privileges-and-attack-paths">Visualizing Privileges and Attack Paths</h3>
<p>At this stage, one must be able to get the full picture of what privileges the service accounts possess on the domain. This requires to collect a bunch of objects and ACEs throughout the entire directory. Then, these need to be mapped in relation to each other so that the analyst can also figure out which objects control the service accounts; perhaps a hint of graph theory sounds adequate. Fortunately, <a href="https://github.com/BloodHoundAD/BloodHound/">BloodHound</a> does exactly this!</p>

<p>gMSA are well covered in BloodHound. The edge <a href="https://bloodhound.readthedocs.io/en/latest/data-analysis/edges.html#readgmsapassword">ReadGMSAPassword</a> lets you know about any principal that can read a gMSA’s password (it gets that information from <code class="language-plaintext highlighter-rouge">msDS-GroupMSAMembership</code> attribute previously mentioned). Just as any other object, the gMSA’s privileges are gathered in order to draw full attack paths:</p>

<p><img src="/assets/img/assessing-smsa/gmsa-rdp.png" width="80%" /></p>

<p>Back to the main subject of sMSA. What if we try to get the same information for <code class="language-plaintext highlighter-rouge">smsa$</code>? Let us begin with getting the first-degree relationships it has:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>MATCH p=(:User {name: 'SMSA$@AD.LOCAL'})--&gt;() RETURN p;
</code></pre></div></div>
<p><img src="/assets/img/assessing-smsa/smsa-relationships.png" width="60%" /></p>

<p>sMSA and gMSA also contain the object class <code class="language-plaintext highlighter-rouge">computer</code>, so it is expected to see these as part of <code class="language-plaintext highlighter-rouge">Domain Computers</code>. The next relationship of <a href="https://bloodhound.readthedocs.io/en/latest/data-analysis/edges.html#genericall">GenericAll</a> over a domain controller is a definite anomaly that should be avoided.</p>

<p>We know that sMSA can only be installed on a single computer, so how about we reveal the computer object that can compromise <code class="language-plaintext highlighter-rouge">smsa$</code>?</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>MATCH p=(:Computer)--&gt;(:User {name: 'SMSA$@AD.LOCAL'}) RETURN p;
</code></pre></div></div>
<p><img src="/assets/img/assessing-smsa/computer-smsa-no-relationship.png" width="50%" /></p>

<p>Is that so? I quite recall earlier seeing the back link attribute <code class="language-plaintext highlighter-rouge">msDS-HostServiceAccountBL</code> stating that <code class="language-plaintext highlighter-rouge">smsa$</code> is installed on <code class="language-plaintext highlighter-rouge">CN=WKS1,CN=Computers,DC=ad,DC=local</code>. Further analysis reveals that the installation is functional, and that any principal able to compromise <code class="language-plaintext highlighter-rouge">WKS1</code> could indeed retrieve <code class="language-plaintext highlighter-rouge">smsa$</code>’s password.</p>

<p>The issue here is that BloodHound does not currently have an edge to represent a relation between an sMSA and the computer where it is installed, so an analyst cannot solely rely on the tool to assess these.</p>

<p>Finally, first-degree controllers of <code class="language-plaintext highlighter-rouge">WKS1</code> can be queried to obtain a general idea of the sMSA’s security posture:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>MATCH p=()--&gt;(:Computer {name: 'WKS1.AD.LOCAL'}) RETURN p;
</code></pre></div></div>
<p><img src="/assets/img/assessing-smsa/wks1-controllers.png" width="80%" /></p>

<p>Apart from the highly privileged groups and the container stating where <code class="language-plaintext highlighter-rouge">WKS1</code> is located, we obtain a valuable piece of information: <code class="language-plaintext highlighter-rouge">BRETT_ANDERSON@AD.LOCAL</code> is a local administrator of <code class="language-plaintext highlighter-rouge">WKS1</code>.</p>

<h2 id="dumping-smsa-passwords">Dumping sMSA Passwords</h2>
<p>After configuring an application to run as <code class="language-plaintext highlighter-rouge">smsa$</code>, <code class="language-plaintext highlighter-rouge">WKS1</code> needs a method to get the sMSA’s password to authenticate on the domain.</p>

<p>When an operator enters the credentials of a regular account to run in a service, scheduled task or IIS application pool, they are stored encrypted as <a href="https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2012-r2-and-2012/hh994565(v%3dws.11)#lsa-secrets-on-the-hard-disk-drive">LSA secrets</a> on the machine. These can be read by <code class="language-plaintext highlighter-rouge">SYSTEM</code> in the registry at <code class="language-plaintext highlighter-rouge">HKEY_LOCAL_MACHINE\SECURITY\Policy\Secrets</code>, or saved to a file by an administrator using <code class="language-plaintext highlighter-rouge">reg save</code>. This is exactly where an sMSA credentials are saved after installation.</p>

<p><img src="/assets/img/assessing-smsa/viewing-lsa-secrets.png" width="65%" /></p>

<p>Your favorite credentials dumping tool should already implement the functionality to dump and decrypt LSA secrets. For instance, <a href="https://github.com/SecureAuthCorp/impacket">impacket</a>’s <a href="https://github.com/SecureAuthCorp/impacket/blob/master/examples/secretsdump.py">secretsdump.py</a> supports it:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>python3 secretsdump.py $DOMAIN/$USER:$PASSWORD@$HOST
</code></pre></div></div>
<figure>
  <img src="/assets/img/assessing-smsa/dumping-smsa.png" />
  <figcaption>Note: the output was altered to display only the parts relevant to the sMSA.</figcaption>
</figure>

<p>The first entry of <code class="language-plaintext highlighter-rouge">_SC_{262E99C9-6160-4871-ACEC-4E61736B6F21}_smsa$</code> represents <code class="language-plaintext highlighter-rouge">smsa$</code>’s password. The part on the left is its value hex-encoded, while the right shows the plain text. Viewing the plain text makes us understand why it bothered to encode it; using it as a password looks quite challenging. The second entry is the same hex value, but on a single line for convenience.</p>

<p>To use the password, one can calculate its NT hash, then leverage pass-the-hash when authenticating. The following python script can be used to compute the hash:</p>

<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="c1"># nt.py
</span><span class="kn">import</span> <span class="nn">sys</span><span class="p">,</span> <span class="n">hashlib</span>

<span class="n">pw_hex</span> <span class="o">=</span> <span class="n">sys</span><span class="p">.</span><span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> 
<span class="n">nt_hash</span> <span class="o">=</span> <span class="n">hashlib</span><span class="p">.</span><span class="n">new</span><span class="p">(</span><span class="s">'md4'</span><span class="p">,</span> <span class="nb">bytes</span><span class="p">.</span><span class="n">fromhex</span><span class="p">(</span><span class="n">pw_hex</span><span class="p">)).</span><span class="n">hexdigest</span><span class="p">()</span>

<span class="k">print</span><span class="p">(</span><span class="s">'</span><span class="se">\n</span><span class="s">'</span> <span class="o">+</span> <span class="n">nt_hash</span><span class="p">)</span></code></pre></figure>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>python3 nt.py $PASSWORD_HEX
</code></pre></div></div>
<p><img src="/assets/img/assessing-smsa/calculate-nt.png" width="80%" /></p>

<p>The credentials can be validated through a myriad of methods, such as calling MS-LSAT’s <a href="https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-lsat/4e3f0a3f-86ae-4d50-aa40-ec25013a89e3">LsarGetUserName</a> via <a href="https://www.samba.org/samba/docs/current/man-html/rpcclient.1.html">rpcclient</a>:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>rpcclient -U '$DOMAIN/$SMSA%$NT_HASH' --pw-nt-hash -c 'getusername;quit' HOST
</code></pre></div></div>
<p><img src="/assets/img/assessing-smsa/lsargetusername.png" width="90%" /></p>

<p>To complete the exploitation by gaining privileged access on the domain controller, an attacker would take advantage of <code class="language-plaintext highlighter-rouge">smsa$</code>’s <code class="language-plaintext highlighter-rouge">GenericAll</code> on the computer object. This allows to perform <a href="https://www.ired.team/offensive-security-experiments/active-directory-kerberos-abuse/resource-based-constrained-delegation-ad-computer-object-take-over-and-privilged-code-execution">resource-based constrained delegation</a>.</p>

<h2 id="final-words">Final Words</h2>
<p>Along with gMSA, sMSA are another source of potential misconfiguration that can lead to domain compromise. As such, this makes them good candidates to look for when assessing a directory. While BloodHound does not currently contain the required information to visualize the full attack path involving an sMSA, an analyst armed with LDAP and Cypher queries can resolve the matter.</p>

<p>To make the analysis simpler, I will propose contributing a new edge regarding this to the BloodHound team.</p>]]></content><author><name></name></author><category term="infosec" /><summary type="html"><![CDATA[Managed Service Accounts (MSA) offer an identity with automatic password management to run applications such as services. They come in two flavors: Standalone Managed Service Accounts (sMSA) and Group Managed Service Accounts (gMSA). The former can only be installed (used) on a single host, as opposed to the latter whose password can be retrieved by a multitude of configured principals, allowing usage on multiple hosts.]]></summary></entry><entry><title type="html">DirSync: Leveraging Replication Get-Changes and Get-Changes-In-Filtered-Set</title><link href="https://simondotsh.com/infosec/2022/07/11/dirsync.html" rel="alternate" type="text/html" title="DirSync: Leveraging Replication Get-Changes and Get-Changes-In-Filtered-Set" /><published>2022-07-11T05:00:00+00:00</published><updated>2022-07-11T05:00:00+00:00</updated><id>https://simondotsh.com/infosec/2022/07/11/dirsync</id><content type="html" xml:base="https://simondotsh.com/infosec/2022/07/11/dirsync.html"><![CDATA[<p><a href="https://attack.mitre.org/techniques/T1003/006/">DCSync</a> can be categorized as one of the most effective technique on a domain, since retrieving the <code class="language-plaintext highlighter-rouge">krbtgt</code> NT hash allows to forge a valid ticket-granting ticket for any domain user. To manage to do so, a principal must possess the right privileges over the root of a domain: <a href="https://docs.microsoft.com/en-us/windows/win32/adschema/r-ds-replication-get-changes">DS-Replication-Get-Changes</a> and <a href="https://docs.microsoft.com/en-us/windows/win32/adschema/r-ds-replication-get-changes-all">DS-Replication-Get-Changes-All</a>. The combination renders it possible to successfully call <a href="https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-drsr/f977faaa-673e-4f66-b9bf-48c640241d47">MS-DRSR</a>’s <a href="https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-drsr/b63730ac-614c-431c-9501-28d6aca91894">GetNCChanges</a> to replicate <a href="https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-drsr/294168d9-81bf-461b-91d7-95bd8a985737">secret attributes</a>.</p>

<p>Recently, I stumbled upon a principal with only <code class="language-plaintext highlighter-rouge">DS-Replication-Get-Changes</code> over a domain, which lead to investigating the impact, along with its sibling <a href="https://docs.microsoft.com/en-us/windows/win32/adschema/r-ds-replication-get-changes-in-filtered-set">DS-Replication-Get-Changes-In-Filtered-Set</a>.</p>

<p>This post is dedicated to presenting the consequences of delegating these privileges, and includes a proof of concept PowerShell module to demonstrate the technique for defensive and adversarial simulation purposes.</p>

<h2 id="a-quick-glance-at-what-can-be-done">A Quick Glance at What Can Be Done</h2>
<ul>
  <li><code class="language-plaintext highlighter-rouge">DS-Replication-Get-Changes</code> allows to read the value of <a href="https://docs.microsoft.com/en-us/troubleshoot/windows-server/windows-security/mark-attribute-as-confidential#summary">confidential attributes</a>.</li>
  <li><code class="language-plaintext highlighter-rouge">DS-Replication-Get-Changes-In-Filtered-Set</code>, coupled with <code class="language-plaintext highlighter-rouge">DS-Replication-Get-Changes</code>, allows to read the value of confidential and <a href="https://docs.microsoft.com/en-us/windows/win32/ad/rodc-and-active-directory-schema#rodc-filtered-attribute-set">Read-Only Domain Controller (RODC) filtered</a> attributes, such as <a href="https://docs.microsoft.com/en-us/defender-for-identity/cas-isp-laps">Local Administrator Password Solution</a>’s (LAPS) <code class="language-plaintext highlighter-rouge">ms-Mcs-AdmPwd</code>.</li>
</ul>

<h2 id="describing-concepts">Describing Concepts</h2>
<p>Before we move on, some Active Directory concepts must first be described.</p>

<h3 id="the-searchflags-attribute">The searchFlags Attribute</h3>
<p>The <a href="https://www.oreilly.com/library/view/active-directory-second/0596004664/ch03s03.html">Schema Naming Context</a>, located at <code class="language-plaintext highlighter-rouge">CN=Schema,CN=Configuration,DC=contoso,DC=com</code> contains an object for all attributes that can be set on objects found in Active Directory. Just as regular objects, these objects also have attributes set on them, effectively having attributes on an attribute object.</p>

<p>To make sense out of this, let us look at this concrete example.</p>

<p>At <code class="language-plaintext highlighter-rouge">CN=ms-Mcs-AdmPwd,CN=Schema,CN=Configuration,DC=contoso,DC=com</code> resides the object of an attribute that you may be familiar with: <code class="language-plaintext highlighter-rouge">ms-Mcs-AdmPwd</code>. This attribute is used to store the password of the local administrator managed by LAPS. When viewing its object, we can see some attributes that are either set or unset, and more specifically in our case, <a href="https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-adts/7c1cdf82-1ecc-4834-827e-d26ff95fb207">searchFlags</a>:</p>

<p><img src="/assets/img/dirsync/ms-mcs-admpwd-attributes.png" /></p>

<p><code class="language-plaintext highlighter-rouge">searchFlags</code> (<code class="language-plaintext highlighter-rouge">CN=Search-Flags</code>) includes an important feature: it can dictate whether an attribute is to be shown to a user that requests to see its value. Indeed, when the <code class="language-plaintext highlighter-rouge">searchFlags</code> attribute contains the flag <code class="language-plaintext highlighter-rouge">fCONFIDENTIAL</code> (0x00000080), a user requesting to see the value must have the explicit privileges to read it (<a href="https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-adts/e6685d31-5d87-42d0-8a5f-e55d337f47cd">RIGHT_DS_READ_PROPERTY and RIGHT_DS_CONTROL_ACCESS</a>).</p>

<p>Going back to our example, since <code class="language-plaintext highlighter-rouge">ms-Mcs-AdmPwd</code>’s <code class="language-plaintext highlighter-rouge">searchFlags</code> has the flag <code class="language-plaintext highlighter-rouge">fCONFIDENTIAL</code>, a user that wants to read the attribute <code class="language-plaintext highlighter-rouge">ms-Mcs-AdmPwd</code> on a computer object must have <code class="language-plaintext highlighter-rouge">RIGHT_DS_READ_PROPERTY</code> and <code class="language-plaintext highlighter-rouge">RIGHT_DS_CONTROL_ACCESS</code> on it; otherwise, the attribute will be returned as empty. By doing so, LAPS ensures that only principals with the right privileges delegated over computer objects can read the attribute.</p>

<p>One last flag supported by <code class="language-plaintext highlighter-rouge">searchFlags</code> worth mentioning is <code class="language-plaintext highlighter-rouge">fRODCFilteredAttribute</code> (0x00000200). Essentially, this states that an attribute cannot be replicated to a RODC. Note that <code class="language-plaintext highlighter-rouge">ms-Mcs-AdmPwd</code> also has this flag, and will come into play later on.</p>

<h3 id="ldap-extended-controls">LDAP Extended Controls</h3>
<p><a href="https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-adts/3c5e87db-4728-4f29-b164-01dd7d7391ea">LDAP Extended Controls</a>, often simply named LDAP Controls, is a feature that was introduced in LDAPv3. By specifically crafting a request message with an object identifier (OID), a Domain Controller (DC) can be asked to perform a certain set of operations. For instance, the control named <a href="https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-adts/2fceb090-40e3-4d37-b3cf-367a37b76417">LDAP_SERVER_CROSSDOM_MOVE_TARGET_OID</a> (OID 1.2.840.113556.1.4.521) is used to request that a DC moves an object to another domain.</p>

<h4 id="ldap_server_dirsync_oid-1284011355614841">LDAP_SERVER_DIRSYNC_OID (1.2.840.113556.1.4.841)</h4>
<p><a href="https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-adts/2213a7f2-0a36-483c-b2a4-8574d53aa1e3">This control</a> is used to retrieve from a DC any changes made to Active Directory objects since the last time a synchronization was requested, and is the technique that will be used to read the values of attributes with the flags <code class="language-plaintext highlighter-rouge">fCONFIDENTIAL</code> and <code class="language-plaintext highlighter-rouge">fRODCFilteredAttribute</code>.</p>

<p>The documentation also contains a valuable piece of information: <a href="https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-drsr/4abd4c7c-c078-4809-95bc-38e657d5c034">pseudocode of the security check</a> performed when a directory synchronization is requested:</p>

<figure class="highlight"><pre><code class="language-c" data-lang="c"><span class="k">if</span> <span class="n">AccessCheckCAR</span><span class="p">(</span><span class="n">msgIn</span><span class="p">.</span><span class="n">pNC</span><span class="p">,</span> <span class="n">Ds</span><span class="o">-</span><span class="n">Replication</span><span class="o">-</span><span class="n">Get</span><span class="o">-</span><span class="n">Changes</span><span class="p">)</span> <span class="o">=</span> <span class="nb">false</span> <span class="n">then</span>
   <span class="k">return</span> <span class="n">insufficientAccessRights</span>
<span class="n">endif</span>
 
<span class="k">if</span> <span class="n">msgIn</span><span class="p">.</span><span class="n">pPartialAttrSet</span><span class="p">.</span><span class="n">cAttrs</span> <span class="err">≠</span> <span class="mi">0</span> <span class="n">and</span>
   <span class="n">IsFilteredAttributePresent</span><span class="p">(</span><span class="n">msgIn</span><span class="p">.</span><span class="n">pPartialAttrSet</span><span class="p">)</span> <span class="o">=</span> <span class="nb">true</span> <span class="n">and</span>
   <span class="n">AccessCheckCAR</span><span class="p">(</span><span class="n">msgIn</span><span class="p">.</span><span class="n">pNC</span><span class="p">,</span> 
                  <span class="n">Ds</span><span class="o">-</span><span class="n">Replication</span><span class="o">-</span><span class="n">Get</span><span class="o">-</span><span class="n">Changes</span><span class="o">-</span><span class="n">In</span><span class="o">-</span><span class="n">Filtered</span><span class="o">-</span><span class="n">Set</span><span class="p">)</span> <span class="o">=</span> <span class="nb">false</span> <span class="n">and</span>
   <span class="n">AccessCheckCAR</span><span class="p">(</span><span class="n">msgIn</span><span class="p">.</span><span class="n">pNC</span><span class="p">,</span> 
                  <span class="n">Ds</span><span class="o">-</span><span class="n">Replication</span><span class="o">-</span><span class="n">Get</span><span class="o">-</span><span class="n">Changes</span><span class="o">-</span><span class="n">All</span><span class="p">)</span> <span class="o">=</span> <span class="nb">false</span>
<span class="n">then</span>
  <span class="k">return</span> <span class="n">insufficientAccessRights</span>
<span class="n">endif</span>
 
<span class="k">return</span> <span class="mi">0</span> <span class="cm">/* success */</span></code></pre></figure>

<h2 id="the-technique">The Technique</h2>
<h3 id="experimenting-with-ldap_server_dirsync_oid">Experimenting With LDAP_SERVER_DIRSYNC_OID</h3>
<p>After going through some documentation, interesting points arise:</p>

<ul>
  <li>.NET’s <a href="https://docs.microsoft.com/en-us/dotnet/api/system.directoryservices.protocols.dirsyncrequestcontrol">System.DirectoryServices.Protocols.DirSyncRequestControl</a> is an easy way to request a directory synchronization, and an example script can be found <a href="http://dloder.blogspot.com/2012/01/powershell-dirsync-sample.html">here</a>.</li>
  <li>The flag <code class="language-plaintext highlighter-rouge">LDAP_DIRSYNC_OBJECT_SECURITY</code> can be used to request a synchronization without having the <code class="language-plaintext highlighter-rouge">DS-Replication-Get-Changes</code> privileges, but only returns attributes that the requester can read. Since we wish to read attributes that we normally cannot, we will avoid sending this.</li>
  <li>An LDAP filter can be specified to only synchronize objects that match it.</li>
  <li>We can also request a specific set of attributes to be synchronized.</li>
</ul>

<p>Before executing the aforementioned example script, the following has been altered:</p>

<ul>
  <li>The script handles a cookie to only get the changes made since our last request, but has been removed since this serves us no purpose.</li>
  <li>Filtering is done on <code class="language-plaintext highlighter-rouge">(samaccountname=Administrator)</code>.</li>
  <li>We only print a few attributes from the response for the sake of clarity, despite requesting all of them.</li>
</ul>

<figure class="highlight"><pre><code class="language-powershell" data-lang="powershell"><span class="n">Add-Type</span><span class="w"> </span><span class="nt">-AssemblyName</span><span class="w"> </span><span class="nx">System.DirectoryServices.Protocols</span><span class="w">

</span><span class="nv">$RootDSE</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[</span><span class="n">ADSI</span><span class="p">]</span><span class="s2">"LDAP://RootDSE"</span><span class="w">
</span><span class="nv">$LDAPConnection</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">New-Object</span><span class="w"> </span><span class="nx">System.DirectoryServices.Protocols.LDAPConnection</span><span class="p">(</span><span class="nv">$RootDSE</span><span class="o">.</span><span class="nf">dnsHostName</span><span class="p">)</span><span class="w">
</span><span class="nv">$Request</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">New-Object</span><span class="w"> </span><span class="nx">System.DirectoryServices.Protocols.SearchRequest</span><span class="p">(</span><span class="nv">$RootDSE</span><span class="o">.</span><span class="nf">defaultNamingContext</span><span class="p">,</span><span class="w"> </span><span class="s2">"(samaccountname=Administrator)"</span><span class="p">,</span><span class="w"> </span><span class="s2">"Subtree"</span><span class="p">,</span><span class="w"> </span><span class="bp">$null</span><span class="p">)</span><span class="w">
</span><span class="nv">$DirSyncRC</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">New-Object</span><span class="w"> </span><span class="nx">System.DirectoryServices.Protocols.DirSyncRequestControl</span><span class="w">
</span><span class="nv">$Request</span><span class="o">.</span><span class="nf">Controls</span><span class="o">.</span><span class="nf">Add</span><span class="p">(</span><span class="nv">$DirSyncRC</span><span class="p">)</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Out-Null</span><span class="w">

</span><span class="nv">$Response</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nv">$LDAPConnection</span><span class="o">.</span><span class="nf">SendRequest</span><span class="p">(</span><span class="nv">$Request</span><span class="p">)</span><span class="w">
</span><span class="nv">$Attributes</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nv">$Response</span><span class="o">.</span><span class="nf">Entries</span><span class="o">.</span><span class="nf">Attributes</span><span class="w">

</span><span class="s2">"samaccountname: "</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="nv">$Attributes</span><span class="p">[</span><span class="s1">'samaccountname'</span><span class="p">][</span><span class="mi">0</span><span class="p">]</span><span class="w">
</span><span class="s2">"description: "</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="nv">$Attributes</span><span class="p">[</span><span class="s1">'description'</span><span class="p">][</span><span class="mi">0</span><span class="p">]</span><span class="w">
</span><span class="s2">"objectcategory: "</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="nv">$Attributes</span><span class="p">[</span><span class="s1">'objectcategory'</span><span class="p">][</span><span class="mi">0</span><span class="p">]</span></code></pre></figure>

<p>Once the executing user has been delegated <code class="language-plaintext highlighter-rouge">DS-Replication-Get-Changes</code>, the directory synchronization succeeds:</p>

<p><img src="/assets/img/dirsync/dirsync-example.png" /></p>

<p>Without these delegated privileges, the <code class="language-plaintext highlighter-rouge">SendRequest</code> function fails, and returns the error message <code class="language-plaintext highlighter-rouge">The user has insufficient access rights</code>, as we also saw from the security check pseudocode.</p>

<p>So far so good, but nothing impressive; these can be retrieved with a standard LDAP query.</p>

<h3 id="accessing-confidential-attributes-with-ds-replication-get-changes">Accessing Confidential Attributes With DS-Replication-Get-Changes</h3>
<p>In the previous <code class="language-plaintext highlighter-rouge">searchFlags</code> section, we have established that some attributes are confidential, and cannot be read without having explicit privileges over the object. While some attributes are confidential by default, it is possible to alter existing <code class="language-plaintext highlighter-rouge">searchFlags</code> to set some other attributes to confidential, or to create your own confidential attribute.</p>

<p>You can query for confidential attributes like so:</p>

<figure class="highlight"><pre><code class="language-powershell" data-lang="powershell"><span class="n">Get-AdObject</span><span class="w"> </span><span class="nt">-SearchBase</span><span class="w"> </span><span class="s1">'CN=Schema,CN=Configuration,DC=contoso,DC=com'</span><span class="w"> </span><span class="nt">-LdapFilter</span><span class="w"> </span><span class="s1">'(&amp;(searchflags:1.2.840.113556.1.4.804:=128)(!(searchflags:1.2.840.113556.1.4.804:=512)))'</span></code></pre></figure>

<ul>
  <li><code class="language-plaintext highlighter-rouge">1.2.840.113556.1.4.804</code> is a <a href="https://ldapwiki.com/wiki/Filtering%20for%20Bit%20Fields">bitwise</a> OR operator ensuring that we look for the flag <code class="language-plaintext highlighter-rouge">fCONFIDENTIAL</code> within all the other flags, and not just this value.</li>
  <li><code class="language-plaintext highlighter-rouge">searchFlags</code> <code class="language-plaintext highlighter-rouge">128</code> (0x00000080) refers to <code class="language-plaintext highlighter-rouge">fCONFIDENTIAL</code>.</li>
  <li>We exclude <code class="language-plaintext highlighter-rouge">512</code> (0x00000200) indicating <code class="language-plaintext highlighter-rouge">fRODCFilteredAttribute</code>, requiring more privileges.</li>
</ul>

<p><img src="/assets/img/dirsync/searching-confidential-attributes.png" /></p>

<p>As a proof of concept, the attribute <code class="language-plaintext highlighter-rouge">unixUserPassword</code> will be used. It is sometimes used to store a user’s <a href="https://www.ibm.com/docs/en/aix/7.3?topic=servers-active-directory-password-attribute-selection">UNIX password hash</a>.</p>

<p>In our example, when querying for this attribute through a regular LDAP query for the user <code class="language-plaintext highlighter-rouge">Administrator</code>, nothing is returned, confirming that we do not have the right privileges over the object:</p>

<figure class="highlight"><pre><code class="language-powershell" data-lang="powershell"><span class="p">(</span><span class="n">Get-ADUser</span><span class="w"> </span><span class="nt">-Identity</span><span class="w"> </span><span class="nx">Administrator</span><span class="w"> </span><span class="nt">-Properties</span><span class="w"> </span><span class="o">*</span><span class="p">)</span><span class="o">.</span><span class="nf">unixUserPassword</span></code></pre></figure>

<p><img src="/assets/img/dirsync/unixuserpassword-empty.png" /></p>

<p>However, if we modify our script to print this attribute, we see that it was indeed synchronized:</p>

<p><img src="/assets/img/dirsync/unixuserpassword-read.png" /></p>

<h3 id="accessing-rodc-filtered-attributes-with-ds-replication-get-changes-in-filtered-set">Accessing RODC Filtered Attributes With DS-Replication-Get-Changes-In-Filtered-Set</h3>
<p>In order to successfully use these privileges, one must also have <code class="language-plaintext highlighter-rouge">DS-Replication-Get-Changes</code>, otherwise the LDAP control will deny the request, as denoted in the pseudocode.</p>

<p>Similarly to the last section, we can query for attributes with the flag <code class="language-plaintext highlighter-rouge">fRODCFilteredAttribute</code>. We do not need to exclude confidential attributes; we also have the required privileges for these:</p>

<figure class="highlight"><pre><code class="language-powershell" data-lang="powershell"><span class="n">Get-AdObject</span><span class="w"> </span><span class="nt">-SearchBase</span><span class="w"> </span><span class="s1">'CN=Schema,CN=Configuration,DC=contoso,DC=com'</span><span class="w"> </span><span class="nt">-LdapFilter</span><span class="w"> </span><span class="s1">'(searchflags:1.2.840.113556.1.4.804:=512)'</span></code></pre></figure>

<p><img src="/assets/img/dirsync/searching-rodc-filtered-attributes.png" /></p>

<p>Other than <code class="language-plaintext highlighter-rouge">ms-Mcs-AdmPwd</code>, these attributes should all be present in a modern, vanilla installation of Active Directory.</p>

<p>Let us focus on <code class="language-plaintext highlighter-rouge">ms-Mcs-AdmPwd</code>. As briefly mentioned in the impact section, this attribute is used by LAPS to store the current password of the managed local administrator, which will either be the default <code class="language-plaintext highlighter-rouge">Administrator</code> account, or a custom one.</p>

<p>With our new privileges, we attempt to retrieve the attribute of a LAPS-enabled computer object through a LDAP query, but to no avail:</p>

<figure class="highlight"><pre><code class="language-powershell" data-lang="powershell"><span class="p">(</span><span class="n">Get-ADComputer</span><span class="w"> </span><span class="nt">-Identity</span><span class="w"> </span><span class="nx">wks1</span><span class="err">$</span><span class="w"> </span><span class="nt">-Properties</span><span class="w"> </span><span class="o">*</span><span class="p">)</span><span class="o">.</span><span class="s1">'ms-Mcs-AdmPwd'</span></code></pre></figure>

<p><img src="/assets/img/dirsync/ms-mcs-admpwd-empty.png" /></p>

<p>What if we mimic the same procedure as before, and simply print the attribute through our script? The value of the attribute turns out to be empty.</p>

<p><img src="/assets/img/dirsync/ms-mcs-admpwd-read-failed.png" /></p>

<p>To successfully get the value of a <code class="language-plaintext highlighter-rouge">fRODCFilteredAttribute</code> attribute, we must explicitly specify that we wish to synchronize it when constructing the <code class="language-plaintext highlighter-rouge">SearchRequest</code>. It can be done by replacing the <code class="language-plaintext highlighter-rouge">$null</code> value in the <code class="language-plaintext highlighter-rouge">SearchRequest</code> object of our previous script to an array containing the list of attributes, for instance <code class="language-plaintext highlighter-rouge">('ms-Mcs-AdmPwd')</code>, that we desire to fetch.</p>

<p>Once edited and executed again, the password is printed:</p>

<p><img src="/assets/img/dirsync/ms-mcs-admpwd-read.png" /></p>

<p>If attempting to run this modified script without <code class="language-plaintext highlighter-rouge">DS-Replication-Get-Changes-In-Filtered-Set</code>, we will be greeted with the same error message <code class="language-plaintext highlighter-rouge">The user has insufficient access rights</code> as before, due to attempting to synchronize a specific attribute with <code class="language-plaintext highlighter-rouge">fRODCFilteredAttribute</code>. Again, this is on par with what was written in the security check pseudocode.</p>

<h2 id="dirsync-a-proof-of-concept-tool">DirSync: A Proof of Concept Tool</h2>
<p><a href="https://github.com/simondotsh/DirSync">DirSync</a> is a simple proof of concept PowerShell module to demonstrate the impact of delegating these privileges. It offers two functions: <code class="language-plaintext highlighter-rouge">Sync-LAPS</code> to directly focus on LAPS, and <code class="language-plaintext highlighter-rouge">Sync-Attributes</code> to synchronize any desired attributes. Usage can be found in the <a href="https://github.com/simondotsh/DirSync/blob/master/README.md">README.md</a> file, and via the usual <code class="language-plaintext highlighter-rouge">Get-Help</code> PowerShell command.</p>

<h3 id="a-remark-for-blue-teamers">A Remark for Blue Teamers</h3>
<p>Keep in mind that this technique uses an LDAP request, and not RPC like <code class="language-plaintext highlighter-rouge">DCSync</code>. While the tool is designed by default to communicate over plain text for compatibility reasons, it also supports LDAPS. If your DCs have a certificate installed allowing to use LDAPS, do not rely on over-the-wire detection, unless you hold decryption capabilities.</p>

<p>The Wireshark website contains an <a href="https://wiki.wireshark.org/LDAP#example-capture-file">example capture file</a> of the <code class="language-plaintext highlighter-rouge">DirSync</code> control over plain LDAP, filtering on <code class="language-plaintext highlighter-rouge">(ObjectClass=*)</code> and requesting no specific attributes.</p>

<h2 id="some-unorganized-notes">Some Unorganized Notes</h2>
<ul>
  <li>It might very well be possible to achieve the same result with these privileges using <code class="language-plaintext highlighter-rouge">MS-DRSR</code>’s <code class="language-plaintext highlighter-rouge">GetNCChanges</code>.</li>
  <li>I did not manage to read secret attributes such as <a href="https://docs.microsoft.com/en-us/windows/win32/adschema/a-unicodepwd">unicodePwd</a> using <code class="language-plaintext highlighter-rouge">DS-Replication-Get-Changes-All</code> through the <code class="language-plaintext highlighter-rouge">DirSync</code> LDAP control.</li>
  <li>As mentioned in the pseudocode, <code class="language-plaintext highlighter-rouge">DS-Replication-Get-Changes-All</code> can be used instead of <code class="language-plaintext highlighter-rouge">DS-Replication-Get-Changes-In-Filtered-Set</code> to read RODC filtered attributes. As you can also perform a full-blown <code class="language-plaintext highlighter-rouge">DCSync</code> with those privileges, I do not expect <code class="language-plaintext highlighter-rouge">DirSync</code> to be the technique of choice, unless it offers some operational security.</li>
  <li>There are multiple ways to <a href="https://docs.microsoft.com/en-us/windows/win32/ad/tracking-changes">track changes</a> in a directory.</li>
  <li>I have not investigated the significance of being able to read the built-in confidential and RODC filtered attributes.</li>
</ul>

<h2 id="to-conclude">To Conclude</h2>
<p>While clearly not as impactful as <code class="language-plaintext highlighter-rouge">DCSync</code>, <code class="language-plaintext highlighter-rouge">DirSync</code> presents an alternative to synchronize confidential and RODC filtered attributes. The demonstrated LAPS scenario could be used to introduce a new edge into <a href="https://github.com/BloodHoundAD/BloodHound">BloodHound</a>, and is a good candidate to look for during Active Directory privileges assessments.</p>

<p>A thank you to <a href="https://twitter.com/joewaredotnet">@joewaredotnet</a> for the article <a href="https://blog.joeware.net/2018/05/25/5824/">Replicating Changes Control Access Right series</a> that contributed to my curiousness.</p>]]></content><author><name></name></author><category term="infosec" /><summary type="html"><![CDATA[DCSync can be categorized as one of the most effective technique on a domain, since retrieving the krbtgt NT hash allows to forge a valid ticket-granting ticket for any domain user. To manage to do so, a principal must possess the right privileges over the root of a domain: DS-Replication-Get-Changes and DS-Replication-Get-Changes-All. The combination renders it possible to successfully call MS-DRSR’s GetNCChanges to replicate secret attributes.]]></summary></entry><entry><title type="html">Beware of BloodHound’s Contains Edge</title><link href="https://simondotsh.com/infosec/2022/06/14/bloodhound-contains-edge.html" rel="alternate" type="text/html" title="Beware of BloodHound’s Contains Edge" /><published>2022-06-14T05:00:00+00:00</published><updated>2022-06-14T05:00:00+00:00</updated><id>https://simondotsh.com/infosec/2022/06/14/bloodhound-contains-edge</id><content type="html" xml:base="https://simondotsh.com/infosec/2022/06/14/bloodhound-contains-edge.html"><![CDATA[<p>As I continue my way through assessing Active Directory privileges without commercial software, I am constantly reminded how complex of a task it is. While determining the objects a single user can control is quite straight forward, finding all paths leading to the compromise of privileged principals can be tricky on a large domain. It is also apparent that in order to successfully do so, one must possess a strong understanding of the analyzed data, especially its weaknesses, or be at risk of reporting erroneous findings.</p>

<p>This post is dedicated to presenting the root cause of real-world scenario where if I did not take a close look at the data, I would have documented twice too many users with a path to domain administration privileges. It also includes a proposed solution so that you can also avoid it.</p>

<h2 id="the--questionable-duality-of-contains">The  Questionable Duality of Contains</h2>
<p><a href="https://github.com/BloodHoundAD/BloodHound/">BloodHound</a>’s <code class="language-plaintext highlighter-rouge">Contains</code> edge aims to describe attack paths both from GPOs, and the potential of descendant objects inheriting permissions. Unfortunately, this cannot be achieved without introducing false positives as will be demonstrated.</p>

<h3 id="gpo-to-domain-admins">GPO to Domain Admins</h3>
<p>Consider the following path:</p>

<p><img src="/assets/img/bh-contains-edge/gpo-path-da.png" width="70%" /></p>

<p>The GPO <code class="language-plaintext highlighter-rouge">GPO ROOT@AD.LOCAL</code> is linked at the root of the domain, but is not enforced as denoted by the dotted <code class="language-plaintext highlighter-rouge">GPLink</code> edge. This signifies that if a domain or OU in the path blocks GPO inheritance, it will not go down to the object that we wish to compromise, which would also be shown in the graph with a dotted <code class="language-plaintext highlighter-rouge">Contains</code> edge. Note that this is an entirely different concept than ACE inheritance, and typically does not matter much due to the possibility to enforce the GPO, resulting in a complete disregard of any blocked inheritance. In this demonstration, it is safe to ignore the state of the link, but is worth understanding.</p>

<p>Further down the path, the domain contains the well-known container <code class="language-plaintext highlighter-rouge">USERS@AD.LOCAL</code> that itself contains the group <code class="language-plaintext highlighter-rouge">DOMAIN ADMINS@AD.LOCAL</code>. Therefore, the <code class="language-plaintext highlighter-rouge">Contains</code> edges allow us to map that due to the GPO being linked at the root of the domain, it can use a filter that would apply to the members of the group that we want to compromise.</p>

<p>So far so good; no potential false positive in sight.</p>

<h3 id="ace-inheritance-to-domain-admins">ACE Inheritance to Domain Admins</h3>
<p>Let us look at this next path:</p>

<p><img src="/assets/img/bh-contains-edge/false-positive-path-da.png" /></p>

<p>It is claimed that since the group <code class="language-plaintext highlighter-rouge">CL-UNI-ADMINGROUP1@AD.LOCAL</code> has <code class="language-plaintext highlighter-rouge">GenericAll</code> over the container where the group <code class="language-plaintext highlighter-rouge">DOMAIN ADMINS@AD.LOCAL</code> resides, it can be compromised. The idea would be to create, on the container, a new ACE that applies to descendant group objects, effectively awarding yourself privileges over the group; however, this is simply not possible since <code class="language-plaintext highlighter-rouge">Domain Admins</code> is a protected group.</p>

<p><a href="https://docs.microsoft.com/en-us/windows-server/identity/ad-ds/plan/security-best-practices/appendix-c--protected-accounts-and-groups-in-active-directory">Protected accounts and groups</a> is an Active Directory feature that applies to specific high privileged accounts, groups and their members to ensure that no unwanted principals get privileges over them. An object named <code class="language-plaintext highlighter-rouge">AdminSDHolder</code> serves as a permissions template that overwrites protected principals’ permissions every 60 minutes by default. Unless modified, this template has a very important property: it is configured to disable ACE inheritance.</p>

<p>Now, going back to the example above, <code class="language-plaintext highlighter-rouge">DOMAIN ADMINS@AD.LOCAL</code> does have ACE inheritance disabled, rendering the path impossible.</p>

<p>This leads to an interesting situation: in the context of a GPO, it is safe to trust the <code class="language-plaintext highlighter-rouge">Contains</code> edges, but in the case of an ACE that must be pushed down, they may yield false positives.</p>

<p>How can we successfully use the edge in both circumstances and avoid false positives? By not using it in both circumstances.</p>

<h2 id="working-on-a-solution">Working on a Solution</h2>
<p>In the previous section, we have established that any principal with ACE inheritance disabled leads to a false positive when an edge expects to compromise it that way. As such, deleting the <code class="language-plaintext highlighter-rouge">Contains</code> relationship would solve the issue, but would also introduce a blind spot in the case of a GPO; nonetheless, it is clear that the relationship is problematic, and must be dealt with.</p>

<h3 id="finding-disabled-inheritance">Finding Disabled Inheritance</h3>
<p>When importing the output of the collection method <code class="language-plaintext highlighter-rouge">DCOnly</code> into BloodHound, the data set does not include the inheritance state of objects but fortunately, the collector does gather that information, and stores it in the JSON files that are ingested:</p>

<p><img src="/assets/img/bh-contains-edge/json-isaclprotected.png" /></p>

<p>Since the desired information cannot be found once imported, it means that the ingesting routine must be modified.</p>

<h3 id="altering-the-ingestor">Altering the Ingestor</h3>
<p>In the release version 4.2.0 of BloodHound, the file <code class="language-plaintext highlighter-rouge">src/js/newingestion.js</code> contains a node creation function for each type of objects located in the JSON files. Since all these objects have the property <code class="language-plaintext highlighter-rouge">isACLProtected</code> set in the collected data, we can simply update each function using the same principle that will be shown. As an example, only the <code class="language-plaintext highlighter-rouge">buildGroupJsonNew</code> function will be updated.</p>

<p>Before the <code class="language-plaintext highlighter-rouge">queries.properties.props.push</code> function call, add the following two lines:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>if (properties.distinguishedname !== undefined)
    properties.isaclprotected = group.IsACLProtected;
</code></pre></div></div>

<p><img src="/assets/img/bh-contains-edge/modifying-buildgroupjsonnew.png" /></p>

<p>The <code class="language-plaintext highlighter-rouge">if</code> comparison is used to avoid an odd behavior in the JSON where some objects such as groups are duplicated, but incomplete. In that case, they did not contain a distinguished name, so we refrain attempting to set the property. Also, GPOs do not have a distinguished name, and therefore the comparison must not be added in that function.</p>

<p>All that is left is to compile the code. This can be achieved easily using the third-party Docker image <a href="https://hub.docker.com/r/electronuserland/builder">electronuserland/builder</a> as such at the root of the edited BloodHound project:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo docker run -it --rm -v ${PWD}:/project electronuserland/builder /bin/bash -c 'npm install &amp;&amp; npm run-script build'
</code></pre></div></div>

<p>Once the compilation is complete, the newly created directory <code class="language-plaintext highlighter-rouge">BloodHound-linux-x64</code> contains the compiled binary when using Linux. Execute it and import your data as normally done. Feel free to open up the developer console by using the keys <code class="language-plaintext highlighter-rouge">Ctrl+Shift+I</code> to find out if any errors were encountered during the import.</p>

<p>At this point, nodes have the new <code class="language-plaintext highlighter-rouge">isaclprotected</code> property set, but relationships must also be adjusted to eliminate false positives.</p>

<h3 id="reworking-relationships">Reworking Relationships</h3>
<p>As briefly mentioned earlier, deleting the <code class="language-plaintext highlighter-rouge">Contains</code> edge when ACE inheritance is disabled resolves false positives, but breaks paths from GPOs. Despite this, the edge must be deleted in that situation, so a way to restore paths from GPOs is needed.</p>

<p>Before executing any of the next queries, make sure to either backup your database (if you have the Enterprise version of Neo4j), or to have access to your collected data to avoid any misfortune.</p>

<p>In <code class="language-plaintext highlighter-rouge">cypher-shell</code>, execute the following queries:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>MATCH (n:GPO)-[:GPLink|Contains*1..]-&gt;(m:User {isaclprotected: True}) CREATE (n)-[:DescendsTo]-&gt;(m);
MATCH (n:GPO)-[:GPLink|Contains*1..]-&gt;(m:Group {isaclprotected: True}) CREATE (n)-[:DescendsTo]-&gt;(m);
MATCH (n:GPO)-[:GPLink|Contains*1..]-&gt;(m:Computer {isaclprotected: True}) CREATE (n)-[:DescendsTo]-&gt;(m);
</code></pre></div></div>

<p>The idea is to add a new edge, here named <code class="language-plaintext highlighter-rouge">DescendsTo</code>, to link GPOs to all objects they can apply to that have ACE inheritance disabled. In a large domain with some GPOs linked at the root of the domain, this may yield a significant amount of newly-added edges, but does not matter much in my experience.</p>

<p>Now that we have linked GPOs to the objects where the path would be lost, we can go ahead and get rid of the <code class="language-plaintext highlighter-rouge">Contains</code> edges that are false positives in the case of ACE inheritance:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>MATCH (n:Domain)-[r:Contains]-&gt;({isaclprotected: True}) DELETE r;
MATCH (n:Container)-[r:Contains]-&gt;({isaclprotected: True}) DELETE r;
MATCH (n:OU)-[r:Contains]-&gt;({isaclprotected: True}) DELETE r;
</code></pre></div></div>

<p>On all types of nodes that can contain other nodes, we delete the <code class="language-plaintext highlighter-rouge">Contains</code> relationship to the contained node when its ACE inheritance is disabled, successfully removing false positives.</p>

<p>Note that the ingestor could be modified to execute these queries at the end of an import to avoid this manual process.</p>

<h3 id="validating-the-results">Validating the Results</h3>
<p>After the previous manipulations, the GPO path described at the beginning of this post gets reported as such, but has two negative points:</p>

<p><img src="/assets/img/bh-contains-edge/descendsto-gpo-path-da.png" width="70%" /></p>

<ol>
  <li>We no longer see where the GPO is linked, and the path it has to take to compromise the user. This could be improved by adding a property to GPO objects depicting where they are linked, or simply query for its <code class="language-plaintext highlighter-rouge">GPLink</code> relationships. Then, we can use the distinguished name of the end object to reconstruct the path the GPO is taking.</li>
  <li>Compromising a principal from a GPO is counted as only one relationship, and therefore risks of being reported more often when using the <code class="language-plaintext highlighter-rouge">shortestPath</code> function. Keep in mind that this problem is also true for many other cases, since all relationships have the same weight in the eyes of <code class="language-plaintext highlighter-rouge">shortestPath</code>. This leads to a chain of <code class="language-plaintext highlighter-rouge">MemberOf</code> costing more than a single <code class="language-plaintext highlighter-rouge">ForceChangePassword</code>, despite needing no operation on the domain to leverage the former.</li>
</ol>

<p>Regardless, in my situation, these downsides are significantly easier to deal with than attempting to manually filter out false positives.</p>

<p>As our last validation, we query for the false positive mentioned earlier:</p>

<p><img src="/assets/img/bh-contains-edge/resolved-false-positive-path-da.png" /></p>

<p>No path is returned due to the <code class="language-plaintext highlighter-rouge">Contains</code> edge getting deleted in the last section, as <code class="language-plaintext highlighter-rouge">DOMAIN ADMINS@AD.LOCAL</code> has ACE inheritance disabled.</p>

<h2 id="more-edge-removal">More Edge Removal</h2>
<p>In this section, we diverge from the <code class="language-plaintext highlighter-rouge">Contains</code> edge, but remain on the topic of false positive reduction. Depending on your use case, you may be perfectly fine with ignoring the following edges, but in my situation they were problematic.</p>

<h3 id="the-getchanges-family">The GetChanges Family</h3>
<p>Prior to version 4.2.0, a singular edge did not exist to identify a principal with <code class="language-plaintext highlighter-rouge">DCSync</code> privileges; the analyst had to query for both <code class="language-plaintext highlighter-rouge">GetChanges</code> and <code class="language-plaintext highlighter-rouge">GetChangesAll</code>. Now, the <code class="language-plaintext highlighter-rouge">DCSync</code> edge is created post-processing when the aforementioned privileges are identified. This is convenient, but the <code class="language-plaintext highlighter-rouge">GetChanges</code> and <code class="language-plaintext highlighter-rouge">GetChangesAll</code> edges are not deleted afterwards, meaning that you may retrieve a path where a principal only has one of them, inducing a false positive.</p>

<p>The same concept applies to the new edge <code class="language-plaintext highlighter-rouge">SyncLAPSPassword</code> (technical details available <a href="https://simondotsh.com/infosec/2022/07/11/dirsync.html">here</a>) combining <code class="language-plaintext highlighter-rouge">GetChanges</code> and <code class="language-plaintext highlighter-rouge">GetChangesInFilteredSet</code>.</p>

<p>For this reason, during my analysis, I delete the <code class="language-plaintext highlighter-rouge">GetChanges*</code> edges, but be aware that this has downsides. Consider the situation where a principal has <code class="language-plaintext highlighter-rouge">GetChanges</code>, but not <code class="language-plaintext highlighter-rouge">GetChangesAll</code>. If they have <code class="language-plaintext highlighter-rouge">GenericWrite</code> on a group that has the missing <code class="language-plaintext highlighter-rouge">GetChangesAll</code>, they could add themselves to the group and be able to perform the <code class="language-plaintext highlighter-rouge">DCSync</code>. If you delete these edges, you would end up missing this possibility.</p>

<p>If this fits your use case, you can delete the potentially false positive-inducing edges like so:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>MATCH ()-[r:GetChanges]-&gt;() DELETE r;
MATCH ()-[r:GetChangesAll]-&gt;() DELETE r;
MATCH ()-[r:GetChangesInFilteredSet]-&gt;() DELETE r;
</code></pre></div></div>

<h3 id="trustedby">TrustedBy</h3>
<p><code class="language-plaintext highlighter-rouge">TrustedBy</code> simply maps trusts between domains. While useful, it leads to erroneous paths when assessing cross-domain privileges.</p>

<p>Consider the next query and the resultant path:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>MATCH p=shortestPath((:User {name: "WILLIAM_STEIN@AD.LOCAL"})-[*1..]-&gt;(:User {name: 'LOWPRIVS@AD2.LOCAL'})) RETURN p;
</code></pre></div></div>

<p><img src="/assets/img/bh-contains-edge/trustedby-false-positive-path.png" /></p>

<p><code class="language-plaintext highlighter-rouge">WILLIAM_STEIN@AD.LOCAL</code> has <code class="language-plaintext highlighter-rouge">DCSync</code> privileges over the domain <code class="language-plaintext highlighter-rouge">AD.LOCAL</code>. Then, <code class="language-plaintext highlighter-rouge">AD2.LOCAL</code> trusts <code class="language-plaintext highlighter-rouge">AD.LOCAL</code> for authentication, and it <code class="language-plaintext highlighter-rouge">Contains</code> the user <code class="language-plaintext highlighter-rouge">LOWPRIVS@AD2.LOCAL</code>.</p>

<p>The <code class="language-plaintext highlighter-rouge">DCSync</code> and <code class="language-plaintext highlighter-rouge">Contains</code> edges are accurate, but the trust mapping acts as if it awards privileges to push ACEs down the <code class="language-plaintext highlighter-rouge">AD2.LOCAL</code> domain object, which is absolutely not the case. A trust itself is not enough; <code class="language-plaintext highlighter-rouge">WILLIAM_STEIN@AD.LOCAL</code> would need to have been given privileges over <code class="language-plaintext highlighter-rouge">AD2.LOCAL</code> to successfully leverage this path.</p>

<p>Although, it is worth noting that this path could indeed exist in the case where the trust has SID history enabled, and that we have the required privileges to forge tickets in the source domain. This should be analyzed independently.</p>

<p>The easiest solution to ensure no false positives result from <code class="language-plaintext highlighter-rouge">TrustedBy</code> is simply to get rid of the edges:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>MATCH ()-[r:TrustedBy]-&gt;() DELETE r;
</code></pre></div></div>

<p>If need be, you can reimport the <code class="language-plaintext highlighter-rouge">*_domains.json</code> file from your BloodHound dump to restore the trust relationships.</p>

<h2 id="concluding">Concluding</h2>
<p>While the proposed solution has negative aspects, it offers the peace of mind of knowing that the <code class="language-plaintext highlighter-rouge">Contains</code> edge will not introduce false positives. As I deem the fix imperfect, I will not be proposing it to the core of BloodHound, but recommend it to anyone assessing privileges at a large scale.</p>

<p>I would like to thank <a href="https://twitter.com/marcan2020">marcan2020</a> for his contribution to the solution, and the <a href="https://github.com/davidprowe/BadBlood">BadBlood</a> project for filling up my directories with plenty of data to play with.</p>]]></content><author><name></name></author><category term="infosec" /><summary type="html"><![CDATA[As I continue my way through assessing Active Directory privileges without commercial software, I am constantly reminded how complex of a task it is. While determining the objects a single user can control is quite straight forward, finding all paths leading to the compromise of privileged principals can be tricky on a large domain. It is also apparent that in order to successfully do so, one must possess a strong understanding of the analyzed data, especially its weaknesses, or be at risk of reporting erroneous findings.]]></summary></entry><entry><title type="html">Capitalizing on BloodHound’s Data: Cypher, Object Ownerships and Trusts</title><link href="https://simondotsh.com/infosec/2022/05/24/bloodhound-cypher.html" rel="alternate" type="text/html" title="Capitalizing on BloodHound’s Data: Cypher, Object Ownerships and Trusts" /><published>2022-05-24T05:00:00+00:00</published><updated>2022-05-24T05:00:00+00:00</updated><id>https://simondotsh.com/infosec/2022/05/24/bloodhound-cypher</id><content type="html" xml:base="https://simondotsh.com/infosec/2022/05/24/bloodhound-cypher.html"><![CDATA[<p><a href="https://github.com/BloodHoundAD/BloodHound/">BloodHound</a> remains the tool of excellence to assess Active Directory privileges. In most domains that have undergone years of dubious management, only analyzing data gathered from the <code class="language-plaintext highlighter-rouge">DCOnly</code> collection method is plenty to keep a fleet of system administrators busy.</p>

<p>While its interface offers built-in Cypher queries, the full potential of the data set is leveraged by crafting answers to your own questions. In fact, relying solely on the built-in queries can lead to missing critical issues.</p>

<h2 id="why-learning-cypher-is-a-must">Why Learning Cypher Is a Must</h2>
<p>Before we move on, one must be familiar with the jargon of graph theory in the context of BloodHound:</p>
<ul>
  <li><a href="https://bloodhound.readthedocs.io/en/latest/data-analysis/nodes.html">A node or object</a> describes an Active Directory object, such as a <code class="language-plaintext highlighter-rouge">User</code>, <code class="language-plaintext highlighter-rouge">Computer</code> and <code class="language-plaintext highlighter-rouge">OU</code>.</li>
  <li><a href="https://bloodhound.readthedocs.io/en/latest/data-analysis/edges.html">An edge or relationship</a> links two nodes together. A few examples include <code class="language-plaintext highlighter-rouge">MemberOf</code>, <code class="language-plaintext highlighter-rouge">GenericAll</code> and <code class="language-plaintext highlighter-rouge">Owns</code>.</li>
</ul>

<p>Now, consider the following case where an analyst wants to ensure that no path exists from one of the groups all users are part of, or a special identity that everyone has such as <code class="language-plaintext highlighter-rouge">Authenticated Users</code>. In our specifically-generated scenario, by setting <code class="language-plaintext highlighter-rouge">Authenticated Users</code> as the starting node and <code class="language-plaintext highlighter-rouge">Domain Admins</code> as the ending one, the interface claims that no path exists:</p>

<p><img src="/assets/img/bh-object-ownership/authenticated-users-no-path.png" /></p>

<p>Under the hood, BloodHound executed this query:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>MATCH (n:Group {objectid: "$DOMAIN_NAME-S-1-5-11"})
MATCH (m:Group {objectid: "$DOMAIN_SID-512"})
MATCH p=allShortestPaths((n)-[r:MemberOf|HasSession|AdminTo|AllExtendedRights|AddMember|ForceChangePassword|GenericAll|GenericWrite|Owns|WriteDacl|WriteOwner|CanRDP|ExecuteDCOM|AllowedToDelegate|ReadLAPSPassword|Contains|GpLink|AddAllowedToAct|AllowedToAct|SQLAdmin|ReadGMSAPassword|HasSIDHistory|CanPSRemote|AZAddMembers|AZContains|AZContributor|AZGetCertificates|AZGetKeys|AZGetSecrets|AZGlobalAdmin|AZOwns|AZPrivilegedRoleAdmin|AZResetPassword|AZUserAccessAdministrator|AZAppAdmin|AZCloudAppAdmin|AZRunsAs|AZKeyVaultContributor|AddSelf|WriteSPN|AddKeyCredentialLink*1..]-&gt;(m))
RETURN p;
</code></pre></div></div>

<p>What happens if we edit it to include all relationships, simply by removing the filter?</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>MATCH (n:Group {objectid: "$DOMAIN_NAME-S-1-5-11"})
MATCH (m:Group {objectid: "$DOMAIN_SID-512"})
MATCH p=allShortestPaths((n)-[*1..]-&gt;(m))
RETURN p;
</code></pre></div></div>

<p><img src="/assets/img/bh-object-ownership/authenticated-users-path-da.png" width="70%" /></p>

<p>The graph then displays that <code class="language-plaintext highlighter-rouge">Authenticated Users</code> has the required privileges on the domain to perform <code class="language-plaintext highlighter-rouge">DCSync</code>. This is caused by the missing edges <code class="language-plaintext highlighter-rouge">GetChanges</code> and <code class="language-plaintext highlighter-rouge">GetChangesAll</code> in the built-in query. Since these two are required to get the <code class="language-plaintext highlighter-rouge">DCSync</code> privileges, matching on both of them is mandatory to confirm a path. I believe this cannot be done without greatly complicating the query, and explains why this case was omitted.</p>

<p>Custom queries also help to identify the root causes of issues in a domain. An analysis activity that often yields interesting output is to look at object owners, which will be the subject of the rest of this post.</p>

<h2 id="querying-object-owners">Querying Object Owners</h2>
<p>To begin, we must step away from the interface since we will be dealing with a large amount of objects. Also, we are interested in the relationships of the objects, and not their visual representation. Therefore, querying Neo4j’s database directly through the binary <code class="language-plaintext highlighter-rouge">cypher-shell</code> is the way to go.</p>

<p>Consider the following query:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>MATCH (n)-[:Owns]-&gt;(m)
RETURN n.name, labels(n), count(m.name) AS count 
ORDER BY count DESC;
</code></pre></div></div>

<p>To explain it simply, we are matching on any type of object that owns other objects, and aggregating results on the count of objects that are owned. The <code class="language-plaintext highlighter-rouge">labels</code> function allows to quickly identify what type of object the owner is.</p>

<h3 id="analyzing-the-results">Analyzing The Results</h3>
<p>In this analysis, we aim to validate that no principals may escalate their privileges through their ownerships. Indeed, owning an object implies that you may award yourself any privileges on it, and therefore compromise it. The expected configuration would be to have only highly privileged principals owning objects, such as <code class="language-plaintext highlighter-rouge">Domain Admins</code> or <code class="language-plaintext highlighter-rouge">Administrators</code>.</p>

<table>
  <thead>
    <tr>
      <th>n.name</th>
      <th>labels(n)</th>
      <th>count</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>“DOMAIN ADMINS@AD.LOCAL”</td>
      <td>[“Group”, “Base”]</td>
      <td>1955</td>
    </tr>
    <tr>
      <td>“SA_IAM@AD.LOCAL”</td>
      <td>[“User”, “Base”]</td>
      <td>475</td>
    </tr>
    <tr>
      <td>“SA_SUPPORT@AD.LOCAL”</td>
      <td>[“User”, “Base”]</td>
      <td>286</td>
    </tr>
    <tr>
      <td>“DOMAIN ADMINS@AD2.LOCAL”</td>
      <td>[“Group”, “Base”]</td>
      <td>261</td>
    </tr>
    <tr>
      <td>“BOBFROMACCOUNTING@AD.LOCAL”</td>
      <td>[“User”, “Base”]</td>
      <td>176</td>
    </tr>
    <tr>
      <td>“WORKSTATION.AD2.LOCAL”</td>
      <td>[“Computer”, “Base”]</td>
      <td>168</td>
    </tr>
    <tr>
      <td>“TEST@AD2.LOCAL”</td>
      <td>[“User”, “Base”]</td>
      <td>94</td>
    </tr>
    <tr>
      <td>“SYSADMIN01@AD.LOCAL”</td>
      <td>[“User”, “Base”]</td>
      <td>32</td>
    </tr>
    <tr>
      <td>“ADMINISTRATORS@AD2.LOCAL”</td>
      <td>[“Group”, “Base”]</td>
      <td>20</td>
    </tr>
    <tr>
      <td>“ADMINISTRATORS@AD.LOCAL”</td>
      <td>[“Group”, “Base”]</td>
      <td>19</td>
    </tr>
  </tbody>
</table>

<p>Already, some thoughts arise:</p>

<ul>
  <li>Highly privileged principals do own some objects, but not all.</li>
  <li>The data set contains two domains: <code class="language-plaintext highlighter-rouge">AD.LOCAL</code> and <code class="language-plaintext highlighter-rouge">AD2.LOCAL</code>. While not reflected here, the built-in query to map domain trusts <code class="language-plaintext highlighter-rouge">MATCH p=(n:Domain)--&gt;(m:Domain) RETURN p;</code> reveals a two-way trust, meaning that both domains trust each other for authentication.</li>
  <li>There may be some automated process creating objects via service accounts (<code class="language-plaintext highlighter-rouge">SA_IAM@AD.LOCAL</code>, <code class="language-plaintext highlighter-rouge">SA_SUPPORT@AD.LOCAL</code>).</li>
  <li><code class="language-plaintext highlighter-rouge">BOBFROMACCOUNTING@AD.LOCAL</code> and <code class="language-plaintext highlighter-rouge">TEST2@AD2.LOCAL</code> should probably not own objects.</li>
  <li>Any malicious actor with <code class="language-plaintext highlighter-rouge">SYSTEM</code> privileges on the computer <code class="language-plaintext highlighter-rouge">WORKSTATION.AD2.LOCAL</code> can compromise 168 objects.</li>
  <li>The system administrator <code class="language-plaintext highlighter-rouge">SYSADMIN01@AD.LOCAL</code> may be creating objects in the Active Directory, resulting in themselves owning them. This may eventually lead to an escalation of privileges, in the case where an object they created is eventually awarded privileges that <code class="language-plaintext highlighter-rouge">SYSADMIN01@AD.LOCAL</code> does not have.</li>
</ul>

<p>The aforementioned two-way trust brings in potential for further analysis: principals may have privileges in the opposite domain, and could very well lead to cross-domain ownerships.</p>

<h3 id="investigating-cross-domain-ownerships">Investigating Cross-domain Ownerships</h3>
<p>In the database, all objects contain an attribute named <code class="language-plaintext highlighter-rouge">domainsid</code>. This helps to quickly identify what domain the object is part of, and is especially useful when investigating cross-domain privileges.</p>

<p>See the following query:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>MATCH (n {domainsid: '$DOMAIN_SID2'})-[:Owns]-&gt;(m {domainsid: '$DOMAIN_SID1'})
RETURN n.name, labels(n), count(m.name) AS count
ORDER BY count DESC;
</code></pre></div></div>

<p>Notice that it is nearly identical to the previous one we ran, except we are filtering on domain SIDs. The goal is to get the count of objects <code class="language-plaintext highlighter-rouge">AD2.LOCAL</code> owns from <code class="language-plaintext highlighter-rouge">AD.LOCAL</code>.</p>

<table>
  <thead>
    <tr>
      <th>n.name</th>
      <th>labels(n)</th>
      <th>count</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>“DOMAIN ADMINS@AD2.LOCAL”</td>
      <td>[“Group”, “Base”]</td>
      <td>203</td>
    </tr>
    <tr>
      <td>“WORKSTATION.AD2.LOCAL”</td>
      <td>[“Computer”, “Base”]</td>
      <td>168</td>
    </tr>
    <tr>
      <td>“TEST@AD2.LOCAL”</td>
      <td>[“User”, “Base”]</td>
      <td>94</td>
    </tr>
  </tbody>
</table>

<p>So, it turns out that <code class="language-plaintext highlighter-rouge">WORKSTATION.AD2.LOCAL</code> and <code class="language-plaintext highlighter-rouge">TEST@AD2.LOCAL</code> ownerships are only located in the <code class="language-plaintext highlighter-rouge">AD.LOCAL</code> domain, while a subset of owned objects by <code class="language-plaintext highlighter-rouge">DOMAIN ADMINS@AD2.LOCAL</code> are also there.</p>

<h3 id="understanding-the-impact">Understanding the Impact</h3>
<p>At this point, we can scope our analysis on understanding the impact of owning other objects. More precisely, in this example, we will look at paths leading to the compromise of the opposite domain.</p>

<p>Depending on your knowledge of a domain, you might understand the implications of compromising certain principals, even if they do not directly possess the required privileges to compromise the entire domain. Focusing on these key principals is usually as valuable as evaluating the paths to <code class="language-plaintext highlighter-rouge">Domain Admins</code>; however, in order to remain configuration agnostic, the next operations will use <code class="language-plaintext highlighter-rouge">Domain Admins</code> as the pot of gold, but may be replaced with any key principal.</p>

<p>The next query will be executed in the interface:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>MATCH (n {domainsid: '$DOMAIN_SID2'})-[:Owns]-&gt;(m)
MATCH p=shortestPath((m)-[*1..]-&gt;(:Group {objectid: '$DOMAIN_SID1-512'}))
RETURN p;
</code></pre></div></div>

<ul>
  <li><code class="language-plaintext highlighter-rouge">MATCH (n {domainsid: '$DOMAIN_SID2'})-[:Owns]-&gt;(m)</code>
    <ul>
      <li>Matching on all objects of <code class="language-plaintext highlighter-rouge">AD2.LOCAL</code> that own objects from any domain.</li>
    </ul>
  </li>
  <li><code class="language-plaintext highlighter-rouge">shortestPath((m)-[*1..]-&gt;(:Group {objectid: '$DOMAIN_SID1-512'}))</code>
    <ul>
      <li>Starting from the <code class="language-plaintext highlighter-rouge">m</code> variable defined in the previous match, which represents objects owned by objects of domain <code class="language-plaintext highlighter-rouge">AD2.LOCAL</code>, we look for any relationship within any amount of hops that leads to the <code class="language-plaintext highlighter-rouge">Domain Admins</code> group of <code class="language-plaintext highlighter-rouge">AD.LOCAL</code>.</li>
      <li>The path is wrapped in the <code class="language-plaintext highlighter-rouge">shortestPath</code> function in order to return only, as it states, the shortest path. This is important, since a query like this in a large domain can be extremely expensive computing-wise.</li>
    </ul>
  </li>
</ul>

<p><img src="/assets/img/bh-object-ownership/owned-group-path-da.png" /></p>

<p>The results signify that some currently unknown object from <code class="language-plaintext highlighter-rouge">AD2.LOCAL</code> owns the group <code class="language-plaintext highlighter-rouge">BE-199-DISTLIST1@AD.LOCAL</code> that has a path to <code class="language-plaintext highlighter-rouge">DOMAIN ADMINS@AD.LOCAL</code> through the <code class="language-plaintext highlighter-rouge">ForceChangePassword</code> edge. Fortunately, we already know the recipe to query who the owner is:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>MATCH (n)-[:Owns]-&gt;(m {name: 'BE-199-DISTLIST1@AD.LOCAL'})
RETURN n.name;
</code></pre></div></div>

<table>
  <thead>
    <tr>
      <th>n.name</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>“WORKSTATION.AD2.LOCAL”</td>
    </tr>
  </tbody>
</table>

<p>At this point, we must further investigate who else has control of the computer object <code class="language-plaintext highlighter-rouge">WORKSTATION.AD2.LOCAL</code> within the same domain, but will be left out of this exercise; though, it is already concluded that a domain administrator of <code class="language-plaintext highlighter-rouge">AD2.LOCAL</code> is a few hops away from administrative privileges in <code class="language-plaintext highlighter-rouge">AD.LOCAL</code>, by hijacking the computer object.</p>

<h3 id="more-cross-domain-fun">More Cross-domain Fun</h3>
<p>Throughout this example, we focused on a single direction to assess privileges (from <code class="language-plaintext highlighter-rouge">AD2.LOCAL</code> to <code class="language-plaintext highlighter-rouge">AD.LOCAL</code>), but remember that we are dealing with a two-way trust. What if an object from <code class="language-plaintext highlighter-rouge">AD.LOCAL</code> can compromise <code class="language-plaintext highlighter-rouge">WORKSTATION.AD2.LOCAL</code>? This would yield a path starting from <code class="language-plaintext highlighter-rouge">AD.LOCAL</code> to compromise an object of <code class="language-plaintext highlighter-rouge">AD2.LOCAL</code>, and then use those privileges to become a domain administrator of <code class="language-plaintext highlighter-rouge">AD.LOCAL</code>.</p>

<p>We can query all objects from <code class="language-plaintext highlighter-rouge">AD.LOCAL</code> that have direct privileges on <code class="language-plaintext highlighter-rouge">WORKSTATION.AD2.LOCAL</code> like so:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>MATCH (n {domainsid: '$DOMAIN_SID1'})--&gt;(m {name: 'WORKSTATION.AD2.LOCAL'})
RETURN n.name;
</code></pre></div></div>

<p>Make sure to query for only one hop, since this avoids the case where objects with indirect privileges are shown.</p>

<table>
  <thead>
    <tr>
      <th>n.name</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>“TAMERA_ARNOLD@AD.LOCAL”</td>
    </tr>
  </tbody>
</table>

<p>There we have it, <code class="language-plaintext highlighter-rouge">TAMERA_ARNOLD@AD.LOCAL</code> has a path to <code class="language-plaintext highlighter-rouge">DOMAIN ADMINS@AD.LOCAL</code> by compromising <code class="language-plaintext highlighter-rouge">WORKSTATION.AD2.LOCAL</code>. This can be seen in the graph, simply by setting <code class="language-plaintext highlighter-rouge">TAMERA_ARNOLD@AD.LOCAL</code> as the starting node, and <code class="language-plaintext highlighter-rouge">DOMAIN ADMINS@AD.LOCAL</code> as the ending one:</p>

<p><img src="/assets/img/bh-object-ownership/tamera-path-da-cross-domain.png" width="80%" /></p>

<p><code class="language-plaintext highlighter-rouge">TAMERA_ARNOLD@AD.LOCAL</code> has <code class="language-plaintext highlighter-rouge">GenericWrite</code> over the computer object, and therefore can compromise it through resource-based constrained delegation.</p>

<h2 id="reporting">Reporting</h2>
<p>Now that we have some findings, we must properly document them to ensure a successful remediation. This step is as critical as the assessment activity itself; what is the point of observing issues if we do not report them adequately?</p>

<p>Despite needing to understand the inner workings of how an Active Directory is configured to be able to tell if some object ownerships are abusive or not, for instance the IAM service account owning objects, it is more than adequate to document your theories, or at least observations so that administrators can confirm if they are intended or not.</p>

<p>In this scenario, I would document the following:</p>

<ul>
  <li>All paths leading to domain compromise from objects that should not have any privileges.</li>
  <li>Suspicious ownerships, even if they do not currently lead to further escalations, e.g. <code class="language-plaintext highlighter-rouge">BOBFROMACCOUNTING@AD.LOCAL</code>, <code class="language-plaintext highlighter-rouge">TEST@AD2.LOCAL</code> and <code class="language-plaintext highlighter-rouge">SYSADMIN01@AD.LOCAL</code>.</li>
  <li>A CSV file containing all ownerships, unless the file would end up being ridiculously large.</li>
</ul>

<p>We can easily generate this by slightly altering our first ownership query to include the object SID:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>MATCH (n)-[:Owns]-&gt;(m) 
RETURN n.name AS Owner, n.objectid AS `Owner SID`, m.name AS `Owned Object`, m.objectid AS `Owned Object SID`
</code></pre></div></div>

<p>Then, we can use the procedure <code class="language-plaintext highlighter-rouge">apoc.export.csv.query</code> to directly export the output in a CSV file, which will be located under <code class="language-plaintext highlighter-rouge">/var/lib/neo4j/import/</code> on Ubuntu:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>WITH "MATCH (n)-[:Owns]-&gt;(m) RETURN n.name AS Owner, n.objectid AS `Owner SID`, m.name AS `Owned Object`, m.objectid AS `Owned Object SID`" AS query
CALL apoc.export.csv.query(query, "ownerships.csv", {})
YIELD file, source, format, nodes, relationships, properties, time, rows, batchSize, batches, done, data
RETURN file, source, format, nodes, relationships, properties, time, rows, batchSize, batches, done, data;
</code></pre></div></div>

<p>Note that the procedure is not installed by default. Refer to <a href="https://neo4j.com/labs/apoc/4.1/installation/">this article</a> for installation.</p>

<p>You may also write a query to avoid exporting objects owned by highly privileged principals such as <code class="language-plaintext highlighter-rouge">Domain Admins</code>, unless they are located in a different domain. This would greatly reduce the potential noise in the export.</p>

<h2 id="conclusion">Conclusion</h2>
<p>In this post, we experimented with a methodology to investigate object ownerships across domains. By harvesting the power of custom Cypher queries, the data set is manipulated to scope on specific issues, then reused to extract information to help the remediation.</p>

<p>I hope this will convince you that using Cypher during a BloodHound assessment is mandatory, and will inspire you to develop a technique to investigate more scenarios.</p>]]></content><author><name></name></author><category term="infosec" /><summary type="html"><![CDATA[BloodHound remains the tool of excellence to assess Active Directory privileges. In most domains that have undergone years of dubious management, only analyzing data gathered from the DCOnly collection method is plenty to keep a fleet of system administrators busy.]]></summary></entry><entry><title type="html">WinRemoteEnum Use Cases</title><link href="https://simondotsh.com/infosec/2022/01/12/winremoteenum-use-cases.html" rel="alternate" type="text/html" title="WinRemoteEnum Use Cases" /><published>2022-01-12T05:00:00+00:00</published><updated>2022-01-12T05:00:00+00:00</updated><id>https://simondotsh.com/infosec/2022/01/12/winremoteenum-use-cases</id><content type="html" xml:base="https://simondotsh.com/infosec/2022/01/12/winremoteenum-use-cases.html"><![CDATA[<p>Enumeration is a key phase of penetration testing. When done in-depth, it can swiftly shift the dreadful “I cannot find anything” to the desirable “I can pivot everywhere”.</p>

<p>In the Windows world, this activity has countless layers due to the complex nature of Active Directory, and the myriad of protocols willing to answer specific questions requested by any domain user.</p>

<p>Typically, when one seeks to get a good context of a domain privileges-wise, they instantly think of <a href="https://github.com/BloodHoundAD/BloodHound">BloodHound</a> and I absolutely agree, but is there an alternative?</p>

<p>Often, I found myself in situations where I was given a scope of 10 to 20 Windows hosts. While BloodHound supports the <code class="language-plaintext highlighter-rouge">-ComputerFile</code> argument to collect information from specific hosts, it feels like there is quite a bit of overhead to answer particular questions, such as “who are the local administrators?” and “what users are logged in?”. In that case, I had to rely on various enumeration tools here and there, without ever feeling like I could get it done efficiently, and more importantly, thoroughly.</p>

<p>This has lead to the development of <a href="https://github.com/simondotsh/WinRemoteEnum">WinRemoteEnum</a>.</p>

<h2 id="what-is-winremoteenum">What is WinRemoteEnum?</h2>
<p>WinRemoteEnum is a module-based collection of operations achievable by a low-privileged domain user, sharing the goal of remotely gathering information of Windows hosts, and their hardening status on commonly-leveraged techniques.</p>

<table>
  <thead>
    <tr>
      <th>Module</th>
      <th>Enumerates</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><a href="https://github.com/simondotsh/WinRemoteEnum/wiki/Module-users">users</a></td>
      <td>Local users, groups and their members.</td>
    </tr>
    <tr>
      <td><a href="https://github.com/simondotsh/WinRemoteEnum/wiki/Module-sessions">sessions</a></td>
      <td>Net sessions established.</td>
    </tr>
    <tr>
      <td><a href="https://github.com/simondotsh/WinRemoteEnum/wiki/Module-logged_on">logged_on</a></td>
      <td>Users logged on.</td>
    </tr>
    <tr>
      <td><a href="https://github.com/simondotsh/WinRemoteEnum/wiki/Module-shares">shares</a></td>
      <td>Shares and their first-level content.</td>
    </tr>
    <tr>
      <td><a href="https://github.com/simondotsh/WinRemoteEnum/wiki/Module-host_info">host_info</a></td>
      <td>Various OS info and whether the executing user has administrative privileges.</td>
    </tr>
  </tbody>
</table>

<p>Since most is enumerated through exposed built-in MS-RPC methods, it is heavily based off <a href="https://github.com/SecureAuthCorp/impacket">impacket</a>.</p>

<h2 id="execution">Execution</h2>
<p>When executing the tool, domain credentials and targets will be required as minimal input. If a list of modules is not given, all will be executed. Once the enumeration has completed, the <code class="language-plaintext highlighter-rouge">results/</code> directory contains another directory matching the timestamp at which the execution has taken place, and stores easy-to-consume HTML and JSON enumeration result files.</p>

<p><code class="language-plaintext highlighter-rouge">python3 winremoteenum.py -u $USER -p $PASSWORD -d $DOMAIN $TARGET</code></p>

<figure>
  <img src="/assets/img/winremoteenum-use-cases/wre-execution.png" />
  <figcaption>A typical do-it-all execution on a single target.</figcaption>
</figure>

<p>As this is the bare minimum, ensure to read the <code class="language-plaintext highlighter-rouge">--help</code> for possible parameters and options.</p>

<h2 id="module-users">Module users</h2>
<p>This module is without a doubt my favorite, and the main reason behind the development of the tool. Prior to this, I did not have an efficient and easy-to-parse way to enumerate local users, groups, and their members from hosts. Thanks to <a href="https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-samr/4df07fab-1bbc-452f-8e92-7853a3c7e380">MS-SAMR</a>, this is indeed possible from a low-privileged domain user on hosts, as long as access to the SAM Remote has not been hardened.</p>

<h3 id="use-case-basic-reconnaissance">Use Case: Basic Reconnaissance</h3>
<p>Before digging into the host, I like to get a general idea of its context by browsing the <code class="language-plaintext highlighter-rouge">users.html</code> file. Quickly, you should be able to tell if it is properly maintained or has gone through years of misuse, simply by looking at the local users and groups, and then answering some questions as such:</p>

<ul>
  <li>How many local users does it have?</li>
  <li>Is the entire cosmos a local administrator?</li>
  <li>Are SIDs getting resolved by the DC, or the users and groups most-likely no longer exist?</li>
</ul>

<figure>
  <img src="/assets/img/winremoteenum-use-cases/wre-users-html.png" />
  <figcaption>An example users.html results file.</figcaption>
</figure>

<h3 id="use-case-analysis-of-remote-access-groups">Use Case: Analysis of Remote Access Groups</h3>
<p>I use the term “Remote Access Group” to refer to any group allowing a user to compromise a host remotely, whether the end result is a privileged access or not.</p>

<p>The newly released version 1.1 of WinRemoteEnum contains an <a href="https://github.com/simondotsh/WinRemoteEnum/blob/master/analysis/users/analyze.py">analysis script</a> that can be ran manually by a user, resulting in a list of members of the groups in question. This yields a map of which users must be compromised in order to gain access to the host, or if you are able to leverage previously-compromised users.</p>

<p><code class="language-plaintext highlighter-rouge">python3 analysis/users/analyze.py results/$RESULTS_DIR/json/users.json</code></p>

<figure>
  <img src="/assets/img/winremoteenum-use-cases/wre-analysis-users.png" />
  <figcaption>A sample analysis of users without filtering.</figcaption>
</figure>

<p>Visit the <a href="https://github.com/simondotsh/WinRemoteEnum/wiki/Analysis-users">wiki</a> for usage information about the script. Do note that some techniques are using services bound on different ports, meaning that proper firewalling may block you from accessing the host.</p>

<p>In the next section, each remote access group will be explained.</p>

<h4 id="builtinremote-management-users">BUILTIN\Remote Management Users</h4>
<p>This group provides access to <a href="https://docs.microsoft.com/en-us/windows/win32/winrm/portal">Windows Remote Management</a> (WinRM).</p>

<p>On Windows, <a href="https://docs.microsoft.com/en-us/powershell/scripting/learn/remoting/running-remote-commands?view=powershell-7.2">PowerShell Remoting</a> allows to leverage this easily by using <code class="language-plaintext highlighter-rouge">Enter-PSSession</code> or <code class="language-plaintext highlighter-rouge">Invoke-Command</code>.</p>

<p>On Linux, <a href="https://github.com/byt3bl33d3r/CrackMapExec">CrackMapExec</a> includes a nice implementation, which can be used like so:</p>

<p><code class="language-plaintext highlighter-rouge">./cme winrm $HOST -u $USER -p $PASSWORD -d $DOMAIN -x $COMMAND</code></p>

<h4 id="builtinremote-desktop-users">BUILTIN\Remote Desktop Users</h4>
<p>If the <code class="language-plaintext highlighter-rouge">Remote Desktop Services</code> are started on the host, simply fire up your favorite Remote Desktop client, and authenticate on the host to gain access.</p>

<h4 id="builtindistributed-com-users-and-builtinperformance-log-users">BUILTIN\Distributed COM Users and BUILTIN\Performance Log Users</h4>
<p>When running the analysis script, these groups are listed as “potential” since they do not grant access to the host out-of-the-box, as opposed to the other groups.</p>

<p>If you wish to learn about these groups and the additional privileges required, you can read about it in my previous post <a href="https://simondotsh.com/infosec/2021/12/29/dcom-without-admin.html">Non-administrative DCOM Execution: Exploring BloodHound’s ExecuteDCOM</a>, or note that you will need <code class="language-plaintext highlighter-rouge">Remote Launch</code> and <code class="language-plaintext highlighter-rouge">Remote Activation</code> over the DCOM object that you want to instantiate.</p>

<p>impacket’s <a href="https://github.com/SecureAuthCorp/impacket/blob/master/examples/dcomexec.py">dcomexec.py</a> can be used to perform the object instantiation:</p>

<p><code class="language-plaintext highlighter-rouge">python3 dcomexec.py -object MMC20 -silentcommand $DOMAIN/$USER:$PASSWORD\$@$HOST $COMMAND</code></p>

<h4 id="builtinadministrators">BUILTIN\Administrators</h4>
<p>This is evidently the Holy Grail of groups, allowing to gain privileged access to the host using the aforementioned techniques, or a multitude of different ones. For instance, impacket additionally offers <a href="https://github.com/SecureAuthCorp/impacket/blob/master/examples/atexec.py">atexec.py</a>, <a href="https://github.com/SecureAuthCorp/impacket/blob/master/examples/psexec.py">psexec.py</a>, <a href="https://github.com/SecureAuthCorp/impacket/blob/master/examples/smbexec.py">smbexec.py</a>, and <a href="https://github.com/SecureAuthCorp/impacket/blob/master/examples/wmiexec.py">wmiexec.py</a>.</p>

<p>If you require to execute commands as the user instead of <code class="language-plaintext highlighter-rouge">NT AUTHORITY\SYSTEM</code>, use <code class="language-plaintext highlighter-rouge">dcomexec.py</code> (ensure to supply the <code class="language-plaintext highlighter-rouge">-object MMC20</code> parameter), or <code class="language-plaintext highlighter-rouge">wmiexec.py</code>.</p>

<h2 id="modules-sessions-and-logged_on">Modules sessions and logged_on</h2>
<p>These two have been grouped due to usually sharing the same goal: hunting users to dump their NT hash.</p>

<p><code class="language-plaintext highlighter-rouge">sessions</code> enumerates net sessions established on a target. A net session is created when a client accesses a target’s resource remotely, such as a network share.</p>

<p>On the other hand, <code class="language-plaintext highlighter-rouge">logged_on</code> returns users logged onto a target by enumerating registry keys through the Remote Registry service.</p>

<h3 id="use-case-basic-reconnaissance-1">Use Case: Basic Reconnaissance</h3>
<p>As goes for most modules, I begin with browsing the <code class="language-plaintext highlighter-rouge">sessions.html</code> and <code class="language-plaintext highlighter-rouge">logged_on.html</code> to get a general idea of what is happening on the host.</p>

<p>Many sessions might very well indicate a file server that has significant traffic. Combined with a writable network share, one may be able to leverage NTLM relay.</p>

<figure>
  <img src="/assets/img/winremoteenum-use-cases/wre-sessions-html.png" />
  <figcaption>An example sessions.html results file.</figcaption>
</figure>

<p>Additionally, logged on users can help understanding the purpose of the host, and how it is used.</p>

<figure>
  <img src="/assets/img/winremoteenum-use-cases/wre-logged-on-html.png" />
  <figcaption>An example logged_on.html results file.</figcaption>
</figure>

<h3 id="use-case-hunting-users">Use Case: Hunting Users</h3>
<p>In the results of <code class="language-plaintext highlighter-rouge">sessions</code>, the <code class="language-plaintext highlighter-rouge">source</code> field indicates where the user has initiated the connection from, meaning that in the case where this user has authenticated on this source using a protocol that caches NT hashes in the LSASS process, a privileged user may leverage credential dumping.</p>

<p>Regarding <code class="language-plaintext highlighter-rouge">logged_on</code>, similarly to <code class="language-plaintext highlighter-rouge">sessions</code>, the users may have authenticated on the target using a protocol that caches their NT hash.</p>

<p>When looking for the location of a specific user, I suggest simply using the <code class="language-plaintext highlighter-rouge">grep</code> command on <code class="language-plaintext highlighter-rouge">sessions.json</code> and <code class="language-plaintext highlighter-rouge">logged_on.json</code>, and see if you get any results back.</p>

<h2 id="module-shares">Module shares</h2>
<p>The <code class="language-plaintext highlighter-rouge">shares</code> module offers precisely what it states: a view of the network shares available on a host through SMB, and if the executing user can read them. It also lists the name of the first-level files and directories. In the case where the count of items exceeds __MAX_SHARE_LIST_ITEMS (currently 30), a total of files and directories will be reported instead.</p>

<p>The tool currently does not report whether the user has write access due to the possibility of polluting the share. You can read about it in the <a href="https://github.com/simondotsh/WinRemoteEnum/wiki/Module-shares#does-my-user-have-write-privileges">wiki</a>.</p>

<h3 id="use-case-basic-reconnaissance-2">Use Case: Basic Reconnaissance</h3>
<p>The <code class="language-plaintext highlighter-rouge">shares.html</code> file quickly shows the state of the host shares-wise, and how many of these can be read.</p>

<figure>
  <img src="/assets/img/winremoteenum-use-cases/wre-shares-html.png" />
  <figcaption>An example shares.html results file.</figcaption>
</figure>

<h3 id="use-case-finding-sensitive-information">Use Case: Finding Sensitive Information</h3>
<p>Network shares are often a successful vector to get initial access on a host, either by getting your hands on some credentials, or through NTLM relay when they are writable.</p>

<p>Since the first-level content of shares are listed, this is a convenient way to identify at a first glance which ones might be worth digging into.</p>

<h2 id="module-host_info">Module host_info</h2>
<p>This module is dedicated to reporting information about the host, such as its OS version, role, domain, SMB hardening, and if the executing user is a local administrator.</p>

<h3 id="use-case-basic-reconnaissance-3">Use Case: Basic Reconnaissance</h3>
<p>Similarly to other modules, <code class="language-plaintext highlighter-rouge">host_info.html</code> helps to understand further the context of the host, by revealing information regarding the OS, and more.</p>

<figure>
  <img src="/assets/img/winremoteenum-use-cases/wre-host-info-html.png" />
  <figcaption>An example host_info.html results file.</figcaption>
</figure>

<h3 id="use-case-local-administrator-privileges">Use Case: Local Administrator Privileges</h3>
<p>By attempting to open the Service Control Manager (SCM) database with all privileges, one can conclude remotely if they have administrative privileges on the host.</p>

<p>This can be seen in the <code class="language-plaintext highlighter-rouge">local_admin</code> field.</p>

<h3 id="use-case-finding-ntlm-relay-targets">Use Case: Finding NTLM Relay Targets</h3>
<p>To successfully relay a NTLM authentication on a target, it must have SMB signing disabled, which otherwise validates that the message has not been tampered with.</p>

<p>The <code class="language-plaintext highlighter-rouge">smb_signing_required</code> field denotes this.</p>

<h2 id="audit">Audit</h2>
<p>WinRemoteEnum offers an auditing feature, accessible simply by providing the <code class="language-plaintext highlighter-rouge">-a</code> parameter. This will report if the enumeration vectors have been hardened against low-privileged users, since most of them are indeed hardenable.</p>

<p>Head over to the audit section of the <a href="https://github.com/simondotsh/WinRemoteEnum/wiki#audit">wiki</a> for a description of what is audited for each module.</p>

<h2 id="learn-more">Learn More</h2>
<p>Make sure to go through the <a href="https://github.com/simondotsh/WinRemoteEnum/blob/master/README.md">README</a> and  the <a href="https://github.com/simondotsh/WinRemoteEnum/wiki">wiki</a> for information about optimization, credentials validation, reporting, modules, auditing, and analysis.</p>]]></content><author><name></name></author><category term="infosec" /><summary type="html"><![CDATA[Enumeration is a key phase of penetration testing. When done in-depth, it can swiftly shift the dreadful “I cannot find anything” to the desirable “I can pivot everywhere”.]]></summary></entry><entry><title type="html">Non-administrative DCOM Execution: Exploring BloodHound’s ExecuteDCOM</title><link href="https://simondotsh.com/infosec/2021/12/29/dcom-without-admin.html" rel="alternate" type="text/html" title="Non-administrative DCOM Execution: Exploring BloodHound’s ExecuteDCOM" /><published>2021-12-29T05:00:00+00:00</published><updated>2021-12-29T05:00:00+00:00</updated><id>https://simondotsh.com/infosec/2021/12/29/dcom-without-admin</id><content type="html" xml:base="https://simondotsh.com/infosec/2021/12/29/dcom-without-admin.html"><![CDATA[<p>Object instantiation through DCOM has been a popular technique to perform lateral movement since Matt Nelson (<a href="https://twitter.com/enigma0x3">@enigma0x3</a>) <a href="https://enigma0x3.net/2017/01/05/lateral-movement-using-the-mmc20-application-com-object/">unveiled its possibility</a> in 2017, while focusing on the administrative privileges required.</p>

<p>When analyzing a <a href="https://github.com/BloodHoundAD/BloodHound/">BloodHound</a> graph, one may see from time to time an edge where a user or group can compromise a host via <a href="https://bloodhound.readthedocs.io/en/latest/data-analysis/edges.html#executedcom">ExecuteDCOM</a>, described as the following:</p>

<blockquote>
  <p>This can allow code execution under certain conditions by instantiating a COM object on a remote machine and invoking its methods.</p>
</blockquote>

<p>What are these certain conditions, exactly?</p>

<h2 id="the-non-administrative-possibility">The Non-administrative Possibility</h2>
<p>In a blog post about <a href="https://blog.cptjesus.com/posts/bloodhound20">BloodHound version 2.0</a> by Rohan Vazarkar (<a href="https://twitter.com/cptjesus">@CptJesus</a>), it is stated that the edge ExecuteDCOM is newly introduced, justified by the possibility that a member of the <code class="language-plaintext highlighter-rouge">BUILTIN\Distributed COM Users</code> group may be able to instantiate objects remotely:</p>

<blockquote>
  <p>The ExecuteDCOM edge runs from a user or a group to a computer and indicates that the principals are part of the <strong>Distributed COM Users</strong> local group on the target system. Depending on the security descriptor on the target system, users in this group sometimes have remote code execution privileges without corresponding administrative permissions on the target system. This provides another lateral movement method that does not require administrative access. Collection of this edge belongs to the new DCOM collection method.</p>
</blockquote>

<p>This changes the impact of the technique from “I can execute code because I have all privileges” to “I can execute code when people might not expect me as able to”. We also learn more about the conditions: they are security descriptors.</p>

<p>So, what if we were to attempt to instantiate one of those convenient objects offering a method with code execution?</p>

<h2 id="tracking-it-down">Tracking It Down</h2>
<p>The following operations have been performed on a domain-joined Windows 10 host.</p>

<h3 id="validating-the-default-behavior">Validating the Default Behavior</h3>
<p>To begin, impacket’s <a href="https://github.com/SecureAuthCorp/impacket/blob/master/examples/dcomexec.py">dcomexec.py</a> was used to achieve object instantiation remotely, providing credentials of a domain user having no particular privileges:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>python3 dcomexec.py -object MMC20 -silentcommand -debug $DOMAIN/$USER:$PASSWORD\$@$HOST 'notepad.exe'
</code></pre></div></div>

<ul>
  <li><code class="language-plaintext highlighter-rouge">-object MMC20</code> specifies that we wish to instantiate the <code class="language-plaintext highlighter-rouge">MMC20.Application</code> object.</li>
  <li><code class="language-plaintext highlighter-rouge">-silentcommand</code> executes the command without attempting to retrieve the output. This is desirable since the script is using an SMB share to write and fetch the output, and our low-privileged user cannot write to any.</li>
  <li><code class="language-plaintext highlighter-rouge">notepad.exe</code> is ran on the target as a simple proof of concept.</li>
</ul>

<p><img src="/assets/img/dcom-without-admin/dcomexec-rpc-denied.png" /></p>

<p>RPC access denied. This is expected as currently, the user is not a member of the <code class="language-plaintext highlighter-rouge">BUILTIN\Distributed COM Users</code> group, and therefore cannot call <a href="https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-dcom/4a893f3d-bd29-48cd-9f43-d9777a4415b0">MS-DCOM</a>’s <a href="https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-dcom/64af4c57-5466-4fdf-9761-753ea926a494">IRemoteSCMActivator::RemoteCreateInstance</a>.</p>

<p>This can also be seen from Wireshark:</p>

<p><img src="/assets/img/dcom-without-admin/wireshark-rpc-denied.png" /></p>

<p>Then, the user is added to the <code class="language-plaintext highlighter-rouge">BUILTIN\Distributed COM Users</code> group, which yields a different error but not from RPC itself:</p>

<p><img src="/assets/img/dcom-without-admin/dcomexec-dcom-sessionerror.png" /></p>

<p>This confirms that only having access to the group is not enough, and that we have already reached the point where these descriptors must be investigated.</p>

<h3 id="leveraging-windows-events">Leveraging Windows Events</h3>
<p>Before chanting a spell to summon a vile creature from the abyss in hopes of compromising the integrity of your soul to obtain, yet again, a glimpse of answer regarding obscure Windows behavior, paying a visit to the Event Viewer may be worthwhile.</p>

<p>In this case, it was indeed quite insightful:</p>

<p><img src="/assets/img/dcom-without-admin/windows-event-dcom-error.png" /></p>

<p>So, <code class="language-plaintext highlighter-rouge">Remote Activation</code> permission issues. Further testing revealed that <code class="language-plaintext highlighter-rouge">Remote Launch</code> privileges were also missing.</p>

<h3 id="looking-into-dcom-security">Looking Into DCOM Security</h3>
<p>Tuning DCOM security appeared to be quite the task until reading another of Matt Nelson’s <a href="https://enigma0x3.net/2017/01/23/lateral-movement-via-dcom-round-2/">blog post</a>. In the “Defenses” section, he discusses the possibility of restricting DCOM access to local administrators by removing their <code class="language-plaintext highlighter-rouge">Remote Launch</code> and <code class="language-plaintext highlighter-rouge">Remote Activation</code> privileges, and goes in depth explaining how to concretely achieve this.</p>

<p>Simply put, DCOM objects can be configured in two ways: either they follow system-wide security configuration, or their own. Therefore, the member of <code class="language-plaintext highlighter-rouge">BUILTIN\Distributed COM Users</code> also needs to have been given <code class="language-plaintext highlighter-rouge">Remote Launch</code> and <code class="language-plaintext highlighter-rouge">Remote Activation</code> privileges according to the object’s configuration, which is not the case out-of-the-box for the <code class="language-plaintext highlighter-rouge">MMC20.Application</code> object as demonstrated above.</p>

<p>By default, the <code class="language-plaintext highlighter-rouge">MMC20.Application</code> object follows system configuration. Once <code class="language-plaintext highlighter-rouge">Remote Launch</code> and <code class="language-plaintext highlighter-rouge">Remote Activation</code> is given system-wide to the group <code class="language-plaintext highlighter-rouge">BUILTIN\Distributed COM Users</code> (I suppose this is a mistake that could be seen in the wild), we test the same command as before:</p>

<p><img src="/assets/img/dcom-without-admin/dcomexec-successful.png" /></p>

<p>The instantiation worked and our command was executed, which I confirmed by seeing that <code class="language-plaintext highlighter-rouge">notepad.exe</code> was running on the host as our executing user.</p>

<h2 id="about-other-objects">About Other Objects</h2>
<h3 id="shellwindows-and-shellbrowserwindow">ShellWindows and ShellBrowserWindow</h3>
<p>These two built-in objects are also known to offer command execution capabilities. As the instantiated objects offer an interface to <code class="language-plaintext highlighter-rouge">Explorer.exe</code>, the command will be executed as the logged on user and therefore should be avoided, since a non-administrative user will most-likely not be able to run commands as them.</p>

<h3 id="custom-objects">Custom Objects</h3>
<p>Keep in mind that other than the built-in objects, a user could have been given privileges over a custom object that may offer methods capable of code execution; however I am unaware of any decent way to investigate these without having local access on the host, which usually defeats the purpose of attempting to find a way to pivot on it.</p>

<h2 id="another-group-builtinperformance-log-users">Another Group: BUILTIN\Performance Log Users</h2>
<p>A member of the group <code class="language-plaintext highlighter-rouge">BUILTIN\Performance Log Users</code> will essentially get the same privileges as <code class="language-plaintext highlighter-rouge">BUILTIN\Distributed COM Users</code>, meaning that they will also be able to call the <code class="language-plaintext highlighter-rouge">RemoteCreateInstance</code> method. Remote object instantiation will be possible, as long as they have been given the <code class="language-plaintext highlighter-rouge">Remote Launch</code> and <code class="language-plaintext highlighter-rouge">Remote Activation</code> privileges.</p>

<p>I believe that this is much less likely that a member of this group will also have the aforementioned remote privileges on a relevant object, but worth noting that it is possible.</p>

<h2 id="to-conclude">To Conclude</h2>
<p>I do not expect this technique to be possible very often in the wild as a non-administrator due to the extra remote privileges required on the objects, but it is interesting to observe another misconfiguration in the Windows world that has significant consequences.</p>

<p>Food for thought: since I could not find a way to tell if my user had the required privileges over an object other than by attempting to create the instance, and since the system logs quite well that a user has attempted to do so, some might like the idea of creating a honeypot in your domain where some key principals are reported as having the <code class="language-plaintext highlighter-rouge">ExecuteDCOM</code> edge on a host.</p>]]></content><author><name></name></author><category term="infosec" /><summary type="html"><![CDATA[Object instantiation through DCOM has been a popular technique to perform lateral movement since Matt Nelson (@enigma0x3) unveiled its possibility in 2017, while focusing on the administrative privileges required.]]></summary></entry><entry><title type="html">Improving My Organization’s Security Posture: A Pentester’s Guidelines</title><link href="https://simondotsh.com/infosec/2021/02/01/improving-security-posture.html" rel="alternate" type="text/html" title="Improving My Organization’s Security Posture: A Pentester’s Guidelines" /><published>2021-02-01T05:00:00+00:00</published><updated>2021-02-01T05:00:00+00:00</updated><id>https://simondotsh.com/infosec/2021/02/01/improving-security-posture</id><content type="html" xml:base="https://simondotsh.com/infosec/2021/02/01/improving-security-posture.html"><![CDATA[<p>With sophistication of attacks ranging from automated CVE exploitation to state-sponsored, it may be easy to get lost in what should be prioritized from a defensive standpoint. Yet, analyzing the initial access vectors from the hottest advanced persistent threats (APT), when an adversary could successfully get a foothold into your private network by using a Metasploit module, seems ambitious and surely disorganized.</p>

<p>As an internal pentester, I have always found it difficult to stick to the old-fashioned idea that your job ends after a report has been written; however, to be quite frank, I understand it due to the politics that are often induced from stepping outside of your boundaries. Nonetheless, understanding the big picture of the information security initiatives in your organization is extremely valuable, with an input that should help steer the ship in the right direction.</p>

<p>Often, it feels like the Groundhog Day when it comes to prioritization. Either these concepts have to be recalled to various people in an organization due to the lack of singular orientation, or they must be reminded as new information security managers or projects arrive. For these reasons, I have decided to write down some thoughts to solve my own problem, and perhaps to distribute them to whoever deems as relevant.</p>

<p>So, if I want to improve my organization’s security posture, what should I be working on and most importantly, what do I prioritize?</p>

<h2 id="expectations">Expectations</h2>
<p>This article will address the question of structuring the initiatives of the so-called red, blue and purple teams, and may be seen as scoping into the technical aspects of information security. We will not be looking at the governance of establishing for instance security standards, risk analysis and acceptance, or a security software development life cycle.</p>

<p>However, this does not mean that security governance teams should not be involved in any of this. In fact, they may be responsible for budgets or projects, depending on your organization’s structure, and be your greatest allies to communicate the needs to the upper management.</p>

<h2 id="the-terminology-of-the-rainbowesque-teams">The Terminology of the Rainbowesque Teams</h2>
<p>A few terms will be explained to make sure that no confusion arises, since these sometimes have different definitions.</p>

<h3 id="red-team">Red Team</h3>
<p>Depending on whom you speak to, this term englobes any offensive security discipline from internal and external pentesting to adversarial simulation. For this reason, I will use the term “Adversarial Simulation” for any initiative that aims, using tactics, techniques and procedures (TTPs), to test the detection abilities and responsiveness of an organization. “Red Team” shall be used to allude to a team that works on the offensive side of information security. Note that by “offensive”, I refer to the idea of performing defense by offense, which usually implies finding issues before the adversaries do.</p>

<h3 id="blue-team">Blue Team</h3>
<p>To remain consistent with the above, a “Blue Team” will refer to any area that focuses on the defensive side, from vulnerability management to threat hunting.</p>

<h3 id="purple-team">Purple Team</h3>
<p>Simply put, a “Purple Team” is a collaboration between the aforementioned teams in order to work jointly on the ultimate goal of protecting the organization. An example of exercise is to perform TTPs in absolute transparency, while the defenders build detection use cases.</p>

<h2 id="disclaimer">Disclaimer</h2>
<p>I will never claim that this is the sole approach to the question, nor that I have experienced and seen it all. Undoubtedly, the wonderful world of information security is rarely black or white (great, more colors!). Despite this, I believe this article will present a strong strategy that should apply to most organizations that face the problem of securing their and their customer’s information.</p>

<p>Furthermore, some security products will be mentioned to assist solving issues, and I am in no way affiliated with these companies. Also, it must be understood that deploying a tool by itself will practically never solve a problem, unless the dedicated resources possess the proper skill set to operate it and analyze its output.</p>

<h2 id="the-basis">The Basis</h2>
<p>Approaching a problem of such complexity is imposing, to say the least. Fortunately, the information security field includes countless talented individuals who have already pondered upon the question, and generously published their work. It is mostly a matter of finding this information through an ocean of buzzwords and snake oil, along with identifying the truly experienced and successful.</p>

<h3 id="derbycon-2018-victor-or-victim-strategies-for-avoiding-an-infosec-cold-war">DerbyCon 2018: “Victor or Victim? Strategies for Avoiding an InfoSec Cold War”</h3>
<p>At a first glance, this quality talk given by Jason Lang (@curi0usJack) and Stuart McIntosh (@contra_blueteam) seems to only address the relationship of the red and blue teams, but much more is shared. In fact, a single slide will be the entire basis of this article. I thank them for their great work.</p>

<h3 id="the-slide">The Slide</h3>
<figure>
  <img src="/assets/img/improving-security-posture/offense-defense-touchpoints.jpeg" />
  <figcaption>Source: <a href="https://github.com/curi0usJack/slides/blob/master/Infosec_Maturity_Testing.pptx">https://github.com/curi0usJack/slides/blob/master/Infosec_Maturity_Testing.pptx</a></figcaption>
</figure>
<p>The idea shared by Jason and Stuart is simple: both the red and blue teams must climb steps synchronously by implementing these practices, starting from the bottom. This synchronization requires to never lose focus of how the other party is doing, otherwise a complete mismatch of maturity will be induced. Your red team may be eager to perform adversarial simulation engagements but if the blue has just started to deploy an Endpoint Detection and Response (EDR) product, you cannot expect them to be able to grow out of the conclusions of your engagements, simply due to not having access to proper tools to monitor the environment.</p>

<p>By laying out clear steps with categories of initiatives, this can be used whether your organization has just started their security program or has been investing into one for quite some time. A team that understands well the current maturity of each of these can quickly identify what needs to be worked on, and most importantly in what order.</p>

<p>The rest of this article will be dedicated to offering a concrete example of how these touchpoints can be shaped for an organization, with hints on how to approach them, as well as how to view the steps in general.</p>

<h2 id="defining-the-rules-when-do-i-climb-a-step">Defining the Rules: When do I climb a Step?</h2>
<p>The answer depends entirely on the context of your organization and requires designing your own rules, which are likely to be challenged during each step. By “context”, I refer to internal politics, budgets, technical debt or inventory issues, to name a few. Surely, the experts of your red and blue team must be brought in to be the judges and designers of the said rules.</p>

<p>Any sizeable organization can face issues that increase the improbability of ever completely mastering a category, for reasons out of control of the colored teams. Should the experts be chasing perfection at the expense of going forward? Should they instead channel their creativity and engineer solutions that could perhaps improve the posture, nonetheless?</p>

<p>Remember that fleet of servers which hosts a critical business application and no longer receives security patches due to being out of support? The source code has been lost and the sysadmins have been unable to migrate it to modern servers. Therefore, it will have to be rewritten from scratch, which will take longer than you can afford. This can be seen as a flaw in your patch management process, since you expose an attack surface that cannot be addressed. What if you were to monitor the logs, develop use cases or implement an EDR instead of not going forward because your patch management is flawed? However, do not get me wrong: in no way does monitoring a host and installing an EDR replace the need to install security patches, but it may help you out in the case of a non-sophisticated actor.</p>

<p>Speaking of threat actors, knowing the step that you are at can help identify what degree of sophistication your organization is mature enough to defend against. For instance, patch management and network controls may be successful against an unsophisticated actor. A properly configured EDR and centralized logging would help for a pentester-level actor, who would be seen as performing actions without perfect operations security. Tuned alerting and threat hunting could aim at defending against a nation-state actor. With this in mind, if your organization has started to work on an initiative targeting a sophisticated actor and you know for a fact that an automated attack would be successful, it might be time to reprioritize.</p>

<h2 id="shaping-the-touchpoints">Shaping the Touchpoints</h2>
<figure>
  <img src="/assets/img/improving-security-posture/offense-defense-touchpoints-edited.jpeg" />
  <figcaption>An edited version of the touchpoints.</figcaption>
</figure>
<p>As all organizations should, I have taken the time to analyze the touchpoints and to shape them to match what I believe would be the best for, in this case, a large organization that has publicly accessible endpoints, and will expect to deal with a significant count of vulnerabilities. Most of the steps remain the same, as the original touchpoints already made a lot of sense.</p>

<p>Other than small alterations to reflect the terminology defined above, the “Network controls” category has also been moved a step higher, alongside implementing an EDR. The reasoning behind the changes is the following: any sizeable organization that deals with vulnerabilities will need to implement a vulnerability management process and team, otherwise the likelihood of correcting (if ever) the wave of vulnerabilities found by scanners and the red team in reasonable timeframes is practically nil. Regarding your publicly available endpoints, not monitoring them is a recipe for disaster. All steps will be explained in further details in their respective sections.</p>

<p>Do not hesitate to add more steps, or even to expand the current steps into smaller ones, if judged appropriate.</p>

<p>Now, onto comments and hints regarding each step.</p>

<h2 id="step-one">Step One</h2>
<h3 id="vulnerability-scanning">Vulnerability Scanning</h3>
<p>This practice involves implementing an automated scanner into your network to identify vulnerabilities. As the tools are often configured to authenticate on your assets, they can dig into configurations and find out what is not adequate security-wise. In fact, they are good at identifying missing patches, misconfigurations or compliance issues, while the most aggressive web-oriented ones might even find a SQL injection here and there.</p>

<p>Depending on the complexity of your network and number of assets, vulnerability scanning can go from quite easy to implement, to requiring an entire project to manage its deployment. This practice is found at the first step due to the findings usually being the low-hanging fruits.</p>

<p>Quickly, you will be asking yourself countless questions, such as:</p>

<ul>
  <li>How often should I be scanning?</li>
  <li>Do I really need to scan every single networking asset?</li>
  <li>What about workstations of employees working remotely? (hint: look into agents)</li>
  <li>What are the odds of taking down a critical server? (it happens)</li>
  <li>Who will be responsible of the scanners’ health?</li>
  <li>When and how do I patch my scanners?</li>
  <li>How do I whitelist my tool’s IP address when scanning external assets?</li>
</ul>

<p>These will need to be discussed and analyzed in great details, and some answers definitely depend on your patching capabilities.</p>

<p>Nowadays, there are countless commercial and non-commercial products, with the former focusing on operating efficiently in large environments. The tools that I have seen the most successfully deployed in corporate environments are <a href="https://www.rapid7.com/products/nexpose/">Nexpose</a> from Rapid7 and <a href="https://www.tenable.com/products/nessus">Nessus</a> from Tenable. Much must be taken into consideration when choosing the right tool, such as their ease of integration into your environment, their efficiency and coverage, their reporting abilities, and of course pricing. I highly advise to perform proof of concepts by deploying a few scanners that seem to fulfill your needs, to see how they truly react and what they find. Do not hesitate to deliberately plant vulnerabilities into some systems (non-production in a segregated network, please!) that will be scanned to find out if they will be identified.</p>

<p>No amount of vulnerability scanning will ever replace a pentest but it is not rare to see a pentester perform this activity to quickly identify the most obvious, which could also help orientate their operations.</p>

<p>Do not forget to quickly work on a plan to scan your publicly reachable assets. If your scanner identifies vulnerabilities, it implies that a malicious actor may be able to leverage them.</p>

<p>Now that your tools found vulnerabilities, what’s next? The harsh reality of dealing with scan results will be discussed in the vulnerability management section.</p>

<h3 id="patch-management">Patch Management</h3>
<p>Unfortunately, this step is far from trivial, yet it is the first one on the blue side. I view patching as the foundation of information security, which justifies implementing its management early.</p>

<p>A vendor has worked on a fix for a vulnerability, either self-identified or from an external researcher, and has shared it with its consumers. Now, all that is required is to install it! Definitely easier said than done in environments that have been plagued by legacy systems out of support.</p>

<p>This gets even more complex when a wide range of operating systems (OS) are in use, since they often use different tools to manage patches. Each of your organization’s OS will need to be analyzed to find out what would be the best strategy to patch them, and what product(s) to use.</p>

<p>Then come potential inventory issues which may become the bane of your existence. Your vulnerability scanner might start scanning countless assets that you never knew existed and won’t even know when these can be safely rebooted after patching (when patch application requires it), once introduced into your new patch management tool. This should be taken into account when building your patch management process.</p>

<p>If unable to patch systems for various reasons, a case needs to be built and brought up to management, in order to create new projects. These will often require projects, since it is extremely unlikely that some resources capable of working on a fix will get enough time in their day-to-day responsibilities to work on this, unless scoped in a project.</p>

<h3 id="vulnerability-management">Vulnerability Management</h3>
<p>I have added vulnerability management to the first blue step due to the fact that I have observed countless times in the past that a strong vulnerability management team and process is mandatory as soon as you deal with a large organization. Otherwise, you will find out that a ton of reported vulnerabilities are not going to be fixed any time soon, since there might be no security standard in your organization that requires an asset owner to do so. Even communicating the information to the owners may be a challenge when no team is dedicated to doing so.</p>

<p>Armed with the results of the newly implemented scanner, the vulnerability management team shall unleash a fury capable of causing many nights of insomnia to the CISO. With this imagery, I refer to the impressive count of vulnerabilities that may be reported by your tool. Before anything, the results need to be properly analyzed since these tools tend to be extremely verbose. For instance, you will often find out that a single patch can fix hundreds of vulnerabilities on a host. Also, false positives are very much a possibility.</p>

<p>I have often found myself completely incapable of properly analyzing any results without having access to the scanner’s database where I could perform my own SQL queries. That way, I was able to filter out any noise, and find out what should be addressed first. As an example, I would query for vulnerabilities that are not related to patching due to those being addressed by patch management eventually, which would lead to finding some gems even more critical.</p>

<p>Since vulnerability management is much more than simply looking at scanners’ results, a solid process must be built. This team also needs to know the organization quite well, or at least have a good understanding of the asset owners, which will be confirmed when the pentesting team starts reporting vulnerabilities on many different hosts.</p>

<p>Here are a few questions to help building your process:</p>

<ul>
  <li>Once reported, how long does an owner have to fix? Is the time length strictly related to criticality?</li>
  <li>What happens when an owner refuses to fix or exceeds the time limit?</li>
  <li>Can the red team document vulnerabilities efficiently or does the process prevents them from doing so?</li>
  <li>How do I handle duplicates? What should I do when a vulnerability is spread on thousands of assets?</li>
  <li>How do I track the status of all these vulnerabilities?</li>
</ul>

<p>The team also needs to have a good understanding of the vulnerabilities that are reported and their impact. In fact, they must be given the power to reprioritize vulnerabilities once analyzed. Pentesters often get little to no context of the environment where they have found a vulnerability, which can lead to miscalculating the criticality.</p>

<p>It is without a doubt that a tool dedicated to vulnerability management must be implemented, which has to be flexible enough to work with your process. I encourage you to engage into proof of concepts of various options, once your process has been built.</p>

<p>To end this section, here is a wonderful tweet by Andrew Robbins (<a href="https://twitter.com/_wald0">@_wald0</a>):</p>
<figure>
  <img src="/assets/img/improving-security-posture/wald0-patch-mgmt-tweet.jpeg" />
  <figcaption>Source: <a href="https://twitter.com/_wald0/status/1309937575147302917">https://twitter.com/_wald0/status/1309937575147302917</a></figcaption>
</figure>

<h2 id="step-two">Step Two</h2>
<h3 id="external-pentesting">External Pentesting</h3>
<p>This step aims at finding any way of exploiting or harvesting data from outside of an organization’s internal network, under the reasoning that automated tools and actors are constantly scanning for vulnerable assets from the leisure of their basement, which justifies implementing it before internal pentesting.</p>

<p>Your team must ensure to get access to a list of assets that are known to be accessible publicly. When hosting internally, inventories or firewall rules can hand out this information. If unavailable, there are various open-source intelligence tools that can help at the task, such as <a href="https://github.com/OWASP/Amass">Amass</a> and <a href="https://www.shodan.io/">Shodan</a>. The use of cloud providers will complexify this, but strategies such as domain/subdomain enumeration (Amass does this) and TLS certificate indexing may work.</p>

<p>Once the targets have been acquired, things become a matter of performing traditional pentesting and harvesting the power of task automation when dealing with a large quantity of assets. Make sure that your pentesters get access to your vulnerability scanning tool so that they can add new targets to scan schedules or view results.</p>

<p>When a vulnerability has been identified, testers must be able to communicate these results quickly and easily, so that your vulnerability management team can start analyzing and prioritize accordingly. Hopefully, your vulnerability management process has already solved this in the previous step.</p>

<p>If your organization does not expose assets publicly, do not view this as a free pass since a lot can be done nonetheless. Here are a few examples:</p>

<ul>
  <li>What information can be enumerated about the organization? Does an employee leak the entire security product suite on their LinkedIn profile?</li>
  <li>Did a developer commit closed-source code or credentials on GitHub?</li>
  <li>By sifting through data breach databases, can I find an employee’s credentials? Does this employee still use this password somewhere in the organization?</li>
  <li>Can I easily manipulate an employee to run malware through phishing? Will your email filters and protections catch known malware?</li>
  <li>Do email headers leak any valuable information that could be leveraged by a malicious actor?</li>
</ul>

<h3 id="external-endpoints-monitoring">External Endpoints Monitoring</h3>
<p>When the red team conducts external pentesting and successfully exploits vulnerabilities, are their operations being noticed by anyone? If not, does it mean that if an external adversary were to get a foothold on your server, no one would find out?</p>

<p>Under the same train of thought that finding publicly exploitable vulnerabilities should be prioritized over internal ones, it makes sense to monitor your external endpoints first. However, the concept of “internal” vs “external” may seem to blend in when you are under the impression that your internal network is much more likely to be breached through phishing. After all, all it takes is an educated actor and a few clicks from one employee. Because of this, I would not necessarily disagree with the initiative of implementing an EDR first on workstations, or at least work on both at the same time, especially if you have absolute confidence over your external assets’ security (make sure that your pentesters confirm this).</p>

<p>Your monitoring solution can involve installing an EDR (which is also planned for next step but at a larger scale) on your endpoints and/or working on use cases. Despite not having centralized logging yet, forwarding and aggregating these logs in a single console where use cases are developed only for your external assets is crucial. Forwarding is mandatory since it gives you a chance to detect an attacker before they are aware of your logging solution and start altering the local logs.</p>

<p>The current pandemic surely has brought in more appliances exposed publicly, usually for VPN-like features. These will need to be opened up, analyzed and modified to be monitored accordingly. I was surprised while working on Citrix NetScaler’s <a href="https://nvd.nist.gov/vuln/detail/CVE-2019-19781">CVE-2019–19781</a> to find out that an organization’s web logging retention was 7 hours. This does not help much when you are trying to identify whether a potential breach may have occurred a while ago. And again, if these logs are not forwarded anywhere, how can you confirm their legitimacy?</p>

<h2 id="step-three">Step Three</h2>
<h3 id="internal-pentesting">Internal Pentesting</h3>
<p>Let us make something clear right away: if you are constantly trying to make your pentesters work in the same conditions as a malicious actor, you will lose. By this, I refer to the thought that pentests must be conducted in a black box way, where the testers don’t have access to source code or a server’s configuration. Reality is that a pentester will have a few days to break into your web application, while an adversary could spend a lifetime. Therefore, your testers will need to work in utter efficiency without having to constantly justify their actions and deal with the occasional “Why would I give you access to the server? Aren’t you supposed to break in?”.</p>

<p>Your organization probably has a role dedicated to something along the lines of guiding projects with an information security-aware advisor. These advisors should be encouraged to request pentests when they have judged them valuable. I would not be surprised if they already have a list of applications or environments that should be tested. Make sure to prioritize according to the attack surface and exposure. If you find out that a new web application built in-house is about to be deployed externally, feel free to catch and test it before it is publicly available.</p>

<p>When it comes to what should be tested first, I like to focus on what is likely to be attempted first by an adversary that would get a foothold into your network. In an Active Directory environment, there are various well-documented techniques which are often successful and, for this reason, very likely to be exploited. You don’t want an actor to get in through phishing, simply launch <a href="https://github.com/BloodHoundAD/BloodHound">BloodHound</a>, find out that their compromised user can authenticate with administrative privileges on a server, pivot there, do credential dumping since NTLM authentication is used, get access to a service account and just keep digging into your network that way.</p>

<p>As suggested per the infrastructure case described above, a practice that I find valuable is conducting non-scoped pentests. While highly effective, especially if you encourage your team to work together on a potential chain of exploit, this must be seen as a great privilege and no reckless operations must be attempted (I’m looking at you, ARP spoofers) unless a resource can confirm it cannot have a negative impact. I suggest getting a written approval from your CISO to ensure they are aware of this practice’s existence, and to help out in the case where those spoofers judged it adequate to poison entire subnets.</p>

<p>The External Pentesting section briefly stated the importance of communication with the vulnerability management team. In fact, before the first internal pentest report is written, I highly recommend agreeing on a reporting standard that both teams will be comfortable with. When the structure is the same from report to report, it is much more convenient both for analysis and when a retest is conducted.</p>

<h3 id="endpoint-detection-and-response-edr">Endpoint Detection and Response (EDR)</h3>
<p>By itself, a properly configured EDR should fend off unsophisticated actors and definitely annoy any pentester who has not taken the time to work on their tools. This is where the defenders will start seeing operations performed during pentests (on the infrastructure side, that is). Generally, I would say an EDR yields great results monitoring and blocking-wise considering the efforts required to configure and deploy.</p>

<p>Regarding what assets the EDR should be installed on: if there is a system on your network and it is compatible with your solution, go ahead and install your EDR on it. Yes, your development environment also needs to be monitored since they tend to have a lot more than just non-production stuff on there.</p>

<p>As you might already expect, before a large-scale deployment is done, seeing how the EDR behaves into your environment is mandatory especially when it offers features that will block any process that it judges as malicious. It is not entirely impossible that your sysadmins have some funky ways of administrating servers or scripts that could be blocked. Once you are pleased with the configuration, feel free to let your pentesters have a go in case something turns out to be too permissive.</p>

<p>There are two products that I’ve had the chance to experience and see in action. The first of them being <a href="https://www.crowdstrike.com/endpoint-security-products/">CrowdStrike Falcon</a> that offers a feature called <a href="https://www.crowdstrike.com/endpoint-security-products/falcon-overwatch-threat-hunting/">Falcon Overwatch</a>, which claims to have a team that performs threat hunting on the logs gathered by the EDR. As a quick anecdote, after performing a privilege escalation on a server that involved running a custom C# reverse shell, this feature eventually raised an alert because the process chain and the network connections between the two subnets were unusual. A pleasant surprise to say the least.</p>

<p>The second product is <a href="https://www.microsoft.com/en-ca/microsoft-365/security/endpoint-defender">Microsoft Defender for Endpoint</a>. As opposed to CrowdStrike, this one has a feature called <a href="https://www.microsoft.com/en-ca/microsoft-365/security/identity-defender">Microsoft Defender for Identity</a>, which has detection capabilities over Active Directory attacks such as kerberoasting, ticket forgery and password spray. However, the conclusion was that it required considerable tuning in order to deal with the false positives.</p>

<h3 id="network-controls">Network Controls</h3>
<p>This section and “Administrative Rights” were moved on par with internal pentesting simply because the pentesters are likely to identify some these in their regular activities. If you are confident you can find these without your pentesting team, feel free to put them back a step before.</p>

<p>Network controls are a big deal and I will kick this off with one of Lee Christensen’s (<a href="https://twitter.com/tifkin_/">@tifkin_</a>) tweet:</p>
<figure>
  <img src="/assets/img/improving-security-posture/tifkin-segmentation.jpeg" />
  <figcaption>Source: <a href="https://twitter.com/tifkin_/status/1205567114653945860">https://twitter.com/tifkin_/status/1205567114653945860</a></figcaption>
</figure>
<p>How healthy is your network segmentation strategy? Is it a flat network, which implies that your workstations can reach any pot of gold without having to pivot through the network? Is there a magical subnet that makes it possible to reach any other subnets? Not only proper segmentation makes it tough from a technical aspect to move further as explained by Lee, but it also improves the odds that a malicious actor will end up making a mistake and reveal their position in your network. Pair up with network architects and see how your strategy could be improved.</p>

<p>Another initiative could be to look into what vectors can be used to communicate or exfiltrate data to an external command and control server. Is the network configured in a way where as soon as you get on a server, they will gladly initiate HTTP, ICMP or DNS requests egress?</p>

<p>This step also brings in the opportunity to look at your network shares, which many organizations have issues with due to years of misuse. A scan authenticating with your regular, non-privileged domain user should quickly give you an idea of how much sensitive data you can get your hands onto, or even service account credentials that have been left off in a configuration file. Your vulnerability scanner may also have a plugin that reports network shares.</p>

<h3 id="administrative-rights">Administrative Rights</h3>
<p>An operation that I love to perform when I get into a new environment is to run a quick BloodHound scan in Domain Controller-only mode (so that I don’t become the new hire who starts querying every single computer in the domain). This gives you an idea of what the domain looks like and if it turns out that every single user is a few hops away from DCSync privileges. Depending on how big your domain is, a resource could be dedicated for a while to deleting relationships that turn out to have excessive privileges.</p>

<p>Then, you have the service or sysadmin accounts and how they are used. Can these accounts authenticate on various servers using NTLM, which suddenly provides a way for an actor to pivot if they have access to dumping the lsass process? Or perhaps some sort of UNIX-based equivalent, where password authentication is used through SSH and then any attacker with system call monitoring privileges would get the password in clear text? Limiting your privileges, using Local Administrator Password Solution (LAPS) and public-key authentication can assist solving these.</p>

<p>Last but not least, workstations and servers must be assessed to make sure that they are not vulnerable to trivial privilege escalations. Elevated privileges always unlock new vectors that can be used by an adversary and also act in a stealthier way.</p>

<h2 id="step-four">Step Four</h2>
<h3 id="purple-team-1">Purple Team</h3>
<p>To quickly reiterate what has been mentioned in the terminology section, a purple teaming activity is defined by the red and blue team becoming one to solve problems regarding detection and processes.</p>

<p>Since this is where the blue will start forwarding and aggregating logs in a centralized database, the timing is adequate to develop use cases. As more and more logs are available to work with, this is a good opportunity to build your coverage of the TTPs contained in the <a href="https://attack.mitre.org/matrices/enterprise/">ATT&amp;CK framework</a>. These have been identified from real-world threats and are therefore techniques that are concretely leveraged by malicious actors. Each of the techniques also mentions in which data sources you can aggregate and build your use case on to detect it.</p>

<p>Both teams must meditate and decide what techniques should be covered first. A good starting point could be to see how to detect techniques typically applied in a domain as described in the internal pentesting section, if the required logs are available. Make sure that you don’t try to catch the most sophisticated actors first. Also, this framework offers matrices built per OS, which you might find useful if you would like to focus on a specific OS first, perhaps the most widely used and more likely to be exploited. Do not overwhelm yourselves with attempting to cover too many techniques in the matrices, as this should be done once your centralized logging is mature.</p>

<p>Once completed, now is the time to perform these TTPs and by looking at the documentation on each technique, see how you can build a use case that would work in your environment, assuming that you do have access to the proper logs.</p>

<p>Other than covering TTPs, any questioning puzzling the blue team can be brought up during this activity to brainstorm an appropriate solution.</p>

<h3 id="centralized-logging">Centralized Logging</h3>
<p>This is where the architecture of your network and infrastructure in general may come to haunt you. Can your network handle the extra load of forwarding logs at a decent interval? Can your archaic data aggregation tool scale with the influx of logs? Despite these potential issues, this step is mandatory to build detection use cases.</p>

<p>You will be required to build a strategy to collect the logs of various systems in your environment, which are your input for use cases. Precisely what logs need to be collected depends entirely on the TTPs that you desire to detect. For instance, the prioritized techniques that have been pinpointed in the purple teaming exercise will orientate the needed logs. The matrices will also give you a heads up on the following techniques, so that you can plan the collection ahead and fix any issues that may arise.</p>

<p>As a hint, your EDR vendor may be able to provide you a list of TTPs their product covers, which implies that you could build use cases covering other techniques (make sure to validate that your EDR really covers what they say).</p>

<h2 id="step-five">Step Five</h2>
<h3 id="adversarial-simulation">Adversarial Simulation</h3>
<p>Now that we have centralized logging, developed use cases, and are working our way through the ATT&amp;CK matrices, we need to validate how these would be handled if detected in a real-world scenario. Will these techniques be detected and reported properly in the day-to-day operations? How will the analysts respond? Are the response processes well-defined and optimized? Will the investigation stop after the containment of a machine, when the attacker has had enough time to pivot elsewhere? This is what adversarial simulation aims to validate and, as opposed to purple teaming, it happens unknowingly to the analysts and responders.</p>

<p>When establishing the scope and which TTPs should be used, feel free to brainstorm with members of the blue team, as long as they will not be responding to your actions. In fact, you need some of the blues to be aware of the exercise to make sure that, for instance, a critical business server does not get rebuilt if a threat from the exercise is detected on it.</p>

<p>It must be stated that simulating a highly sophisticated actor at this step using TTPs not covered by any use case has little to no value, since the conclusion is already known to the blue team. However, using an unmonitored technique is justified if its entire purpose is to place the operators in a situation where they will be able to perform actions that will test detection mechanisms currently in place. I have also seen the case where the level of sophistication simulated was at the highest, as a last resort to secure funding by proving that your organization can be breached (ah, politics!).</p>

<p>At the conclusion of an engagement, do not hesitate to plan a purple team exercise so that both teams collaborate on solving the identified issues.</p>

<p>Implementing and structuring an adversarial simulation practice in your organization is complex and deserves its own book. Luckily, a great one has already been written by Joe Vest (<a href="https://twitter.com/joevest">@joevest</a>) and James Tubberville (<a href="https://twitter.com/minis_io">@minis_io</a>) named “<a href="https://redteam.guide/">Red Team Development and Operations</a>”. I highly recommend it.</p>

<h3 id="finely-tuned-alerting-and-response">Finely Tuned Alerting and Response</h3>
<p>Other than covering more TTPs, you must ensure at this point to develop impeccable alerting. If your analysts start to ignore a use case because it ends up being a false positive, it must be corrected. Also, this is the step where conclusions of adversarial simulation engagements are analyzed and addressed.</p>

<p>You can also look into your developed cases with members of the red and find out the amount of effort that would be needed to bypass some of these, and adjust accordingly. Is it trivial or would it require complete knowledge of how the detection is built? This could be done in an adversarial simulation engagement or a purple teaming exercise, but feel free to structure this in any way that makes sense, and, most importantly, in the most efficient way.</p>

<p>The information security field is in constant evolution. When it is not a researcher that published their findings, a new APT may act in a clever way that ends up being a blind spot in your detection use cases. Keeping up with these is required when your organization has achieved a level of maturity where it is able to do so.</p>

<h2 id="step-six">Step Six</h2>
<p>I have not had the chance yet to experience this section in an organization that was truly mature enough to properly implement these. However, these two practices were existent, or at least teams did claim to do precisely that, but since the steps below were not brought up to fruition, they had to spend most of their time working on lower steps.</p>

<h3 id="non-scoped-and-long-term-adversarial-simulation">Non-scoped and Long-term Adversarial Simulation</h3>
<p>The highest level of sophistication is simulated in this practice and the idea is the following: without having a specific scope or time limit, your operators will stealthily get multiple footholds in your network, and proceed in the best operations security they can. To avoid getting caught on aggregation, they might decide to let an implant sleep for weeks at a time after performing an action, or even longer if it is planned to be leveraged only in the future.</p>

<p>Then after a while, an implant may be sacrificed knowingly or by mistake. Will the blue team manage to put the pieces of the puzzle together, and perhaps find more implants or reconstruct how this one has gotten access to the system? Will your threat hunting team find your implants in their day-to-day operations?</p>

<p>It is without a doubt that both teams will train each other, either by having the blue share how the red’s operations security was flawed, or how the red managed to remain undetected to the blue. This collaborative relationship is necessary in order to achieve a growth capable of detecting the most sophisticated attacks.</p>

<h3 id="threat-hunting">Threat Hunting</h3>
<p>Proactivity is a key component to threat hunting. This team will not rely on your built detection use cases and will instead focus on the idea that an actor has managed to breach and not raise any alert. They will still be good consumers of your centralized logs and will build their own queries to analyze the data.</p>

<p>This assumed breach model involves looking for indicators of compromise that have fallen through your detection mechanisms, either by your non-scoped adversarial simulation exercise or an actual threat. In fact, this practice has the best probability of identifying a sophisticated actor that would for instance, compromise your environment through a supply-chain attack using custom malware, and slowly dig through your network in operational security. This would not be caught by your typical use cases and instead requires an extremely knowledgeable resource who understands how these actors operate.</p>

<p>The practice is brought in at the end due to the fact that it relies on the healthiness of the initiatives beforehand, as true for most steps. If your environment is still facing issues that makes it extremely noisy and near impossible to investigate, which allows even the least experienced threat actor to operate in peace, you do not need threat hunting.</p>

<h2 id="to-sum-it-up">To Sum it Up</h2>
<p>Experts of both the red and blue team must ensure that they share the same vision on the “what” and “how” of prioritization, and work together to avoid maturity differences. Guidelines are made to do exactly what they say: offer lines of guidance. Therefore, a critical overview of your organization depends entirely on understanding well its context, a mandatory prerequisite to having educated thoughts on how to approach these issues in your environment.</p>

<p>I encourage every organization to fully engage into the exercise of building their own steps of offense defense touchpoints, by collecting input from all the information security professionals.</p>]]></content><author><name></name></author><category term="infosec" /><summary type="html"><![CDATA[With sophistication of attacks ranging from automated CVE exploitation to state-sponsored, it may be easy to get lost in what should be prioritized from a defensive standpoint. Yet, analyzing the initial access vectors from the hottest advanced persistent threats (APT), when an adversary could successfully get a foothold into your private network by using a Metasploit module, seems ambitious and surely disorganized.]]></summary></entry></feed>