RuCTF RULES
Servers have a pre-installed set of vulnerable services.
The task of the participants is finding vulnerabilities, closing them on your own and using them to retrieve private information (flags) from your opponents. The game is continuously monitored by the checking jury system, regularly placing new flags on the teams' servers. In addition, the system accepts from the teams flags captured from the opponents.
It is difficult to give a complete set of rules for a CTF challenge, so these rules can change without notice at any moment prior to game start. That is why we recommend checking the rules one more time before the competition starts. Just in case :)
- Do whatever they want within their network segment.
- Most likely, the team would like to patch vulnerabilities in their services or block exploitation of vulnerabilities;
- Attack other teams. Didn't expect that, huh?
- Attack the game infrastructure operated by organizers;
- Attack the game infrastructure operated by organizers;
- Generate excessive amounts of traffic that pose a threat to network stability of any other team
It’s very important to read this section. We’ve made some changes in scoring rules in comparison to our previous competitions. Please read all the text carefully to avoid misunderstandings.
Key parameters in the scoring system are SLA and FlagPoints. Their values are individual for each service of each team. A team score is calculated as the sum of the products of the corresponding SLA and FlagPoints of all team's services.
def get_score(team): score = 0 for service in services: score += SLA(team, service) * FlagPoints(team, service) return score
SLA(team, service) is the ratio of the number of rounds when the service was UP to the service lifetime. For instance, if the service is always UP, SLA will be 1. If 4 hours passed from the beginning (or from the deployment of the service) and the service is UP during the first hour and then is not UP for the rest 3 hours, SLA will be 0.25.
FlagPoints(team, service) is the number that correlates with a team’s understanding of the service. FlagPoints depend on a team's attack capabilities (exploiting vulnerabilities against other teams) and defense capabilities (fixing vulnerabilities in their own service). At the beginning of the game, all teams have equal FlagPoints.
Each flag has its base price. The base flag price is calculated at the beginning of the round when this flag has been generated. All flags created in one round for one service have an equal base price.
If team A submits the non-expired flag from service X of team B then FlagPoints(teamA, serviceX) is increased by the base price of flag X raised to the power of Motivation(teamA, teamB). At the same time FlagPoints(teamB, serviceX) is decreased by the same value but never goes below zero.
Shortly, it’s just
where pos A and pos B are the positions of team A and team B in the scoreboard correspondingly. Both pos A and posB are integers between 1 and teams_count.
As you can see, Motivation(teamA, teamB) is a non-negative number that is less than or equal to 1. If team A has less score in the game than team B (thereby posA > posB), then team A gets the base flag price for this attack. Otherwise, their income decreases as the distance between teams grows on the scoreboard.
This parameter motivates you to hack teams stronger than you ^_^
Fortunately, this complex logic can be implemented by the following code:
def motivation(teamA, teamB): posA = scoreboard.get_position(teamA) posB = scoreboard.get_position(teamB) if posA >= posB: return 1 return 1 - (posB - posA) / (len(teams) - 1) def on_attack(teamA, teamB, flag): price = flag.base_price ** motivation(teamA, teamB) flag_points[teamA][flag.service] = flag_points[teamA][flag.service] + price flag_points[teamB][flag.service] = max(0, flag_points[teamB][flag.service] - price)
As we said before, the base flag price doesn’t depend on the team, it is calculated at the start of the round and depends only on the service. For any flag it’s base price never changes. Base flag price is always more than or equal to 1.
Service lifetime is divided into 3 phases: «Heating», «Cooling down» and «Dying».
The Heating phase starts when the service becomes available (at the beginning of the game or when we decide to add it). During this phase, the base flag price starts at START_FLAG_PRICE points and grows by a constant HEATING_SPEED each round. If the heating phase lasts too long, the base flag price freezes at MAX_FLAG_PRICE and doesn’t increase further. The Heating phase finishes when a total number of different submitted flags by all teams reaches HEATING_FLAGS_LIMIT.
In the next round after the Heating phase has ended, the Cooling down phase begins. At this moment, the base flag price (that was reached by the end of the Heating phase) is multiplied by the preselected constant COOLING_DOWN, which is less than or equal to 1.
As before, the base price of new flags is updated at the beginning of the corresponding round. It decreases linearly with the number of successful flag submissions that have been made by all teams since the beginning of the Cooling down phase. The phase finishes when this amount reaches COOLING_SUBMISSIONS_LIMIT.
The last phase is Dying. At this stage, the base flag price is constant and equals DYING_FLAG_PRICE.
START_FLAG_PRICE = 10
HEATING_SPEED = 1/12
MAX_FLAG_PRICE = 30
HEATING_FLAGS_LIMIT = 20 × teams count
COOLING_DOWN = 1/2
COOLING_SUBMISSIONS_LIMIT = 500 × teams count
DYING_FLAG_PRICE = 1