AI crawler user-agents are being spoofed to reach cloud metadata endpoints HoneyLabs reported that a single HTTP client operating from 26 IP addresses, 25 of which are on Google Cloud (AS396982), spoofed AI crawler user-agents to probe cloud metadata endpoints, sending 105,438 requests between 2 August and 6 September with 42,321 distinct user-agent strings. The client, identified by JA4H fingerprint ge11nn05en_813e32c09d15, accounted for 86.8% of all AI-crawler-claimed requests in HoneyLabs' dataset, and on 25 August it began generating 6,648 different spellings of Claude-User alone, breaking allow-lists that match on the name. 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/