Back to Intelligence

Shadow AI and Low-Code 'Wild Code': How Security Teams Can Detect and Govern Unmanaged Apps, Agents, and Automations

SA
Security Arsenal Team
October 21, 2026
11 min read

A sales manager builds an AI-assisted workflow that routes leads, touches customer records, summarizes inbound requests, and posts updates to a shared channel. It works. It spreads. And at no point did anyone involve security, IT, or legal.

Multiply that one workflow by every department in your organization, and you have the defining exposure of 2026: wild code — applications, AI agents, and automations built by non-developers using low-code platforms, LLM copilots, and integration tools, operating entirely outside your inventory, your SDLC, and your monitoring.

This is not a theoretical problem. In our IR engagements over the past 12 months, some of the most damaging incidents did not start with a zero-day — they started with an unsanctioned automation holding live API tokens, an AI agent with read access to an entire SharePoint tenant, or a citizen-built app syncing customer PII to a personal cloud account. Attackers do not need to exploit your firewall if your own employees have already built the data egress path for them.

This post breaks down why wild code is dangerous, how to detect it in your environment today, and how to build governance that channels innovation instead of blocking it.


Why Wild Code Is a First-Order Security Risk

Traditional shadow IT meant a rogue Dropbox account or an unauthorized SaaS subscription. Wild code is categorically worse, because it is executable logic with credentials attached:

  • It holds secrets. Low-code flows and AI agents authenticate with OAuth tokens, API keys, and service accounts. These credentials are frequently over-scoped, never rotated, and stored outside your secrets management.
  • It moves data across trust boundaries. A workflow that 'summarizes inbound requests and posts updates to a shared channel' is, technically, a data pipeline: CRM → LLM API → collaboration platform. Every hop is a potential leak, and none of it is in your DLP policy.
  • It bypasses change control. There is no code review, no dependency scanning, no secrets detection, and no rollback plan for something nobody knows exists.
  • It inherits AI risk. When citizen-built agents call LLM APIs, they inherit prompt injection, data exposure through model inputs, and non-deterministic behavior — deployed by people who have never heard those terms.
  • It creates attacker opportunity. Adversaries actively hunt for over-permissioned OAuth grants and orphaned service principals as initial access and persistence vectors. A consent grant your employee forgot about is a backdoor with an audit trail nobody reads.

The critical mindset shift: wild code is not a policy violation to be punished — it is an asset class to be discovered, inventoried, and governed. Employees are building these things because the business needs them. Your job is to make the safe path the easy path.


Technical Analysis: The Anatomy of Unmanaged Automation

While there is no single CVE here, the attack surface is well-defined and observable. In enterprise environments (Microsoft 365, Google Workspace, Slack, and SaaS ecosystems), wild code typically materializes in four forms:

1. Over-Permissioned OAuth Consent Grants

When an employee connects a low-code tool or AI assistant to Microsoft 365 or Google Workspace, the OAuth consent flow is the security boundary. The dangerous patterns:

  • User consent to high-risk scopes: Mail.Read, Files.Read.All, offline_access, full_access_as_app, Google Workspace's .../auth/drive and .../auth/gmail.readonly
  • Consent to unverified third-party publishers
  • Admin consent granted by a helpdesk or IT admin without security review, tenant-wide

These grants are durable — offline_access means refresh tokens that survive password changes. They are the modern persistence mechanism.

2. Unmanaged AI/LLM API Egress

Citizen-built agents call api.openai.com, api.anthropic.com, or self-hosted models (Ollama on port 11434, LM Studio on 1234) directly from workstations, Power Automate flows, or Zapier/Make scenarios. If your egress monitoring only looks for known-malware destinations, this traffic is invisible — and it may be carrying customer records.

3. Low-Code Platform Sprawl

Power Automate, Power Apps, Zapier, Make (Integromat), n8n, and Workato flows execute in the vendor's cloud, not on your endpoints. Endpoint telemetry won't see them. Your visibility comes from the platform's own audit logs and from the SaaS connectors they touch.

4. Local Agent Frameworks and Runner Processes

The 2026 wave of desktop AI agents (MCP clients, agentic IDE extensions, ollama, n8n, flowise, langflow) runs locally, spawns child processes, opens listening ports, and reads local files. These are detectable with standard process and network telemetry — if you're looking.

Exploitation Status

There is no single in-the-wild exploit — the risk is structural. Threat actors routinely abuse legitimate SaaS integrations and stolen OAuth refresh tokens for BEC, data theft, and persistence (MITRE ATT&CK T1528 – Steal Application Access Token, T1550.001 – Use Alternate Authentication Material: Web Session Cookie, T1078 – Valid Accounts). The wild code problem multiplies the number of tokens, grants, and data paths available to steal.


Detection & Response

Sigma Rules

The following rules target two high-signal behaviors: risky OAuth consent grants in Microsoft Entra ID, and local execution of AI agent/automation runtimes. Both are designed to minimize noise — the OAuth rule keys on the consent operation plus scope, and the process rule keys on known agent runtimes spawning command interpreters.

YAML
---
title: High-Risk OAuth Consent Grant to Third-Party Application
id: 3f8a2c14-7b9e-4d51-a6c2-8e1f0a3b5d77
status: experimental
description: Detects user or admin consent grants to applications requesting high-risk Microsoft Graph scopes such as Mail.Read, Files.Read.All, or offline_access — a common wild code and attacker persistence pattern.
references:
  - https://attack.mitre.org/techniques/T1550/001/
  - https://learn.microsoft.com/en-us/entra/identity/enterprise-apps/manage-app-consent-policies
author: Security Arsenal
date: 2026/02/10
tags:
  - attack.persistence
  - attack.t1550.001
  - attack.t1078
logsource:
  product: azure
  service: auditlogs
detection:
  selection_operation:
    OperationName|contains:
      - 'Consent to application'
      - 'Add delegated permission grant'
      - 'Add app role assignment to service principal'
  selection_scope:
    TargetResources|contains:
      - 'Mail.Read'
      - 'Mail.ReadWrite'
      - 'Mail.Send'
      - 'Files.Read.All'
      - 'Files.ReadWrite.All'
      - 'Sites.FullControl.All'
      - 'offline_access'
      - 'full_access_as_app'
      - 'Directory.Read.All'
  condition: selection_operation and selection_scope
falsepositives:
  - Approved enterprise application onboarding (maintain an allowlist of sanctioned app IDs)
  - IT-driven integrations following change control
level: high
---
title: Local AI Agent or Automation Runtime Spawning Shell or Script Interpreter
id: 9d4c6b81-2e7a-4f38-b915-5c8d3a0e6f42
status: experimental
description: Detects AI agent frameworks and local automation runtimes (Ollama, n8n, Flowise, Langflow, MCP clients) spawning command shells or script interpreters, indicating autonomous tool execution on the endpoint.
references:
  - https://attack.mitre.org/techniques/T1059/
author: Security Arsenal
date: 2026/02/10
tags:
  - attack.execution
  - attack.t1059
logsource:
  category: process_creation
  product: windows
detection:
  selection_parent:
    ParentImage|endswith:
      - '\ollama.exe'
      - '\n8n.exe'
      - '\flowise.exe'
      - '\langflow.exe'
      - '\mcp.exe'
    ParentCommandLine|contains:
      - '\n8n'
      - 'ollama serve'
      - 'modelcontextprotocol'
  selection_child:
    Image|endswith:
      - '\cmd.exe'
      - '\powershell.exe'
      - '\pwsh.exe'
      - '\python.exe'
      - '\node.exe'
      - '\curl.exe'
      - '\wscript.exe'
  condition: selection_parent and selection_child
falsepositives:
  - Sanctioned AI development activity on developer workstations (scope by department or device group)
level: medium

Tune the second rule by scoping it to non-developer device groups. On a finance workstation, n8n spawning powershell.exe should be investigated immediately; on a sanctioned ML engineering box, it's Tuesday.

KQL — Microsoft Sentinel / Defender Hunt

This query hunts for newly consented applications with high-risk permissions over the past 14 days, correlating the consent grant with the sign-in activity of the service principal — surfacing wild code integrations that are actually in use.

KQL — Microsoft Sentinel / Defender
let Lookback = 14d;
let HighRiskScopes = dynamic(["Mail.Read", "Mail.ReadWrite", "Mail.Send", "Files.Read.All", "Files.ReadWrite.All", "Sites.FullControl.All", "Directory.Read.All", "offline_access", "full_access_as_app"]);
let RiskyConsents =
    AuditLogs
    | where TimeGenerated > ago(Lookback)
    | where OperationName in~ ("Consent to application", "Add delegated permission grant", "Add app role assignment to service principal")
    | extend TargetApp = tostring(TargetResources[0].displayName)
    | extend TargetAppId = tostring(TargetResources[0].id)
    | mv-expand ModifiedProps = TargetResources[0].modifiedProperties
    | extend PropName = tostring(ModifiedProps.displayName)
    | extend NewValue = tostring(ModifiedProps.newValue)
    | where PropName has "Permission" or NewValue has_any (HighRiskScopes)
    | extend GrantedScopes = NewValue
    | project ConsentTime = TimeGenerated, TargetApp, TargetAppId, GrantedScopes,
              ConsentActor = tostring(InitiatedBy.user.userPrincipalName), Result;
RiskyConsents
| join kind=leftouter (
    SigninLogs
    | where TimeGenerated > ago(Lookback)
    | summarize SigninCount = count(), LastSignin = max(TimeGenerated),
                SourceIPs = make_set(IPAddress) by AppId
) on $left.TargetAppId == $right.AppId
| project ConsentTime, TargetApp, GrantedScopes, ConsentActor, SigninCount, LastSignin, SourceIPs
| order by ConsentTime desc

A complementary endpoint hunt finds workstations making outbound connections to LLM API endpoints from non-browser processes — the signature of a locally running agent rather than sanctioned web usage:

KQL — Microsoft Sentinel / Defender
let LLMDomains = dynamic(["api.openai.com", "api.anthropic.com", "generativelanguage.googleapis.com", "api.cohere.com", "openrouter.ai", "api.mistral.ai"]);
let Browsers = dynamic(["msedge.exe", "chrome.exe", "firefox.exe", "brave.exe", "safari", "iexplore.exe"]);
DeviceNetworkEvents
| where TimeGenerated > ago(7d)
| extend RemoteHost = parse_url(tostring(RemoteUrl)).Host
| where tostring(RemoteHost) has_any (LLMDomains) or RemotePort in (11434, 1234, 7860)
| where InitiatingProcessFileName !in~ (Browsers)
| summarize Connections = count(), FirstSeen = min(TimeGenerated), LastSeen = max(TimeGenerated),
            RemoteHosts = make_set(RemoteHost), Ports = make_set(RemotePort)
    by DeviceName, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| order by Connections desc

Velociraptor VQL

This artifact hunts endpoints for local AI runtimes and automation engines — identifying the process, its parent, listening ports, and the account context, so responders can determine whether an agent is sanctioned or wild.

VQL — Velociraptor
-- Hunt for local AI agent runtimes and automation engines with network listeners
SELECT Pid, Ppid, Name, CommandLine, Exe, Username, CreateTime
FROM pslist()
WHERE Name =~ '(?i)(ollama|n8n|flowise|langflow|lm-studio|lmstudio|anything-llm|mcp)'
   OR CommandLine =~ '(?i)(ollama serve|n8n start|modelcontextprotocol|flowise start)'

-- Correlate with listening ports commonly used by local AI/agent frameworks
SELECT Pid, Name, LocalAddress as LocalAddr, LocalPort, Status
FROM netstat()
WHERE Status =~ 'LISTEN'
  AND LocalPort in (11434, 1234, 3000, 5678, 7860, 8000)
  AND Name =~ '(?i)(ollama|n8n|flowise|langflow|node|python|lm-studio)'

Run this as a fleet-wide hunt weekly. Any hit on a non-engineering endpoint warrants a conversation with the asset owner — and often reveals an entire shadow automation stack.

Audit & Hardening Script

The script below audits Entra ID for high-risk delegated permission grants (wild code integrations) and hardens the tenant by disabling end-user consent for unverified apps, routing requests through an admin approval workflow instead. Review before running; the hardening section is commented out by default.

PowerShell
#Requires -Modules Microsoft.Graph.Identity.SignIns, Microsoft.Graph.Applications
# Audit high-risk OAuth consent grants (wild code discovery)
Connect-MgGraph -Scopes "Directory.Read.All","Policy.Read.All","Application.Read.All"

$highRiskScopes = @("Mail.Read","Mail.ReadWrite","Mail.Send","Files.Read.All",
  "Files.ReadWrite.All","Sites.FullControl.All","Directory.Read.All",
  "offline_access","full_access_as_app")

$report = @()
$servicePrincipals = Get-MgServicePrincipal -All
foreach ($sp in $servicePrincipals) {
  $grants = Get-MgServicePrincipalOauth2PermissionGrant -ServicePrincipalId $sp.Id -All -ErrorAction SilentlyContinue
  foreach ($grant in $grants) {
    $matched = $highRiskScopes | Where-Object { $grant.Scope -match [regex]::Escape($_) }
    if ($matched) {
      $report += [PSCustomObject]@{
        AppDisplayName  = $sp.DisplayName
        AppId           = $sp.AppId
        PublisherVerified = $sp.VerifiedPublisher.IsVerified
        ConsentType     = $grant.ConsentType   # AllPrincipals = admin consent
        GrantedScopes   = $grant.Scope
        MatchedRisk     = ($matched -join ",")
        CreatedDate     = $sp.AdditionalProperties.createdDateTime
      }
    }
  }
}
$report | Sort-Object AppDisplayName | Format-Table -AutoSize
$report | Export-Csv -Path ".\WildCode_OAuth_Audit_$(Get-Date -Format 'yyyyMMdd').csv" -NoTypeInformation
Write-Host "[+] $($report.Count) high-risk grants found. Review CSV output." -ForegroundColor Yellow

# --- HARDENING (uncomment to enforce) ---
# Disable user consent entirely; route through admin consent workflow instead
# Update-MgPolicyAuthorizationPolicy -BodyParameter @{
#   defaultUserRolePermissions = @{
#     permissionGrantPoliciesAssigned = @()   # blocks all user consent
#   }
# }
# Enable the admin consent request workflow so users can request approval
# Update-MgPolicyAdminConsentRequestPolicy -BodyParameter @{
#   isEnabled = $true
#   notifyReviewers = $true
#   remindersEnabled = $true
#   requestDurationInDays = 7
# }

Remediation & Governance: Taming Without Strangling

Blocking low-code and AI tools outright does not work — it drives usage underground, where you lose all visibility. The organizations that succeed treat this as a governance and architecture problem:

  1. Discover and inventory continuously. Run the OAuth audit above monthly. Pull admin/audit logs from Power Platform, Zapier, Make, n8n, and Slack integrations. Every discovered app gets an owner, a business purpose, and a risk classification. No owner → revoke.

  2. Lock down consent, enable the request path. Disable user consent for unverified apps (or all apps) and enable the Entra admin consent workflow. Employees can still get the tools they need — security just sees them first. Do the equivalent in Google Workspace (API access controls → restrict high-risk scopes).

  3. Establish a sanctioned 'paved road.' Offer pre-approved connectors, a corporate LLM gateway (with logging, redaction, and data-use terms), and templates for common automations. Wild code thrives where the official path is slow.

  4. Classify data paths, not just apps. The risk is what the automation touches. Require that any workflow connecting to CRM, HR, or customer data systems routes through the approved gateway and appears in DLP policy.

  5. Apply DLP and egress controls. Enforce Power Platform DLP policies (block consumer connectors in the default environment). Alert on outbound traffic to LLM APIs from non-browser processes. Consider requiring all LLM traffic to traverse a proxy gateway you control.

  6. Treat citizen-built agents like code. If it executes logic and holds credentials, it goes in the inventory, gets threat-modeled, and has an offboarding plan when the builder leaves. Orphaned automations with live tokens are a breach waiting for a calendar invite.

  7. Monitor for persistence via tokens. Alert on risky consent grants (the Sigma rule above), watch for service principals signing in from unusual geographies, and include OAuth grants in your offboarding checklist — user deprovisioning does not always revoke refresh tokens.

The sales manager's lead-routing workflow is not the enemy. Done right, it's a productivity win. Your mission is to make sure security is present at the design table — not reading about it in a post-incident forensics report.

Related Resources

Security Arsenal Alert Triage Automation AlertMonitor Platform Book a SOC Assessment platform Intel Hub

Is your security operations ready?

Get a free SOC assessment or see how AlertMonitor cuts through alert noise with automated triage.