Files
attack-defense-platform/receiver/challenges/xvi/Pasta.py
T
Cyrene ae50acfe40 fix(ssh): per-challenge SSH login + phew buffering/leak/timeout
Passwords failed on 10/16 challenges while state.json looked correct:
- only the 6 native GEMASTIK XVIII images provision 'ctfuser'; every imported
  XVI/XVII image does 'echo root:${PASSWORD} | chpasswd' and logs in as root.
  set_ssh_passwords() hardcoded ctfuser, so chpasswd set a password on an
  account nobody uses -> 'Permission denied' everywhere.
  Registry gains a per-challenge 'ssh_user'; chpasswd now targets the real
  login (and ctfuser/ctf when present) and reports failures loudly.
- phew checker: chall.py block-buffers stdout through the docker exec pipe
  (PYTHONUNBUFFERED now set) and leaks chall.py inside the container on
  timeout (26 orphans, container saturated) -> reaps the whole exec process
  group. Startup does a fresh Pailier keygen (~12 s) so crypto reads need
  _CRYPTO_TIMEOUT, not the 5 s prompt default.

Adds panel/verify_ssh_creds.py (proves the state->container binding from
inside via a real login), audit_ssh_users.sh, reset_runtime.sh.
2026-09-26 14:40:10 +08:00

108 lines
3.9 KiB
Python

from .Challenge import Challenge
import requests
class Pasta(Challenge):
flag_location = 'flags/pasta.txt'
history_location = 'history/pasta.txt'
def distribute(self, flag):
try:
with open(self.flag_location, 'w') as f:
f.write(flag)
with open(self.history_location, 'a') as f:
f.write(flag + '\n')
self.logger.info(f'Flag {flag} written to {self.flag_location}')
return True
except Exception as e:
self.logger.error(f'Could not write flag to {self.flag_location}: {e}')
return False
def check(self):
try:
url = self.url()
username = f"checker-{self.random_string(8)}"
pwd = self.random_string(12)
flag = open(self.flag_location).read()
admin_data = {
'username': 'deomkicer_number_1_fans',
'password': getattr(self.settings, f'PASSWORD_{self.port}'),
}
# login admin and check flag
response = requests.post(
f"{url}/auth",
json=admin_data).json()
token = response.get('token')
assert token, "Token is missing in login admin"
check_flag = requests.get(f"{url}/flag", headers={'Authorization': f"Bearer {token}"}).json()
assert check_flag.get('flag') == flag, "Flag is missing/mismatch"
# register
response = requests.post(
f"{url}/register",
json={
"username": f"{username}",
"password": f"{pwd}"}).json()
assert response.get('success') == "User registered succesfully", "Register failed"
# login with version 1
response = requests.post(
f"{url}/auth?version=1",
json={
"username": f"{username}",
"password": f"{pwd}"}).json()
token = response.get('token')
assert token, "Token is missing in login v1"
check_home = requests.get(f"{url}/", headers={'Authorization': f"Bearer {token}"}).json()
assert check_home.get('username') == username, "Different username found in login v1"
# login with version 2
response = requests.post(
f"{url}/auth?version=2",
json={
"username": f"{username}",
"password": f"{pwd}"}).json()
token = response.get('token')
assert token, "Token is missing in login v2"
check_home = requests.get(f"{url}/", headers={'Authorization': f"Bearer {token}"}).json()
assert check_home.get('username') == username, "Different username found in login v2"
# login with version 3
response = requests.post(
f"{url}/auth?version=3",
json={
"username": f"{username}",
"password": f"{pwd}"}).json()
token = response.get('token')
assert token, "Token is missing in login v3"
check_home = requests.get(f"{url}/", headers={'Authorization': f"Bearer {token}"}).json()
assert check_home.get('username') == username, "Different username found in login v3"
# login with version 4
response = requests.post(
f"{url}/auth?version=4",
json={
"username": f"{username}",
"password": f"{pwd}"}).json()
token = response.get('token')
assert token, "Token is missing in login v4"
check_home = requests.get(f"{url}/", headers={'Authorization': f"Bearer {token}"}).json()
assert check_home.get('username') == username, "Different username found in login v4"
return True
except Exception as e:
self.logger.error(f'Could not check pasta: {e}')
return False