ECSC 2026 A/D
The European Cybersecurity Challenge 2026 will take place from the 12th to 16th of October 2026 in Bochum, Germany, hosted by NFITS e.V.. The Attack-Defense CTF will be organized by Attacking-Lab and take place on 15th of October, with the scored game starting at 11:00 CEST and lasting 8 hours until 19:00 CEST.
Attack-Defense
Attack-Defense CTFs are a type of cybersecurity competition in which participating teams host services and attempt to exploit each other over a shared, private network. The goal of the game is to earn points by stealing secrets stored in your opponents' service instances, and to avoid losing points by preventing your own secrets from being stolen and submitted, all the while keeping the services available and functioning. The team with the most points by the end wins.
Test and Demo Slots
Several slots are reserved ahead of the competition for demos and for players to set up and test their vulnbox and exploiter. Only the scored game counts towards the main score.
| Slot | Date & Time1 |
|---|---|
| Demo 1 | 19.09.2026 @ 12:00 – 20.09.2026 @ 20:00 |
| Demo 2 | 03.10.2026 @ 12:00 – 04.10.2026 @ 20:00 |
| Onsite test | 14.10.2026 @ 10:00 – 12:00 |
| Final setup and test | 15.10.2026 @ 10:00 – 11:00 |
CTF Schedule
The schedule for the day of the Attack-Defense CTF:
| Time1 | Event / State change |
|---|---|
| 10:00 | Platform goes online, players may login via Discord |
| - | Players can download WireGuard configs and connect to the game network |
| - | Players submit SSH keys to the platform |
| - | VPN Connection works within but not between teams |
| - | Scoreboard 10.60.249.1 and flag submission 10.60.249.2 are pingable |
| 10:30 | Organizers start all vulnboxes and exploiters with submitted keys |
| 11:00 | The Attack-Defense CTF officially begins |
| - | Network access to vulnboxes and exploiters is unblocked |
| - | Players are given control over their VMs via the platform |
| - | Scoreboard serves game state (empty during network close) |
| - | Flag submission at 10.60.249.2:31337 accepts connections |
| 12:00 | Network opens and teams can communicate with other vulnboxes |
| 18:00 | The scoreboard scores are frozen for the last hour |
| 19:00 | The Attack-Defense CTF officially ends |
1 all times CEST
Changelog
Short summaries of notable changes to this wiki, newest first.
2026-09-14
- VPN Access: specified that each player is provisioned 2 personal configs, and each team an additional 10 configs for infrastructure.
- Initial release of the wiki.
Game Overview
Each team is given root access to a cloud-hosted Linux-based virtual machine that exposes vulnerable services to other teams over a private virtual network.
Over the course of every round, lasting 60 seconds, so-called checkers store text snippets called flags in the services on each team's vulnbox and test their functionality to make sure they are working as intended. Extracting these flags from other teams' services and submitting them to a central flag submission each round to earn ATK-points is the game's primary goal.
Flag stores
A checker may store multiple unique flags each round in distinct areas of a service, so-called flag stores, and there may be more than one intended vulnerability to reach each one.
To incentivize teams to keep their services available to other teams to exploit, a series of checks is performed each round against every service of every team by the organizers' checkers. Checkers retrieve flags from flag stores of the previous 4 rounds in addition to the current round. Conversely, flags award points when submitted no more than 4 rounds after the round they were deployed in.
Attack info
Checkers may provide hints for successfully stored flags to help guide
exploits as attack info.
In some cases, this info is crucial to exploiting the vulnerability at all.
It can be retrieved via the scoreboard at /api/attack.json.
Please use the ecsc2026ad Python package, which provides client-side caching and may also be used to query the scoreboard.
The tests performed by checkers define the so-called Service-Level Agreement (SLA); the functionality required for a team to earn SLA-points each round.
Checker hold
The last 5 seconds of every round are a quiet period during which no checker tasks are running. Use this window to cleanly restart your services without a check hitting a service mid-restart and costing you SLA-points.
Each round, a team also receives DEF-points for every service if they were able to defend against an attack. The amount of points earned is highest when the service is unexploited, and decreases with the amount of other teams exploiting it.
These points combine to calculate the team score using the scoring formula.
Flag Format
Each flag is matched by the regular expression /^ECSC\{[A-Za-z0-9-_]{32}\}$/
Each flag consists of a prefix and suffix that wrap a base64-encoded1 payload with the following format:
2 bytes: round id2 bytes: team id2 bytes: service id2 bytes: flagstore id16 bytes: SHA256-HMAC (of previous 8 bytes, truncated)
Flag Submission
Players can submit stolen flags by sending them line-delimited in a plain TCP
connection to 10.60.249.2 on port 31337. This must be done via the game
network, since the source IP is used to determine the submitting team.
For each line, in the order that they are received, the flag submission will return one of the following results on a new line:
[OK]: The flag is valid and was accepted[ERR] Own flag: The flag is from the submitting team[ERR] NOP flag: NOP team flags are not counted[ERR] Expired: The flag is not valid anymore[ERR] Wrong length: The flag has the wrong length[ERR] Invalid source IP: The submitting IP cannot be attributed to a team[ERR] Already submitted: The flag has already been submitted by this team[ERR] Invalid flag (format): The flag does not match the flag format[ERR] Invalid flag (service): The flag references an unknown service[ERR] Invalid flag (team): The flag references an unknown team[ERR] Invalid flag (hmac): The signature of the flag is incorrect[ERR] Internal error (database): A backend error occurred
-
This payload is encoded using the base64url charset ↩
Team VMs
Cloud-hosted vulnboxes and exploiter VMs are provided by the organizers to all teams. These VMs are controlled via the platform. They have access to the internet via a public IPv4 and IPv6 address, as well as to the game network through WireGuard, but they are not reachable from the internet.
Setup
Vulnboxes are provisioned by the organizers with 4 cores and exploiters with 3 cores, both with 32 GB of RAM and 128 GB of storage. Self-hosting is allowed, but to avoid checker fingerprinting, we highly recommend using the supplied VMs and only offloading compute where necessary.
Each vulnbox is provisioned with an organizer SSH key in /root/.ssh/authorized_keys.
Teams are free to remove this SSH key; however, doing so limits
the amount of support and automated fixes we can provide.
Game Network
The A/D CTF takes place in a dedicated game network. Players connect to this network through a WireGuard tunnel. WireGuard configuration files will be made available before the game starts via the platform. They can be used with standard WireGuard tooling such as wg-quick.
VPN Access
Every WireGuard config allows exactly one host to connect to the game network. Trying to use the same config on multiple hosts simultaneously will make the connection unstable for all hosts using that config.
Each player is provisioned 2 personal configs, highlighted and not available for download by other players. On top of that, every team is provisioned 10 extra configs for other infrastructure you may want to hook up to the network - coordinate within your team to avoid using the same config in different places.
The config files contain credentials and information about the VPN endpoint. Do not share any of this information with anyone outside your team. This includes the endpoint information - it is different for every team. All VPN endpoints support both IPv4 and IPv6.
The VPN connection is used to access the game network. Most importantly, your vulnbox, your team members, other teams' vulnboxes, the flag submission, attack info, and the scoreboard. Your devices cannot use the VPN connection to access the internet.
Game Network IPs
The game network uses the address range: 10.60.0.0/16.
Every team has its own subnet, the team network: 10.60.<TEAM>.0/24
Each team's vulnbox gets the IP 10.60.<TEAM>.2, its exploiter the IP 10.60.<TEAM>.3.
Every team network has a gateway 10.60.<TEAM>.254, controlled by the infrastructure.
Every host connected to the game network has an IP in its team's subnet and is reachable from other hosts in the team subnet.
The NOP (non-playing) team is assigned the team ID 1.
Therefore, the NOP vulnbox is available at the IP 10.60.1.2.
The scoreboard is hosted at 10.60.249.1 (port 80) and the flag submission
at 10.60.249.2 (port 31337). Attack info, scores, team and service metadata
are available via the ecsc2026ad Python
package, which handles client-side caching.
Bandwidth Limits
We impose a bandwidth limit on traffic between any two distinct teams. Each team may send 10mbps of traffic via outbound connections, and reply with 10mbps to inbound traffic.
The overall bandwidth for each team's in- and outbound VPN traffic is capped at 1gbit/s respectively. Keep this in mind if you plan to ship pcaps from your vulnbox to another host.
Note that the effective bandwidth for game network connections may be lower due to how our traffic anonymization policies affect TCP throughput.
Traffic Anonymization
Connections originating from outside your team's network are anonymized to prevent checker fingerprinting.
All connections from checkers and other teams will appear to originate from
10.60.<TEAM>.254 - your team's gateway.
You should consider the following when encountering network issues:
- Packets with IP header options are rejected since they are likely used
unintentionally and are easily fingerprintable. The game network will reply with ICMP type
Destination unreachable(3) and codeAdministratively Prohibited(13). - TCP headers are normalized to prevent teams from telling apart checkers from exploiters via patterns in TCP options/flags usage.
- TTLs of packets entering team networks are capped at 32 such that traffic to vulnboxes arrives with the same TTL regardless of where it was sent.
- TTLs are not decremented in our router mesh to prevent
traceroute-ing of router topology. - We artificially introduce latency and degrade performance to prevent fingerprinting based on network conditions/response time of external exploiters.
- MTU negotiations are dropped to prevent using cached PMTU values to keep a single host fingerprinted.
- TCP MSS is set to a fixed value (MTU - 40) to prevent fingerprinting and potentially causing high packet rates.1
Game Firewall
While traffic within one team is almost unrestricted, traffic between teams is heavily restricted. The only allowed traffic is:
- TCP connections, but only to vulnboxes
- ICMP traffic of type
Echo Request(8),Echo Reply(0), andDestination unreachable(3) with codes 0, 1, 2, 3, 10, and 13.
Additionally, the game firewall blocks connections to your vulnbox from other teams except in the service port range 9000 to 9999 (inclusive). This ensures tooling outside this port range can not be easily accessed by other teams - even if the host firewall would allow it.
Note that attacking other teams' hosts other than the vulnbox is generally prohibited.
Finally, the network drops malformed packets, packets with invalid checksums, and packets that can't be attributed to connections. These firewall rules are designed to protect teams and infrastructure from malicious traffic that does not target the services. You generally won't receive ICMP messages for prohibited traffic; it will just be discarded.
-
The router MTU and MSS values for the final CTF will be announced at a later date, since the on-site connection needs to be tested for this. The infrastructure demo will use an MTU of 1420 and an MSS of 1380. ↩
CTF Platform
The CTF platform is hosted at ad.ecsc2026.de. It is used for distributing WireGuard configs, collecting SSH keys for VM access, and controlling teams' cloud-hosted VM instances.
Players may log in via their Discord account; they will be automatically joined to their respective team.
Scoring Formula
In Jeopardy CTFs, dynamic scoring is used to infer the difficulty of a challenge based on the number of teams that can solve it. This scoring formula applies the same concept to A/D.
In effect, each round is treated as a Jeopardy CTF with the following challenges:
- For each flag you capture, you receive ATK points based on the number of teams that capture that flag.
- For each service and each flag store, you receive DEF points for each actively exploiting team that did not capture your flag, weighted by how difficult that team's exploit was to defend against, inferred (via the dynamic scoring formula) from how few teams managed to defend against it.
Additionally, you gain a fixed amount of SLA points per flag
store, split evenly across its flags that are still valid (submittable for
points). You earn the share of each such flag that is retrievable from the
service, as long as the checker status is SUCCESS
or RECOVERING.
Checker Status
The checker returns one of the following results for each service:
SUCCESSif all flags could be successfully deployed and retrieved, and functionality checks were successful.RECOVERINGif all checks for the current round succeed, but at least one flag from the past 4 rounds is missing.MUMBLEif any functionality checks for the current round failed.OFFLINEif the checker failed to establish a connection to the service.TIMEOUTif a service did not answer within its timeout.CRASHEDif a checker task failed for an unknown reason.REVOKEDif a checker task didnt not produce a result in time.
If you see CRASHED or REVOKED for only your own service,
please notify us with context in a ticket.
Implementation
The formula may be evaluated against real CTF data using our simulator, whose implementation has been tested to match the gameserver.
In the following sections, we will reference code from the simulator to aid the explanation of different components of the scoring formula.
Dynamic Scoring
NFITS chose the following dynamic scoring formula for the ECSC 2026 Jeopardy CTF, so we base the A/D dynamic scoring on it. Using the same formula for both contests means points map to skill in the same way across the two scoreboards, which is what makes merging them fair. We scale the value range to make it more AD-friendly.1
The formula determines the value of each challenge by anchoring it at two
exact fixed points: (1, max_points) and (teams, min_points).
The value of a challenge is exactly max_points when only one team solves it
and exactly min_points when every team solves it.
With the default
alpha = 0.705, the value drops steeply for the first few solves and then
flattens out, so that rare exploits remain clearly the most valuable.
Attack Points
You earn ATK points for every flag you capture. Each flag's value comes from the dynamic scoring formula: the fewer teams that capture it, the more it is worth. Every round the gameserver recounts how many teams have submitted each still-valid flag and recalculates its value, so a flag is worth less the more often it is stolen. When a flag's value decreases, so do the scores of the teams that captured it in earlier rounds, to match its reduced worth.
On top of each flag's value, an attacker also receives a bonus equal to the DEF points a team would earn from defending against that attack with perfect uptime. This ensures an attack never earns its targets more DEF points than it earns the attacker.
def attack_points_flagstore(put_round: int, live_round: int, attackers: int,
teams: int, captured: set[int]):
max_round = min(live_round, put_round + flag_rounds_valid)
points = defense_points_attack(len(captured), attackers, teams,
put_round, max_round, lambda _: True)
for flag in captured:
captures = flag_captures[flag]
points += attack_points_capture(captures, teams)
return points
Defense Points
You earn DEF points for every attacker you successfully defend against. The value of defending a flag is set by the dynamic scoring formula from how many teams held off that same attacker: the fewer teams that managed to defend, the harder the exploit was to defend against, and the more each successful defense is worth.
These points are scaled up by the maximum number of victims, so that defending stays roughly as rewarding as attacking. We divide by the number of active attackers, but this cancels out roughly with the number of attackers you were actually able to defend against.
A flag's defense points are spread evenly across all rounds it must stay
retrievable, and a round only pays out if the flag was actually available.
The flag_ok parameter is a per-team, per-flag predicate: the service
must be SUCCESS
or RECOVERING that round and retrieving the
flag must have succeeded.
This stops teams from deleting their own flags to dodge attacks: if a flag is
not at risk, defending it earns nothing.
def defense_points_attack(victims: int, attackers: int, teams: int,
put_round: int, live_round: int, flag_ok):
max_points = defense_points_attack_max(victims, attackers, teams)
rounds_retrievable = sum(flag_ok(r) for r in range(put_round, live_round))
return max_points * rounds_retrievable / flag_rounds_valid
The total defense points per flag store per round are calculated by summing over every active attack we are not a victim of. As an exception, the NOP team does not gain defense points.
def defense_points_flagstore(put_round: int, live_round: int, team: str,
teams: int, service: str, flagstore: int):
max_round = min(live_round, put_round + flag_rounds_valid)
points = 0
attackers = victim_map[put_round, service, flagstore]
for attacker, captures in attackers.items():
victims = {flag_owner[flag] for flag in captures}
if team in victims or attacker == team:
continue
flag_ok = flag_ok_fn(put_round, team, service, flagstore)
points += defense_points_attack(len(victims), len(attackers),
teams, put_round, max_round, flag_ok)
return points
SLA Points
You earn SLA points each round for keeping your services healthy and their
flags retrievable. A service in SUCCESS earns the
full reward (sla_scale * max_points for each of its flag stores) each round,
while a RECOVERING service earns a partial reward,
based on the fraction of flags in play that are actually retrievable.
Any other checker status earns nothing.
At game start, no flags have been deployed yet, and thus fewer flags are in play
to be checked. SLA is scaled to compensate for these missing flags, such that
one round of downtime always costs at least sla_scale * max_points per flagstore,
and more if flags were not able to be placed and/or remain unretrievable.
This necessarily increases the lifetime value of early game flags.
Total Points
The component points are the previously defined scores summed over every flag store of every service, for every round played so far. A team's total score is the sum of all three components; the scoreboard displays them separately as ATK , DEF , and SLA .
Final Scores
The final team scores are calculated at the end of the game by subtracting the NOP team score from each team's total score. Given that it earns neither attack nor defense points, the NOP team represents a team that only managed to keep its services up, without exploiting anyone or defending against any exploits. We thereby treat its score as a baseline of points which did not require any effort by teams to be earned.
It is highly unlikely for a playing team to earn fewer points than NOP.
Insights
- A flag is worth more the fewer teams capture it, so attackers are rewarded for pulling off harder exploits.
- Defense works the same way: the fewer teams that fend off an attack, the more each successful defense against it is worth.
- Defending a flag store never earns more than the attacker gains from that attack.
- Attacking every team but one effectively hands that team the defense points, so it pays to attack as widely as possible.
- Reducing an attacker's attack points is realistically never worth the cost of downtime for the victim.
- The NOP team earns neither attack points nor defense points.
FAQ
Why is our team losing defense/attack points?
Teams may appear to lose defense or attack points when the value of the attacks they defended against or the flags they submitted decreases. This calculation is retroactive, as flags may be submitted up to 4 rounds after the round in which they are deployed.
Why can the defense points be non-zero in a round where our service status is neither SUCCESS nor RECOVERING?
Most likely, a team was attacking your service before it went down and submitted (at least some of) those flags in the round before it went down. These flags are only considered in the next round, and you are then awarded defense points for defending against this exploit from the previous round retroactively. Crucially, you do not gain defense points for any flag stores not retrievable in the round in which your service was down.
-
For the A/D we scale the original jeopardy
max_valueandmin_valuedown by a factor of 100 to prevent the scores from getting unwieldy, but this does not affect the final ranking and does not devalue the A/D points since only linear operations are applied to the jeopardy formula. The scaling cancels out when the aggregated scores are calculated by normalization. ↩
Rules of Conduct
Be nice to each other. ECSC is a competitive event, but also an opportunity for teams to learn from each other and have fun. Players should help foster that environment.
The following rules outline behavior prohibited during the competition, beyond those defined by common law (and common sense):
- Sharing flags, challenge details, or solutions to anyone outside your own team before the end of the competition is strictly prohibited.
- Giving or accepting assistance from anyone outside your own team and organizers for issues related to the competition is strictly prohibited.
- Using AI tools (chatbots, code assistants, AI-generated search completions/overviews, etc.) for anything related to the competition is strictly forbidden. This includes AI overviews shown by default in search engine results; players are encouraged to install a blocker such as Google AI Overviews Blocker to avoid accidental exposure.
- Gathering and/or taking advantage of insider information relating to the competition is prohibited.
- Attempting to elicit unintended behavior in any devices or services not designated as challenges for the competition running in the game network is strictly forbidden.
- Physically interacting or digitally tampering with the physical infrastructure provided by the organizers without permission is prohibited.
- Any action with the effect of creating excessive load for the contest or team infrastructure is prohibited, even if such actions are in the interest of the competition.
- If you are not sure if something is allowed or not, use the ticketing system to ask before doing it.
- Any unfair behavior with respect to the competition or the other players is forbidden, even if not explicitly described in the rules above; the organizers, the jury, and any other relevant authority reserve the right to evaluate each case independently.
- Any action with the effect of intentionally making another team's service unavailable or non-functioning when interacted with by players or the checkers is prohibited.
- The deployment of fake flags is prohibited.
Please be aware that the entire game network traffic is logged and will be accessed in case of a suspected rule violation.
Cheatsheet
A quick-reference for the most important information during the CTF.
| Key | Value |
|---|---|
| Platform | https://ad.ecsc2026.de |
| Public Scoreboard | https://scoreboard.ad.ecsc2026.de |
| Internal Scoreboard | http://10.60.249.1 |
| Game Info | pip install ecsc2026ad — see ecsc2026ad |
| Game Network | 10.60.0.0/16 |
| Team Network | 10.60.X.0/24 |
| Vulnbox IP | 10.60.X.2 |
| Exploiter IP | 10.60.X.3 |
| Gateway IP | 10.60.X.254 |
| Flag submission | nc 10.60.249.2 31337 |
Contact
Responsible Disclosure
Please report vulnerabilities you find in the platform to the organizers via the ticket bot in the ECSC 2026 Discord server.
In the unlikely case that Discord is unavailable, please send an email to the following address: ecsc2026+bugs@attacking-lab.com
Bug Bounty
We want to reward players who find and report bugs in our platform.
If you report a bug before the competition and help us triage, we will do our best to reward that show of sportsmanship.
Support
For support during the CTF, please open a ticket via the ticket bot in the ECSC 2026 Discord server.
In the unlikely case that Discord is unavailable, please send an email to the following address: ecsc2026+support@attacking-lab.com