An AWS access key starts with the four letters AKIA. They are the long-lived credentials AWS issues to IAM users. When one of them leaks — into a .env file, into a git history, into an S3 bucket left public — anyone who finds the key can act as that IAM user until it is rotated. Adversaries who specialize in this kind of theft (the practice that Sysdig and Permiso both call LLMjacking when the motive is to run AI inference for free) have built end-to-end pipelines: scan the internet for the leaked file, validate the key, escalate inside the victim’s account, persist.
This post documents one of those pipelines in motion. Across 100 minutes on May 3, 2026, an attacker harvested two AWS keys from one of our honeypots, validated both against AWS, paused, and then returned with seven coordinated Hong Kong VPS hosts running the AWS IAM persistence chain GetCallerIdentity → AttachUserPolicy → CreateUser → CreateAccessKey. The keys were Thinkst Canarytokens — fake credentials wired to a tripwire AWS account — so every IAM call against them produced a webhook with the attacker’s source IP, ASN, geolocation, and exact API event at millisecond precision. The persistence operations almost certainly returned AccessDenied at AWS. We observed intent, not compromise.
What makes the event worth a post is the seam in the middle. One of the seven Hong Kong IPs that appeared in the persistence phase, 103.116.72.119, had been publicly named eleven days earlier — by Sysdig’s analysis of CVE-2026-33626, an LMDeploy SSRF that the same IP used to pull live IAM credentials out of GPU-instance metadata. Same IP, different vector, eleven days apart. The connective tissue is the AWS credential pipeline.
What our honeypot captured
At 05:35:35 UTC on 3 May 2026, a single source IP hit our Frankfurt sensor and pulled two AWS access keys (one from an MCP-platform lure, one from an Open-WebUI lure) using a 50-path secrets walk we hadn’t seen before. Two minutes later it validated both against STS. Then a deliberate 97-minute pause. At 07:15:03 UTC, eight IAM events fired across both keys in 11.3 seconds, this time from seven Hong Kong VPS hosts.
We had end-to-end visibility because Beelzebub logs full HTTP bodies and Canarytokens forwards every IAM call as a structured webhook. No gaps in the timeline.
Phase 1: Harvest
The probe came from 195.170.172[.]108 (AS41608, NextGenWebs S.L., Lelystad, Netherlands). The host has 49 prior pulses on AlienVault OTX and an AbuseIPDB confidence of 100/100 across 203 reports, almost all tagging it as a port scanner. None describe what we caught.
Every session opened with one request:
GET /awsc_probe_f4499ac03cf864ff446618a5
GET /awsc_probe_e38a6b80ec039f4a9d114884A 24-character hex string (12 bytes), unique per target, sent before anything else. Google and GitHub code search both come up empty for the pattern. It looks like a correlation token. The operator’s backend uses it to match harvested AKIAs back to specific victims without parsing response bodies, which suggests a database-backed scanning pipeline.
Then the burst: ~320 requests per second on one lure surface, ~690 on the other. Pre-compiled wordlist, single concurrent dispatch. Below is one of the two captured walks. The other shares 44 of these paths and swaps five long-tail variants, which reads as a small randomization layer rather than a different scanner.
/ /awsc_probe_<24-hex-nonce>
/settings-local.yaml /.env.backup
/.env.prod /.env.development
/config.toml /.aws/config
/.aws/credentials /aws/credentials
/.boto /home/ubuntu/.aws/credentials
/root/.aws/credentials /.env
/values.yml /aws/config
/.aws/sso/cache/ /.aws/boto/config
/kubeconfig /home/ec2-user/.aws/credentials
/docker-compose.yml /values.yaml
/.dockerconfigjson /docker-compose.yaml
/config.yaml /config.json
/auth.json /settings.yml
/secrets.yaml /credentials.json
/settings.yaml /config/secrets.yml
/application-prod.yml /actuator/heapdump
/application-prod.properties /settings.json
/secrets.yml /connections.json
/credentials.yaml /credentials.yml
/config.php /application.properties
/application-production.yml /.config/gcloud/application_default_credentials.json
/rclone.conf /.config/gcloud/access_tokens.db
/actuator/env /.aws/cli/cache/
/heapdump /envThe list isn’t fishing, it’s a curated cloud-credential catalog:
- AWS (deep):
.aws/{config, credentials, sso/cache, cli/cache, boto/config}, theubuntu/ec2-user/roothome variants,.boto - GCP:
.config/gcloud/application_default_credentials.json,.config/gcloud/access_tokens.db - Kubernetes / Docker / Helm:
kubeconfig,.dockerconfigjson,docker-compose.{yml,yaml},values.{yml,yaml} - Spring Boot:
actuator/{env, heapdump, configprops},application-prod.{yml, properties},application.properties,heapdump - rclone:
rclone.conf - Generic:
.env*,config.{json, yaml, toml, php},secrets.*,credentials.*,auth.json,connections.json
The absences matter as much as the presences. No .azure/*. No .vault-token. No .netrc. No .git/*. This scanner cares about cloud credentials specifically, not generic config exposure.
Two smaller details. The list includes .aws/sso/cache/, .aws/cli/cache/, and .boto, which are local-machine SDK paths that almost never sit on a webserver. Their presence suggests the wordlist was built from AWS SDK awareness rather than from observed leaks. And the forged User-Agent on the harvest stream is Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36. Chrome 109 shipped in January 2023, which means the operator baked the UA into their binary years ago and never updated it.
Each Canarytoken encoded which lure surface served it, so every later AWS event was attributable to the right key.
Phase 2: Self-validation
About two minutes after the harvest, the same IP changed gears. Five GetCallerIdentity calls in 1.3 seconds: four against the first key, one against the second.
The User-Agent flipped from forged Chrome 109 to bare Go-http-client/1.1. That string is what Go’s net/http package emits when nothing sets a User-Agent. It is not what the AWS SDK for Go produces. The SDK injects its own UA via middleware, in formats like aws-sdk-go-v2/<version> os/<os>. So the scanner is signing STS calls by hand on top of raw HTTP, not going through the SDK. Smaller binary, fewer dependencies, less SDK-specific metadata to fingerprint. A common offensive-Go pattern.
The four-fire burst on one key is the operator iterating across STS regional endpoints, either to fingerprint region-restricted permissions or as a custom retry loop in their signing code. The other key fires once, and the binary moves on.
Then the host went silent for 97 minutes. The keys had been validated; they were now waiting in a queue.
Phase 3: Distribution and escalation
At 07:15:03.328 UTC the keys came back online. Eight IAM events across both AKIAs in 11.3 seconds. Nothing from the original Spanish harvester. Every request came from one of seven Hong Kong VPS hosts spanning six autonomous systems, all using curl/8.7.1.
The event sequence is the canonical AWS persistence chain (MITRE ATT&CK: T1078.004 → T1098.003 → T1136.003 → T1098.001):
GetCallerIdentity → AttachUserPolicy → CreateUser → CreateAccessKey| Time (UTC) | AKIA Source | IP | ASN | Event |
|---|---|---|---|---|
| 07:15:03.328 | HTTP | 156.0.200[.]138 | AS23961 Misaka | CreateUser |
| 07:15:03.889 | HTTP | 222.167.198[.]5 | AS202662 Hytron | CreateAccessKey |
| 07:15:04.398 | MCP | 154.12.177[.]192 | AS906 DMIT | GetCallerIdentity |
| 07:15:04.731 | MCP | 212.107.29[.]70 | AS41378 Kirino | AttachUserPolicy |
| 07:15:05.049 | MCP | 45.202.219[.]39 | AS398704 STACKS | CreateUser |
| 07:15:05.388 | MCP | 222.167.198[.]5 | AS202662 Hytron | CreateAccessKey |
| 07:15:05.743 | HTTP | 103.116.72[.]119 | AS400618 Prime | AttachUserPolicy |
| 07:15:14.662 | HTTP | 154.92.130[.]71 | AS398704 STACKS | GetCallerIdentity |
Two overlaps tell us this is a coordinated pool rather than a string of unrelated hosts:
222.167.198[.]5(Hytron) ranCreateAccessKeyon both keys, 1.5 seconds apart. Same VPS, two AKIAs, parallel chains.- AS398704 (STACKS) shows up as two distinct IPs: one running
CreateUseron the MCP key, the other runningGetCallerIdentityon the HTTP key.
By Phase 3 both AKIAs were sitting in the same regional pool, and the 11.3-second burst is automated dispatch, not a human at a keyboard.
Key insights
The most loaded part of the timeline is the shift from Phase 2 to Phase 3. Go scanner with hand-rolled SigV4 on a Spanish ASN out, seven Hong Kong VPS hosts running curl in. Honeypot telemetry alone doesn’t let us pick between two readings:
- Brokered handoff. The harvester dropped the keys into a shared channel (a vetted forum thread, a Telegram drop, a paid pool) and a coordinated set of HK hosts dequeued them. The tooling change, the infrastructure change, and the ASN-class fan-out all fit.
- Same operator, multi-region with deliberate rotation. One operator runs the scanner from Spain and the post-exploitation from a fleet of cheap HK VPS, switching tooling and IPs on purpose to compartmentalise.
Either way the defender position is the same: both AKIAs reached a coordinated pool that ran a kit-driven IAM persistence chain in 11.3 seconds.
The 97-minute gap is what lets you read the pattern. Public credential markets like Russian Market storefronts and vetted IAB forum threads surface keys hours to days after capture, with vetting and escrow built in. In-binary chaining runs validation and exploitation in the same process within seconds. Ninety-seven minutes is too short for the first and too long for the second; it lines up with channel-based redistribution (Telegram drops, vetted threads, small dispatch lists where keys go to a known pool for time-bounded use). The 11.3-second Phase-3 burst from seven IPs reinforces that: an automated dequeue, not a marketplace transaction.
The closest publicly documented family is the Bissa Scanner toolchain, profiled by The DFIR Report on 22 April 2026. Same goal, same Go architecture, same sts:GetCallerIdentity validation step, same Telegram-bot exfil pattern. Bissa pivots through a Next.js SSRF (CVE-2025-55182) instead of walking a fixed wordlist, so this isn’t the same binary, but the family resemblance is hard to miss.
The reappearance of 103.116.72[.]119 is the connective tissue. Sysdig had already named it as the source IP for CVE-2026-33626 exploitation eleven days earlier. One IP overlap isn’t proof of common attribution, but the shared HK infrastructure, the AWS-credential focus, and the sub-two-week cadence sketch a consistent operator profile. If you run an AWS account, treat LLM-adjacent SSRF on live IAM roles and high-speed static-AKIA harvesting as two angles on the same problem.
Detection guidance
Network layer
The most useful new signature is the per-target nonce path:
^/awsc_probe_[a-f0-9]{24}$Block or alert on it across any internet-facing surface. In our corpus it shows up only during this scanner family’s enumeration phase.
Pair it with a throughput rule: any host hitting more than 30 credential-related paths (.env*, /.aws/*, /credentials*, /config*) in under 500 ms is a credential harvester regardless of what else it does.
AWS / cloud layer
GuardDuty, SigmaHQ, and most CSPM products already alert on the IAM events individually. The signal that matters is correlation across sourceIPAddress and userIdentity.accessKeyId inside a 30-second window:
sts:GetCallerIdentity
→ iam:AttachUserPolicy (or AttachRolePolicy)
→ iam:CreateUser
→ iam:CreateAccessKeyAlert on the progression, not the individual events. Even if the writes return AccessDenied, the attempt is the signal.
Indicators of compromise
Phase 1: harvester
| Indicator | Value |
|---|---|
| Source IP | 195.170.172.108 |
| ASN | AS41608 NextGenWebs S.L. |
| Geolocation | Lelystad, Netherlands |
| AbuseIPDB | 100/100 confidence, 203 reports |
| User-Agent (harvest) | Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36 (forged; stale Jan 2023 build) |
| User-Agent (validation) | Go-http-client/1.1 (bare net/http default; not the AWS SDK) |
| Path regex | ^/awsc_probe_[a-f0-9]{24}$ |
| Throughput | 50 unique paths per lure in under 250 ms; ~320 / ~690 req/s |
| MITRE ATT&CK | T1595.003 (Wordlist Scanning), T1552.001 (Unsecured Credentials in Files) |
Phase 3: Hong Kong persistence pool
| Source IP | ASN | Notes |
|---|---|---|
156.0.200.138 * | AS23961 Misaka Network | CreateUser on HTTP key |
222.167.198.5 * | AS202662 Hytron Network | Both AKIAs; CreateAccessKey ×2 |
154.12.177.192 * | AS906 DMIT Cloud Services | GetCallerIdentity on MCP key |
212.107.29.70 | AS41378 Kirino LLC | 29 prior OTX pulses |
45.202.219.39 * | AS398704 STACKS INC | CreateUser, paired with 154.92.130.71 |
154.92.130.71 * | AS398704 STACKS INC | GetCallerIdentity, paired with 45.202.219.39 |
103.116.72.119 | AS400618 Prime Security Corp. | Also Sysdig 2026-04-22 (CVE-2026-33626 SSRF) |
* = first public disclosure. No prior threat-intel pulses on AlienVault OTX at the time of writing.
| Indicator | Value |
|---|---|
| User-Agent | curl/8.7.1 |
| Behaviour | IAM persistence chain from single sourceIPAddress within 30 s |
| Behaviour | Same VPS executes CreateAccessKey on both AKIAs in 1.5 s |
| Behaviour | Same hosting block contributes two distinct IPs to same key set |
| MITRE ATT&CK | T1078.004, T1098.001, T1098.003, T1136.003 |
Cross-vector reference
103.116.72.119 is the same source IP cited by Sysdig (2026-04-30) in their CVE-2026-33626 LMDeploy SSRF analysis. Sysdig’s Phase-1 SSRF target from this host on 2026-04-22 was the AWS IMDS endpoint:
http://169.254.169.254/latest/meta-data/iam/security-credentials/A direct attempt to lift the live IAM role credentials of the underlying GPU instance running the vulnerable LMDeploy server.
What we did not observe
- Successful escalation. The Canarytoken principal has no write permissions in Thinkst’s tripwire account, so the persistence operations almost certainly returned
AccessDenied. We have no AWS-side response data. - The credential exchange. The 97-minute gap is consistent with channel-based redistribution, but we can’t say which mechanism (Telegram drop, forum thread, internal pool dispatch), or rule out the same operator simply rotating to different infrastructure.
- Same-actor attribution across the Sysdig overlap. One IP in common is corroboration, not proof. The infrastructure, tooling, and ASN-class shifts between Phase 1 and Phase 3 are consistent with either a brokered handoff between distinct operators or one operator deliberately compartmentalising.
- Active reconnaissance. All enrichment is passive: canarytokens.org webhook, AlienVault OTX, AbuseIPDB, our session corpus, Sysdig’s published research.
Acknowledgements
Sysdig’s Threat Research Team for the CVE-2026-33626 analysis. Without it the 103.116.72[.]119 cross-reference doesn’t exist, and this post wasn’t worth writing.
The DFIR Report for the 22 April 2026 Bissa Scanner profile, the closest reference family we had to compare against.
Thinkst Canary and the Canarytokens project. Every IAM call against a fired token comes back as a structured webhook with source IP, ASN, geolocation, UA, exact event name, and timestamp at millisecond precision.
The Beelzebub honeypot framework, the open-source deception platform powering the lure surfaces in this post.