From 70756e5a0f25523d8a21e038dd779431c11a2f5d Mon Sep 17 00:00:00 2001 From: SecT0uch Date: Fri, 23 Oct 2020 21:10:23 +0200 Subject: [PATCH] Upload files to '' --- CTFsetup.py | 239 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 239 insertions(+) create mode 100644 CTFsetup.py diff --git a/CTFsetup.py b/CTFsetup.py new file mode 100644 index 0000000..ef9c912 --- /dev/null +++ b/CTFsetup.py @@ -0,0 +1,239 @@ +#!/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 = 'https://api.www.root-me.org/login/' + + 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} + s.cookies.update(cookie) + + +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: + return + + 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]]) + rows.extend(rows_missed) + + 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 = 'https://www.root-me.org/fr/Capture-The-Flag/CTF-all-the-day/' + 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}') + get_ctf_info(room_id) + 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 = 'https://api.www.root-me.org/environnements_virtuels/' + 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 + col_print(ctf_list) + 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 = 'https://www.root-me.org/fr/Capture-The-Flag/CTF-all-the-day/' + 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('https://www.root-me.org/', 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'} + + s.post('https://www.root-me.org/', params=params, data=data) + print(f'Starting "{ctf_name}" on {freeroom_name}...') + print('Read the description of the CTF meanwhile:') + get_ctf_info(freeroom_id) + + while 'Waiting for room to start': + time.sleep(5) + r = s.get('https://www.root-me.org/', 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}.root-me.org") + return freeroom_name + + +def get_ctf_info(room_id): + params = {'lang': 'en', 'page': 'ctf_alltheday', 'id_salle': room_id} + r = s.get('https://www.root-me.org/', 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('

', '\n').replace('

', '\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}.root-me.org" + + 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.") + sys.exit() + + command = f"sshpass -p {pw_orig} ssh-copy-id {user}@{host}" + subprocess.call(command, 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}'" + subprocess.call(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' + subprocess.call(command, shell=True, stdout=subprocess.DEVNULL) + + send_ssh_key() + change_password() + + if "kitty" in os.listdir("/usr/bin"): + send_kitty_terminfo() + + 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 + login(user) + get_ctf_list() + 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: + config_ctf(room_name) + + +main()