# AI crawler user-agents are being spoofed to reach cloud metadata endpoints

> Source: <https://honeylabs.net/blog/spoofed-ai-crawlers-one-client>
> Published: 2026-09-08 05:44:31+00:00

[Blog](/blog) ·  · HoneyLabs

# 26 machines using 42,321 user-agents to spoof AI crawlers

The scanners spoofing AI crawler user-agents to hunt credentials turned out to be one HTTP client running on 26 addresses. On 25 August it switched from nine fixed strings to a generator that has produced 6,648 different spellings of Claude-User alone, which breaks every allow-list that matches on the name.

On 25 August one of our sensors started seeing a lot of Claude.

However, not one Claude crawler, but four thousand nine hundred and six of them, in a single day, each announcing itself with a slightly different user-agent string. On every previous day we had seen this client, going back to 11 August, there had been nine.

Nine is around what you'd expect, as there are only so many AI crawlers and each of them uses a settled user-agent. What arrived on 25 August was a generator, and following it back turned out to be worth more than the generator itself.

## One client, 42,321 names

Every one of those strings carries the same JA4H fingerprint, [`ge11nn05en_813e32c09d15`](/ja4h/ge11nn05en_813e32c09d15), which is computed from the shape of an HTTP request instead of its contents. The fields are the method, the HTTP version, whether a Cookie and a Referer are present, how many other headers there are, the first two characters of Accept-Language, and a hash of the header names in the order they arrived. Note that the count excludes Cookie and Referer, which is why the two POST profiles below carry eight and ten headers but encode `07` and `09`. It describes the client library, so it survives precisely the thing this operator spends its effort on.

Between 2 August and 6 September that fingerprint sent 105,438 requests from 26 addresses, 25 of them on Google Cloud ([AS396982](/asn/396982)), carrying 42,321 distinct user-agent strings between them. It is also most of the category, since 121,540 requests across our whole dataset have claimed to be an AI crawler and 86.8 percent of them carry this one fingerprint.

## Building a spoofed string

The generator grafts a real crawler token onto a randomly generated browser string, so that four of the 6,648 Claude-User variants share only their middle:

```
Mozilla/5.0 (X11; Linux x86_64; rv:143.11) Gecko/20100101 Firefox/143.11;
  compatible; Claude-User/1.0; +Claude-User@anthropic.com
Mozilla/5.0 (Windows NT 10.0; rv:132.12) Gecko/20100101 Firefox/132.12;
  compatible; Claude-User/1.0; +https://www.anthropic.com/claude-user
Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko); compatible;
  Claude-User/1.0; +Claude-User@anthropic.com
Mozilla/5.0 (iPhone; CPU iPhone OS 18_4 like Mac OS X)
  AppleWebKit/605.1.15 (KHTML, like Gecko; compatible; Claude-User/1.0;
  +https://www.anthropic.com/claude-user) Version/17.0 Mobile/15E148 Safari/604.1
```

Three things vary independently, being the browser prefix, the placement of the parenthesis and semicolon around `compatible`, and which of two contact suffixes follows the token, while the crawler token itself is left alone because the token is the part that has to be recognised.

Anthropic does not publish a user-agent string for these at all. It documents three bot *names*, which is what a `robots.txt` line matches on, and points site owners at [a published list of its crawler addresses](https://claude.com/crawling/bots.json) for verification. One name, and we logged 6,648 different strings carrying it.

The Firefox prefixes carry 200 distinct version strings, of which 190 have a minor component that is not `.0`, among them `78.10`, `105.9`, `121.18`, `143.11` and `149.9`. Firefox never puts a minor version there, since point releases like 152.0.6 still send `Firefox/152.0`, so a `.10` or a `.18` is already wrong.

The `rv:` token is the better tell, and it is the one the generator gets exactly backwards. Mozilla [froze `rv:` at `109.0`](https://bugzilla.mozilla.org/show_bug.cgi?id=1805967) for Firefox 110 and later, because sites checking for `rv:11` were misreading `rv:110` as Internet Explorer 11. A genuine Firefox 143 therefore announces itself as `rv:109.0) Gecko/20100101 Firefox/143.0`, with the two numbers deliberately disagreeing. This generator makes them agree, which is what Firefox did until three years ago. Of the 6,926 Firefox-flavoured variants here, not one carries `rv:109.0`, and all 5,521 that claim Firefox 110 or newer have an `rv:` that a real build of that version would never send.

## Following the fingerprint back

A fingerprint only tells you where to look next. The useful move is to follow it sideways, taking the addresses that produced it and asking what every other fingerprint those same addresses emit looks like. Doing that turns one client into something considerably less tidy.

Those addresses run four distinct client profiles, and the address sets of the other three sit almost entirely inside the first: 24 of 24 for the metadata module, 16 of 16 for the JSON-bodied one, and 23 of 24 for the GraphQL module, whose single outlier is an Alibaba Cloud host that sent two requests. That degree of overlap is not something chance arranges. All four spoof AI crawler names, between 62 and 68 percent of their requests each, so every part of the toolkit wears the same disguise.

| Profile | Shape | First seen on these addresses | What it does | 
|---|---|---|---|
| `ge11nn05en_813e32c09d15` | GET, 5 headers | 11 Aug | credential and config files | 
| `ge11nn0600_3e3d7a122305` | GET, 6 headers | 16 Aug | SSRF at cloud metadata | 
| `po11nr0700_7963de0d52cd` | POST, 8 headers | 20 Aug | GraphQL introspection | 
| `po11nr0900_e84d48e0c8fb` | POST, 10 headers | 28 Aug | SSRF with a JSON body | 

Read down the date column and you are watching someone build a tool in production, against the whole internet, one module a week. Those dates are when each profile first appeared on this address set rather than when the fingerprint first existed anywhere, since two of the four are shapes other software also produces and go back months in our data on unrelated hosts.

## What the quieter modules are for

The credential-file module is the loud one and the least dangerous. The two SSRF modules are the point of the exercise, and they sweep a matrix rather than guessing: fourteen endpoints that a preview, screenshot or webhook service might expose, crossed with seven names for the parameter that takes a URL.

```
endpoints   /fetch /proxy /redirect /image /preview /screenshot /webhook
            /api/fetch /api/proxy /api/image /api/preview /api/download
            /api/webhook /api/v1/fetch
parameters  url path dest uri target src file
```

Every combination is pointed at the same three places, which are the AWS instance metadata service at `169.254.169.254/latest/meta-data/iam/security-credentials/`, then `file:///root/.aws/credentials` and `file:///proc/self/environ` through the identical parameter when the fetcher will accept a `file://` scheme. The POST variant carries the same targets as JSON:

```
{"url":"http://169.254.169.254/latest/meta-data/iam/security-credentials/"}   867
{"url":"file:///root/.aws/credentials"}                                       214
{"url":"file:///proc/self/environ"}                                           213
```

The GraphQL module asks every endpoint it can think of for a full schema dump, using the standard introspection query against `/graphql`, `/v1/graphql`, `/api/graphql` and `/graphql/console`, which is reconnaissance for whatever comes after.

So the AI crawler names are the costume. Underneath is a cloud credential thief that would quite like to read your instance metadata, and it wears the costume because a growing number of sites now treat a crawler name as a reason to be more permissive.

## What gives the whole thing away

Reading the raw headers produces two signatures that are cheaper and far more reliable than counting Firefox versions.

The first is that the `Host` header carries a bare IP address in 167,777 of the 167,780 requests this fingerprint sent us, with only three naming a hostname. A real crawler arrives because it followed a link and therefore always has a hostname, whereas this arrives because it is walking address space.

The second has no benign explanation at all. Two of the four profiles send `Metadata-Flavor: Google` and `Metadata: true` on every request, 21,959 times from 24 addresses, including to ordinary web servers that have nothing to do with cloud metadata. Those headers exist to satisfy the GCP and Azure metadata endpoints, and the tool sets them globally in the hope that something downstream forwards them. Nothing legitimate sends them to your webserver.

The header set itself is assembled from mismatched parts. `Accept-Encoding: gzip` alone is what Go's standard library sends when the caller sets nothing, the `Accept` value is the long-standing Firefox form, and the `Accept-Language` of `en-US,en;q=0.9` is the Chrome default rather than the `q=0.5` Firefox uses. No single browser produces that combination, and the same five header keys arrive in the same order across all 167,780 requests.

## Prior reporting

GreyNoise [documented this behaviour on 28 August](https://www.greynoise.io/blog/threat-actors-posing-as-ai-crawlers). Between 28 July and 23 August they observed six spoofed crawler names belonging to four companies arriving on one HTTP client fingerprint, from 824 addresses spread across 795 separate /24 networks, and noted that the fingerprint identifies the software making the requests rather than the machine running it. They recorded that `/robots.txt` was never requested under those six names, and measured Anthropic's real crawler over the same window as a control, where `/robots.txt` was the most requested path at 12 percent of its traffic and no credential file was requested at all.

Our window runs later and our vantage point is narrower. What is new here is the rest of the operator's toolkit, the structure of the user-agent generator, and what changed after 23 August.

Through 23 August this client used nine fixed strings, which is the period their measurement covers. Two days later it began producing thousands a day, so the 6,648 spellings of Claude-User above belong to a later stage of the operation than the traffic in their report.

## Failing matching strategies

Every current scheme for controlling AI crawlers keys on the user-agent string, whether that is a `robots.txt` directive naming an agent, Cloudflare's AI crawler controls, the various pay-per-crawl proposals, or a hand-written nginx rule.

Against this generator both matching strategies fail in opposite directions, because exact matching catches only the two well-formed spellings in our data and misses the other 6,646 Claude-User variants, while substring matching on `Claude-User` catches all of them and then grants the spoofer whatever the real crawler was going to get, which for a growing number of sites is a lighter rate limit or a pass through the bot gate.

The check that does hold is forward-confirmed reverse DNS, where the address has to resolve to a hostname under a domain the operator controls and that hostname has to resolve back to the same address. It is the method Google and Bing both document, and it is the only test discussed here that cannot be satisfied by editing a string, because it asks a question of the address rather than of the client.

## Limitations

Our sensors are honeypots that host nothing a legitimate crawler would want, so the absence of real AI crawler traffic in this data is expected and says nothing about the ratio on a normal website, and what our vantage point supports is the shape of the spoofed traffic rather than its share of anyone else's logs.

The fingerprint is also a weaker signal on its own than it looks, since `ge11nn05en` describes a GET over HTTP/1.1 with five headers, no cookie and an Accept-Language, which other tools produce too. What makes this cluster coherent is the combination of one fingerprint family, one network, one wordlist, a user-agent generator with a recognisable grammar, and four profiles whose address sets nest almost perfectly, at 24 of 24, 23 of 24 and 16 of 16.

Two further fingerprints appear from these addresses and are deliberately excluded here, because they are generic shapes that millions of unrelated requests share and only a couple of hundred of ours came from this operator. And 26 addresses is only our slice, since we see what reaches our own sensors and nothing else.

## For your logs

Four checks, in order of how cheap they are to run.

Anything sending `Metadata-Flavor: Google` or `Metadata: true` to your webserver is hostile, with no exceptions worth carving out.

A `Host` header containing a bare IP address, from a client claiming to be a major crawler, means the client found you by scanning rather than by following a link.

Any user-agent claiming `Firefox/110` or newer whose `rv:` is not `109.0` is not Firefox, and a `Firefox/<major>.<minor>` with a non-zero minor is not Firefox either. Both hold whatever crawler name is bolted on beside them.

Anything claiming to be a major crawler that has never requested `/robots.txt` is not crawling, whatever else it may be doing, which GreyNoise also recorded, and which holds across our 105,438 requests.

Then verify by address rather than by string. We checked all 26 of these addresses against every published crawler range we could find, which is Anthropic's list, OpenAI's three for GPTBot, ChatGPT-User and OAI-SearchBot, Google's three, and Perplexity's two. That comes to 1,136 IPv4 prefixes, and not one of the 26 appears in any of them. Amazon, ByteDance and xAI publish no machine-readable range file that we could locate, so the Amazonbot, Amzn-SearchBot, Bytespider and GrokBot names cannot be settled the same way; those requests come from Google Cloud, which is not where those crawlers run, but that is circumstantial rather than a list check.

Do not shortcut any of this into blocking the network. 18 of Anthropic's own 26 prefixes sit on Google Cloud, the same place these requests come from, so an operator who blocks AS396982 loses the real crawler and keeps the problem. The address either appears on the vendor's list or it does not, and forward-confirmed reverse DNS covers the crawlers that publish no list.

The live fingerprint page is at [`ge11nn05en_813e32c09d15`](/ja4h/ge11nn05en_813e32c09d15) and updates as the client moves. [Method and redaction](/methodology).
