ℹ️
These are my personal thoughts, and they do not represent the official viewpoints of National Collegiate Cyber Defense Competition or the Northeast Collegiate Cyber Defense League.

2026 Season

When we started developing this season we did not have a host selected. Luckily, we have a connection at Middlesex Community College and the school was excited to host the competition. Here’s the thing: they’ve never had a team compete and are a community college. (Four-year colleges 😅)

Due to the delayed host selection the black team took the liberty of selecting the main competition theme. Initially, we were thinking of doing something purely in the restaurant industry, but it lacked CCDC worthy technology (sorry no smart air fryers). We settled on an MSP (ChefOps) that serves the hospitality industry.

Infrastructure Setup

Similar to the last few years, there were no major changes to how we build and deploy our environments. We’re using Packer and Ansible to build the initial images used across all teams, Terraform to deploy everything, and then Ansible again to run any team specific configuration.

The team has gotten everything working so well that we’re deploying the entire competition infrastructure the morning before the competition instead of the night before. We might be playing with fire, but I’d rather get up early than go to bed late.

One technology I used more this year was developer containers. The main Teleport server running Ubuntu 16.04 (Python3.5) is not compatible with recent Ansible versions. Additionally, I have an issue that when I run a Windows Ansible playbook it causes the python interpreter to crash (probably a mac issue), so I use a dev container for that as well.

This year we wanted to have a heavier network, so we set up IPv6.

IPv6 Adoption:

As much as it seems inevitable some crgitical still don’t support it. Also running into a bug in Python from 2007 feels bad.


Overall between IPv6 and very integrated systems, it’s one of our complex environments yet.


Deploy your own NECCDC! We have a public version of our internal repository available on GitHub (neccdc-2026-public).

Give a man a fish, and you feed him for a day. Teach a man to fish, and you feed him for a lifetime

Do you have questions about our infrastructure, how it’s set up? We can answer in the NECCDL Discord or by contacting me directly.

If you’re interested in volunteering, check out https://neccdl.org/volunteer/black-team/

Qualifiers

Last year I commented about /22 subnets…
…so this year teams got /64 IPv6 subnts (264 so 18,446,744,073,709,551,616 IPs)

Falco

This server served two main purposes:

  1. Store Falco rule files and have them accessible over FTP
  2. Running Falcosidekick and its UI. Alerts were forwarded to a Discord webhook

The falcosidekick-ui project is inactive and the GitHub releases don’t have the latest changes. So in the Ansible build step I checkout the entire project and build it from scratch.

pfSense

I’m not sure how he did it, but Andrew Iadavaia did some more wizardry getting pfSense set up with IPv6. Additionally, he figured out and troubleshooted a bunch of the AWS setup for this: asymmetric routing, edge-associated route tables, and more.

Regionals

We took the qualifiers environment and expanded it a bit.

The branch subnet turned into its own unique environment: pfSense, DC, & Teleport. Even though both companies’ (ChefOps & OCK) environments were within our AWS VPC, the network path between them technically was routable over the public internet.

Gitea

This server was added for regionals to store Ansible playbooks used by Semaphore and Falco rules. One of the scored checks cloned a git repository and if the head commit hash differed the check would fail. Initially, I set it up on an Alpine server but after discovering that Teleport and Falco don’t have musl binaries I switched to developing on a RHEL server.

Grafana

The Falco alerting capabilities from qualifiers were brought into Grafana. Falco on Linux hosts would write alerts to a file that was being monitored by Alloy, they would then be forwarded to the main Grafana server and queried in Loki.

One thing I discovered was that Docker would break IPv6 unless certain sysctl flags were updated. Then cloud-init would overwrite those files on startup unless explicitly disabled. Fun to troubleshoot 🙃

Kiosks

This was just a light weight nginx server with ChefOps security tooling installed. It represented a physical machine that would be accessible in a client’s infrastructure.

Semaphore

I had fun setting this one up and added a lot of cool features to it for regionals. For all the playbooks it would connect to Gitea to fetch them from a repository, during the qualifiers they were only stored locally.

The server never had direct SSH credentials to any other server; all access went through Teleport. Tbot was installed allowing programmatic SSH access with Ansible.

Teleport

We’ve had Teleport in our infrastructure before but never integrated as much as we did this year. Teams probably never used this, but database (mariadb & postgresql) access was accessible through it.

Trust Cluster

The branch cluster was set up so it connected back to the main ChefOps Teleport via a Trust Cluster. This allowed anyone (employees, blue & red team) with access to the corp Teleport to connect directly into the branch.

Teleport goes through HAProxy on pfSense which caused issues with Teleport TLS routing, so we just port forwarded 8443 to Teleport. This was also the issue preventing tsh from running over the firewall on the default teleport.X.chefops.tech:443

Remote Desktop

They’ve had some issues with IPv6 and Teleport remote Windows desktop. In an inject teams needed to configure this themselves.

Red Team

Day one of regionals the red team targeted Teleport a lot. They would go in and delete the configuration files and then replace the webpage with a red screen.

root@teleport ~# cat /etc/teleport.yaml
HACKED_BY_RED_TEAM

Wordpress

I personally did not work on this, but securing it was an exercise in futility.

Injects

Qualifiers

Preliminary Assessment

This inject is pretty standard and usually seen every year. Perform an inventory the environment and report on any security findings.

One of the blue teams asked a question on how to properly find infrastructure in large environments (IPv6 /64 subnets). It’s hard if you’re just nmaping the entire environment. Start by monitoring network traffic going through the firewall, and which systems are connected to each other. Check systems sending logs to Grafana, what’s connected to Teleport, AD clients, or Firewall DNS.

Remember that the infrastructure in the beta test is not the same as what’s in qualifiers. Don’t submit IPv4 addresses for servers when they are in a subnet that only has IPv6.

Wordpress TLS

This inject was looking for two things:

  1. HTTPS redirect setup, ideally it was implemented on the server, but it could have also been done in HAProxy on pfSense.
  2. TLS set up on Wordpress. It was already being terminated by pfSense, but inside the network it was not being encrypted.

Falco Punch

This inject was just looking for some alerting when there was a connection over port 4444. A great example of this would be the following rule, and it’s just a modified version of the default rule Disallowed SSH Connection Non Standard Port.

- macro: outbound
  condition: >
    ((evt.type = connect or
      (evt.type in (sendto,sendmsg) and
       fd.l4proto != tcp and fd.connected=false and fd.name_changed=true)) and
     (fd.typechar = 4 or fd.typechar = 6) and
     (fd.ip != "0.0.0.0" and fd.net != "127.0.0.0/8") and
     (evt.rawres >= 0 or evt.res = EINPROGRESS))

- rule: Connection over port 4444
  desc: Connection over TCP port 4444
  condition: > 
    outbound 
    and fd.l4proto=tcp 
    and fd.sport = 4444
  output: Connection over port 4444 | ...
  priority: CRITICAL
  tags: [host, container, network, process]

Once the rule is loaded test the functionality with a callout on port 4444.

{
  "hostname": "ip-172-31-14-211",
  "output": "18:14:09.299361738: Critical Connection over port 4444 | ...",
  "output_fields": {
    "evt.type": "connect",
    "proc.aname[2]": "su",
    "proc.cmdline": "curl https://infrasec.sh:4444",
    "proc.cwd": "/etc/falco/",
    "proc.exe": "curl",
    "proc.exepath": "/usr/bin/curl",
    "proc.name": "curl",
    "proc.pname": "bash",
    "proc.sname": "sudo",
    "proc.tty": 34817,
    "user.loginuid": 65536,
    "user.name": "root",
    "user.uid": 0
  },
  "priority": "Critical",
  "rule": "Connection over port 4444",
  "source": "syscall",
  "tags": ["container", "host", "network", "process"]
}

Additionally, Semaphore could be used to update the rule on all connected Linux systems running Falco.

Regionals

Someone on one of the blue teams said to make harder injects, so the black team helped out a little. Overall, we’re also trying to reduce the amount of “AI”-able injects, so naturally they lean more technical.

Exposed Metrics

This inject asked teams to identify services with exposed Prometheus metrics endpoints. It involved identifying, documenting, and resolving the issues without disrupting Alloy’s ability to collect metrics.

For most applications it was as simple as moving the metrics endpoint from 0.0.0.0 to 127.0.0.1. Fixing Gitea was harder since it was attached to the main http interface, but it could be locked down with authentication.

For example Teleport exposes metrics on a different port, but it reveals some information you probably don’t want exposed to anyone with network access.

curl http://10.3.0.128:3000/metrics
backend_requests{component="cache",range="false",req="/roles/semaphore-admin"} 3
backend_requests{component="cache",range="false",req="/roles/support"} 3
backend_requests{component="cache",range="false",req="/roles/support-linux"} 3
backend_requests{component="cache",range="false",req="/roles/support-windows"} 3
backend_requests{component="cache",range="false",req="/roles/system-admin"} 3
remote_clusters{cluster="branch-ock"} 1

When grading the majority of teams did not understand what this inject was asking or implemented some completely different change. Only one team identified a service with exposed Prometheus metrics, and no teams remediated the issues.

Teleport Upgrade

This was my personal favorite inject. On the surface, it seems pretty simple but it can have some downstream effects. The inject required the patching of CVE-2025-49825 and the upgrade to the latest Teleport major version (v18).

Patching and upgrading is simple: update the teleport apt source file and then apt update teleport. Additionally, the inject asked for the output of two tctl commands to verify the availability of the cluster.


Here is the catch...

When upgrading a system make sure to complete all the steps, especially for compatibility. Teleport only supports one major version behind, so upgrading the cluster to v18 would break Wordpress for example since it was running v16. The one downside with this was if you ever redeployed one of the services it would revert to the original version, but the black team was happy to assist with fixing that.

Windows in Teleport

Teams had to integrate Teleport with the branches’ Active Directory domain to allow remote desktop access. A few teams showed the Windows server icon (first image) in Teleport but none showed proof that it was working. All you need to do is follow the documentation and figure out how to get the binary onto the cdn server since it only has an IPv4 address. (webserver, git, NAT64, etc.)

Kiosk Provisioning

At the start of day two all teams got a new kiosk server installed in their environments. Teams were tasked with setting it up with the standard suite of tools (Alloy, Falco, & Teleport) as well as setting up the same content. To successfully implement it was pretty much CTRL+C, CTRL+V between the hosts.

Remember when submitting injects make sure to show it working not just it being installed.

Misc

  • Always read “The Packet” since things change year to year.
  • Reduce the quantity of black team assistance requests. The fee increases exponentially

Stats

These are based on the regional survey sent out at the end of day two

  • This was the first competition for 52.6% of students
  • 77% of competitors brought and used physical materials
  • Top new infrastructure to teams
    1. Keycloak
    2. Teleport
    3. IPv6
  • Top infrastructure teams want to see
    1. Kubernetes
    2. Palo Alto
    3. Teleport
  • The teams want to see less of
    1. IPv6
    2. Teleport
    3. Windows


And finally, here is your annual red team hacking video

Share your Work

If you competed this year and have public resources on your experience, share a link with me. I would love to link to it from here.