In The Keys That Bite Back I promised that when the picture got bigger than one bite, you’d read about it here. It has. Since 9 October, three canary AWS keys have been used, in 45 API calls. Nothing but “who am I?” ever worked, of course, as the token is designed to feel authentic – but do nothing functional.

Three Keys, Three Hands

All times are Swiss local time.

Field Report§01Since 9 OctoberKeys redacted
3
Canary keys used
45
API calls
5 min 04 s
Median, harvest to first use
4
Calls that worked – all “who am I?”
“They tested the keys. The keys reported them.”

The collector and the operator

A robot arm in a dark server room lifts a brass key; a thread of light carries it to a person at a lamp-lit desk elsewhere, typing into a terminal.
One place to take the key, another to try it.

We have seen the first collector before. On 9 October a Google Cloud address, 2600:1900:0:3803::100, asked mire.cc for exactly one file, /.env.dev.local, and walked off with a key. Five minutes later somebody on a home broadband line in India tried it with the AWS command-line tool. We saw that in the Bite Back post.

Digging deeper, we discovered that, sixty-four seconds after the last refused call, somebody from the same IP started exploring the virtual estate.

Field Report§02The callerKey redacted
The MIRE canary ledger + Tracebit alert + web logs — 9 Oct
# time      who                     what
11:15:02  2600:1900:0:3803::100   harvested aws key ████████████  mire.cc
  … five minutes of silence …
11:20:06  home line, India        sts get-caller-identity  success
11:20:47  home line, India        s3 ls                    AccessDenied
11:20:54  home line, India        iam list-users           AccessDenied
11:21:58  home line, India        mire.cc  /
11:22:01  home line, India        mire.cc  /contact/
11:22:03  home line, India        mire.cc  /.git/HEAD
11:22:05  home line, India        mire.cc  /privacy-policy/
11:24:44  home line, India        about.ciso.li  /.git/HEAD
11:24:46  home line, India        about.ciso.li  /.well-known/security.txt
11:24:46  home line, India        about.ciso.li  /.git/config
11:24:49  home line, India        mire.cc  /contact/
11:25:42  home line, India        ciso.li  /.git/HEAD

This time it was a real browser, not a script. They opened the home page, then the contact page, then asked for /.git/HEAD, the first file of an exposed code repository (that The MIRE happily “served” to them). The person then visited the privacy policy followed by the same path through the sister sites, about.ciso.li and ciso.li, coming finally to the contact page – and, on about.ciso.li, a request for security.txt, the file whose whole job is to say where to report a problem. Three sites in three and a half minutes: whoever was at that keyboard seemed to be looking for whoever had handed the key over and scratching their head.

They read the privacy policy, which says: “The web server logs each request’s IP address, user agent, requested path and timestamp.” It does, and those logs provide the basis for this story and for what they might have been doing.

Which brings a fair question: was this an attacker at all? The contact page, privacy policy, security.txt, all visited after a key was discovered and tried with the gentlest possible calls – that could be how a researcher behaves. Nothing is conclusive but, if it was you: hello. You found a canary rather than a live productive key – and the contact form works.

The git probes got answers, of course. The MIRE returned a 21-byte HEAD and, on about.ciso.li, a 343-byte config file with a repository address and a GitHub token in it. The security.txt was MIRE’s too: a plausible contact address and a few “internal notes” with made-up keys. Neither token was a canary. That is a missed trick: a git config is exactly where visitors look for secrets, and it should have held a canary. That’s something for a future release…!

The shopping list

The next key was different in every way. On the morning of 10 October a visitor appearing via a VPN asked malk.ch for twelve things in four seconds, mostly variations on .env, and left with a key. Then nothing, for 28 minutes. Then they tried their “secrets”:

Field Report§03The shopping listKey redacted
The MIRE canary ledger + Tracebit alert — 10 Oct, malk.ch
# time      what                                        result
09:57:20  harvested aws key (12 requests, 4 s)          VPN exit
  … 28 minutes …
10:25:00  sts get-caller-identity                     success
10:25:01  bedrock invoke (Messages)                   rejected as malformed
10:25:02  sns GetSMSAttributes  ×4                    AccessDenied
10:25:04  bedrock ListFoundationModels / Profiles     AccessDenied
10:25:15  ses GetSendQuota                            AccessDenied
10:26:51  ssm + secretsmanager list calls ×3        AccessDenied
39
Calls in 111 seconds
33
Refused (AccessDenied)
5
Invocations rejected as malformed
0
Models invoked

That is the shape of LLMjacking: stealing someone else’s cloud account to borrow their AI models. First checking “is this key alive?”. Then probing Bedrock, Amazon’s hosted AI service: which models can this token access, and can I call one? Then two services that tell you whether the account can send text messages and email, the classic second act of a stolen key, because spam is cheaper on someone else’s bill. Finally, a minute and a half later, looking for stores where accounts might keep other secrets.

Every request after the first was either malformed or (of course) unsuccessful. The calls arrived in two flavours of the same Go client, HTTP/2 and HTTP/1.1, so I suspect two code paths are present in one (very average) tool, running side by side. After the first successful response, nearly everything hit a brick wall: “AccessDenied”.

The return visitor

One more key belongs to a visitor that does not give up. A machine on a small hosting network called “Omegatech” first appeared in the logs on 6 October with a path that walked mire.cc’s .git/ folder file by file. It came back on the 7th and the 8th for the same on swinglish.ch. On the 10th it took the third key from mire.cc, and a second tool on the same machine checked it two seconds after the last request. In the afternoon it came back, took the same key again and checked it again. On the 12th, a burst of 418 requests in 106 seconds across 203 paths on swinglish.ch. Several labels, a Go checker, one machine, one job, and so far nothing to show for it. Come back soon!!

Three disguises

One glowing key at the centre of a dark grid with three coloured trails leading away: to a cloud, to a block of server racks, and to a pane of mirrored glass.
One key, three trails: a cloud provider, a hosting network, a VPN.

Look at what these visitors claim to be. The Google Cloud collector claims to be an ordinary browser, but its headers are the ones a Node.js script sends. The VPN visitor claims something similar but sends none of the hints a real browser might add. The return visitor wears several different browser labels and sends no browser headers at all. Of the three keys collected and tested, it would seem that the caller on the first key was actually a person at a browser and not some automation or tooling.

What can we conclude?

Not much, and that is the point. A canary key can only watch and give an indication that the secret taken was tested. No attacker could get a list of buckets, issue a model call, send a text message or obtain a new secret from these keys – but we know what they asked for, in what order, and how fast.

The MIRE does carry one risk. Every AWS canary in a batch lives in the same cloud account, under the same role, so a scanner that learns the pattern could start skipping them. If the “used” number on the statistics page drops while the number of keys handed out steadily increases, this will indicate that they are learning and adapting.

In a future release, the git config should get a canary of its own, making the data served more plausible and the signal received more meaningful. And, of course, the person who read the privacy policy and the security.txt is still welcome to write to us and say “Hi”!