This repository has been archived on 2024-02-18. You can view files and clone it, but cannot push or open issues or pull requests.

240 lines
7.8 KiB

#!/usr/bin/env python3
import getpass
import requests
import time
from bs4 import BeautifulSoup
default_user = 'SecT0uch'
s = requests.Session()
def login(user):
print('Logging in...')
api_login_endpoint = ''
pw = getpass.getpass()
# Send the login request
params = {'login': user, 'password': pw}
r = s.get(api_login_endpoint, params=params)
# Get the session cookie
token = r.json()[0]['info']['spip_session']
# Set the session cookie as default for next requests
cookie = {'spip_session': token}
def col_print(lines, term_width=None, indent=0, pad=2):
if not term_width:
import shutil
size = shutil.get_terminal_size((80, 20))
term_width = size.columns
n_lines = len(lines)
if n_lines == 0:
col_width = max(len(line) for line in lines)
n_cols = int((term_width + pad - indent)/(col_width + pad))
n_cols = min(n_lines, max(1, n_cols))
col_len = int(n_lines/n_cols) + (0 if n_lines % n_cols == 0 else 1)
if (n_cols - 1) * col_len >= n_lines:
n_cols -= 1
cols = [lines[i*col_len: i*col_len + col_len] for i in range(n_cols)]
rows = list(zip(*cols))
rows_missed = zip(*[col[len(rows):] for col in cols[:-1]])
for row in rows:
print(" "*indent + (" "*pad).join(line.ljust(col_width) for line in row))
def is_ctf_started(user):
print('Checking if a CTF is already started...')
url = ''
r = s.get(url)
soup = BeautifulSoup(r.text, 'html.parser')
for room in soup.find_all('tr', class_=['row_even', 'row_odd']):
room_users = room.find_all('a', class_='forum')
for room_user in room_users:
room_user = room_user.string
if room_user == user:
room_name = room.find('a').string
room_id = room.find('a').get('href').split('id_salle=')[1].split('&')[0]
print(f' ==> You are registered in {room_name}')
return room_id, room_name
print(' ==> You are not registered in any room yet.')
def get_ctf_list():
print('Getting CTF list...')
api_ctf_endpoint = ''
r = s.get(api_ctf_endpoint)
ctf_list = []
while True: # For each page
results = r.json()
ctfs = results[0]
for key in ctfs:
ctf = ctfs[key]
id = ctf['id_environnement_virtuel'].ljust(3, ' ')
name = ctf['nom']
ctf_list.append(f"{id} - {name}")
# Check if next page
type = results[-1]['rel']
if type == 'previous': # If last page break
elif type == "next":
next_page = results[-1]['href']
time.sleep(0.5) # Avoid getting HTTP 429 (Too Many Requests)
r = s.get(next_page)
def start_ctf(ctf_id):
def get_free_room():
print('Finding a free room...')
url = ''
r = s.get(url)
soup = BeautifulSoup(r.text, 'html.parser')
for room in soup.find_all('tr', class_=['row_even', 'row_odd']):
if room.find('img', alt='waiting'):
freeroom_name = room.find('a').string
freeroom_id = room.find('a').get('href').split('id_salle=')[1].split('&')[0]
print(f" ==> Room {freeroom_name} is free!")
return freeroom_name, freeroom_id
freeroom_name, freeroom_id = get_free_room()
params = {'lang': 'en', 'page': 'ctf_alltheday', 'id_salle': freeroom_id}
r = s.get('', params=params)
soup = BeautifulSoup(r.text, 'html.parser')
form = soup.find('form', {'name': 'formulaire_ctf_alltheday_affiche_partie'})
formulaire_action_args = form.find('input', {'name': 'formulaire_action_args'})['value']
ctf_name = form.find('option', {'value': ctf_id}).string
data = {'var_ajax': 'form',
'page': 'ctf_alltheday',
'lang': 'en',
'formulaire_action': 'ctf_alltheday_affiche_partie',
'formulaire_action_args': formulaire_action_args,
'id_environnement_virtuel': ctf_id,
'action_partie': 'enregistrer_choix',
'enregistrer_choix': 'Save',
'demarrer': 'Start+the+game'}'', params=params, data=data)
print(f'Starting "{ctf_name}" on {freeroom_name}...')
print('Read the description of the CTF meanwhile:')
while 'Waiting for room to start':
r = s.get('', params=params)
soup = BeautifulSoup(r.text, 'html.parser')
success = soup.find('div', class_="success")
if success is not None:
print(f"The environnement is now available at {freeroom_name}")
return freeroom_name
def get_ctf_info(room_id):
params = {'lang': 'en', 'page': 'ctf_alltheday', 'id_salle': room_id}
r = s.get('', params=params)
soup = BeautifulSoup(r.text, 'html.parser')
desc = soup.find('ul', class_='spip')
# desc = re.sub(r'(.*)(Description.*)(Game duration.*min)', r'\1\n\2\n\n\3\n', desc, flags=re.DOTALL)
desc = str(desc).replace('<p>', '\n').replace('</p>', '\n')
soup = BeautifulSoup(desc, 'html.parser')
desc = soup.text
print(f'{"#" * 30}\n{desc}\n{"#" * 30}')
def yes_or_no(question, default=None):
while "The answer is invalid":
reply = input(question).lower().strip()
if reply[:1] == 'y':
return True
if reply[:1] == 'n':
return False
if not reply:
if default == 'Y':
return True
elif default == 'N':
return False
def config_ctf(room_name):
import os
import subprocess
user = input('SSH user: ')
pw_orig = getpass.getpass()
host = f"{room_name}"
def send_ssh_key():
if "ssh-copy-id" not in os.listdir("/usr/bin") or "sshpass" not in os.listdir("/usr/bin"):
import sys
print("ssh-copy-id and sshpass are required.\nAfter installation, be sure to have SSH keys.")
command = f"sshpass -p {pw_orig} ssh-copy-id {user}@{host}", shell=True)
def change_password():
import secrets
import string
alphabet = string.ascii_letters + string.digits
pw = ''.join(secrets.choice(alphabet) for i in range(20))
local_command = f'echo "{pw_orig}\n{pw}\n{pw}" | passwd'
command = f"ssh {user}@{host} '{local_command}'", shell=True)
print(f"Password changed: {pw}")
def send_kitty_terminfo():
"""For kitty shell users"""
command = f'infocmp xterm-kitty | ssh {user}@{host} tic -x -o \\~/.terminfo /dev/stdin', shell=True, stdout=subprocess.DEVNULL)
if "kitty" in os.listdir("/usr/bin"):
print(f'The environnement is now configured, you can connect with: ssh {user}@{host}')
def main():
user = input(f'Username [{default_user}]:') or default_user
started_room_id, room_name = is_ctf_started(user)
if not started_room_id: # If no room running, proceed to selection and start
ctf_id = input('Choose a CTF ID :')
room_name = start_ctf(ctf_id)
config = yes_or_no('Does this CTF need SSH and would like to configure it ? (Keys, password change...) [Y]/n?', default='Y')
if config: