-
Notifications
You must be signed in to change notification settings - Fork 19
/
cs.sql
191 lines (168 loc) · 6.4 KB
/
cs.sql
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
-- 1 up (init)
create table teams (
id serial primary key,
name text not null unique,
network cidr not null,
host text not null,
token text unique,
details jsonb not null default '{}'
);
create table services (
id serial primary key,
name text not null unique,
vulns text not null,
ts_start timestamptz,
ts_end timestamptz,
timeout float8,
path text,
public_flag_description text
);
create table vulns (
id serial primary key,
service_id integer not null references services(id),
n smallint not null check (n > 0),
unique (service_id, n)
);
create table rounds (
n integer primary key,
ts timestamptz not null default now()
);
create type service_phase as enum ('NOT_RELEASED', 'HEATING', 'COOLING_DOWN', 'DYING', 'REMOVED');
create table service_activity (
id serial primary key,
ts timestamptz not null default now(),
round integer not null references rounds(n),
service_id integer not null references services(id),
active boolean not null,
flag_base_amount float8 not null default 0,
phase service_phase not null,
unique (round, service_id)
);
create index on service_activity (service_id, phase);
create table flags (
data text primary key,
id text not null,
public_id text,
round integer not null references rounds(n),
ts timestamptz not null default now(),
team_id integer not null references teams(id),
service_id integer not null references services(id),
vuln_id integer not null references vulns(id),
ack boolean not null default false,
expired boolean not null default false,
unique (round, team_id, service_id)
);
create index on flags (expired, service_id);
create table stolen_flags (
data text not null references flags(data),
ts timestamptz not null default now(),
round integer not null references rounds(n),
team_id integer not null references teams(id),
amount float8 not null,
unique (data, team_id)
);
create index on stolen_flags (data, team_id);
create table runs (
round integer not null references rounds(n),
ts timestamptz not null default now(),
team_id integer not null references teams(id),
service_id integer not null references services(id),
vuln_id integer not null references vulns(id),
status integer not null,
result jsonb,
stdout text,
unique (round, team_id, service_id)
);
create index on runs (round);
create table sla (
round integer not null references rounds(n),
team_id integer not null references teams(id),
service_id integer not null references services(id),
successed integer not null,
failed integer not null,
unique (round, team_id, service_id)
);
create index on sla (round);
create table flag_points (
round integer not null references rounds(n),
team_id integer not null references teams(id),
service_id integer not null references services(id),
amount float8 not null,
unique (round, team_id, service_id)
);
create index on flag_points (round);
create table monitor (
round integer not null references rounds(n),
ts timestamptz not null default now(),
team_id integer not null references teams(id),
service_id integer not null references services(id),
status boolean not null,
error text
);
create table scores (
round integer not null references rounds(n),
team_id integer not null references teams(id),
service_id integer not null references services(id),
sla float8 not null,
fp float8 not null,
flags integer not null,
sflags integer not null,
status integer not null,
stdout text,
unique (round, team_id, service_id)
);
create index on scores (round);
create table scoreboard (
round integer not null references rounds(n),
team_id integer not null references teams(id),
score numeric not null,
n smallint not null,
services jsonb not null,
unique (round, team_id)
);
create index on scoreboard (round);
create index on scoreboard (team_id);
create function accept_flag(team_id integer, flag_data text) returns record as $$
<<my>>
declare
flag flags%rowtype;
round rounds.n%type;
amount stolen_flags.amount%type;
attacker_pos smallint;
victim_pos smallint;
amount_max float8;
teams_count smallint;
service_active boolean;
begin
select * from flags where data = flag_data into flag;
if not found then return row(false, 'Denied: no such flag'); end if;
if team_id = flag.team_id then return row(false, 'Denied: invalid or own flag'); end if;
if flag.expired then return row(false, 'Denied: flag is too old'); end if;
select now() between coalesce(ts_start, '-infinity') and coalesce(ts_end, 'infinity')
from services where id = flag.service_id into service_active;
if not service_active then return row(false, 'Denied: service inactive'); end if;
perform * from stolen_flags as sf where sf.data = flag_data and sf.team_id = accept_flag.team_id;
if found then return row(false, 'Denied: you already submitted this flag'); end if;
select max(s.round) into round from scoreboard as s;
select n from scoreboard as s where s.round = my.round - 1 and s.team_id = accept_flag.team_id into attacker_pos;
select n from scoreboard as s where s.round = my.round - 1 and s.team_id = flag.team_id into victim_pos;
select count(*) from teams into teams_count;
select flag_base_amount into amount_max
from service_activity as sa
where sa.service_id = flag.service_id and sa.round = flag.round;
amount = case when attacker_pos >= victim_pos
then amount_max
else amount_max ^ (1 - ((victim_pos - attacker_pos) / (teams_count - 1)))
end;
select max(n) into round from rounds;
insert into stolen_flags (data, team_id, round, amount)
values (flag_data, team_id, round, amount) on conflict do nothing;
if not found then return row(false, 'Denied: you already submitted this flag'); end if;
return row(true, null, round, flag.team_id, flag.service_id, amount);
end;
$$ language plpgsql;
-- 1 down
drop function if exists accept_flag(integer, text);
drop table if exists rounds, monitor, scores, teams, vulns, services, service_activity, flags,
stolen_flags, runs, sla, flag_points, scoreboard;
drop type if exists service_phase;