Go back

Same Attacker, Different Door: Our Canaries Caught Microsoft's Agentic AI Secret Hunter Exploiting Langflow

Patrick Garrity

Patrick Garrity

in/patrickmgarrity/

On November 5, Microsoft Threat Intelligence published research on threat actors using agentic AI tooling to hunt for secrets exposed on GitHub. With those secrets they got into Entra ID tenants, Azure Key Vaults and Azure AI services. The IP at the centre of that activity was 199.19.77.84.

When we checked VulnCheck Canaries, we found the same address had been hitting our infrastructure too, by another route. Instead of leaked GitHub secrets, it was exploiting Langflow to pull credentials straight off the box.

In our post, we looked at two attackers who both went after Langflow, one for credentials and one for cryptomining. This is a third, and it may be the most interesting of the three. Over 36 minutes it ran 11 payloads that read less like an exploit kit and more like an agent working the problem.

Below we walk through what we saw, how it lines up with Microsoft's research, and what defenders can do about it. Let's dive in.

What Did Microsoft Find?

Microsoft describes an agentic framework that sends large numbers of GitHub searches for secrets, from August 2026 on. The searches combine target names with keywords for cloud and AI services. From October 28, Microsoft saw successful service principal sign-ins from 199.19.77.84 using leaked application credentials. Some were multitenant vendor apps, which opened the way into downstream tenants.

After signing in, the agent enumerated Azure, read Key Vault secrets and storage keys, pulled SharePoint and OneDrive content, and validated stolen Azure AI resources. In one case it went from a GitHub search to Key Vault exfiltration in five minutes. Microsoft also points to adaptive troubleshooting: failed ARM calls retried 28–80 seconds later with a corrected API version.

Microsoft tied two more IPs to the campaign. It linked vulnerability exploitation to 65.87.223.26 and AI-workload reconnaissance to 157.22.126.128. In Microsoft's account, 199.19.77.84 is the credential-abuse address.

Todyl and Huntress reported the same IP signing into Entra ID with stolen app secrets. Todyl counts at least 27 affected organizations and places the address on AS203662, a Poland-based hosting provider. Huntress saw the actor obtain Azure tokens in four customer tenants. Neither report describes this IP exploiting a vulnerability, and that is where our data comes in.

What Did Our Canaries See?

VulnCheck canaries logged 25 events from 199.19.77.84 across three countries, from 13 distinct requests. Each PHP probe tripped two signatures at once, and 10 of the 11 Langflow stages were sent twice, usually less than a second apart. All times are UTC, newest first.

Time (UTC)CanaryCVEActivity
Nov 7, 00:35–00:38GBCVE-2025-3248Stages 4–11: cloud and Docker recon, log scraping, credential decryption
Nov 7, 00:30–00:32GBCVE-2025-3248Stages 2–3: env dump, SQLite discovery under /app
Nov 7, 00:02GBCVE-2025-3248Stage 1: env, .env files and langflow.db sweep
Nov 2, 08:44SE, BRCVE-2026-109618, CVE-2026-109619PHP upload-progress session priming probes

The Langflow activity on November 7 UTC is the evening of November 6 in US time zones. That is one day after Microsoft published, so the operator was still active with the same infrastructure.

Breaking Down the Langflow Attack

All 11 stages use one wrapper. CVE-2025-3248 lets an unauthenticated caller send Python to /api/v1/validate/code. Langflow compiles that code to validate it, and a decorator runs at function definition, so the payload runs immediately:

import os

@exec('\nimport os, json\ndef _gather():\n    return {"pid": os.getpid(), "cwd": os.getcwd(), "host": os.uname().nodename,\n            "env": {k: v for k, v in os.environ.items()}}\nraise Exception("|RAISE|" + json.dumps(_gather())[:80000])\n')
def _lf_probe():
    pass

This is stage 2 exactly as sent. Later stages swap in more code, rename the helper to _g() and change the size cap, but the shape never changes.

The stolen data doesn't leave through a separate channel. Each stage puts its results in an exception marked |RAISE|, and the vulnerable endpoint returns exception text in its HTTP response, so the reply to each request carries the data. None of the payloads call back to attacker infrastructure, download anything or write a file. The only outbound traffic stays inside the host. One stage queries the cloud metadata service at 169.254.169.254 and talks to the Docker socket, and that is the traffic worth alerting on.

The stages build on each other:

  1. Sweep (00:02). Dump os.environ. Read .env files under /app, /home and /root. Glob for langflow.db and dump tables named like config, variable, user, apikey, credential, provider or flow.
  2. Map the host (00:30–00:35). List /app/data, walk for SQLite files and read /proc/1/cmdline. Check for /var/run/docker.sock and, if present, request GET /containers/json?all=1. Fetch DigitalOcean metadata and user-data from 169.254.169.254.
  3. Look for other intruders (00:36). Read /proc/*/cmdline for miner processes (xm, cpuminer) and run crontab -l. Read files left by earlier attackers, such as /app/data/.kxc/mine.log, /tmp/.kk9/.d.py and /tmp/keepx.sh. Some of these, including /tmp/.l.sh, were dropped on the same canary two days earlier by a separate cryptomining crew that had hit the same canary. One code comment says it outright: "leftover scripts of other actors".
  4. Scrape for keys (00:36–00:37). Grep langflow.log and the flow table for sk-, AIza, AKIA, AQ.Ab8, JQQJ99, Ocp-Apim, BEGIN PRIVATE KEY, openai.azure, cognitiveservices and services.ai.azure. Pull api_key and base_url fields from saved flows. Read the mem0 memory-layer config.
  5. Decrypt the vault (00:36–00:38). Langflow encrypts stored variables with Fernet. The agent tried four approaches in turn: Langflow's own decrypt helper, grep -rn "def decrypt" over the package, a candidate secret that appears only after its two environment dumps (LANGFLOW_SECRET_KEY=[redacted], our decoy's key) run through seven key derivations, and finally Langflow's own ensure_valid_key() fed that real secret. The last stage decrypts every row of variable joined to its owner's username. Because it uses Langflow's own key derivation with the decoy's key, that is the method that works. Against a real instance, this is where the provider keys come out. Against our canary, all it could recover was decoy data.
  6. Wrap up (00:38). Dump the file and vertex_build tables and list upload directories.

Is There an AI Agent Behind the Keyboard?

We can't prove from network traffic alone that an AI agent ran this session. That said, it lines up with what Microsoft describes.

  • Each stage depends on the last. Paths, table names and key-derivation guesses in later stages come from what earlier stages returned. A fixed kit would send the same payload to every target.
  • Retry at machine speed. The decryption attempts took about 80 seconds from first try to the method that works, with each failure followed by a new variation. That matches the 28–80 second ARM retries in Microsoft's report.
  • Narrating comments. Payloads carry comments such as # try langflow decrypt on any encrypted-looking values and # leftover scripts of other actors: grep, typical of generated code.
  • Python HTTP clients. Stage 1 and the PHP probes follow python-requests' header order (User-Agent, then Accept-Encoding, Accept: */* and Connection). Stages 2–11 match urllib (Accept-Encoding: identity first, Connection: close). Microsoft lists Python HTTP clients among ARTEX's default tools.
  • Hunting for AI keys. Every key pattern and endpoint in the payloads points at an LLM provider or a cloud AI service. Microsoft saw the same focus.

Before Langflow: Poking at PHP

On November 2, the same IP sent one multipart POST / each to canaries in Sweden and Brazil. Both tripped our CVE-2026-109618 and CVE-2026-109619 detections.

The request picks its own session ID and sets an upload-progress key. This is the priming step for php2shell, the pair of PHP request-memory bugs behind CVE-2026-109618 and CVE-2026-109619 that can lead to remote code execution. The content here is harmless, so this was a check for exposure, not an exploit.

Not the Only One at the Door

199.19.77.84 wasn't the only intruder after AI credentials in our canaries that week. On November 2, about five hours before .84 probed our Brazil and Sweden canaries for the PHP bug, 179.43.191.82 (Private Layer, Switzerland) ran an interactive session against our Brazil Langflow canary through CVE-2025-3248.

Its playbook looks a lot like .84's:

  • Langflow secrets. It read /app/data/.cache/langflow/secret_key and /proc/1/environ, and grepped langflow.log for sk-, api_key, openai, anthropic, aiza, gsk_, hf_ and vertex.
  • Agent memory. It listed and read the .mem0 and .composio stores, which hold memory and tool-integration config for AI agents.
  • Other intruders. It read leftover files such as /app/data/.cache/.sync/.s.sh and dl.out.
  • The container. It checked the Docker and containerd sockets and port-scanned the container network.
  • Azure identity. It tried several times to get a managed-identity token for management.azure.com, queried Azure's instance metadata endpoint, grepped the environment for IDENTITY|MSI|AZURE, and tested whether it could reach Azure's WireServer at 168.63.129[.]16.

Eight hours later, the same IP hit HKUDS AutoAgent (CVE-2026-86124) with a script that harvests AWS IMDSv1/v2 role credentials, user-data, and OCI and GCP metadata. Across five days it touched four CVEs, all in AI tooling: CVE-2025-3248, CVE-2026-7873 and CVE-2026-51886 in Langflow, and CVE-2026-86124 in AutoAgent.

We don't attribute .82 to the same operator. Its code is different: a def foo() stub, a bare exception with no |RAISE| marker, and the default python-requests/2.34.2 user agent where .84 spoofed Chrome. Neither Microsoft nor Todyl names it. But the convergence on targeting is the point. Exposed AI tooling has become a front door to cloud identities, and several actors are working through it with the same checklist.

Indicators and Fingerprints

Microsoft points out that agentic frameworks swap IPs and user agents quickly. Code that an agent writes fresh for each session changes just as fast, so expect the strings below to change too. Hunt on the behaviour.

TypeIndicatorNotes
IP199.19.77.84Seen in our canaries and in Microsoft's, Todyl's and Huntress's reporting
IP199.19.77.18Microsoft, Tor exit node, may include unrelated traffic
IP65.87.223.26Microsoft, vulnerability exploitation and App Service token attempts
IP157.22.126.128Microsoft, recon of Azure AI workloads

Fingerprints:

TypeIndicatorConfidenceNotes
Request body|RAISE| exception prefix, def _lf_probe():Medium (retro-hunt)Unique to this actor in our data, but likely generated per session and unlikely to persist
BehaviourMost requests sent twice, usually less than a second apart; staged validate/code POSTs escalating from env dump to database decryptionMediumSurvives IP, user-agent and code changes better than strings

Related activity (not attributed to this actor):

TypeIndicatorNotes
IP179.43.191.82Langflow credential theft and Azure managed-identity token attempts, Oct 29 – Nov 2

The CVEs we observed this actor targeting are CVE-2025-3248 (Langflow), CVE-2026-109618 and CVE-2026-109619 (PHP). The related actor also targeted CVE-2026-7873 and CVE-2026-51886 (Langflow) and CVE-2026-86124 (HKUDS AutoAgent).

So What Should Defenders Do?

Microsoft's guidance covers the cloud side: rotate leaked secrets, lock down service principals and hunt sign-ins from these IPs. Our data adds the application side. If you run Langflow or similar AI tooling, assume the keys stored inside it are what attackers are after.

  • Get Langflow off the internet, and patch it. CVE-2025-3248 has been on CISA KEV since May 2025, and attackers are still landing it in November 2026. Put Langflow behind authentication and a VPN or private network.
  • Treat every key in Langflow as exposed if the instance was reachable. That includes variables, flow configs, .env files and the secret_key. Rotate the provider keys (OpenAI, Azure, Gemini, Anthropic, AWS), then rotate the Langflow secret key. Once an attacker has that key, Langflow's encryption no longer protects the rest.
  • Check for squatters. These actors went looking for other attackers' leftovers, so look for them too: /app/data/.kxc, /app/data/.cache/.sync/, /tmp/.kk9, /tmp/.kkx, /tmp/.l.sh, /tmp/.cache/h.py, /tmp/keepx.sh, miner processes and unexpected cron jobs.
  • Watch the provider side. Look for AI API usage from unfamiliar IPs or unusual model calls on keys tied to Langflow. Stolen AI keys often get resold or used to run the attacker's own agents.
  • Close the paths out of the container. Don't mount docker.sock into Langflow, and block the metadata endpoint (169.254.169.254) from app containers where you can. On Azure, scope managed identities tightly. An attacker with code execution in the container can request tokens for whatever the identity can reach.
  • Hunt on behaviour, not just IPs. Look for bursts of POST /api/v1/validate/code with large bodies, error responses carrying JSON blobs, and requests containing @exec(.

VulnCheck Canaries pulled this agent into a 36-minute session against a decoy, which is the kind of deception that helps understand how the attacker works. Every stage it ran gave us intel on its playbook, and none of it touched a real customer.

Two Doors, One Goal

Microsoft saw this actor coming in through leaked GitHub secrets. We saw it coming in through a Langflow exploit. Both routes end in the same place: credentials for AI services and the cloud resources behind them. In layman's terms, AI tooling has become both the target and the tool. Attackers use agents to steal the keys that power other people's agents.

That's the pattern we flagged in our 1H-2026 State of Exploitation report and our last Langflow post. AI platforms are being deployed fast, they're often exposed, and they hold the keys to everything around them. The difference now is speed. When an agent can go from first request to a working decryption method in about half an hour, patch windows measured in weeks don't hold up.

Thanks to the Microsoft Threat Intelligence team for publishing their research. Sharing indicators like these is what helps connect the dots.

Sources

About VulnCheck

VulnCheck is helping organizations not just to solve the vulnerability prioritization challenge - we’re working to help equip any product manager, CSIRT/PSIRT or SecOps team and Threat Hunting team to get faster and more accurate with infinite efficiency using VulnCheck solutions.

We knew that we needed better data, faster across the board, in our industry. So that’s what we deliver to the market. We’re going to continue to deliver key insights on vulnerability management, exploitation and major trends we can extrapolate from our dataset to continuously support practitioners.

Are you interested in learning more? If so, VulnCheck's Exploit & Vulnerability Intelligence has broad threat actor coverage. Register and demo our data today.

Ready to get Started?

Explore VulnCheck, a next-generation Cyber Threat Intelligence platform, which provides exploit and vulnerability intelligence to help you prioritize and remediate vulnerabilities that matter.
  • Vulnerability Prioritization
    Prioritize vulnerabilities that matter based on the threat landscape and defer vulnerabilities that don't.
  • Early Warning System
    Real-time alerting of changes in the vulnerability landscape so that you can take action before the attacks start.