Executive summary
- The tool and its infrastructure: Between 18 September and 2 November 2026, we tracked a tool (Tool A in this post) operating from 43 different IP addresses against the web applications we protect. All of them are Google Cloud addresses; 42 are in the Belgium region (europe-west1).
- The list: Since 8 October every scan uses a fixed 112-path core list, plus a small "theme" that changes in most scans.
- Next to
.env, Git and AWS files, the core includes.claude/settings.json,.env.anthropicand.env.openai. - With the themes, the number of AI-assistant, LLM-platform and MCP paths grows to 32 (44 if other model providers such as AI21 and Mistral and voice/image APIs are counted). They include credential files such as
.claude/credentials.jsonand.openai/credentials, and MCP configurations such as.cursor/mcp.jsonand.vscode/mcp.json.
- Next to
- Apparently centrally coordinated: the same theme arrived from different IPs on the same days (exactly the same set from 7 IPs between 30 October and 2 November), while the same IP brought a different theme on almost every visit (21 of the 22 repeat IPs).
- Speed and returns: Half of the scans finish in under 2.5 seconds, so an IP block cannot stop the scan itself. But the addresses come back: 41 of 83 sessions came from an IP we had already seen.
- What protected us: edge rules, which stopped 69% of the requests, and the fact that there was nothing to find in the web root. No data was exposed in any of the scans we reviewed.
- The most important lesson: our SIEM saw only 11 of the tool's 42 addresses in October–November. We found the rest by searching WAF logs for the core list.
- Not the only tool: in the same weeks we saw a second tool using five different browser identities, and in August a third tool that tried 17 AI-related paths. In 30 days, 205 different IPs looked for secret files on our systems; 54% of them were Google Cloud addresses.
Background
Harvesting keys from exposed files, and then monetizing them, is not new:
- Unit 42 (2024): documented an extortion operation that used keys from exposed
.envfiles on 110,000 domains to get into AWS environments, delete S3 data and leave ransom notes. - Sysdig, EMERALDWHALE (2024): reported an operation that scanned exposed
.git/configfiles and collected more than 15,000 cloud credentials. - Sysdig, "LLMjacking" (2024): showed stolen cloud credentials being used to access AI models while the account owner paid the bill.
Our observations show this trend continuing. At least two separate scanning tools now look for the configuration and credential files of AI tools alongside cloud keys.
How we found Tool A
The first clue came from the SIEM. From late September, Wazuh raised alerts for scans of the same kind with its "many 4xx in a short time" rule. When we noticed a fixed 112-path list behind those alerts, we searched our WAF logs for the same list. In October–November, 11 addresses had produced alerts; 42 had tried the same list.
| Period | IPs | Source |
|---|---|---|
| 18 September | 2 | Alert lines and that day's traffic review note: 35.205.254[.]119 came with Tool A's truncated browser identity; 34.38.113[.]44 looked for .claude, .anthropic and gcloud files |
| 24 September | 1 | 34.62.82[.]165: a much larger list (283 requests according to the review note; a 4xx on 111 different paths, AI paths included) |
| 26 September | 1 | 34.38.113[.]44: a list very close to the October core (24 of the 30 paths that got a 4xx are in the core) |
| 5 October – 2 November | 42 | WAF logs: 83 sessions over 20 days, 100 target scans |
Both addresses in the 18 September row also scanned with the core list in October: 35.205.254[.]119 on 12 and 19 October, 34.38.113[.]44 on 10 and 20 October. We saw 34.62.82[.]165 only on 24 September.
How Tool A works
Fixed core, rotating theme.
- All 93 target scans between 8 October and 2 November contained all 111 non-homepage core paths.
- Most scans also added a mostly related set of 0–10 paths; most often 6.
- Up to the afternoon of 23 October, the 66 scans that had a theme used 51 different sets. Apart from a session's second target receiving the same set, only 3 sets reappeared in another session.
- From the afternoon of 23 October this changed:
/.cursor/mcp.jsonand/.netrc, both first seen that day, were present in all 24 later scans. These trial paths seem to have joined the permanent list.
| First seen | Theme | Example paths |
|---|---|---|
| 5 October | social media and accounting API keys, Vault, CI workflows | /.env.facebook, /xero.json, /.vault.json, /.github/workflows/production.yml |
| 8 October | AI and media APIs, mobile app folders | /.env.claude, /.env.elevenlabs, /.env.stability, /flutter/.env |
| 9 October | monitoring and email services | /.env.betterstack, /.env.uptimerobot, /.env.brevo |
| 10 October | self-hosted CI, hosting platforms | /.drone.yaml, /.woodpecker.yaml, /.netlify/.env, /.vercel/.env |
| 11 October | AI credentials; payments, authentication, email marketing | /.claude/credentials.json, /.openai/credentials, /openai.key, /.env.mercadopago, /.env.frontegg |
| 12 October | AI and observability platforms | /.env.ai21, /.env.writer, /grafana.json, /dynatrace.json |
| 13 October | AI assistants and LLM platforms, Azure | /.claude/auth.json, /.anthropic/credentials.json, /.gemini/settings.json, /langsmith.json, /portkey.json, /.azure/credentials.json |
| 16 October | API key files, GitHub / GitLab tokens | /.api-keys, /.github/token, /.gitlab/credentials |
| 17 October | Elasticsearch / Solr, business apps and content management systems | /_cat/indices, /odoo.conf, /owncloud/config/config.php, /strapi/.env |
| 18 October | cloud development environments, Matrix, Zabbix | /.config/gitpod/credentials, /.coder/credentials, /matrix/homeserver.yaml, /zabbix/conf/zabbix.conf.php |
| 19 October | hosting and fintech platforms | /.env.kinsta, /.env.qovery, /.env.lithic |
| 22 October | MCP, gcloud | /.continue/config.json, /mcp.json, /.config/gcloud/application_default_credentials.json |
| 23 October | MCP and credential files | /.cursor/mcp.json, /.vscode/mcp.json, /.netrc, /.composer/auth.json |
| 30 October | server admin endpoints | /actuator/env, /server-info, /.htaccess |
Themes do not seem to match the target's technology. For example, the payment-provider theme was tried against an application that takes no payments, and the LLM-platform theme against an application that uses no AI.
The theme follows the task, not the IP.
- 21 of the 22 IPs that came more than once brought different themes on different visits. For example, 34.53.184[.]30 tried 8 different themes in 10 target scans.
- The same theme, meanwhile, came from different IPs:
- Between 23 and 28 October, 6 IPs came with exactly the same three extra paths (
/.cursor/mcp.json,/.netrc,/sentry.properties); 4 more IPs added a few paths to that trio. - Between 30 October and 2 November, 7 IPs came with exactly the same set of extras.
- Between 23 and 28 October, 6 IPs came with exactly the same three extra paths (
This pattern suggests worker machines receiving tasks or list updates from a shared controller.
Speed.
- One IP's scan within an hour (a "session" in this post; 83 sessions) took a median of 2.5 seconds.
- 29 sessions finished in under 2 seconds and 63 in under 10 seconds. The longest took 41 seconds.
- 111–128 paths were tried per target.
Identity.
- 97% of the requests came with this truncated browser identity (User-Agent) and an
Accept-Encoding: gzip, deflateheader:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 - A real Chrome ends this string with
(KHTML, like Gecko) Chrome/… Safari/…. - The remaining requests used a full browser identity, most often a two-year-old Chrome 128. These were the home page and page resources.
- In October–November, 53 addresses outside Google Cloud used the same truncated identity; none of them tried more than two thirds of the core. The identity alone is not a sufficient signature.
It reads the page first. The home page was requested in 83 of 100 target scans, and the JavaScript bundles the page loads in 22. The tool is most likely looking for keys left in those files.
Targets. Scans hit both the www and bare form of domain names, and also the raw IP addresses of our load balancers; 13 different names and addresses in total. We do not know how targets are selected.
What it looks for
Breakdown of the 112-path core:
| Category | Paths | Examples |
|---|---|---|
.env files |
49 | /.env, /.env.production, /api/.env, /laravel/.env |
| Application configuration | 22 | /appsettings.Production.json, /secrets.json, /config.php, /phpinfo.php |
| Cloud credentials | 12 | /.aws/credentials, /.boto, /.s3cfg, /aws-exports.js |
| Git | 8 | /.git/config, /.git-credentials, /.github/workflows/deploy.yml |
| Docker and deployment | 7 | /docker-compose.yml, /.docker/config.json, /Dockerfile |
| Backups and databases | 6 | /database.sql, /.env.bak, /wp-config.php.bak |
| AI tools | 4 | /.claude/settings.json, /.claude/settings.local.json, /.env.anthropic, /.env.openai |
| Other | 4 | home page, /phpinfo, /_profiler/phpinfo, /wp-content/debug.log |
The list of paths Tool A tried between 5 October and 2 November is attached to this post: after removing 21 paths specific to our sites, 411 paths remain; 32 belong to AI assistants, LLM platforms and MCP (44 with other AI APIs).
Why AI files
Coding assistants and LLM platforms keep their settings, and sometimes their keys, in plain-text files inside the project folder:
.claude/settings.json,.gemini/settings.json: assistant settings, allowed commands, sometimes environment variables..claude/credentials.json,.anthropic/credentials.json,.openai/credentials,openai.key: as the names suggest, files meant to hold keys or session credentials directly..cursor/mcp.json,.vscode/mcp.json,mcp.json,.continue/config.json: MCP server definitions. These files can contain tokens for databases, GitHub or cloud accounts; a single file can carry keys to several systems at once..env.anthropic,.env.openai,langsmith.json,portkey.json,helicone.json: keys for model providers and LLM observability platforms.
If these files are copied to the web root along with the project folder by mistake, a single GET request can hand someone else the key to your AI account (leaving you with the bill) and the tokens to systems connected through MCP. AI paths sitting in the fixed core, and new ones being tried regularly, show that attackers consider these files worth looking for. Whether they actually find them, we do not know.
A second tool in the same weeks
Between 22 September and 7 October, another tool, also from Google Cloud, hit one of the applications Tool A also scanned (Tool B):
- 12 IPs, all in the US East (us-east1) and Taiwan (asia-east1) regions. It never came from the Belgium region Tool A used.
- Five full browser identities: Chrome 131, Edge 130, Firefox 133, Safari/macOS and Safari/iPhone. The same target received requests with these five identities within a few seconds, and most paths were requested two or three times.
- The same list within each period:
- In each period, the IPs' lists of paths that got a 4xx are exactly the same: 18 paths on 22–24 September, 44 on 25–29 September, 74 on 2–7 October.
- At least part of the difference between periods comes from our side. On 24 September we started returning 404 for paths with extensions too; none of the 26 paths added after 25 September starts with a dot.
- The two IPs whose full list we saw in October's WAF logs tried 132 different paths. That list contained only 36 of the 112 paths in Tool A's core (32%).
- No AI paths. Its list is mostly developer and server files such as
.bash_history,.pgpass,.pypirc,.vault-tokenand.kube/config.
We do not know whether the two tools belong to the same person or group.
These are not the only tools
The two tools are part of a much broader scanning picture in our logs:
- Between 3 October and 2 November, 205 different IPs tried at least 10 different sensitive paths on our systems. 110 of them (54%) were Google Cloud addresses; 42 were Tool A. 92% of these IPs' requests to the paths in the query below were stopped at the WAF.
- In August, a third tool, also from Google Cloud addresses, did the same job with path-encoding tricks (
/%2eenv,..%252F..%252F.env,/proc/self/environ):- It presented itself as more than 20 different bots, including ChatGPT-User, GPTBot, OAI-SearchBot, ClaudeBot, Claude-User, PerplexityBot, Amazonbot and Meta's crawler.
- It even used the Google-Extended identity, which is only a robots.txt token; no real request ever arrives with it.
- Each of its five IPs tried the same 17 AI paths:
/.cursor/mcp.json,/.mcp.json,/.claude.json,/claude_desktop_config.json,/.codex/config.toml,/.aider.conf.yml,/.continue/config.jsonand others.
- Another tool on 2 November changed identity on almost every request (158 different identities in 187 requests). Some of them imitated internet scanning services such as Censys, LeakIX, ZoomEye, Shodan and Palo Alto Expanse.
Lesson: real bots do not try hundreds of secret-file paths in a row. Before trusting a bot identity, verify it against the provider's published IP lists or with reverse DNS.
What worked for us, and what did not
What worked:
- Edge rules.
- Tool A's 42 IPs sent 11,962 requests in October–November; 8,274 of them (69%) were stopped at the WAF.
- 4,191 of the blocked requests were stopped by our own rule for path segments that start with a dot and known secret-file paths.
- Nothing to find.
- Requests that passed the WAF received a 404 or the empty application shell.
- During incident reviews, we confirmed by size and MD5 hash that the 200 responses were the home page.
- We also scanned our JavaScript bundles for keys separately.
What did not work, or came too late:
- SIEM visibility.
- Only 12 of the 83 sessions in October–November produced a Wazuh alert. 31 of the tool's 42 addresses never reached the SIEM in October–November.
- These sessions either hit targets whose web logs we were not feeding into the SIEM at the time, or never reached the server because the WAF stopped every request.
- Lesson: feed your WAF logs into the SIEM too.
- IP blocking.
- Half of the scans are over in less than 2.5 seconds; however fast the alert, decision and block chain is, that scan is usually done already.
- Behind an application-layer load balancer such as ALB, a local firewall rule on the server does not stop the traffic: packets come from the load balancer, not from the client. If your server does not log the real client IP, it is worse: the automated block can block the load balancer's own address and cause an outage.
- A block does not stop that scan, but it can stop the returns: 41 of the 83 sessions came from an IP we had already seen.
- Assuming we had blocked each IP after its scan:
- A 30-minute block (the duration of our server rule) would have stopped none of them.
- A 24-hour block would have cut 16.
- A 7-day WAF IP list would have cut 31.
- Had we tied the block to SIEM alerts only, a 7-day list would have cut 7 sessions, not 31. Trigger the block from WAF logs.
- IP reputation lists, partly. AWS's managed IP reputation list recognized only 16 of the tool's 42 addresses (26% of blocked requests). Freshly rented cloud IPs can reach these lists late.
- "200 for everything" (the SPA trap).
- In mid-September we returned the home page with status 200 for every unknown path.
- In that state the scanning tool may think it "found" something, and the SIEM raises no alert because it never sees an error.
- Once we started returning 404 for paths with extensions and paths starting with a dot, the scans became visible in the SIEM.
What you can do in your own environment
A 30-second self-test
SITE=https://yoursite.com
for path in / /this-path-does-not-exist /.env /.git/config /.aws/credentials /.claude/settings.json /.cursor/mcp.json /docker-compose.yml /Dockerfile; do
printf '%-28s %s\n' "$path" "$(curl -s -o /dev/null -w '%{http_code} %{size_download} bytes' "$SITE$path")"
done
- The first two lines are baselines: the home page and a path that does not exist. All sensitive paths should return 404 (or 403). If you get a 200, compare the size with the first two lines: if it is the same, you are in the SPA trap; if it differs, the file may really be public, so remove it immediately and rotate the keys inside.
- Run the test against your server's raw IP address (with
curl -k) and the default virtual host as well. Tool A scanned those too.
nginx
We tested the configuration below on nginx 1.27.5. All 24 sensitive paths we tried returned 404. The application's own pages and API were not affected.
server {
# ...
# ACME challenge: 404 if the file does not exist (never fall through to the SPA)
location ^~ /.well-known/acme-challenge/ { try_files $uri =404; }
# API: the ^~ modifier stops the regex rules below from being tried, so sensitive paths are blocked here separately
location ^~ /api/ {
location ~ /\. { return 404; }
location ~* "^/api/(.*/)?(actuator|_profiler|phpinfo|server-info|server-status)" { return 404; }
proxy_pass http://app;
}
# Any path with a segment that starts with a dot returns 404 (only /.well-known/ is allowed)
location ~ /\.(?!well-known/) { return 404; }
# Known sensitive names without an extension, and admin endpoints
location ~* "^/(dockerfile|jenkinsfile|procfile|makefile|credentials|env|id_rsa|id_ed25519|kubeconfig|phpinfo|server-info|server-status|actuator|_profiler|_cat|_cluster|_nodes|_users|_config|solr)(/|$)" { return 404; }
# Paths with an extension: served if the file really exists, otherwise 404 (never index.html)
location ~* "\.[a-z0-9]{1,8}$" { try_files $uri =404; }
# Everything else goes to the SPA
location / { try_files $uri /index.html; }
}
Before you use it:
- This configuration is for an SPA whose files are served from disk. If the whole application is proxied to a backend (Next.js, .NET, Node), the extension rule can break static files; in that case use only the dot and sensitive-name rules.
- SPA routes that contain a dot (
/users/john.doe,/docs/v1.2) will also get a 404. - If your load balancer's health check uses an endpoint these rules block, such as
/actuator/health, open it first withlocation = /actuator/health { … }; an exact match wins over regular expressions. The same applies to monitoring endpoints such asserver-status. - Put regular expressions that contain curly braces or semicolons in quotes; otherwise nginx will not load the configuration (
nginx -tfails).
AWS WAF
A rule that catches, at the edge, requests with a path segment starting with a dot (except /.well-known/). It passed AWS's capacity check (32 WCU).
{
"Name": "secret-file-paths",
"Priority": 10,
"Statement": { "AndStatement": { "Statements": [
{ "ByteMatchStatement": { "SearchString": "/.", "FieldToMatch": { "UriPath": {} },
"TextTransformations": [ { "Priority": 0, "Type": "URL_DECODE" }, { "Priority": 1, "Type": "NORMALIZE_PATH" } ],
"PositionalConstraint": "CONTAINS" } },
{ "NotStatement": { "Statement": { "ByteMatchStatement": { "SearchString": "/.well-known/", "FieldToMatch": { "UriPath": {} },
"TextTransformations": [ { "Priority": 0, "Type": "URL_DECODE" }, { "Priority": 1, "Type": "NORMALIZE_PATH" } ],
"PositionalConstraint": "STARTS_WITH" } } } }
] } },
"Action": { "Count": {} },
"VisibilityConfig": { "SampledRequestsEnabled": true, "CloudWatchMetricsEnabled": true, "MetricName": "secret-file-paths" }
}
- Run it in Count mode for a few days first; switch it to Block once you are sure it does not catch a legitimate path.
update-web-aclreplaces the whole rule list. Fetch the current rules withget-web-aclfirst, add this rule, and send it with theLockToken. Make sure thePriorityvalue does not clash with an existing rule.SearchStringis a binary (blob) field; to pass plain text to AWS CLI v2, add the--cli-binary-format raw-in-base64-outparameter.
Search your WAF logs for Tool A (CloudWatch Logs Insights)
This query uses 10 paths from the core list that are rare in other scanning tools. Over a month of traffic in our own logs, it returned only Tool A's 42 IPs: each of them tried all 10 paths, and no other IP reached 6.
fields httpRequest.clientIp as ip, httpRequest.uri as uri
| filter uri in ["/.claude/settings.local.json", "/.env.anthropic", "/.env.openai", "/.amplifyrc", "/aws-exports.js", "/sendgrid.env", "/runtime-config.js", "/env-config.js", "/.docker/laravel/app/.env", "/config/aws.yml"]
| stats count_distinct(uri) as signature_paths, count(*) as requests, min(@timestamp) as first_seen, max(@timestamp) as last_seen by ip
| filter signature_paths >= 6
| sort first_seen asc
A second query to see secret-file scanning in general. In our logs it returned 205 IPs: Tool A's 42, the 2 Tool B IPs with WAF records, and 161 other addresses.
fields @timestamp, httpRequest.clientIp as ip, httpRequest.uri as uri, action
| filter uri like /(?i)\/(\.|%2e)env|\/(\.|%2e)git\/config|\/(\.|%2e)aws\/credentials|\/(\.|%2e)claude\/settings|\/(\.|%2e)cursor\/mcp\.json/
| stats count_distinct(uri) as distinct_paths, count(*) as requests, sum(action = "BLOCK") as blocked, min(@timestamp) as first_seen, max(@timestamp) as last_seen by ip
| filter distinct_paths >= 10
| sort requests desc
- If
blockedis lower thanrequests, some of that IP's requests reached your application; check your application logs for the responses returned to those requests. - The queries work if your WAF logs go to CloudWatch Logs; if they go to S3, build the same logic in Athena. If a logging filter limits WAF logs to blocked requests only, the counts will be incomplete.
Wazuh
- Wazuh's built-in rule 31151 (many 4xx from the same source in a short time;
frequency="14"within 90 seconds in the rule definition) catches these scans, but only if your web server really returns 404 for unknown paths (see the SPA trap). - Write blocks to a WAF IP set shared by all your web ACLs and keep them for at least a few days. Trigger them from WAF logs, because not every scan reaches the SIEM.
On the development side
- Put only the build output (
dist/,public/) in the web root, never the project folder. Keep.claude/,.cursor/,.vscode/,.gemini/,.git/and.env*out of your deployment package entirely. - Add
.env*files and personal settings files such as.claude/settings.local.jsonto.gitignore. A shared.claude/settings.jsoncan stay in the repository, but it must not contain keys. - Keep keys in a secrets manager, not in configuration files. In MCP configurations, pass tokens through environment variables instead of writing them into the file.
- Scan front-end bundles for keys during the build.
- If you suspect a key was exposed, rotate the key first, then look at the logs.
Indicators
Cloud IPs change hands often. Instead of blocking these addresses permanently, we recommend turning the behavioral signature below into a detection rule that runs on WAF or edge logs.
Tool A's behavioral signature:
- More than 110 different secret-file paths from the same source in less than a minute, including the 10 paths of the signature query above.
- The truncated identity
Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36withAccept-Encoding: gzip, deflateon secret-file requests. Other tools use this identity too, so it is not sufficient on its own.
Tool A's IP addresses (43 addresses; defanged with [.]; dates in UTC; regions verified against Google's published IP ranges on 2 November 2026):
IP first seen last seen region
35.205.254[.]119 2026-09-18 2026-10-19 europe-west1
34.38.113[.]44 2026-09-18 2026-10-20 europe-west1
34.62.82[.]165 2026-09-24 2026-09-24 europe-west1
34.52.229[.]253 2026-10-05 2026-10-05 europe-west1
34.77.137[.]207 2026-10-05 2026-10-05 europe-west1
34.78.213[.]249 2026-10-05 2026-10-05 europe-west1
34.140.234[.]80 2026-10-05 2026-10-08 europe-west1
35.189.229[.]134 2026-10-05 2026-10-13 europe-west1
34.140.132[.]132 2026-10-08 2026-10-11 europe-west1
34.79.51[.]100 2026-10-08 2026-10-22 europe-west1
34.52.133[.]111 2026-10-09 2026-10-09 europe-west1
35.205.88[.]64 2026-10-09 2026-10-24 europe-west1
34.156.249[.]47 2026-10-10 2026-10-10 europe-west1
34.52.150[.]124 2026-10-10 2026-10-10 europe-west1
34.62.189[.]80 2026-10-10 2026-10-20 europe-west1
35.241.178[.]74 2026-10-11 2026-10-11 europe-west1
207.175.194[.]102 2026-10-11 2026-10-18 europe-west1
207.175.220[.]59 2026-10-11 2026-10-24 europe-west1
34.53.184[.]30 2026-10-12 2026-10-30 europe-west1
34.79.12[.]228 2026-10-13 2026-10-23 europe-west1
35.240.100[.]200 2026-10-13 2026-10-24 europe-west1
34.156.22[.]151 2026-10-16 2026-10-17 europe-west1
34.62.250[.]35 2026-10-16 2026-10-17 europe-west1
136.65.74[.]74 2026-10-17 2026-10-17 us-central1
34.14.99[.]143 2026-10-17 2026-10-24 europe-west1
35.241.239[.]86 2026-10-18 2026-10-23 europe-west1
34.62.25[.]61 2026-10-18 2026-10-27 europe-west1
34.22.126[.]178 2026-10-19 2026-10-19 europe-west1
34.34.160[.]116 2026-10-19 2026-10-19 europe-west1
34.53.212[.]113 2026-10-19 2026-10-27 europe-west1
35.241.202[.]92 2026-10-20 2026-10-20 europe-west1
34.79.59[.]9 2026-10-20 2026-10-22 europe-west1
207.175.98[.]108 2026-10-22 2026-10-22 europe-west1
34.62.18[.]205 2026-10-22 2026-10-22 europe-west1
34.53.227[.]51 2026-10-22 2026-10-23 europe-west1
34.140.179[.]23 2026-10-23 2026-10-23 europe-west1
104.199.36[.]53 2026-10-28 2026-10-28 europe-west1
34.62.116[.]145 2026-10-30 2026-10-30 europe-west1
34.52.234[.]95 2026-11-01 2026-11-01 europe-west1
34.77.241[.]117 2026-11-01 2026-11-01 europe-west1
23.251.135[.]142 2026-11-02 2026-11-02 europe-west1
34.62.246[.]24 2026-11-02 2026-11-02 europe-west1
34.76.27[.]30 2026-11-02 2026-11-02 europe-west1
Tool B's IP addresses (22 September – 7 October; all from SIEM records, two also with WAF records):
| Period | IPs | Regions |
|---|---|---|
| 22–24 September | 34.75.172[.]231 · 34.80.89[.]28 · 104.155.231[.]26 | us-east1, asia-east1 |
| 25–29 September | 35.231.170[.]94 · 34.81.122[.]163 · 34.138.59[.]13 · 34.23.9[.]107 · 35.201.136[.]76 | us-east1, asia-east1 |
| 2–7 October | 35.190.129[.]55 · 34.80.69[.]73 · 34.138.20[.]242 · 136.108.44[.]83 | us-east1, asia-east1 |
Downloads:
MITRE ATT&CK mapping
| Technique | Observation |
|---|---|
| T1595.003 — Active Scanning: Wordlist Scanning | secret-file scanning with a fixed core plus a rotating theme |
| T1594 — Search Victim-Owned Websites | downloading the home page and JavaScript bundles |
| T1589.001 — Gather Victim Identity Information: Credentials | attempt to collect exposed credentials |
| T1552.001 — Unsecured Credentials: Credentials In Files | the targeted files; had a file been found, this is the technique that would follow |
Every scan we observed stayed at the attempt stage. Wazuh tags rule 31151 as T1595.002.
Method, data sources and limitations
Definitions:
- Session: one IP's requests within one hour.
- Target scan: the part of a session that goes to a single name or address. The 83 sessions give 100 target scans.
Data sources:
- AWS WAF logs, 3 October – 2 November 2026 (up to 22:40 UTC). For one of the applications, also 4 August – 3 September.
- Wazuh alerts in our SOC data lake, August – November. These alerts include the raw log lines (path and browser identity) of requests that received a 4xx.
- Our incident review notes.
- We have no WAF logs for 3 September – 3 October. September details come from alert lines and review notes.
How we assigned an IP to a tool:
- Tool A:
- In the period with WAF logs, trying at least 105 of the 111 non-homepage core paths. Of the 95 addresses using the truncated identity, 42 met this; the next one tried only 70.
- In September (no WAF logs), using only the truncated identity in alert lines and largely containing the October core paths that reach the server: 83% for 34.38.113[.]44's 26 September scan, 69% for 34.62.82[.]165.
- Tool B:
- It uses the same five full identities.
- In each period, the IPs' lists of paths that got a 4xx are exactly the same.
- Kept separate: addresses that look similar but do not meet these criteria, for example two scans on 31 October and 2 November (42–43% of the core).
Definition of the core: paths seen in at least 70% of Tool A's 100 target scans. The list does not change for any threshold between 50% and 80%.
Reading notes:
- Alert counts only cover requests that passed the WAF and got a 4xx, so they vary with the target and its WAF configuration.
- "First seen" is the day a path first appeared in Tool A's records. Other tools may have tried the same path earlier; for example,
/.cursor/mcp.jsonwas also in the third tool's list in August. - On 22 August, a single line from an internal address whose real client we do not know also carries the same truncated identity. So 18 September is the earliest trace we can tie to an IP address, not a definite start.
- We do not know who runs these scans; this post makes no attribution claim. These addresses may belong to virtual machines rented by the attacker. This is not a Google Cloud vulnerability.
References
- Unit 42 (Palo Alto Networks), "Leaked Environment Variables Allow Large-Scale Extortion Operation in Cloud Environments", August 2024 — https://unit42.paloaltonetworks.com/large-scale-cloud-extortion-operation/
- Sysdig, "EMERALDWHALE: 15k Cloud credentials stolen in operation targeting exposed Git config files", October 2024 — https://sysdig.com/blog/emeraldwhale/
- Sysdig, "LLMjacking: Stolen Cloud Credentials Used in New AI Attack", May 2024 — https://sysdig.com/blog/llmjacking-stolen-cloud-credentials-used-in-new-ai-attack/
- Google Cloud IP ranges — https://www.gstatic.com/ipranges/cloud.json
- MITRE ATT&CK — T1595.003, T1594, T1589.001, T1552.001
This post is based on observations of real traffic in the SOC operated by 113SEC. For questions, or to share what you see in your own logs: info@113sec.com