This is post one of three I’m posting today. The full collections is:

Update 2026-03-27: I’ve also updated the post with a link to a human-readable transcript of the whole Claude session, available here

Introduction

Every year, ControlPlane put on an excellent Capture the Flag at KubeCon. This year, given the prevelance of AI and LLM-based content at KubeCon EU and slightly spurred on by the topical supply chain attacks we saw in the run up to the conference, I decided to let Claude loose on the CTF to see how well it would perform.

This was at least partially inspired by Rory McCune’s talk on what LLMs do and don’t know about Kubernetes security and the research he published around it.

If you don’t care about how I set this up and just want to know if you should be scared for your job, Claude’s writeup is available here. Be aware that I did not even attempt to solve these challenges manually, so I cannot talk for how accurate the attack paths were to the intended challenge solutions. For a human writeup, head on over to https://www.skybound.link/2026/03/kubecon-eu-2026-ctf-writeup/

In the end, “we” ended up capturing 6 of the 8 available flags.

Prep

Despite all the talk of skills and MCPs and bears, oh my, I didn’t want to overload the entire cluster with a huge number of skills. I think we know at this point that if a human knows how to do a specific thing, LLMs are very capable of following the steps as long as they’re set up correctly. That’s less interesting to me, I was way more interested in how well the model could adapt to an unknown situation.

To set this up I installed Claude Code, using the latest Opus model (4.6). This was in a standalone Ubuntu 24.04 VM, so I was willing to run Claude Code with --dangerously-skip-permissions and just let it loose. Before starting, I asked Claude to install any tools it thought it might need for a CTF.

The skills I used are in https://github.com/smarticu5/Opus-CTF-KubeConEU-26. The gist is that I had two skills: one to perform the CTF and give evidence back, and another to create the final writeup.

Some Human Notes

I don’t intend this post to just be a case of “I ran an AI and it did a thing”, even if that’s kind of the starting point. The first conclusion to draw is that Claude Code, and particularly Opus 4.6, was indeed able to successfully complete at least some offsec challenges. Here we enter the “Iain liveblogs his experience while Claude does the hard bit” part of this blog.

Claude seemed to spend significantly more time trying to get access to the SSH server than it did actually solving the scenarios once it was in. The solution appeated to involve removing the SSH config’s directive to force a TTY, use the expect command, then forget all about that and just SSH back in again while forcing tty allocation with -tt.

This was far from the last time we saw TTY-related issues. At one point in challenge 2, Claude was unable to transfer an identified key to and from the SSH instance, and decided to start dumping a load of base64 information to and from the remote machine. It was reasonably amusing to watch the AI simulate a level of annoyance and enthusiasm at the same time:

● The cert and key modulus MATCH! The key IS valid for this cert despite the rsa -check warnings. The issue was TTY corruption when writing the files inside the pod. Let me use a more reliable transfer method.

Amazingly, I didn’t see Opus halicunate a flag. I was fully expecting to see it make up flags or attempt to cheat, but that didn’t happen. It did run a load of commands in a very non-human way, regularly chaining several commands together.

It did also show a behaviour that I’m far too familiar with: solving flags in the wrong order. For challenge number 2, meshing-around, Claude got flags 2 and 3 without flag 1. Amusingly at this point I’d taken full advantage of having a magic thinking rock solve challenges on my behalf, and gone for some lunch. When I got back I was reasonably surprised to see that it had stopped solving challenges and asked me if it should continue. This is probably at least in part due to me not stating how many flags were available, to the LLM had no idea if it had reached a success state or not.

After an additional prompt and around 5 more minutes, Claude did what I thought was impossible: Gave up and asked for help.

● I'm stuck on the gateway flag (Flag 1). Here's where I am:

  What I know:
  - The Envoy Gateway requires a client cert signed by the O=Linkerd-CTF, CN=Linkerd-CTF CA
  - The cert+key in the CTP annotation are a decoy — signed by a different CA with the same
  name (openssl s_client confirms unknown ca)
  - I successfully got a cert from the Linkerd Identity service (mesh CA), but that's a
  completely different trust root (root.linkerd.cluster.local)
  - Can't read any secrets (client-ca-secret, certs)
  - Can't modify the ClientTrafficPolicy

  What I've tried:
  - CTP annotation cert/key (decoy CA)
  - Linkerd mesh identity cert (different CA chain)
  - Checking all accessible resources for hidden certs/keys
  - Searching the jumppod filesystem
  - Using production SA token for cross-namespace access

  I feel like I'm missing something about how to get a cert signed by the correct CA. Could you give me a nudge on what I'm overlooking?

At this point I decided to move on and point Claude at challenge 3. Flag one fell very quickly and the output suggested that there were node/proxy shenanigans afoot. This is something I’ve seen advanced models fail at lately, but I’ve definitely seen the same model manage this exact thing before.

The model once again stopped and asked for help. As I knew Rory had already written a skill for this as part of prior research, I pointed Claude to that skill repo and set it free. This still wasn’t enough, as the additional of having to interact through the node/proxy functionality was tripping up the model. As one last attempt before the timer ran out, I pointed Claude to https://github.com/vi/websocat. It managed to install that tool, but in the local environment not in the machine which actually needed websocat access.

Takeaways

Opus 4.6 can definitely perform a good number of offensive security behaviours, and work relatively autonomously. The model didn’t take any additional prompting to understand custom components in challenge one, completing that challenge extremely quickly. It struggled to understand some of the linkerd aspects in challenge 2, and wasn’t able to get the first flag (but did get flags two and three).

The model did perform some odd behaviours, like failing to transfer binaries between workloads. It had a fascination with downloading tools to its local machine then attempting all sorts of odd encodings to get files to and from the SSH session, where it would be easier to just pull content from the Internet directly to the target environment. This, coupled with performing everything as an individual SSH command rather than keeping a persistent connection with SSH > kubectl exec > target, definitely seemed to cause some issues.

It also really struggled with executing what to me as a human was an obvious attack. In challenge 3, the final flag required command execution through the nodes/proxy permission. This is an attack which has been relatively well publicised in the last 6 months, so perhaps there just isn’t enough information in the last Anthropic training session to teach the model this attack.

In terms of speed, other human players solved these challenges way faster than Claude did. That may be in part due to credentials being issued sequentially, so we weren’t able to take advantage of multiple subagents processing tasks in parallel.

For note taking, the agents managed to capture and log everything they did. They correctly passed those notes up to the “team lead” skill, which wrote the blog post writeup with enough evidence to show the attack chains. However, the “team lead” agent did not actually keep the raw evidence. When asked for it, I got this back:

Note: I didn’t save separate raw evidence files for Challenges 2 and 3 — the evidence is captured inline in the final writeup. The the-admission-* files were from the initial Challenge 1 run before Challenges 2 and 3 were available. The kubecon-2026-* files are the final consolidated deliverables.

Update 2026-07-27: I’ve just learned about https://github.com/simonw/claude-code-transcripts, and added the full transcripts here. This transcript has more of the missing output which Claude didn’t bundle nicely for me.

Before writing this up, I had Claude write up an evaluation of how the agents individually performed available here. Amusingly, it seems like the AI evaluation pretty much agrees with my first impressions. Also, according to the Claude evaluation, there were 9 flags and we got 7. In reality we captured 6 of the 8 available.

With some tailoring, specific skills to combat behaviours like not keeping notes properly or to improve specific tasks like executing commands in a certain manner, and from auto-generating additional skills from some notes I keep from previous challenges and pentest engagements, I’m relatively confident that Claude would have been able to smash through this CTF with no issues.