BPL Logo Banner
CareersContact

Image prompt injection, AI feature risk, and what security teams should be doing about it.


We’ve been curious about prompt injection for a while. Not in the abstract, theoretical sense, but in the “let’s actually build the attack and see what happens” sense. So we did.

In September 2025, we published a technical breakdown of the attack. This post is about the broader implications for security teams evaluating AI tools.

The setup was simple. We took the kind of image you would find in any repository, like a logo or a screenshot, and embedded malicious instructions inside it as text using PyRIT’s AddImageTextConverter. Then we dropped it into a git repository alongside normal project files and checked whether Claude Code would follow those instructions when a developer ran a routine query. Something like “analyze the files in this directory,” the kind of thing developers do constantly, without giving it a second thought.

It worked. Claude Code read the image, interpreted the rendered text as instructions within its LLM context, and chose to run the suggested commands. No exploit. No CVE. No vulnerability in the traditional sense, although prompt injection is increasingly recognized as its own vulnerability class (it’s LLM01 in the OWASP Top 10 for LLMs). This is an AI tool doing what AI tools do, processing input, and an attacker controlling what that input says.

You can read the full technical breakdown, including the PyRIT pipeline we used to automate and score the testing, in our AI Red Teaming Part 3 post. 

What We Found

This isn’t a story about Claude Code being an insecure product. Claude Code has meaningful security mitigations in place. It’s about a default configuration that hadn’t anticipated this specific attack vector, which is a much more common situation than outright negligence, and arguably a more important one to understand.

In our testing, the injected instructions executed 'whoami' and ifconfig without the user being prompted for permission. Both fall within Claude Code’s default allowed command set — read-only operations that the tool considers safe to run without explicit approval. It’s worth noting that ifconfig is allowed by default, while the newer ip commands require approval. This is the kind of inconsistency worth auditing in your own deployment. We also tested commands that would require user approval and, to Claude Code’s credit, those were blocked or prompted correctly.

So, from a default installation, the immediate impact is information disclosure: system user context and network configuration, triggered by something as innocent as analyzing a repository. That alone is worth knowing about. But the risk compounds from there. Combine it with a user environment that has broader permissions configured, or with social engineering techniques like Lies-In-The-Loop that trick a user into approving additional commands, and you’re looking at something considerably worse.

The only requirement for the attack to work was for the victim’s Claude Code session to access the poisoned file. That’s a low bar.

For teams using Claude Code or evaluating it for broader deployment, the mitigations exist and they work within their defined scope. The gap is in default configurations not being designed with adversarial image input in mind. That’s worth knowing, and worth testing in your specific environment before assuming the defaults are sufficient.

Why This Matters Beyond Claude Code

Prompt injection via image input isn’t a Claude Code problem. It’s a problem for any AI system that processes user-controlled content without adequate input validation, and that describes a rapidly growing category of tools.

Think about what’s getting AI features right now: code assistants, document summarization tools, customer support chatbots, and AI agents that send emails or query internal systems on a user’s behalf. Each of these has an input surface. Each of them, to varying degrees, follows instructions found in that input. Most of them haven’t been adversarially tested for prompt injection specifically.

To put this into context, imagine a contractor submits a pull request that includes a logo image with injection text rendered onto it. A developer on your team runs Claude Code to review the PR. The image gets processed, the instructions execute, and your developer never saw anything suspicious — they were just reviewing code. That’s the scenario. It doesn’t require a sophisticated attacker. It requires someone who has read this blog post.

We tested Claude Code because it’s a tool we use and wanted to understand it better. The methodology of using PyRIT to build a testing pipeline, automate prompt variants, and score results systematically can be directly applied to other AI targets. 

What to Add to Your AI Procurement Checklist

One practical consequence of AI features shipping fast is that standard vendor security assessments don’t necessarily cover them well. A SOC 2 report covers the vendor’s infrastructure. It doesn’t say anything about what their AI feature does with your data once it leaves that infrastructure. Third-party model providers, training data policies, and prompt injection testing don’t show up in the standard assessment. You have to specifically ask for it.

These questions are worth adding to your standard vendor questionnaire now, before the next procurement cycle:

Most vendors won’t have polished answers to all of these yet. That’s fine. The goal right now is to start building the baseline and understand where the gaps are, not to disqualify vendors who are still figuring it out.

Where to Start if You Want to Build This Capability

The right starting point depends on where your team is. There’s no single tool that works for everyone. If you’re just getting started, here’s what we would recommend: 

Start manual. Open the AI tool, try to inject an instruction into whatever input it accepts, and see if it follows it. No setup, no tooling, immediate feedback. Try this before anything else. 

Second, read the OWASP Top 10 for LLMs. It’s the clearest map of AI-specific risk categories available, written for security practitioners rather than AI researchers. It’ll give you language for what you’re finding.

Third, try PyRIT when manual testing starts feeling repetitive. It’s Microsoft’s open source AI red teaming framework that handles automation so you can focus on the interesting parts. Our Part 1 post walks through the setup and a first challenge end-to-end. It has a learning curve, but it’s worth it once you’re past the basics.

Finally, look at Garak. Different tool, different approach to probing model behavior. Some practitioners prefer it; at the very least, it’s worth knowing it exists.

Treat AI input surfaces the same way you’d treat any other user-facing input: assume someone will eventually try to abuse it, understand what the system can do when it follows those instructions, and test whether your controls would catch it. That framing applies regardless of which tools you use to get there. 

What to Fix

First, figure out what the AI tool can actually do. What can it run, read, or reach without asking for permission? That’s your starting point. Anything your team does not need is a risk you can cut by changing the configuration.

Second, think about what ends up in your working directories. The attack we ran worked because one poisoned file was sitting where Claude Code was told to look. If your team uses AI tools to analyze code repositories that pull in third-party dependencies or external content, that’s a similar setup. 

Third, check the default allowed command set and verify the approval prompts actually fire the way they’re supposed to. Default installations behave one way; customized ones sometimes don’t. Test your actual environment, not the documentation.

None of this requires a dedicated project. Most of it is an afternoon of configuration review. The point is to do it before someone else does it for you.

Which AI Tools to Test First

Start with whatever can do the most damage if it goes wrong, especially if someone else is controlling the instructions. We’re talking about tools that can take action: run commands, write files, call APIs, and send communications. 

Then move to tools that can surface sensitive data even if they can’t act on it, such as document summarization, internal search, and customer-facing chatbots with account access. Lower urgency, still worth finding.

You can save the read-only, no-backend tools for last. They’re not zero risk,  but they’re not where you need to start.

Within each tier, lean toward newer implementations first. Fresh AI features get less scrutiny than products that have been in production for years. That’s usually where the gaps are.

The Bottom Line

We want to be clear that the point of publishing this isn’t to claim Claude Code is broken. It isn’t. The point is that even well-built tools have edge cases that weren’t anticipated in their default configurations, and the only way to find those edge cases is to look for them.

That’s true of the AI tools you’re evaluating for procurement. It’s true of the ones already running in your environment. And it’s probably true of tools your staff is using that you don’t have visibility into yet.

The risk doesn’t show up in a vulnerability scanner. It shows up when someone with the right methodology goes looking for it. We’d rather that someone be you.

Our full PyRIT series, including setup, methodology, and real targets — is on the BreakPoint Labs blog. If you want to talk through AI testing scope for your environment, get in touch.


Frequently Asked Questions

What is prompt injection?

Prompt injection is an attack where a malicious actor embeds instructions into content that an AI model processes. When the model reads that content, it interprets the embedded instructions as legitimate commands and follows them. It’s classified as LLM01 in the OWASP Top 10 for Large Language Models

What is image prompt injection?

Image prompt injection is a variation of the attack where malicious instructions are embedded as visible text within an image file. When an AI tool processes the image, it reads the rendered text as part of its input and may run the instructions without user awareness.

Is Claude Code vulnerable to prompt injection?

Claude Code has security mitigations in place and handles many attack scenarios correctly. In our testing, a default installation didn’t anticipate adversarial image input as an attack vector. Injected commands that fell within the default allowed command set ran without prompting the user. Commands requiring explicit approval were blocked correctly. The risk is in default configurations, not a fundamental flaw in the product.

Does this attack require technical expertise?

No. The attack requires embedding text into an image file and placing it in a location the AI tool will access. No exploit, no CVE, and no advanced technical skill are required beyond understanding how the tool processes input.

What should security teams do right now?

Audit what your AI tools can do without user approval. Review what files and directories those tools access. Verify that approval prompts fire correctly in your specific environment, not just in vendor documentation. Test your actual deployment, not the default installation.

How do I test my own AI tools for prompt injection?

Start manually. Open the tool, inject an instruction into whatever input it accepts, and observe the result. When manual testing feels repetitive, try PyRIT, Microsoft’s open source AI red teaming framework. It automates prompt variant generation and scores results at scale. It has a learning curve, but it is worth the investment once you’re past the setup. The OWASP Top 10 for LLMs is the best starting reference for understanding the full risk category.

Context discovery is a critical part of an in-depth application testing methodology. This is an area where loads of risk can be uncovered that web application scanners and past security assessments might have missed. At BreakPoint Labs (BPL), we place a strong focus on application content discovery. This blog post will share our approach to content discovery, with the hope of inspiring others to dig deeper in their next application security assessment.

If your content discovery workflow is just pointing ffuf or gobuster at a target with a giant generic wordlist, you’re probably leaving a massive amount of attack surface on the table. While there is nothing inherently wrong with these tools or using large generic wordlists, there are better ways.

Oftentimes, we get in a “one size fits all” mindset when it comes to specific workflows in our methodology. Run the same tools and syntax for each engagement, hoping to find everything in a single easy command. But think about it, if we are scanning a web application for unlinked content, do we think we are going to find all those hidden endpoints with one list? Doubtful.

A smarter approach to unlinked content is to build it in stages. Focus on the target. What tech stack is running on the application? What endpoints have we already discovered? What endpoints or words/patterns do we already know about? These are all questions we should ask ourselves when picking wordlists, and remember, this doesn’t have to be done in one large sweep. In fact, it is often better to do this in stages, analyzing what you have found (and not found) to better craft your next scan.

Lastly, consider the tools we are using for this. These content discovery tools are a dime a dozen. Ffuf, gobuster, dirsearch, feroxbuster, the list goes on. Nothing wrong with these tools, but they all basically do the same thing. So really, it’s up to you which one you like. If it works for you, use that. I typically stay within Burp Suite, using the Discover Content feature and sometimes Intruder. My main selling point here is simple: I can easily add what I discover to the site map, passively analyze the endpoints I discover for new linked content, and configure Burp Suite to leverage what it finds to build custom approaches for the specific target.

Leveraging the Power of Burp Suite Discover Content Tool

The tool can be accessed by right-clicking a host in your site map and going to Engagement Tools -> Discover Content. You will see three (3) tabs: Control, Config, and Site Map.

Let’s dive into the Config tab, where we can really fine-tune the scans we run. While I won’t go into every possible configuration, I will cover each unique setting, what they do, and the specific use cases I like to use.

The Target section is where we can, of course choose the target, change the discover types (Files and/or Directories), and a recursion limit. I typically leave this as is, depending on the target, so not much to talk about here.

The Filenames section focuses on which wordlist(s) to use and how to configure them.

The File extensions section is helpful for controlling the addition of various file extensions to file stems. When Burp tests for a file, it will also check for these additional file extensions configured in this section.

Lastly, the Discovery Engine section is used to control how the scan is used for things such as crawling the discovered content and interactions with the site map.

So far, we have just talked about how we can configure this tool to dynamically add extensibility to our content discovery. Now let’s talk about authentication. Most tools, like ffuf, at least to my knowledge, don’t have a great way to handle this. You can pass in authentication headers and cookies into these tools, but when the session expires, there is no automated way to handle it.

This is another perk of using Burp Suite’s Discover Content tool: it can be used with Burp’s built-in session handling and cookie jar features to automate this, making authenticated discovery easier. We won’t go into how to set up session handling rules, macros, or the cookie jar, as there are plenty of resources available for this. However, when using these features with the Discover Content tool, be sure to update the Use cookies from Burp’s cookie jar rule to include both the Target and Scanner tools. This will ensure your scan uses the cookie(s) in place there.

Words are Hard: Choose the Right Wordlist

Now let’s talk about wordlists. What to use, when to use it, how to use it, and why it even matters. I see a lot of times people do one of two things: they either use a large, generic, publicly available wordlist that just hits on all cylinders. Or, they build custom wordlists that, well, are just collections of other wordlists that again hit on all cylinders. Don’t get me wrong, this, in and of itself, is not a bad idea. The problem is this is where they stop, but for me, this is just the beginning.

The Baseline

This is stage one; these are generic wordlists I like to use to see what’s out there. These are the classic wordlists we have all seen used. But they can get us a good idea of what is going on with the target and, in some cases, find easy low-hanging fruit. A few examples I like to use for this are:

Target Specific

Once you have completed a thorough walk-through of the application, maybe even crawled the site and conducted some unlinked content discovery using the lists above, it’s time to fine-tune your next phase.

I like to start by looking at the site map, noting all extensions I see, headers that can help identify the tech being used, interesting endpoints such as /api, /admin, /wp-content, etc. Also, don’t forget to look at things like Wapalyzer and passive collection notes in Burp to find additional tech you may not be tracking.

Once this data has been collected, it’s time to plan the next phase. Find wordlists that are specific to the application. For example, if the site is running WordPress, then use a WordPress-specific wordlist. If the site has a lot of php, then find wordlists targeting php endpoints, etc. This seems basic, but a lot of times people don’t take the time to really plan out this phase, and instead just try to throw everything at once and move on.

I’ll give a few examples I like to use here, but keep in mind there are plenty more out there. Do your research and find what you like.

Custom Wordlists

Last tip I will give here is, be sure to also use custom wordlists. Starting with public wordlists like this is a great place to start, but there can be real power in using custom wordlists, not only for specific targets using tools like GAP and backup-gen, but for your own collection of private wordlists that can be used for other engagements. When you find endpoints or filenames that look interesting for a site you are targeting, be sure to add them to your wordlists wherever you find the most valuable, such as php files to your php-specific wordlists. Never know when you may find it again.

Another valuable thing when you have a large scope of many applications on a target domain is to use words, parameters, etc, from the other sites on the site you are focusing on. These sites could share similar patterns depending on the developers. For example, if you have dev sites in your scope, you may find that they use various parameters that are used in dev but that have made it into production. So be sure to grab these with GAP and try them on your site.

Putting it All Together

Let’s run through a quick example, putting all this information together to show how I typically approach new applications to test.

Summary

In summary, Burp Suite’s Discover Content tool, when configured properly, can greatly improve content discovery and, in my experience, find more content than other fuzzers. I’m not saying other tools are bad, nor that they don’t provide functionality that beats this tool. However, I wanted to take this opportunity to show the power of understanding your toolkit and leveraging all the features it has to offer to improve your workflow. If you already have a tool like this built into your methodology and don’t want to change, I highly encourage you to explore all the features your tool offers. Test out different configurations and wordlists, find what works best for you. In the end, the tool you use is subjective, but I hope this article helps you improve how you approach content discovery.

Author Image

About the Author

Trevor Isenberg is a Senior Red Team Operator at BreakPoint Labs for a DoD-certified Red Team, responsible for assessing and securing critical ICS/SCADA assets. Coming from a background in system administration, Trevor provides a unique perspective when targeting complex networks and solving challenges. He holds numerous professional certifications and is especially interested in Web Application, Active Directory and ICS/SCADA pentesting. When he’s not in the middle of a red team engagment, you’ll find him spending time with family, playing guitar and working on projects at night to continuing growing his skillset.

Welcome to the final installment of The Attacker’s Playbook. So far, we’ve covered exploiting the human element (Part 1), finding unlocked doors (Part 2), and using forgotten keys to the kingdom (Part 3) to get inside a network.

But initial access is just the beginning. The real damage is done when we move through the internal network, abusing trust and misconfigurations to turn a minor foothold into a major compromise.

Navigating the Active Directory Maze

Active Directory (AD) is the heart of most corporate networks, but its permissions can become a tangled “rat’s nest” of complexity. An automated vulnerability scanner can’t understand the intricate web of trust relationships that a human attacker can exploit.

The Flaw: A standard user account may seem harmless, but it could have a hidden, indirect path to administrative power. It’s not uncommon to find that the default “Domain Users” group has overly broad permissions, such as the ability to log into nearly every computer on the network, including servers. 

Specifically, we look for the “GenericAll” permission. This allows one group to fully control another object. Even if the target account is disabled, an attacker with GenericAll rights can enable it and abuse those permissions.

The Attack: As we acquire user accounts, we use Bloodhound to map complex relationships and find unexpected privilege escalation paths.

This exact flaw was enumerated on a recent pentest where the “Domain Users” group had GenericAll access to a disabled service account. Seemingly for no reason, this permission existed on an account no one was monitoring.

From there, we enabled the account, reset the password, and masqueraded as that service account on the network. We discovered this service account had interesting permissions, including the ability to modify the password of a Domain Administrator.

After confirming this action was allowed within the Rules of Engagement (ROE), the team abused these permissions to take over the entire AD forest and ultimately the network.

The Result:  By manually navigating this maze, we can turn a low-privilege account into a full Domain Administrator, demonstrating a critical risk that no vulnerability scanner would discover.

Mitigation Strategies: Active Directory is a common target because it’s often a “rat’s nest” of complex permissions.

Thinking Like an Attacker

The most damaging breaches rarely rely on a single vulnerability. Instead, they emerge from creative chaining of minor, overlooked issues, a forgotten file, an exposed port, a default password—into a pathway through the maze of internal trust.

Key Takeaways:

Defending against these threats requires adopting the attacker’s perspective. This is where red team expertise provides the greatest value, simulating real-world thinking, not just automated scans.

Your security journey doesn’t end with this playbook. Contact us to learn how our expert-led penetration testing helps organizations secure their networks from the inside out.


That wraps up The Attacker’s Playbook. But your security journey shouldn’t stop here. Check out our deeper dives on Active Directory in  “Abusing Active Directory: Down the Rabbit Hole We Go,” and watch this short clip from our recent webinar on Active Directory permission risks  — or get in touch to test your defenses before attackers do.

When you picture a cyberattack, what comes to mind? Is it a shadowy figure in a hoodie, typing code to execute a brilliant, sophisticated exploit against a firewall? While that makes for a great movie scene, the reality is often far less cinematic.

Organizations invest millions in state-of-the-art security technology, but time and again, we find that the most critical breaches don’t start with a zero-day exploit. They start with a simple, predictable, and profoundly human mistake. To show how a small error can have massive consequences, let’s look at a real case from one of our healthcare assessments.

The Accidental Goldmine: How One Text File Compromised a Network

During a large-scale assessment of a healthcare network, our team was inside the environment, sifting through terabytes of data on internal file shares. This is where manual work often yields the most critical findings.

The Flaw: Using a tool called Snaffler, which scans network shares for interesting data, we received a massive output file to analyze. Through careful analysis, a team member spotted something unusual in a temporary folder on a random system: a file named p.kdbx. We immediately recognized this as a KeePass password database. Right next to it was a simple text file: p.txt.

The Attack: We opened p.txt. It contained a single string of text. On a hunch, we used that string as the password to open the KeePass database. It worked.

The Result: The database exposed numerous sensitive accounts. Most notably, it provided us with the credentials for a service account with a direct path to full Domain Administrator privileges. A single, forgotten text file gave us the keys to the entire kingdom.

For a detailed breakdown of the tools and techniques we use to discover exposed files and credentials on network shares, read our in-depth guide: Introduction to Share Analysis.

Lessons from the Human Element

This is the human element at its purest. It wasn’t a complex software vulnerability or a misconfigured firewall. It was one person’s simple mistake that completely undermined the organization’s security posture.

This is a perfect example of what we look for. However, “human error” is a broad category that attackers systematically target. It can be a process failure, like forgetting to decommission a public-facing web server. It can be a hygiene lapse or a policy gap, such as granting overly permissive access rights to users for convenience.

This is why proactive testing is so critical. It’s not about blame; it’s about building resilience against the predictable patterns of human behavior. You can’t defend against an adversary until you understand what they are looking for first.

From Insight to Action

The story of a single text file compromising a network isn’t just hypothetical; it’s a prime example of the cybersecurity threats in healthcare that begin with people.

Key Takeaways:

Our security assessments combine technical testing with social engineering to help organizations identify and fix these risks before attackers exploit them. Because mistakes are inevitable, but breaches don’t have to be.


Think a forgotten text file can’t cause real damage? Think again. In Part 2, we’ll show how attackers don’t always need passwords — sometimes all it takes is an unlocked door, both digital and physical. Stay tuned for the Patient Room Hack and other real-world entry points.

Finding the paths to escalate privileges in Active Directory (AD) can be challenging when dealing with a complex web of permissions, as shown in the picture above. This blog explains a method for discovering less obvious escalation paths by focusing on accounts with broad access that are not well protected (AdminCount False). 

Often, during offensive engagements, you’ll find yourself with some initial access to the network, and you want to expand your access. On most networks, this can be achieved most efficiently by escalating privileges within Active Directory (AD). There are numerous ways to identify escalation paths, which generally involve identifying AD objects that are not properly protected and can perform a variety of interesting actions. 

Understanding AdminCount 

One interesting way to identify this is by analyzing the AD object attribute “AdminCount”. Microsoft leverages this attribute to apply additional protections to the object. I won’t be going over this in detail here, but you can learn more here: https://learn.microsoft.com/en-us/windows/win32/adschema/a-admincount 

This value is set to either “True” or “False” with an AD object. AD objects that can do a lot of actions within the Domain are assigned a value of “True” (Example: Domain Admins, Enterprise Admins, etc.). The way I see networks is that you can divide them into two sections based on inbound permissions: objects with AdminCount: False, which allow more things to reach them, and objects with AdminCount: True, which allow fewer things to reach them. 

It’s actually somewhat predictable when looking at an object’s inbound permissions. If AdminCount False, usually the same set of objects can reach it, and if it’s True, the same objects can reach it. Typically, you will not have an object with AdminCount: False that can reach an object that has AdminCount: True. If you do this, you are likely to identify new escalation paths. 

When performing engagements, I often find a handful of accounts that numerous AD objects can reach due to AdminCount: False, which have excessive permissions that expose the entire domain to takeover. Generally, it’s just these few accounts that open the door for any attacker who can gain access from a lower-level object (for example, a Tier 1 help desk) and then escalate to these accounts, ultimately taking over the entire domain. 

Common Vulnerabilities/Examples

CVE-2017-8613: A good example of this common issue is with the Azure Sync account (MSOL_XXX). These accounts are frequently vulnerable to this permission abuse path because, for a considerable period, Microsoft failed to apply the AdminCount: True setting to these high-access accounts (they can dcsync by design).  

https://cve.mitre.org/cgi-bin/cvename.cgi?name=cve-2017-8613

https://learn.microsoft.com/en-us/security-updates/securityadvisories/2017/4033453

This same vulnerability is introduced to AD environments due to poor permission configuration. I observe this pattern in every engagement, and I wanted to share my analysis techniques to help others identify these escalation paths. 

Analysis Technique 

So, what I started doing on engagements is auditing all user accounts with AdminCount: False and their outbound rights. The idea is to identify accounts with lower inbound protections that can achieve a lot. 

In Bloodhound, you can see this attribute in the “Node Info” tab:

You can list out all users with their AdminCount value via a simple query: 

MATCH (u:User) return u.samaccountname, u.admincount 

Note: I find it helpful to return the admincount attribute for most of the queries. This is provided as a simple example. It’s just good context to have when analyzing various attack paths. 

Now, to automate this further, we need to determine their outbound rights to find who is admincount: false and can do more in the domain, as this will find interesting escalation paths similar to CVE-2017-8613: 

Code Snippet Explanation 

def get_admincountFalseUsers_trans(): 

    with GraphDatabase.driver(URI, auth=AUTH) as driver: 

        result = do_query(driver, "MATCH (m:User {admincount: false}) return m.name, m.objectid, m.admincount") 

        admincount_outbound_file=open("admincount_false_outbound_trans_rights.txt", "w") 

        for record in result: 

          if record["m.name"]: 

              username = record["m.name"] 

              objectid = record["m.objectid"] 

              admincount= record["m.admincount"] 

              result1 = do_query(driver, "MATCH (n) WHERE NOT n.objectid='"+objectid+"' MATCH p=shortestPath((u:User {objectid: '"+objectid+"'})-[r1:MemberOf|AddSelf|WriteSPN|AddKeyCredentialLink|AddMember|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner|Owns*1..]->(n)) RETURN count(p)") 

              for record1 in result1: 

                  if record1["count(p)"]: 

                      trans_rights=str(record1["count(p)"]) 

                      admincount_outbound_file.write("[-] User: "+username+" Transitive Outbound Rights: "+trans_rights+" Admincount: "+str(admincount)+"\n") 

        admincount_outbound_file.close() 

 

    with open("admincount_false_outbound_trans_rights.txt", "r") as fp: 

        entries = str(len(fp.readlines())) 

    print("[+] Generating a List of Users with AdminCount=False Transitive Outbound Rights: admincount_false_user_outbound_trans_rights.txt ("+entries+") lines")

Interpreting Results  

The code snippet is pulled from AD-RECON; it will retrieve a list of users with AdminCount: False and then gather the numerical value associated with their transitive outbound rights. Then I will sort the output with bash because I failed to produce the output sorted in Python; maybe I will change that, but for now, just do the following: 

``` 

└─$ cat admincount_false_outbound_trans_rights.txt| sort -k 7,7 -nr | less -S 

``` 

This will sort the output based on the 7th element in the line, which is the numerical count for the transitive outbound rights in reverse numerical order (so highest rights on top): 

 

``` 

[-] User: MSOL_F367GGD8CDD8@ORGRIMMAR.ORG Transitive Outbound Rights: 43848 Admincount: False 

[-] User: acct1@ORGRIMMAR.ORG Transitive Outbound Rights: 43847 Admincount: False 

[-] User: passreset@ORGRIMMAR.ORG Transitive Outbound Rights: 43847 Admincount: False 

[-] User: domainjoin@ORGRIMMAR.ORG Transitive Outbound Rights: 43847 Admincount: False 

[-] User: carol@ORGRIMMAR.ORG Transitive Outbound Rights: 43847 Admincount: False 

... 

 

``` 

The result is output that quickly identifies accounts with lower inbound protections, which can lead to various consequences. Now, all these aren’t always exactly the problem themselves, so you must look at the actual transitive outbound rights for these objects to see how they’re getting their permissions. When you do this, you’ll see there are a handful of accounts ending up in this graph that are allowing these lower-level Admincount: False accounts to do a lot of things. 

Example: Since the MSOL account can dcsync and essentially take over the domain, and its Admincount is false, the “passreset” account can change its password and thus has an escalation path to take over the domain. Had the MSOL account been adequately protected with AdminCount set to True, this escalation path might not have existed. On most networks, you will find that the MSOL account is a problem, but it is not the only account that extends permissions up to higher-level AD objects.  

Another example we often see is that the account used to add computers to the domain may not be properly configured and may not have permissions over the Domain Controllers (such as WriteAccountRestrictions and Owns), which creates a potential escalation path. See, getting to the domain controllers might be hard, but because the AdminCount: False for the “domainjoin” account, you just need to get any account that can reset a password (example, a Tier 1 help desk), and then take over the domainjoin account, and then the Domain Controllers.

Key Takeaways 

When diving deep into AD, as in this case, you can discover numerous additional escalation paths to enhance security, not just the one initially required to achieve the desired access. Please note that on a Red Team, this might not be the primary goal, but for a penetration test (PenTest), it’s beneficial to include as many escalation paths as you can identify. 

Author Image

About the Author

Andrew McNicol is the Chief Technology Officer and a co-founder of BreakPoint Labs, where he oversees the company’s technical strategy and manages the Cybersecurity Assessments line of business. He is a recognized expert in adversarial penetration testing, with more than 15 years of experience leading numerous technical teams on red team operations, vulnerability assessments, and penetration testing engagements to achieve each client’s specific objectives. Andrew holds numerous professional certifications and is a frequent speaker at government and industry security forums.

In the first part of this series, we introduced the ‘turtles all the way down’ concept of the OT attack surface. Then, we dove into the layers of trust within that stack, exploring supply chain and insider threats in Part 2. Now, we move from the digital and trusted to the tangible and physical. In this third installment, based on Ray Blasko’s presentation at OT.SEC.CON, we’ll explore the world of physical security assessments. You can watch the full recordings on the BreakPoint Labs video hub.

You’ve spent millions on firewalls, endpoint detection, and sophisticated monitoring tools. Your digital fortress is state-of-the-art. But what if an attacker could bypass all of it in less than 60 seconds with a cheap lockpick and a bit of nerve?

For an offensive security professional, a physical compromise is often the most fun and eye-opening part of an assessment. While you’ve secured the digital front door, attackers are often able to just walk in through the back.

Case Study: Compromising a Water Utility

If an attacker needs to gain access to your OT environment as quickly as possible, the easiest path is often direct physical access. In the words of our Technical Director, Ray Blasko, “the first thing I’m going to do is I’m going to go find a remote site that’s unmanned and get in that way.”

These sites are a goldmine of vulnerabilities. Let’s walk through a real-world example from one of our assessments.

A water utility took us to a remote water tower. From their perspective, it was well-defended:

The defender saw strengths. An attacker saw a playground of opportunities:

Inside that unlocked building was a connected router inside an (unlocked) PLC cabinet. We plugged in a laptop and instantly had access not only to that site but to their entire main OT environment. Total time from arrival to full network compromise: under a minute.

The Wireless HID Attack and Other Physical Threats

Physical threats aren’t just about doors and fences. A wireless HID attack is particularly dangerous. Attackers have gotten creative with weaponized USB devices. This goes far beyond leaving a thumb drive in the parking lot. We’ve seen malicious hardware hidden in charging cables and even vape pens.

One of the most effective attacks uses wireless mice and keyboards. An attacker can use a tiny, cheap RF dongle to send keystrokes to a wireless mouse receiver from up to 50 feet away, telling the computer to execute commands. Even if you block USB drives, you can’t block Human Interface Devices (HID) like a mouse or keyboard, making this a perfect way to bypass controls and attack secure or even air-gapped networks. An attacker can plug one of these tiny receivers into the back of a machine, and it will likely go unnoticed for a long time.

How to Improve Your Physical Security Posture

Your digital security is only as strong as the physical security that protects it. Don’t let an unlocked door be the downfall of your entire security program.


Up Next: In our final post, we’ll tie it all together and discuss how to build a proactive defense that can even stand up to advanced, nation-state level threats in “Proactive OT Security: Defending Against Zero-Days & Nation-States”

How resilient is your physical security? BreakPoint Labs conducts comprehensive physical security assessments to uncover the gaps you can’t see. Contact us to learn more.

Author Image

About the Author

Ray Blasko is the Technical Director for offensive operations at BreakPoint Labs and a Red Team Operator for a DoD-certified Red Team, responsible for assessing and securing critical ICS/SCADA assets. He is recognized as a subject matter expert in both attacking and defending IT and OT environments, and he excels at resolving the technical and strategic issues that arise in protecting critical infrastructure. Ray holds numerous professional certifications and regularly presents at DoD Red Team conferences, trade conventions, and information security events.

In penetration tests and red team engagements, we frequently exploit privilege escalation paths in Active Directory (AD). When discussing remediation, clients often think, “I’ll just disable that account to eliminate the risk.” However, in some instances, simply disabling the account does not remove the risk. When we explain this to customers, they tend to look confused because the account is indeed disabled. Still, the residual risk is tied to specific escalations involving permissions over a higher-privilege account from alternative access. For example, privileges such as “WriteDACL” or “GenericAll” can enable an attacker to exploit a disabled account by re-enabling it and leveraging the escalation path as if it were never disabled.

Basic Example:

During an engagement, the team gains access to several users through password guessing or by discovering cleartext passwords on shared drives and begins evaluating the access and privilege escalation paths of these compromised users within Active Directory. The team identifies one user (User A) who has (GenericAll) rights over User (B).

This is especially interesting because appears that User A’s access to User B is somewhat outside the normal permissions, as the rest of the permission paths reside with high-value targets.

Further investigation shows that User B can do a lot of interesting things (effectively has control over all the domain objects):

In this instance, User B is disabled. However, since the attackers have User A, who has “GenericAll” over User B, they can enable the user and leverage it to escalate their privileges.

This perfectly demonstrates how simply disabling the account of User B does not reduce the risk for this attack path; it just adds an extra step for the attacker to enable the account, which is trivial.

So What Should You Do?

Now that you understand simply disabling accounts within AD doesn’t always eliminate the attack surface, you should consider the following:

  1. Removing the account if it’s no longer needed
  2. Removing the access from the account if you are unable to remove it entirely
  3. Evaluate attack paths to this disabled account to see if paths exist that expose you to risk

Tips for Investigation:

We find it useful to assess the outbound rights of all disabled accounts (we do this for every account in the domain, but to focus on the topic of this blog, we will concentrate on disabled users) to identify those with significant access. From this point, examine who has rights to reach them (inbound permissions). This can assist in pinpointing potential escalation paths from lower targets to disabled accounts that possess considerable access. Below are some helpful Cypher Queries you can leverage within Neo4j against the Bloodhound database:

List all Disabled Users:

MATCH (m:User {enabled: false}) return m.name, m.objectid, m.admincount

Using the SID for each user (m.objectid), you can list out transitive permissions for each user based on a count:

MATCH (n) WHERE NOT n.objectid='"+objectid+"' MATCH p=shortestPath((u:User 
{objectid: '"+objectid+"'})-
[r1:MemberOf|AddSelf|WriteSPN|AddKeyCredentialLink|AddMember|AllExtendedRights
|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner|Owns*1..]->(n)) RETURN count(p)

In practice, we find querying is best done in Python. Below is a more complete snippet of code:

def get_disabledUserOutboundRights_transitive():
    with GraphDatabase.driver(URI, auth=AUTH) as driver:
        result = do_query(driver, "MATCH (m:User {enabled: false}) return m.name, m.objectid, m.admincount")
        disabled_outbound_file=open("disabled_user_outbound_transitive_rights.txt", "w")
        for record in result:
          if record["m.name"]:
              username = record["m.name"]
              objectid = record["m.objectid"]
              admincount= record["m.admincount"]
              result1 = do_query(driver, "MATCH (n) WHERE NOT n.objectid='"+objectid+"' MATCH 
p=shortestPath((u:User {objectid: '"+objectid+"'})-[r1:MemberOf|AddSelf|WriteSPN|AddKeyCredentialLink|AddMember|
AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner|Owns*1..]->(n)) RETURN count(p)")
              for record1 in result1:
                  if record1["count(p)"]:
                      trans_rights=str(record1["count(p)"])
                      disabled_outbound_file.write("[-] User: "+username+" Transitive Outbound Rights: "+trans_rights+" 
Admincount: "+str(admincount)+"\n")
        disabled_outbound_file.close()

    with open("disabled_user_outbound_transitive_rights.txt", "r") as fp:
        entries = str(len(fp.readlines()))
    print("[+] Generating a List of Disabled Users with Transitive Outbound Rights: disabled_user_outbound_trans_rights.txt 
("+entries+") lines")

The result will make a text file of all the disabled users and their transitive outbound rights, one per line. This can help us analyze the outbound rights of all disabled users quickly to find interesting targets for additional analysis:

The output will look like this:

[-] User: userb@BREAKPOINT.LABS First Degree Outbound Rights: 20771 Admincount: True

So to sort the data based on "who has the most rights", first we will use the Linux sort utility based on a number in reverse 
order on the 7th element in the line (which is the outbound right count).

└─$ cat disabled_users_outbound_transitive_rights.txt | sort -nr -k 7,7 | less -S
[-] User: userb@BREAKPOINT.LABS Transitive Outbound Rights: 20771 Admincount: True
[-] User: userc@BREAKPOINT.LABS Transitive Outbound Rights: 6165 Admincount: True
[-] User:userd@BREAKPOINT.LABS Transitive Outbound Rights: 5098 Admincount: True
[-] User: usere@BREAKPOINT.LABS Transitive Outbound Rights: 4821 Admincount: True
[-] User: userf@BREAKPOINT.LABS Transitive Outbound Rights: 4821 Admincount: True

From here, review the inbound rights for these accounts in Bloodhound, and you might discover an interesting way to control one of these disabled users who have extensive rights in the domain.

Key Takeaways

Disabling an Active Directory account does not always equate to eliminating risk. Attackers can potentially re-enable disabled accounts if they possess the necessary permissions, such as “GenericAll” or “WriteDACL,” on those accounts. To truly mitigate risks, organizations should go beyond simply disabling accounts. Consider removing the account entirely if it’s no longer needed, revoking its access rights, and thoroughly evaluating potential attack paths leading to these disabled accounts.

By proactively assessing outbound and inbound rights, especially for disabled accounts with significant access, and utilizing tools like Bloodhound and Cypher queries, you can uncover hidden vulnerabilities and strengthen your Active Directory security posture. Remember, a seemingly disabled account can still pose a threat if the underlying permissions are not properly addressed.

Author Image

About the Author

Andrew McNicol is the Chief Technology Officer and a co-founder of BreakPoint Labs, where he oversees the company’s technical strategy and manages the Cybersecurity Assessments line of business. He is a recognized expert in adversarial penetration testing, with more than 16 years of experience leading numerous technical teams on red team operations, vulnerability assessments, and penetration testing engagements to achieve each client’s specific objectives. Andrew holds numerous professional certifications and is a frequent speaker at government and industry security forums.

chevron-down