feat: challenge registry-driven platform + XVI/XVII imports + admin toggle + domain rename
- Rename repo/domain: attack-defense-platform / attackdefense.imrnes.team (all refs replaced)
- challenge_registry.json: single source of truth (28 challs across gemastik18/xvi/xvii)
- teams.py: registry-driven CHALLENGES, set_challenge_enabled, sync_challenge_runtime
(apply enable/disable to live teams: build/up or stop/remove + receiver restart)
- compose_gen.py: render per-team compose from canonical per-challenge templates
(image reuse, per-team ports 30xxx, flag mounts, passwords)
- gen_canonical_composes.py: canonical docker-compose.yml for all services
- import_new_challenges.py: import XVI/XVII services + EOL base image fixes
(debian:buster→bookworm, node:14→20, python:3.7-slim→3.11)
- receiver: xvi package (10 checkers) + xvii package (12 generic checkers),
Challenge base reads PASSWORD_<team_port> from env; gen_receiver_main.py
generates per-team main.py from registry
- main.py: /api/challenges returns full registry; PATCH /api/challenges/<name>
toggles enabled + applies to live teams
- index.html: 🏗️ Challenge Manager tab (toggle per challenge, grouped by set)
- SLA bonus now dynamic (all enabled challenges, not hardcoded 6)
This commit is contained in:
@@ -0,0 +1,105 @@
|
||||
import random
|
||||
import time
|
||||
import pok
|
||||
|
||||
FLAG = open("../flag.txt", "r").read()
|
||||
|
||||
TICKET_PRICE = {
|
||||
"normal": 2,
|
||||
"premium": 5,
|
||||
"VIP": 10,
|
||||
"FLAG": 100,
|
||||
}
|
||||
|
||||
TICKET_VALUE = {
|
||||
"normal": 1,
|
||||
"premium": 3,
|
||||
"VIP": 5,
|
||||
}
|
||||
|
||||
|
||||
class Ticketer:
|
||||
def __init__(self, username: str):
|
||||
pk, vk = pok.keygen(username.encode())
|
||||
self.username = username
|
||||
self.proving_key = pk
|
||||
self.verifying_key = vk
|
||||
self.balance = 10
|
||||
self.used_tickets = []
|
||||
self.issued_tickets = {}
|
||||
|
||||
def buy_ticket(self, ticket_type):
|
||||
price = TICKET_PRICE.get(ticket_type)
|
||||
if self.balance >= price:
|
||||
self.balance -= price
|
||||
new_ticket_id = int.to_bytes(random.getrandbits(128), 16, "big").hex()
|
||||
self.issued_tickets[new_ticket_id] = ticket_type
|
||||
ticket = pok.prove(new_ticket_id.encode(), self.proving_key)
|
||||
|
||||
if ticket_type == "FLAG":
|
||||
print(FLAG)
|
||||
else:
|
||||
print(f"Your ticket: {new_ticket_id+ticket.hex()}")
|
||||
else:
|
||||
print("[ERROR] Insufficient balance")
|
||||
|
||||
def refund_ticket(self):
|
||||
try:
|
||||
ticket = input("Input ticket: ")
|
||||
ticket_id, proof = ticket[:32], bytes.fromhex(ticket[32:])
|
||||
|
||||
if pok.verify(ticket_id.encode(), self.verifying_key, proof):
|
||||
if ticket_id in self.issued_tickets and ticket not in self.used_tickets:
|
||||
ticket_type = self.issued_tickets.get(ticket_id)
|
||||
value = TICKET_VALUE.get(ticket_type, 0)
|
||||
self.balance += value
|
||||
self.used_tickets.append(ticket)
|
||||
print("Ticket succesfully refunded!")
|
||||
else:
|
||||
print("[ERROR] Invalid ticket")
|
||||
else:
|
||||
print("[ERROR] Invalid ticket")
|
||||
except Exception:
|
||||
print("[ERROR] Invalid ticket")
|
||||
|
||||
def checker_menu(self):
|
||||
self.balance = 150
|
||||
|
||||
def menu(self):
|
||||
print("[1] Buy normal ticket ($2)")
|
||||
print("[2] Buy premium ticket ($5)")
|
||||
print("[3] Buy VIP ticket ($10)")
|
||||
print("[4] Buy FLAG ticket ($100)")
|
||||
print("[5] Refund ticket")
|
||||
print("[6] Exit")
|
||||
print("")
|
||||
print(f"Your balance: ${self.balance}")
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
username = input("Username: ")
|
||||
ticketer = Ticketer(username)
|
||||
|
||||
while True:
|
||||
time.sleep(0.5)
|
||||
ticketer.menu()
|
||||
option = input("$> ")
|
||||
if option == "1":
|
||||
ticketer.buy_ticket("normal")
|
||||
elif option == "2":
|
||||
ticketer.buy_ticket("premium")
|
||||
elif option == "3":
|
||||
ticketer.buy_ticket("VIP")
|
||||
elif option == "4":
|
||||
ticketer.buy_ticket("FLAG")
|
||||
elif option == "5":
|
||||
ticketer.refund_ticket()
|
||||
elif option == FLAG:
|
||||
ticketer.checker_menu()
|
||||
else:
|
||||
break
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,28 @@
|
||||
import hashlib
|
||||
from fastecdsa import curve, ecdsa
|
||||
|
||||
def H(m):
|
||||
return decode(hashlib.shake_256(m).digest(128))
|
||||
|
||||
def encode(x):
|
||||
return int.to_bytes(x, 32, 'big')
|
||||
|
||||
def decode(b):
|
||||
return int.from_bytes(b, 'big') % curve.secp256k1.q
|
||||
|
||||
def keygen(seed):
|
||||
pk = H(seed)
|
||||
vk = pk * curve.secp256k1.G
|
||||
|
||||
return pk, vk
|
||||
|
||||
def prove(m, pk):
|
||||
u, z = ecdsa.sign(m, pk, curve.secp256k1)
|
||||
return encode(u)+encode(z)
|
||||
|
||||
def verify(m, vk, proof):
|
||||
if len(proof) != 64:
|
||||
return False
|
||||
|
||||
u, z = decode(proof[:32]), decode(proof[32:])
|
||||
return ecdsa.verify((u, z), m, vk, curve.secp256k1)
|
||||
Reference in New Issue
Block a user