Wiz CTF #11: Split Horizon

Wiz CTF stamp 11

No flags are included in this writeup.

Overview

Split Horizon placed me inside a Kubernetes lab through a restricted bastion account. The service account had very limited permissions: it could view node-level metadata, but it could not list pods, list services, or proxy freely through kubelet/API server paths.

At first this looked like a dead end. The key idea was that node metadata still leaked enough networking information to understand how the cluster routed traffic internally.

The final solve required using Kubernetes DNS and the node overlay network to discover and reach an internal pod that was not visible through the API.

Field Value
Month April 2026
Points 30
Main area Kubernetes networking, flannel VXLAN, CoreDNS

Initial Enumeration

I started by checking what the bastion account was allowed to access:

kubectl auth can-i --list

The important permission was:

nodes get/list

Direct approaches such as node proxying and kubelet access were blocked. That confirmed the intended route was not “just use the Kubernetes API harder.”

The next step was inspecting the nodes:

kubectl get nodes -o json

This revealed useful networking metadata:

  • Pod CIDRs assigned to each node
  • Cluster service CIDR
  • Cluster DNS IP
  • flannel VXLAN backend details
  • node internal IPs
  • flannel VTEP information

That was the turning point. The account could not see workloads, but it could still see enough of the network map.

Reconstructing The Network Path

The cluster used flannel with VXLAN. Since the node metadata exposed the relevant flannel configuration, I recreated a compatible VXLAN interface on the bastion and added routes for the pod and service CIDRs.

This allowed the bastion to send traffic into the Kubernetes overlay network even though it was not itself a Kubernetes workload.

After setting up the route, CoreDNS became reachable:

dig @10.43.0.10 kubernetes.default.svc.cluster.local

That confirmed the bastion could now communicate with the internal Kubernetes service network.

Using DNS As The Service Oracle

The API did not allow service listing, but CoreDNS could still answer DNS queries.

The useful trick was reverse DNS lookup across the service CIDR:

dig @<cluster-dns> -x <cluster-ip>

By scanning the service CIDR, I could discover internal service names that were hidden from the Kubernetes API permissions.

Once the interesting service name appeared, the rest was network routing and careful probing. The solve path was no longer about Kubernetes object permissions. It was about reaching the workload directly through the reconstructed overlay path.

Root Cause

The challenge demonstrates that “node-only” Kubernetes visibility is not necessarily blind.

Even without access to Pods or Services, node metadata can expose:

  • Pod CIDR assignments
  • overlay network configuration
  • service CIDR
  • cluster DNS location
  • node routing details

With that information, it is possible to reconstruct enough of the network path to query internal DNS and reach workloads directly.

The core issue is that Kubernetes networking metadata can become an unintended discovery and access primitive when combined with network-level access from a bastion.

Takeaways

My biggest notes:

  • Kubernetes API restrictions do not always prevent network-level discovery
  • CoreDNS can reveal service names even when the API blocks service listing
  • reverse DNS over ClusterIPs can expose hidden internal endpoints
  • node metadata can leak enough overlay-network details to reconstruct pod/service routing
  • direct network reach and Kubernetes object permissions are different security boundaries

Split Horizon is currently the hardest challenge by public solve count in this run. The final flag name fit the solution perfectly: packets had to take the scenic route.