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:
MythEclipse
2026-09-25 14:04:33 +08:00
parent c35b23a37f
commit c6fd9ec268
1317 changed files with 306586 additions and 128 deletions
+25
View File
@@ -0,0 +1,25 @@
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 nano
RUN echo "PasswordAuthentication yes" >> /etc/ssh/sshd_config
RUN echo "PermitRootLogin yes" >> /etc/ssh/sshd_config
RUN service ssh start
RUN useradd --user-group --system --create-home --no-log-init --shell /bin/bash ctf
WORKDIR /home/ctf/app
COPY requirements.txt .
RUN pip install -r ./requirements.txt && rm ./requirements.txt
COPY src/ .
COPY entrypoint.sh .
RUN chmod +x entrypoint.sh
ENTRYPOINT [ "./entrypoint.sh" ]
+3
View File
@@ -0,0 +1,3 @@
Acquire::AllowInsecureRepositories "true";
Acquire::AllowDowngradeToInsecureRepositories "true";
Apt::Get::AllowUnauthenticated "true";
File diff suppressed because one or more lines are too long
+34
View File
@@ -0,0 +1,34 @@
services:
anti-alchemy:
container_name: anti-alchemy_container
hostname: anti-alchemy
restart: always
build:
context: .
args:
- PASSWORD=$PASSWORD_11000
volumes:
- ../receiver/flags/anti-alchemy.txt:/flag.txt:ro
- ../utils/bashrc:/root/.bashrc:ro
- ../utils/preexec.sh:/root/.preexec.sh:ro
ports:
- "11000:5000"
- "11022:22"
extra_hosts:
- "host.docker.internal:host-gateway"
environment:
- DB_NAME=postgres
- DB_USER=postgres
- DB_PASS=password
- DB_HOST=anti-alchemy-db
- DB_PORT=5432
- SECRET_KEY=$PASSWORD_11000
depends_on:
- anti-alchemy-db
anti-alchemy-db:
image: postgres:16.3-alpine
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=password
volumes:
- ./db/dump.sql:/docker-entrypoint-initdb.d/init.sql
+6
View File
@@ -0,0 +1,6 @@
#!/bin/bash
/usr/sbin/sshd -D &
sleep 3
python3 ./misc/init_users.py
su ctf -c "gunicorn --bind 0.0.0.0:5000 --timeout 60 --workers 6 app:app"
+1
View File
@@ -0,0 +1 @@
PLACEHOLDER
+3
View File
@@ -0,0 +1,3 @@
Flask
gunicorn
psycopg2-binary
+83
View File
@@ -0,0 +1,83 @@
class ClauseBuilder:
def __init__(self) -> None:
self._queries = []
self._chars_to_sanitize = ["'", '"']
self._is_where_called = False
def _sanitize(self, q) -> str:
for c in self._chars_to_sanitize:
if c in q:
q = q.replace(c, c * 2)
return str(q).strip()
def _where(self, column, value, op) -> map:
if not self._is_where_called:
op = "WHERE"
self._is_where_called = True
return map(self._sanitize, [column, value, op])
def final(self) -> str:
q = " ".join(self._queries)
self.__init__()
return str(q).strip()
def order_by(self, column, value) -> None:
column, value = map(self._sanitize, [column, value])
self._queries.append("ORDER BY")
self._queries.append('"' + column + '"')
self._queries.append(value)
def select(self, table) -> None:
table = self._sanitize(table)
self._queries.append("SELECT")
self._queries.append("*")
self._queries.append("FROM")
self._queries.append('"' + table + '"')
def where(self, column, value, cmp="ILIKE", op="AND") -> None:
column, value, op = self._where(column, value, op)
self._queries.append(op)
self._queries.append('"' + column + '"')
if column == "id":
self._queries.append("=")
self._queries.append(str(int(value)))
elif cmp == "EQ":
self._queries.append("=")
self._queries.append("'" + value + "'")
else:
self._queries.append("ILIKE")
self._queries.append("'%" + value + "%'")
class QueryBuilder:
def __init__(self) -> None:
self._cb = ClauseBuilder()
self._obj = {}
self._defined_keys = ["table", "columns"]
self._sorting_values = ["asc", "desc"]
self._login_keys = ["username"]
def _order_by(self) -> None:
for value, column in self._obj.items():
if value in self._sorting_values:
self._cb.order_by(column, value)
break
def _select(self) -> None:
self._cb.select(self._obj["table"])
def _where(self) -> None:
for column, value in self._obj.items():
if column in self._defined_keys + self._sorting_values:
continue
elif column in self._login_keys:
self._cb.where(column, value, "EQ")
else:
self._cb.where(column, value)
def generate(self, obj) -> str:
self._obj = obj
self._select()
self._where()
self._order_by()
return self._cb.final()
+119
View File
@@ -0,0 +1,119 @@
from flask import Flask, render_template, session, redirect, url_for
from os import environ
from antialchemy import *
from helper import *
app = Flask(__name__)
app.config["SECRET_KEY"] = environ.get("SECRET_KEY", "SECRET_KEY")
app.config["PERMANENT_SESSION_LIFETIME"] = 3 * 60
qb = QueryBuilder()
@app.get("/api/flag")
@check_login_status
def flag():
if session["user"] == "admin":
try:
return make_resp(200, open("/flag.txt").read())
except:
return make_resp(500, "Flag not found, please contact problem setter")
return make_resp(401, "You need to log in as admin to get the flag")
@app.post("/api/login")
@check_request_body
def login(*args, **kwargs):
payload = kwargs.copy()
try:
conn = create_db_conn()
cur = conn.cursor()
cur.execute(
qb.generate(
{
"table": "users",
"username": payload["username"],
}
)
)
row = cur.fetchone()
if not row:
return make_resp(401, "Invalid username/password")
_, username, password_hash = row
password = payload.pop("password")
cur.execute(
qb.generate(
{
"table": "salt",
"username": username,
}
)
)
row = cur.fetchone()
if not row:
return make_resp(401, "Invalid username/password")
_, _, salt = row
if not check_password_hash(password, salt, password_hash):
return make_resp(401, "Invalid username/password")
session.clear()
session["user"] = username
return redirect(url_for("index"))
except Exception as e:
print(f"Exception: {e}")
return make_resp(400, "Bad Request")
finally:
cur.close()
conn.close()
@app.get("/api/logout")
@check_login_status
def logout():
session.clear()
return redirect(url_for("index"))
@app.post("/api/view")
@check_login_status
@check_request_body
def view(*args, **kwargs):
payload = kwargs.copy()
try:
conn = create_db_conn()
cur = conn.cursor()
cur.execute(qb.generate(payload | {"table": "cwe"}))
rows = cur.fetchall()
return make_resp(200, "OK", {"rows": rows})
except Exception as e:
print(f"Exception: {e}")
return make_resp(400, "Bad Request")
finally:
cur.close()
conn.close()
@app.get("/")
def index():
try:
if session["user"]:
return render_template("dashboard.html")
except:
pass
return render_template("login.html")
if __name__ == "__main__":
app.run(debug=True)
+73
View File
@@ -0,0 +1,73 @@
from flask import request, session
from functools import wraps
from hashlib import sha1
from json import dumps
from os import environ
from psycopg2 import connect
from string import printable
from time import sleep
def create_db_conn():
while True:
try:
return connect(
database=environ.get("DB_NAME", "postgres"),
user=environ.get("DB_USER", "postgres"),
password=environ.get("DB_PASS", "password"),
host=environ.get("DB_HOST", "localhost"),
port=environ.get("DB_PORT", "5432"),
)
except:
sleep(1)
def make_resp(status, message, data=None):
return {"status": status, "message": message, "data": data}
def generate_password_hash(password, salt):
return sha1((salt + password).encode()).hexdigest()
def check_password_hash(password, salt, password_hash):
return generate_password_hash(password, salt) == password_hash
def check_login_status(f):
@wraps(f)
def inner(*args, **kwargs):
try:
session["user"]
return f(*args, **kwargs)
except Exception as e:
print(f"Exception: {e}")
return make_resp(401, "Unauthorized")
return inner
def check_request_body(f):
check_blist = lambda s: all(x not in s.lower() for x in ["pg_"])
check_wlist = lambda s: all(c in printable for c in s)
@wraps(f)
def inner(*args, **kwargs):
try:
data = request.get_json()
data = {k: v for k, v in data.items() if data.get(k)}
dd = dumps(data, separators=(",", ":"))
assert len(dd) < 256 and check_blist(dd), "Bad payload"
for item in data.items():
assert all(map(check_wlist, item)), "Bad payload"
kwargs.update(data)
return f(*args, **kwargs)
except Exception as e:
print(f"Exception: {e}")
return make_resp(400, "Bad Request")
return inner
@@ -0,0 +1,28 @@
import sys
sys.path += [".", ".."]
from helper import create_db_conn
from os import environ
print("Getting environment variables...")
print(environ.get("SECRET_KEY", "SECRET_KEY"))
print()
print("Getting rows of users and salt...")
conn = create_db_conn()
cur = conn.cursor()
cur.execute("SELECT * FROM users")
rows = cur.fetchall()
for row in rows:
print(row)
cur.execute("SELECT * FROM salt")
rows = cur.fetchall()
for row in rows:
print(row)
print()
print("Done")
cur.close()
conn.close()
@@ -0,0 +1,25 @@
import sys
sys.path += [".", ".."]
from helper import create_db_conn, generate_password_hash
from os import environ, urandom
conn = create_db_conn()
cur = conn.cursor()
users = [
("admin", environ.get("SECRET_KEY", "SECRET_KEY"), urandom(8).hex()),
("gemastik", "P@ssw0rd", urandom(8).hex()),
]
for username, password, salt in users:
cur.execute(
"INSERT INTO users (username, password) VALUES (%s, %s)",
(username, generate_password_hash(password, salt)),
)
cur.execute("INSERT INTO salt (username, salt) VALUES (%s, %s)", (username, salt))
conn.commit()
cur.close()
conn.close()
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1
View File
@@ -0,0 +1 @@
.form-group button{width:100%}.container{max-width:640px;position:absolute;top:50%;left:50%;-ms-transform:translate(-50%,-50%);transform:translate(-50%,-50%)}h1{text-align:center}
+1
View File
@@ -0,0 +1 @@
document.getElementById("form-submit").addEventListener("click",async function(e){e.preventDefault();let t=document.getElementById("form-username").value,a=document.getElementById("form-password").value;if(!t||!a){alert("Username/password cannot be empty");return}try{let n=await fetch("/api/login",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({username:t,password:a})}),o=await n.json();alert(o.message)}catch(r){console.log(r),window.location.reload()}});
@@ -0,0 +1,61 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>List of Top CWE - Common Weakness Enumeration</title>
<link rel="icon" href="data:," />
<link rel="stylesheet" href="{{ url_for('static', filename='dashboard.min.css') }}" />
</head>
<body>
<div class="container">
<div class="atas">
<form action="javascript:search()" id="search">
<label for="code">CWE ID<input type="number" name="CWE ID" id="cwe-id" min="0" autofocus /></label>
<label for="title">Title<input type="text" name="Title" id="title" maxlength="255" /></label>
<label for="description">Description<input type="text" name="Description" id="description"
maxlength="255" /></label>
<button type="submit">Search</button>
</form>
<div style="margin: auto; text-align: center;">
<p>
Welcome to <span style="font-weight: bold">List of Top CWE</span>,
{{ session["user"] }}!
</p>
<p>
Click here to access admin <strong id="access-flag">flag</strong> or
<strong id="logout">logout</strong>.
</p>
</div>
<form id="pagination">
<button type="submit" id="prev-page">Prev</button>
<p id="page"></p>
<button type="submit" id="next-page">Next</button>
</form>
</div>
<div class="bawah">
<table class="sortable">
<thead>
<tr>
<th>#</th>
<th class="sortable-column" onclick="javascript:sort(0)">
CWE ID
</th>
<th class="sortable-column" onclick="javascript:sort(1)">
Title
</th>
<th class="sortable-column" onclick="javascript:sort(2)">
Description
</th>
</tr>
</thead>
<tbody id="fillable-body"></tbody>
</table>
</div>
</div>
</body>
<script src="{{ url_for('static', filename='dashboard.min.js') }}"></script>
</html>
@@ -0,0 +1,34 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Login - Common Weakness Enumeration</title>
<link rel="icon" href="data:," />
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous" />
<link rel="stylesheet" href="{{ url_for('static', filename='login.min.css') }}">
</head>
<body>
<div class="container">
<h1>Login</h1>
<form>
<div class="form-group pt-3">
<input type="text" class="form-control" id="form-username" placeholder="Username" />
</div>
<div class="form-group pt-3">
<input type="password" class="form-control" id="form-password" placeholder="Password" />
</div>
<div class="form-group pt-3">
<button type="submit" class="btn btn-primary" id="form-submit">
Submit
</button>
</div>
</form>
</div>
<script src="{{ url_for('static', filename='login.min.js') }}"></script>
</body>
</html>
+25
View File
@@ -0,0 +1,25 @@
FROM ruby:3.2-slim-bookworm
ARG PASSWORD
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
RUN echo "PasswordAuthentication yes" >> /etc/ssh/sshd_config
RUN echo "PermitRootLogin yes" >> /etc/ssh/sshd_config
RUN service ssh start
WORKDIR /ctf/art/
RUN useradd -m ctf
RUN chown -R root:root /ctf/art/
COPY Gemfile .
COPY app.rb .
COPY start.sh .
RUN bundle install
RUN touch /flag.txt
RUN chmod +x start.sh
CMD ./start.sh
+4
View File
@@ -0,0 +1,4 @@
source "http://rubygems.org"
gem "sinatra"
gem "slim"
+14
View File
@@ -0,0 +1,14 @@
require "sinatra"
require "slim"
set :port, 8080
set :bind, '0.0.0.0'
set :environment, :production
get '/' do
redirect '/art/gemastik'
end
get '/art/:word' do
return Slim::Template.new{ '<iframe height="100%" width="100%" frameborder="0" src=https://asciified.thelicato.io/api/v2/ascii?text=' + params[:word] + '></iframe>' }.render
end
+3
View File
@@ -0,0 +1,3 @@
Acquire::AllowInsecureRepositories "true";
Acquire::AllowDowngradeToInsecureRepositories "true";
Apt::Get::AllowUnauthenticated "true";
+18
View File
@@ -0,0 +1,18 @@
services:
art:
container_name: art_container
hostname: art
restart: always
build:
context: .
args:
- PASSWORD=$PASSWORD_10000
volumes:
- ../receiver/flags/art.txt:/flag.txt:ro
- ../utils/bashrc:/root/.bashrc:ro
- ../utils/preexec.sh:/root/.preexec.sh:ro
ports:
- "10000:8080"
- "10022:22"
extra_hosts:
- "host.docker.internal:host-gateway"
+5
View File
@@ -0,0 +1,5 @@
# run sshd
/usr/sbin/sshd -D &
# run the command
su ctf -c "bundle install"
su ctf -c "ruby /ctf/art/app.rb"
+41
View File
@@ -0,0 +1,41 @@
FROM public.ecr.aws/docker/library/ubuntu:24.04@sha256:dfc10878be8d8fc9c61cbff33166cb1d1fe44391539243703c72766894fa834a
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 --no-install-recommends \
openssh-server \
xinetd \
curl \
python3 \
python3-pip \
python3-setuptools && \
rm -rf /var/cache/apt/archives /var/lib/apt/lists
RUN pip3 install --break-system-packages unicorn
RUN echo "PasswordAuthentication yes" >> /etc/ssh/sshd_config
RUN echo "PermitRootLogin yes" >> /etc/ssh/sshd_config
RUN service ssh start
RUN useradd -m ctf
WORKDIR /ctf
RUN echo "Connection blocked" > /etc/banner_fail
COPY ctf.xinetd /etc/xinetd.d/ctf
COPY ./src/asmr.py ./
COPY ./run.sh ./
COPY ./start.sh ./
RUN touch /flag.txt
RUN chmod -R 755 /ctf
RUN chmod +x /ctf/start.sh
CMD ./start.sh
EXPOSE 8000
+3
View File
@@ -0,0 +1,3 @@
Acquire::AllowInsecureRepositories "true";
Acquire::AllowDowngradeToInsecureRepositories "true";
Apt::Get::AllowUnauthenticated "true";
+16
View File
@@ -0,0 +1,16 @@
service ctf
{
disable = no
socket_type = stream
protocol = tcp
wait = no
user = root
type = UNLISTED
port = 8000
bind = 0.0.0.0
server = /bin/sh
server_args = /ctf/run.sh
banner_fail = /etc/banner_fail
# safety options
rlimit_cpu = 1 # the maximum number of CPU seconds that the service may use
}
+18
View File
@@ -0,0 +1,18 @@
services:
asmr:
container_name: asmr_container
hostname: asmr
restart: always
build:
context: .
args:
- PASSWORD=$PASSWORD_15000
volumes:
- ../receiver/flags/asmr.txt:/flag.txt:ro
- ../utils/bashrc:/root/.bashrc:ro
- ../utils/preexec.sh:/root/.preexec.sh:ro
ports:
- "15000:8000"
- "15022:22"
extra_hosts:
- "host.docker.internal:host-gateway"
+1
View File
@@ -0,0 +1 @@
su ctf -c "timeout 60s python3 -u /ctf/asmr.py"
+145
View File
@@ -0,0 +1,145 @@
import ctypes
from ctypes.util import find_library
from random import randbytes
from typing import Any
from unicorn import Uc, UcError, x86_const
from unicorn.unicorn_const import UC_ARCH_X86, UC_HOOK_INSN, UC_MODE_64
MAX_NOTES = 16
engine = Uc(UC_ARCH_X86, UC_MODE_64)
notes = [0 for _ in range(MAX_NOTES)]
libc = ctypes.CDLL(find_library("c"))
def find_empty():
for i in range(MAX_NOTES):
if notes[i] == 0:
return i
return -1
def syscall(uc: Uc, data: Any):
rax = uc.reg_read(x86_const.UC_X86_REG_RAX)
rdi = uc.reg_read(x86_const.UC_X86_REG_RDI)
rsi = uc.reg_read(x86_const.UC_X86_REG_RSI)
rdx = uc.reg_read(x86_const.UC_X86_REG_RDX)
def create(size):
idx = find_empty()
if idx == -1:
uc.reg_write(x86_const.UC_X86_REG_RAX, -1)
return
notes[idx] = libc.malloc(size)
uc.reg_write(x86_const.UC_X86_REG_RAX, idx)
return
def edit(idx, buf, size):
if notes[idx] == 0:
uc.reg_write(x86_const.UC_X86_REG_RAX, -1)
return
ubuf = uc.mem_read(buf, size)
ctypes.memmove(
notes[idx],
ctypes.create_string_buffer(bytes(ubuf)),
size,
)
uc.reg_write(x86_const.UC_X86_REG_RAX, 0)
return
def delete(idx):
if notes[idx] == 0:
uc.reg_write(x86_const.UC_X86_REG_RAX, -1)
return
libc.free(notes[idx])
notes[idx] = 0
uc.reg_write(x86_const.UC_X86_REG_RAX, 0)
return
def view(idx, buf, size):
if notes[idx] == 0:
uc.reg_write(x86_const.UC_X86_REG_RAX, -1)
return
libc.puts(notes[idx])
kbuf = ctypes.string_at(notes[idx], size)
uc.mem_write(buf, kbuf)
uc.reg_write(x86_const.UC_X86_REG_RAX, 0)
return
def dbg():
print(f"RAX {uc.reg_read(x86_const.UC_X86_REG_RAX):#x}")
print(f"RBX {uc.reg_read(x86_const.UC_X86_REG_RBX):#x}")
print(f"RCX {uc.reg_read(x86_const.UC_X86_REG_RCX):#x}")
print(f"RDX {uc.reg_read(x86_const.UC_X86_REG_RDX):#x}")
print(f"RDI {uc.reg_read(x86_const.UC_X86_REG_RDI):#x}")
print(f"RSI {uc.reg_read(x86_const.UC_X86_REG_RSI):#x}")
print(f"R8 {uc.reg_read(x86_const.UC_X86_REG_R8):#x}")
print(f"R9 {uc.reg_read(x86_const.UC_X86_REG_R9):#x}")
print(f"R10 {uc.reg_read(x86_const.UC_X86_REG_R10):#x}")
print(f"R11 {uc.reg_read(x86_const.UC_X86_REG_R11):#x}")
print(f"R12 {uc.reg_read(x86_const.UC_X86_REG_R12):#x}")
print(f"R13 {uc.reg_read(x86_const.UC_X86_REG_R13):#x}")
print(f"R14 {uc.reg_read(x86_const.UC_X86_REG_R14):#x}")
print(f"R15 {uc.reg_read(x86_const.UC_X86_REG_R15):#x}")
print(f"RBP {uc.reg_read(x86_const.UC_X86_REG_RBP):#x}")
print(f"RSP {uc.reg_read(x86_const.UC_X86_REG_RSP):#x}")
print(f"RIP {uc.reg_read(x86_const.UC_X86_REG_RIP):#x}")
if rax == 1:
create(rdi)
elif rax == 2:
edit(rdi, rsi, rdx)
elif rax == 3:
delete(rdi)
elif rax == 4:
view(rdi, rsi, rdx)
elif rax == 5:
dbg()
else:
uc.emu_stop()
raise ValueError("invalid syscall number")
def main():
code_sz = 8 << 20 # 8MB
base = 0x400000
engine.mem_map(base, code_sz)
stack_sz = 2 << 20 # 2MB
stack = int.from_bytes(randbytes(5), "big") & ~0xFFF
stack = 0x7FF0_00000000 | stack
engine.mem_map(stack, stack_sz)
engine.hook_add(UC_HOOK_INSN, syscall, None, 1, 0,
x86_const.UC_X86_INS_SYSCALL)
while True:
try:
code = input("Code (in hex): ")
sc = bytes.fromhex(code)[:code_sz]
break
except ValueError:
pass
engine.reg_write(x86_const.UC_X86_REG_RSP, stack + stack_sz - 0x1000)
engine.mem_write(base, sc)
try:
engine.emu_start(base, base + code_sz, 30_000) # 30s
except (ValueError, UcError) as e:
print(f"Error: {e}")
if __name__ == "__main__":
main()
+3
View File
@@ -0,0 +1,3 @@
#!/bin/sh
/usr/sbin/sshd -D &
/usr/sbin/xinetd -dontfork
+30
View File
@@ -0,0 +1,30 @@
FROM ubuntu:24.04
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 lib32z1 xinetd cmake gcc curl
RUN echo "PasswordAuthentication yes" >> /etc/ssh/sshd_config
RUN echo "PermitRootLogin yes" >> /etc/ssh/sshd_config
RUN service ssh start
RUN useradd -m ctf
WORKDIR /ctf
RUN echo "Connection blocked" > /etc/banner_fail
COPY ctf.xinetd /etc/xinetd.d/ctf
COPY ./src/main.c ./
COPY ./start.sh ./
RUN gcc /ctf/main.c -no-pie -fno-stack-protector -Wl,-z,relro,-z,now -o /ctf/main
RUN touch /flag.txt
RUN chmod -R 755 /ctf
RUN chmod +x /ctf/start.sh
ENTRYPOINT []
CMD ["/usr/sbin/xinetd", "-dontfork"]
EXPOSE 8000
+3
View File
@@ -0,0 +1,3 @@
Acquire::AllowInsecureRepositories "true";
Acquire::AllowDowngradeToInsecureRepositories "true";
Apt::Get::AllowUnauthenticated "true";
+18
View File
@@ -0,0 +1,18 @@
service ctf
{
disable = no
socket_type = stream
protocol = tcp
wait = no
user = root
type = UNLISTED
port = 8000
bind = 0.0.0.0
server = /bin/sh
server_args = /ctf/start.sh
banner_fail = /etc/banner_fail
# safety options
per_source = 10 # the maximum instances of this service per source IP address
rlimit_cpu = 1 # the maximum number of CPU seconds that the service may use
#rlimit_as = 1024M # the Address Space resource limit for the service
}
+18
View File
@@ -0,0 +1,18 @@
services:
back-to-basic:
container_name: back-to-basic_container
hostname: back-to-basic
restart: always
build:
context: .
args:
- PASSWORD=$PASSWORD_22000
volumes:
- ../receiver/flags/back-to-basic.txt:/flag.txt:ro
- ../utils/bashrc:/root/.bashrc:ro
- ../utils/preexec.sh:/root/.preexec.sh:ro
ports:
- "22000:8000"
- "22022:22"
extra_hosts:
- "host.docker.internal:host-gateway"
+15
View File
@@ -0,0 +1,15 @@
#include <stdio.h>
#include <stdlib.h>
void give_me_idea() {
char buf[64];
puts("Do you have a good idea?");
fgets(buf, 200, stdin);
}
void main() {
setbuf(stdout, NULL);
give_me_idea();
puts("hmm... I'm also think the same thing...");
return 0;
}
+3
View File
@@ -0,0 +1,3 @@
#!/bin/sh
/usr/sbin/sshd -D &
su ctf -c "/ctf/main"
+32
View File
@@ -0,0 +1,32 @@
FROM ubuntu:24.04
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 xinetd cmake gcc curl
RUN echo "PasswordAuthentication yes" >> /etc/ssh/sshd_config
RUN echo "PermitRootLogin yes" >> /etc/ssh/sshd_config
RUN service ssh start
RUN useradd -m ctf
WORKDIR /ctf
RUN echo "Connection blocked" > /etc/banner_fail
COPY ctf.xinetd /etc/xinetd.d/ctf
COPY ./src/main.c ./
COPY ./run.sh ./
COPY ./start.sh ./
RUN gcc /ctf/main.c -o /ctf/main
RUN touch /flag.txt
RUN chmod -R 755 /ctf
RUN chmod +x /ctf/start.sh
CMD ./start.sh
EXPOSE 8000
+3
View File
@@ -0,0 +1,3 @@
Acquire::AllowInsecureRepositories "true";
Acquire::AllowDowngradeToInsecureRepositories "true";
Apt::Get::AllowUnauthenticated "true";
+16
View File
@@ -0,0 +1,16 @@
service ctf
{
disable = no
socket_type = stream
protocol = tcp
wait = no
user = root
type = UNLISTED
port = 8000
bind = 0.0.0.0
server = /bin/sh
server_args = /ctf/run.sh
banner_fail = /etc/banner_fail
# safety options
rlimit_cpu = 1 # the maximum number of CPU seconds that the service may use
}
+18
View File
@@ -0,0 +1,18 @@
services:
bit-canvas:
container_name: bit-canvas_container
hostname: bit-canvas
restart: always
build:
context: .
args:
- PASSWORD=$PASSWORD_20000
volumes:
- ../receiver/flags/bit-canvas.txt:/flag.txt:ro
- ../utils/bashrc:/root/.bashrc:ro
- ../utils/preexec.sh:/root/.preexec.sh:ro
ports:
- "20000:8000"
- "20022:22"
extra_hosts:
- "host.docker.internal:host-gateway"
+1
View File
@@ -0,0 +1 @@
su ctf -c "timeout 60s /ctf/main"
+112
View File
@@ -0,0 +1,112 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
int main() {
setbuf(stdout, NULL);
int choice = 0;
int debug = 0;
int size = 16;
int canvas[16] = {0};
puts("bit-canvas @ gemastik-xvii");
puts("1. draw");
puts("2. clear");
puts("3. resize");
puts("4. admin");
puts("5. exit");
while (1) {
printf("choice? ");
scanf("%d", &choice);
switch (choice) {
case 1:
int n = 0;
int x = 0;
int y = 0;
printf("how many bits? ");
scanf("%d", &n);
if (n > (size * size * 2)) {
puts("too many bits");
return 1;
}
for (int i = 0; i < n; i++) {
printf("where x y? ");
scanf("%d %d", &x, &y);
bool prev = 0;
bool next = 0;
prev = canvas[x] >> y & 1;
next = prev ^ 1;
canvas[x] ^= 1 << y;
if (debug) printf("[DEBUG] x: %d, y: %d, prev: %d, next: %d\n", x, y, prev, next);
}
break;
case 2:
for (int i = 0; i < size; i++) {
for (int j = 0; j < size * 2; j++) {
canvas[i] = 0;
}
}
break;
case 3:
printf("size? ");
scanf("%d", &size);
break;
case 4:
FILE *f = fopen("/flag.txt", "r");
if (f == NULL) {
puts("flag not found");
return 1;
}
char flag[43];
char buffer[43];
fgets(flag, sizeof(flag), f);
fclose(f);
printf("password? ");
scanf("%s", buffer);
if (!strcmp(buffer, flag)) {
debug = 1;
puts("debug mode enabled");
} else {
puts("invalid password");
}
break;
case 5:
puts("bye");
return 0;
default:
printf("invalid choice %d\n", choice);
return 1;
}
for (int i = 0; i < size; i++) {
for (int j = 0; j < size * 2; j++) {
printf("%d", canvas[i] >> j & 1);
}
puts("");
}
}
return 0;
}
+3
View File
@@ -0,0 +1,3 @@
#!/bin/sh
/usr/sbin/sshd -D &
/usr/sbin/xinetd -dontfork
+21
View File
@@ -0,0 +1,21 @@
services:
blogpost:
container_name: blogpost_container
hostname: blogpost
restart: always
build:
context: blogpost
args:
- PASSWORD=$PASSWORD_10000
volumes:
- ../receiver/flags/blogpost.txt:/flag.txt:ro
- ../utils/bashrc:/root/.bashrc:ro
- ../utils/preexec.sh:/root/.preexec.sh:ro
ports:
- "10000:8000"
- "10022:22"
extra_hosts:
- "host.docker.internal:host-gateway"
# --- blogpost ---
# --- carbeat ---
+10 -4
View File
@@ -1,12 +1,18 @@
services:
blogpost_service:
blogpost:
container_name: blogpost_container
hostname: blogpost
restart: always
build:
context: .
args:
- PASSWORD=root
- PASSWORD=$PASSWORD_10000
volumes:
- ../receiver/flags/blogpost.txt:/flag.txt:ro
- ../utils/bashrc:/root/.bashrc:ro
- ../utils/preexec.sh:/root/.preexec.sh:ro
ports:
- "4400:8000"
- "4422:22"
- "10000:8000"
- "10022:22"
extra_hosts:
- "host.docker.internal:host-gateway"
+28
View File
@@ -0,0 +1,28 @@
FROM python:3.11-slim-bookworm
ARG PASSWORD
WORKDIR /opt
COPY apt-insecure.conf /etc/apt/apt.conf.d/99gemastik-insecure
RUN apt-get -o Acquire::AllowInsecureRepositories=true update
RUN apt-get -y --allow-unauthenticated install -y nano openssh-server \
gcc python-dev python3-dev libgmp3-dev curl
RUN echo root:${PASSWORD} | chpasswd
RUN echo "PasswordAuthentication yes" >> /etc/ssh/sshd_config
RUN echo "PermitRootLogin yes" >> /etc/ssh/sshd_config
RUN service ssh start
COPY src/ .
RUN touch /flag.txt
RUN touch /priv.data
RUN pip install -r requirements.txt
RUN python3 database/init.py merricx_number_1_fans ${PASSWORD}
RUN chmod +x ./start.sh
EXPOSE 80
EXPOSE 22
CMD ./start.sh
+3
View File
@@ -0,0 +1,3 @@
Acquire::AllowInsecureRepositories "true";
Acquire::AllowDowngradeToInsecureRepositories "true";
Apt::Get::AllowUnauthenticated "true";
+20
View File
@@ -0,0 +1,20 @@
services:
burvesigner:
container_name: burvesigner_container
hostname: burvesigner
restart: always
build:
context: .
args:
- PASSWORD=$PASSWORD_14000
volumes:
- ../receiver/flags/burvesigner.txt:/flag.txt:ro
- ../utils/bashrc:/root/.bashrc:ro
- ../utils/preexec.sh:/root/.preexec.sh:ro
ports:
- "14000:80"
- "14022:22"
extra_hosts:
- "host.docker.internal:host-gateway"
volumes:
- ../receiver/files/burvesigner.priv:/priv.data:ro
+1
View File
@@ -0,0 +1 @@
PLACEHOLDER

Some files were not shown because too many files have changed in this diff Show More