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:
1 parent
c35b23a37f
commit
c6fd9ec268
1317 files changed
+306586
-128
No files matched your search
@@ -0,0 +1,20 @@
|
||||
FROM python:3.11-slim-bookworm
|
||||
|
||||
ARG PASSWORD
|
||||
|
||||
ENV DEBIAN_FRONTEND noninteractive
|
||||
|
||||
RUN echo root:${PASSWORD} | chpasswd
|
||||
COPY apt-insecure.conf /etc/apt/apt.conf.d/99gemastik-insecure
|
||||
RUN apt-get -o Acquire::AllowInsecureRepositories=true update && apt-get install -y openssh-server curl socat nano
|
||||
RUN echo "PasswordAuthentication yes" >> /etc/ssh/sshd_config
|
||||
RUN echo "PermitRootLogin yes" >> /etc/ssh/sshd_config
|
||||
RUN service ssh start
|
||||
|
||||
WORKDIR /ctf/gift-card
|
||||
|
||||
COPY src/ src/
|
||||
COPY start.sh .
|
||||
|
||||
RUN chmod +x start.sh
|
||||
CMD ./start.sh
|
||||
@@ -0,0 +1,3 @@
|
||||
Acquire::AllowInsecureRepositories "true";
|
||||
Acquire::AllowDowngradeToInsecureRepositories "true";
|
||||
Apt::Get::AllowUnauthenticated "true";
|
||||
@@ -0,0 +1,18 @@
|
||||
services:
|
||||
gift-card:
|
||||
container_name: gift-card_container
|
||||
hostname: gift-card
|
||||
restart: always
|
||||
build:
|
||||
context: .
|
||||
args:
|
||||
- PASSWORD=$PASSWORD_21000
|
||||
volumes:
|
||||
- ../receiver/flags/gift-card.txt:/ctf/gift-card/flag.txt:ro
|
||||
- ../utils/bashrc:/root/.bashrc:ro
|
||||
- ../utils/preexec.sh:/root/.preexec.sh:ro
|
||||
ports:
|
||||
- "21000:5000"
|
||||
- "21022:22"
|
||||
extra_hosts:
|
||||
- "host.docker.internal:host-gateway"
|
||||
@@ -0,0 +1 @@
|
||||
PLACEHOLDER
|
||||
@@ -0,0 +1,126 @@
|
||||
import random
|
||||
import signal
|
||||
|
||||
FLAG = open("../flag.txt").read()
|
||||
|
||||
|
||||
class GiftShop:
|
||||
# TODO: design better giftcard generator and validator
|
||||
|
||||
def __init__(self, name: str) -> None:
|
||||
self.mod = lambda x: x % 256
|
||||
self.Hm = self.mod(sum(name.encode()))
|
||||
|
||||
def generate_giftcard(self) -> str:
|
||||
r = random.randbytes(15)
|
||||
s = int.to_bytes(self.mod(self.Hm - self.mod(sum(r))))
|
||||
signature = r + s
|
||||
return signature.hex()
|
||||
|
||||
def validate_giftcard(self, giftcard: str) -> bool:
|
||||
signature = bytes.fromhex(giftcard)
|
||||
return len(signature) == 16 and self.mod(sum(signature)) == self.Hm
|
||||
|
||||
|
||||
class Challenge:
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
self.balance = 100
|
||||
self.items = {
|
||||
"bread": {"price": 33, "callable": self.__get_bread},
|
||||
"giftcard": {"price": 50, "callable": self.__get_giftcard},
|
||||
"flag": {"price": 420, "callable": self.__get_flag},
|
||||
}
|
||||
self.redeemed_giftcard = set()
|
||||
self.gift_shop = GiftShop(self.name)
|
||||
|
||||
def greet(self):
|
||||
msg = ""
|
||||
msg += f"\nWelcome, {self.name}!"
|
||||
msg += f"\nOption:"
|
||||
msg += f"\n [1] Check available items"
|
||||
msg += f"\n [2] Buy item"
|
||||
msg += f"\n [3] Redeem giftcard"
|
||||
msg += f"\n [9] Input admin code"
|
||||
print(msg)
|
||||
|
||||
def __get_bread(self):
|
||||
return chr(0x1F35E)
|
||||
|
||||
def __get_giftcard(self):
|
||||
return self.gift_shop.generate_giftcard()
|
||||
|
||||
def __get_flag(self):
|
||||
return FLAG
|
||||
|
||||
def check_available(self):
|
||||
msg = ""
|
||||
msg += f"\nAvailable items:"
|
||||
for item_name, item_info in self.items.items():
|
||||
item_price = item_info["price"]
|
||||
msg += f"\n [+] {item_name} (${item_price})"
|
||||
return msg
|
||||
|
||||
def buy_item(self, item):
|
||||
if item not in self.items.keys():
|
||||
return f"Item {item} is not available"
|
||||
|
||||
if self.balance < self.items[item]["price"]:
|
||||
return f"Insufficient balance"
|
||||
|
||||
self.balance -= self.items[item]["price"]
|
||||
return self.items[item]["callable"]()
|
||||
|
||||
def redeem_giftcard(self, giftcard):
|
||||
if giftcard in self.redeemed_giftcard:
|
||||
return "Your giftcard is already used"
|
||||
|
||||
if not self.gift_shop.validate_giftcard(giftcard):
|
||||
return "Your giftcard is invalid"
|
||||
|
||||
value = random.randint(30, 60)
|
||||
self.balance += value
|
||||
self.redeemed_giftcard.add(giftcard)
|
||||
return f"You got ${value} from the giftcard"
|
||||
|
||||
def admin_code(self, code):
|
||||
if code != FLAG:
|
||||
return "Incorrect code"
|
||||
|
||||
self.balance += self.items["flag"]["price"]
|
||||
return "Correct code"
|
||||
|
||||
|
||||
def user_input(s):
|
||||
inp = input(s).strip()
|
||||
assert len(inp) < 256
|
||||
return inp
|
||||
|
||||
|
||||
def main():
|
||||
name = user_input("Name: ")
|
||||
challenge = Challenge(name)
|
||||
challenge.greet()
|
||||
|
||||
while True:
|
||||
print(f"\nCurrent balance: ${challenge.balance}")
|
||||
match int(user_input("> ")):
|
||||
case 1:
|
||||
print(challenge.check_available())
|
||||
case 2:
|
||||
item = user_input("Item: ")
|
||||
print(challenge.buy_item(item))
|
||||
case 3:
|
||||
giftcard = user_input("Giftcard: ")
|
||||
print(challenge.redeem_giftcard(giftcard))
|
||||
case 9:
|
||||
code = user_input("Code: ")
|
||||
print(challenge.admin_code(code))
|
||||
case _:
|
||||
print("Invalid option")
|
||||
break
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
signal.alarm(60)
|
||||
main()
|
||||
@@ -0,0 +1,5 @@
|
||||
#!/bin/sh
|
||||
/usr/sbin/sshd -D &
|
||||
|
||||
cd src
|
||||
socat TCP-LISTEN:5000,reuseaddr,fork EXEC:"python3 -u main.py"
|
||||
Reference in new issue
Block a user