# Environnement Système CTF Il faut impérativement une VM pour que Docker soit fluide ## Installation de Docker ``` apt-get install apt-transport-https ca-certificates curl gnupg2 software-properties-common curl -fsSL https://download.docker.com/linux/debian/gpg | apt-key add - add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/debian $(lsb_release -cs) stable" apt-get update apt-get install docker-ce ``` ## Mise en place de l'environement système La restauration des challenges actuels est expliquée à la fin. ### Procédure pour la création d'un nouveau challenge Placer les Dockerfile est la source du challenge dans /home/systeme/SystemeChall/ Le Dockerfile doit idéalement être fait à partir de debmod, créer un utilisateur systemeXX et contenir ``` USER systemeXX WORKDIR /home/systemeXX ``` ### Installation et création de l'utilisateur administrateur non root ``` apt-get install python3-pip pip3 install --upgrade docker pip3 install --upgrade argcomplete adduser systeme adduser systeme sudo adduser systeme docker su systeme ``` #### /usr/local/bin/dockersh ```python #!/usr/bin/env python3 # PYTHON_ARGCOMPLETE_OK import os os.environ['TERM'] = 'xterm' import argparse from configparser import ConfigParser, ExtendedInterpolation import docker import random import string import sys from pwd import getpwnam prog = 'dockersh' config_file = "/etc/dockertempsh.ini" import os user = os.getlogin() host = os.uname()[1] cli = docker.APIClient() def containers(image_filter='', container_filter='', sort_by='Created', all=True): cs = cli.containers(all=all, filters={'label': "user="+user}) cs.sort(key=lambda c: c[sort_by]) cs = [c for c in cs if str(c['Image']+':latest').startswith(image_filter)] cs = [c for c in cs if c['Names'][0][1:].startswith(container_filter)] return cs def random_string(length): def random_char(): return random.choice(string.ascii_uppercase + string.digits) return ''.join(random_char() for _ in range(length)) def strip(s, suffix=''): for c in ['/', ':', '.', ' ']: s = s.replace(c, '') if s.endswith(suffix): s = s[:len(s)-len(suffix)] return s def image_split(s): sp = s.split(':') if len(sp) == 1: return sp[0], 'latest' else: return sp[0], sp[1] #Chargement du fichier de configuration config_envir = { "USER": user, "HOME": os.environ['HOME'] } cfg = ConfigParser(config_envir, interpolation=ExtendedInterpolation()) cfg.read(config_file) ini = cfg[user] if cfg.has_section(user) else cfg['DEFAULT'] #Spécification particulière post_cmd = "" name = "" image = "" home = ini['homedir'] suffix = ini['suffix'] #Vérification des spécifications name_passed = (name != "") image_passed = (image != "") #Génération du conteneur temporaire #Création du nom random if not image_passed: image = ini['image'] image_base, image_tag = image_split(image) image = image_base + ':' + image_tag name = strip(image) + '_tmp' + random_string(4) full_name = name + suffix #Création if len(containers(container_filter=name)) == 0: volumes = [] if "volumes" in ini: volumes = volumes + ini["volumes"].split(",") volumes = [v.split(":") for v in volumes] binds = {v[0].strip():{"bind":v[1].strip(),"mode":v[2].strip()} for v in volumes} volumes = [v[1] for v in volumes] host_config = cli.create_host_config( binds=binds, restart_policy={'Name' : 'unless-stopped'}, cap_add='SYS_PTRACE', #GDB security_opt=['seccomp:unconfined']) #GDB userpwd = getpwnam(user) cli.create_container(image, stdin_open=True, tty=True, name=full_name, hostname='systemekrkn', labels={'group': prog, 'user': user}, volumes=volumes, working_dir=home, environment={ "HOST_USER_ID": userpwd.pw_uid, "HOST_USER_GID": userpwd.pw_gid, "HOST_USER_NAME": user }, host_config=host_config ) #Lancement et attach cli.start(full_name) os.popen('docker exec '+full_name + ' echo Initialization finished.').read().split(":")[-1] user_bash = "/bin/bash" #Path par défaut cmd = post_cmd if post_cmd else user_bash #Donne la posibilité de spécifié le path dans le .ini os.system('docker exec -u '+user+" " + "-it" +' '+ full_name + ' ' + cmd) #Arrêt à la fin cli.remove_container(full_name, v=True, force=True) cli.close() ``` ``` chmod +x /usr/local/bin/dockersh ``` ### Création d'une image docker de base #### Dockerfile ``` FROM debian:latest RUN dpkg --add-architecture i386 RUN apt update && apt install -y gdb elfutils binutils python-minimal perl zip pwgen nano gcc ``` Création de l'image de base debmod ``` docker built -t debmod . ``` ### Script pour la création des images Docker à partir des Dockerfile Usage : ./createImg ```bash #!/bin/bash if [ "$#" -lt "2" ] then echo "Usage : ./createImg " else if [ -f "$2" ];then docker build -t $1 $2 else echo "Usage : ./createImg " exit 0 fi fi ``` ### Script pour la création d'un utilisateur et son ajout à DockerTemp Usage ./deployEnv ```bash #!/bin/bash if [ "$#" -eq "0" ] then echo "Usage : ./deployEnv " else if grep -q "$1" /etc/dockertempsh.ini then echo "Utilisateur déjà crée dans /etc/dockertempsh.ini ECHEC" exit 1 else useradd -m -p $1 -s /usr/local/bin/dockersh $1 echo "$1:$1" | chpasswd adduser $1 docker echo -e "[$1] image = $1 suffix = _$1 homedir = /home/$1 volumes = /globalinfo:/globalinfo:ro" >> /etc/dockertempsh.ini fi fi ``` Une fois le programme mis au bon endroit et les deux scripts exécutés avec succès tout est prêt. Pour personnaliser le message d'accueil, il faut modifier le /etc/motd de la VM et non celui des conteneurs Docker. ## Restauration des challenges déjà existants Voilà la correspondance utilisateur / challenge ``` systeme1 -> easyshell systeme2 -> pwn_my_home systeme3 -> overflow systeme4 -> shellcode1 systeme5 -> shellcode3 systeme6 -> shellcode3 systeme7 -> history ``` Extraire l'archive des challenge dans /home/systeme/SystemeChall/ ### Script qui utilisera les deux autres pour tout déployer ```bash #!/bin/bash declare -a path=(easyshell pwn_my_home overflow shellcode1 shellcode2 shellcode3 history) for i in `seq 0 6`; do ./createImg.sh systeme$(($i+1)) "/home/systeme/SystemeChall/${path[${i}]}" ./deployEnv.sh systeme$(($i+1)) done ```