3257 words
16 minutes
Crea tu CTF en Docker
2024-07-28

📑 En este blog veremos:

  • 📰 ¿Qué es Docker?
    • 📌 ¿Como instalar Docker?
    • 📌 Como usar Docker
      • 📍Desplegar nuestro primer contenedor
  • 📗 Recomendaciones al iniciar nuestro contenedor
  • ⚙️ Configuración de servicios
    • 📌 Iniciar servicio OpenSSH
    • 📌 Iniciar servicio FTP
    • 📌 Iniciar servicio Apache2
    • 📌 Iniciar servicio Nginx
    • 📌 Crear servicio personalizado
  • ⚙️ Configuración de usuarios
    • 📌 Crear usuarios nuevos
    • 📌 Eliminar usuarios
  • ⚙️ Configuración de permisos
    • 📌 Establecer permisos sudoers a usuarios
    • 📌 Permisos en Apache2
  • ⚙️ Configuración de Dockerfile
    • 📌 Exportar contenedor a .tar
  • 📝 Creación del Writeup
  • 👋 Despedida

📰¿Qué es Docker?#

Docker es un proyecto OpenSource. Con Docker podemos utiliizar los contenedores como máquinas virtuales muy livianas y modulares.

📌¿Como instalar Docker?#

Para instalar Docker en nuestros equipos Linux solo tendremos que poner lo siguiente:

Debian/Derivados:

 sudo apt install docker.io

ArchLinux/Derivados:

 sudo pacman -S docker

Fedora/Derivados:

 sudo dnf install docker

📌Como usar Docker#

Si queremos ver las imágenes de Docker que tengamos podremos hacer un:

 docker images
 docker images
REPOSITORY   TAG       IMAGE ID       CREATED       SIZE
ubuntu       latest    35a88802559d   7 weeks ago   78.1MB

Si queremos sacar una imagen ya hecha de Ubuntu,Debian,etc… con docker pull podremos:

docker pull debian:latest

Y nos descargará la última versión de la imagen de Debian que exista en Docker. Podemos sacar más imágenes en DockerHub

Si queremos listar los contenedores que tengamos podremos emplear docker ps:

 docker ps
CONTAINER ID   IMAGE     COMMAND       CREATED       STATUS       PORTS     NAMES
fc58d5436a68   ubuntu    "/bin/bash"   3 hours ago   Up 3 hours             focused_mayer

Con docker ps de manera predeterminada solo nos muestra los contenedores que estén activos, si queremos ver todos los contenedores tendremos que agregar el parámetro -a o --all:

 docker ps -a
CONTAINER ID   IMAGE      COMMAND      CREATED      STATUS                      PORTS      NAMES
fc58d5436a68   ubuntu    "/bin/bash"  3 hours ago   Up 3 hours                             focused_mayer
27fc4260aae8   debian    "/bin/bash"  2 hours ago   Exited (137) 3 seconds ago             agitated_jennings

Si queremos borrar contenedores usaremos docker rm:

Para emplear bien este comando seguiremos esta estructura:

docker rm IDCONTENEDOR

Recordemos que podemos usar el docker ps -a para ver los ID de todos los contenedores dando si están activos o no podemos emplear docker ps con el parámetro -a o --all:

 docker ps -a
CONTAINER ID   IMAGE      COMMAND      CREATED          STATUS                            PORTS      NAMES
7050ab52f603   debian      "bash"      2 minutes ago    Exited (137) About a minute ago              agitated_jennings
fc58d5436a68   ubuntu    "/bin/bash"   3 hours ago      Up 3 hours                                   focused_mayer

Si queremos borrar todos los contenedores a la vez podemos usar los parámetros -a y -q, el -q nos servirá para pillar solo el CONTAINER ID:

 docker ps -aq
fc58d5436a68
27fc4260aae8
157570dd64f0

Sabiendo esto podemos dentro del docker rm la ejecución del comando docker ps -aq:

 docker rm $(docker ps -aq) --force
fc58d5436a68
27fc4260aae8
157570dd64f0

Usamos el --force para forzar que se elimine tanto contenedores inactivos o activos.

{{< details “Estado docker ps después de eliminarlo” >}}

 docker ps -a
CONTAINER ID   IMAGE      COMMAND      CREATED          STATUS        PORTS        NAMES

{{< /details >}}

Esto mismo podemos aplicarlo a las imágenes, solo con la diferencia que no usaremos rm emplearemos rmi que nos servirá para eliminar imágenes Docker. Al igual que con docker ps -aq podemos usarlo con images:

 docker images -aq
2e5b8d3ef33e
35a88802559d

Ahora lo borraremos:

 docker rmi $(docker images -aq) --force
Untagged: debian:latest
Untagged: debian@sha256:45f2e735295654f13e3be10da2a6892c708f71a71be845818f6058982761a6d3
Deleted: sha256:2e5b8d3ef33ebde2b2a943fadb241a63de1535a007a7bd185e7037abfff99f63
Deleted: sha256:f6faf32734e0870d82ea890737958fe33ce9ddfed27b3b157576d2aadbab3322
Untagged: ubuntu:latest
Untagged: ubuntu@sha256:2e863c44b718727c860746568e1d54afd13b2fa71b160f5cd9058fc436217b30
Deleted: sha256:35a88802559dd2077e584394471ddaa1a2c5bfd16893b829ea57619301eb3908
Deleted: sha256:a30a5965a4f7d9d5ff76a46eb8939f58e95be844de1ac4a4b452d5d31158fdea
 docker images -a
REPOSITORY      TAG       IMAGE ID      CREATED      SIZE

También podemos hacer “snapshots” de nuestros contenedores de docker:

Primero pillaremos el ID CONTAINER del contenedor activo que queramos hacer una snapshot:

 docker ps
CONTAINER ID   IMAGE     COMMAND       CREATED          STATUS          PORTS     NAMES
e5b9bcc4bcf6   ubuntu    "/bin/bash"   12 minutes ago   Up 12 minutes             competent_moser

Ahora emplearemos docker commit con la siguiente estructura:

docker commit IDCONTAINER nombresnapshot:latest

Ahora lo emplearé para crear una snapshot de mi contenedor ubuntu:

 docker commit e5b9bcc4bcf6 snapshot1:latest
sha256:8d230ff6c31a853ed18423b3feed105cad8e448f665ee45e0f29702665fd66dc

Ahora si hacemos un docker images podremos ver la imagen snapshot1:

 docker images
REPOSITORY   TAG       IMAGE ID       CREATED          SIZE
snapshot1    latest    8d230ff6c31a   38 seconds ago   226MB
ubuntu       latest    35a88802559d   7 weeks ago      78.1MB

📍Desplegar nuestro primer contenedor#

Ya sabiendo como usar Docker a nivel básico vamos a desplegar nuestro primer contenedor, para ello podemos hacer:

 sudo docker pull ubuntu:latest
 docker pull ubuntu:latest
latest: Pulling from library/ubuntu
9c704ecd0c69: Pull complete
Digest: sha256:2e863c44b718727c860746568e1d54afd13b2fa71b160f5cd9058fc436217b30
Status: Downloaded newer image for ubuntu:latest
docker.io/library/ubuntu:latest

Si ahora hacemos un docker images podremos ver la imagen de ubuntu:latest:

 docker images
REPOSITORY   TAG       IMAGE ID       CREATED       SIZE
ubuntu       latest    35a88802559d   7 weeks ago   78.1MB

Ahora lo que haremos es iniciar nuestro contenedor:

 docker run -dit ubuntu
fc58d5436a6882d7a99172ae3df4b2a372b9809a7454e8a64a75bdf0aff261e1

Yo recomiendo para evitar problemas si tenemos más contenedores es iniciarlos por su IMAGE ID:

 docker run -dit 35a88802559d
fc58d5436a6882d7a99172ae3df4b2a372b9809a7454e8a64a75bdf0aff261e1

Si hacemos un docker ps podremos ver que se esta iniciando en segundo plano como bien le hemos indicado:

 docker ps
CONTAINER ID   IMAGE     COMMAND       CREATED          STATUS          PORTS     NAMES
fc58d5436a68   ubuntu    "/bin/bash"   42 seconds ago   Up 40 seconds             focused_mayer

Para obtener acceso a una bash del contedor pondremos:

 docker exec -it fc58d5436a68 bash
root@fc58d5436a68:/#

Aquí también le tendremos que indicar el CONTAINER ID.


📗Recomendaciones al iniciar nuestro contenedor#

Ya teniendo acceso a una bash del contenedor recomiendo actualizar el contenedor para evitar a futuro problemas al instalar dependencias:

root@fc58d5436a68:/# apt update -y && apt upgrade -y

⚙️Configuración de servicios#

📌Iniciar servicio OpenSSH#

Para instalar OpenSSH tendremos que poner lo siguiente:

root@fc58d5436a68:/# apt install ssh

Una vez ya tengamos ssh instalado pondremos lo siguiente para iniciar el servicio:

root@fc58d5436a68:/# service ssh start
 * Starting OpenBSD Secure Shell server sshd               [ OK ]

Para confirmar podemos hacerle un escaneo nmap y ver si lo tiene abierto:

 nmap -p- --open -n -Pn -vvv 172.17.0.2
Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-07-28 06:42 EDT
Initiating ARP Ping Scan at 06:42
Scanning 172.17.0.2 [1 port]
Completed ARP Ping Scan at 06:42, 0.04s elapsed (1 total hosts)
Initiating SYN Stealth Scan at 06:42
Scanning 172.17.0.2 [65535 ports]
Discovered open port 22/tcp on 172.17.0.2
Completed SYN Stealth Scan at 06:42, 1.05s elapsed (65535 total ports)
Nmap scan report for 172.17.0.2
Host is up, received arp-response (0.0000070s latency).
Scanned at 2024-07-28 06:42:07 EDT for 1s
Not shown: 65534 closed tcp ports (reset)
PORT   STATE SERVICE REASON
22/tcp open  ssh     syn-ack ttl 64
MAC Address: 02:42:AC:11:00:02 (Unknown)

Read data files from: /usr/bin/../share/nmap
Nmap done: 1 IP address (1 host up) scanned in 1.25 seconds
           Raw packets sent: 65536 (2.884MB) | Rcvd: 65536 (2.621MB)

📌Iniciar servicio FTP#

Para instalar FTP pondremos lo siguiente:

root@fc58d5436a68:/# apt install vsftpd

Una vez finalizada la instalación iniciaremos su servicio:

root@fc58d5436a68:/# service vsftpd start
 * Starting FTP server vsftpd               [ OK ]
 nmap -p- --open -n -Pn -vvv 172.17.0.2
Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-07-28 06:47 EDT
Initiating ARP Ping Scan at 06:47
Scanning 172.17.0.2 [1 port]
Completed ARP Ping Scan at 06:47, 0.07s elapsed (1 total hosts)
Initiating SYN Stealth Scan at 06:47
Scanning 172.17.0.2 [65535 ports]
Discovered open port 21/tcp on 172.17.0.2
Completed SYN Stealth Scan at 06:47, 0.98s elapsed (65535 total ports)
Nmap scan report for 172.17.0.2
Host is up, received arp-response (0.0000070s latency).
Scanned at 2024-07-28 06:47:51 EDT for 1s
Not shown: 65534 closed tcp ports (reset)
PORT   STATE SERVICE REASON
21/tcp open  ftp     syn-ack ttl 64
MAC Address: 02:42:AC:11:00:02 (Unknown)

Read data files from: /usr/bin/../share/nmap
Nmap done: 1 IP address (1 host up) scanned in 1.19 seconds
           Raw packets sent: 65536 (2.884MB) | Rcvd: 65536 (2.621MB)

Si por ejemplo queremos hacer una máquina donde está habilitado el anonymous tendremos que editar con nano que tendremos que instalarlo nosotros porque en el contenedor ubuntu no viene y con nano editaremos el /etc/vsftpd.conf:

Instalamos nano:

apt install nano

Editamos el archivo vsftpd.conf y buscaremos la linea que ponga:

# Allow anonymous FTP? (Disabled by default).
anonymous_enable=NO

Y cambiaremos el anonymous_enable=NO por:

anonymous_enable=YES

Guardamos y reiniciamos el servicio:

root@fc58d5436a68:/etc# service vsftpd restart
 * Stopping FTP server                      [ OK ]
 * Starting FTP server vsftpd               [ OK ]

Ahora lanzaremos un nmap indicandole el script ftp-anon:

 nmap -p21 --script=ftp-anon 172.17.0.2
Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-07-28 06:54 EDT
Nmap scan report for 172.17.0.2
Host is up (0.000042s latency).

PORT   STATE SERVICE
21/tcp open  ftp
|_ftp-anon: Anonymous FTP login allowed (FTP code 230)
MAC Address: 02:42:AC:11:00:02 (Unknown)

Nmap done: 1 IP address (1 host up) scanned in 0.39 seconds

📌Iniciar servicio Apache2#

Para instalar Apache2 pondremos lo siguiente:

root@fc58d5436a68:~# apt install apache2

Una vez finalizada la instalación iniciaremos el servicio:

root@fc58d5436a68:~# service apache2 start
 * Starting Apache httpd web server apache2
 AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using 172.17.0.2. Set the 'ServerName' directive globally to suppress this message*

Ahora si vamos a nuestro navegador y ponemos la IP del contenedor podremos ver la plantilla predeterminada de Apache2:

Si queremos modificar la web tendremos que ir al /var/www/html y ahi dentro podremos meter una plantilla html o crear a mano la web:

En mi caso he modificado el index.html:

<h1>Blog Pylon - Crea tu CTF en Docker<h1>
<p>Espero que te este gustando!!</p>

📌Iniciar servicio Nginx#

Para instalar nginx pondremos lo siguiente:

root@fc58d5436a68:~# apt install nginx

Una vez finalizada la instalación vamos a iniciar el servicio:

root@fc58d5436a68:~# service nginx start
 * Starting nginx nginx               [ OK ]

Para modificar o crear la web usa la misma ruta que Apache2 que es la /var/www/html.

📌Crear servicio personalizado#

Hay a veces donde nos puede pasar que necesitemos crear un servicio personalizado, por ejemplo para un producto que se inicie en local mediante un script y no un servicio como apache2,nginx,ftp,ssh….

La estructura de un servicio es así:

[Unit]
Description=NombreServicio
After=network.target

[Service]
ExecStart=/ruta/al/ejecutable
WorkingDirectory=/ruta/al/directorio/del/ejecutable
Restart=always
User=usuario
Group=grupo

[Install]
WantedBy=multi-user.target

Vamos a crear un servicio personalizado que corra un servidor de python3 en el 8080, para ello primero instalaremos python3:

root@fc58d5436a68:~# apt install python3

Si hacemos un python3 -m http.server 8080 contemplaremos que se iniciara un servidor web por el 8080, así que lo tomaremos como de referencia. Para crear el servicio tendremos que ir a la siguiente ruta:

/etc/systemd/system

Y ahí dentro crearemos un archivo con la extension .service, y dentro de ese archivo meteremos la estructura que he hemos visto anteriormente:

root@fc58d5436a68:/etc/systemd/system# cat personalizado.service
[Unit]
Description=NombreServicio
After=network.target

[Service]
ExecStart=/ruta/al/ejecutable
WorkingDirectory=/ruta/al/directorio/del/ejecutable
Restart=always
User=usuario
Group=grupo

[Install]
WantedBy=multi-user.target

Ahora cambiare los valores de Description ExecStart y WorkingDirectory:

[Unit]
Description=serverpersonalizado
After=network.target

[Service]
ExecStart=/usr/bin/python3 -m http.server 8080 -d /var/www/html/
WorkingDirectory=/usr/bin/
Restart=always
User=root
Group=root

[Install]
WantedBy=multi-user.target
  • Con el -d le indico el directorio, que en mi caso es el /var/www/html.

Antes de iniciarlo instalaremos systemctl:

root@fc58d5436a68:/etc/systemd/system# apt install systemctl

Ahora haremos un daemon-reload:

root@fc58d5436a68:/etc/systemd/system# systemctl daemon-reload
/usr/bin/systemctl:1541: SyntaxWarning: invalid escape sequence '\w'
  expanded = re.sub("[$](\w+)", lambda m: get_env1(m), cmd.replace("\\\n",""))
/usr/bin/systemctl:1543: SyntaxWarning: invalid escape sequence '\w'
  new_text = re.sub("[$][{](\w+)[}]", lambda m: get_env2(m), expanded)
/usr/bin/systemctl:1628: SyntaxWarning: invalid escape sequence '\w'
  cmd3 = re.sub("[$](\w+)", lambda m: get_env1(m), cmd2)
/usr/bin/systemctl:1631: SyntaxWarning: invalid escape sequence '\w'
  newcmd += [ re.sub("[$][{](\w+)[}]", lambda m: get_env2(m), part) ]
ERROR:systemctl: dbus.service: Executable path is not absolute, ignoring: @/usr/bin/dbus-daemon @dbus-daemon --system --address=systemd: --nofork --nopidfile --systemd-activation --syslog-only

Podremos ver que nos da varios errores pero podremos iniciar el servicio sin ningun problema.

Ahora iniciaremos el servicio:

root@fc58d5436a68:/etc/systemd/system# systemctl start personalizado.service
/usr/bin/systemctl:1541: SyntaxWarning: invalid escape sequence '\w'
  expanded = re.sub("[$](\w+)", lambda m: get_env1(m), cmd.replace("\\\n",""))
/usr/bin/systemctl:1543: SyntaxWarning: invalid escape sequence '\w'
  new_text = re.sub("[$][{](\w+)[}]", lambda m: get_env2(m), expanded)
/usr/bin/systemctl:1628: SyntaxWarning: invalid escape sequence '\w'
  cmd3 = re.sub("[$](\w+)", lambda m: get_env1(m), cmd2)
/usr/bin/systemctl:1631: SyntaxWarning: invalid escape sequence '\w'
  newcmd += [ re.sub("[$][{](\w+)[}]", lambda m: get_env2(m), part) ]
 nmap -p- --open -n -Pn -vvv 172.17.0.2
Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-07-28 07:32 EDT
Initiating ARP Ping Scan at 07:32
Scanning 172.17.0.2 [1 port]
Completed ARP Ping Scan at 07:32, 0.05s elapsed (1 total hosts)
Initiating SYN Stealth Scan at 07:32
Scanning 172.17.0.2 [65535 ports]
Discovered open port 8080/tcp on 172.17.0.2
Completed SYN Stealth Scan at 07:32, 1.00s elapsed (65535 total ports)
Nmap scan report for 172.17.0.2
Host is up, received arp-response (0.0000070s latency).
Scanned at 2024-07-28 07:32:16 EDT for 1s
Not shown: 65534 closed tcp ports (reset)
PORT     STATE SERVICE    REASON
8080/tcp open  http-proxy syn-ack ttl 64
MAC Address: 02:42:AC:11:00:02 (Unknown)

Read data files from: /usr/bin/../share/nmap
Nmap done: 1 IP address (1 host up) scanned in 1.20 seconds
           Raw packets sent: 65536 (2.884MB) | Rcvd: 65536 (2.621MB)

Y podremos ver la web:


⚙️Configuración de usuarios#

📌Crear usuarios nuevos#

Para crear usuario nuevos podremos usar adduser:

root@fc58d5436a68:~# adduser usuarionuevo
info: Adding user `usuarionuevo' ...
info: Selecting UID/GID from range 1000 to 59999 ...
info: Adding new group `usuarionuevo' (1001) ...
info: Adding new user `usuarionuevo' (1001) with group `usuarionuevo (1001)' ...
info: Creating home directory `/home/usuarionuevo' ...
info: Copying files from `/etc/skel' ...
New password:
Retype new password:
passwd: password updated successfully
Changing the user information for usuarionuevo
Enter the new value, or press ENTER for the default
        Full Name []:
        Room Number []:
        Work Phone []:
        Home Phone []:
        Other []:
Is the information correct? [Y/n]
info: Adding new user `usuarionuevo' to supplemental / extra groups `users' ...
info: Adding user `usuarionuevo' to group `users' ...

📌Eliminar usuarios#

Para eliminar un usuario podremos usar deluser:

root@fc58d5436a68:~# deluser usuarionuevo

info: Removing crontab ...
info: Removing user `usuarionuevo' ...

⚙️Configuración de permisos#

📌Establecer permisos sudoers a usuarios#

Antes de todo tendremos que instalar sudo:

root@fc58d5436a68:~# apt install sudo

Para establecer permisos sudoers a un usuario tendremos que editar el /etc/sudoers y bajaremos hasta la zona donde ponga:

# User privilege specification
root    ALL=(ALL:ALL) ALL

Para especificar un permiso sudo, tendremos que seguir la siguiente estructura:

usuario  ALL=(usuario o root[ALL:ALL]) /ruta/del/binario/o/lo/que/sea

Por ejemplo yo he creado un usuario llamado sudoersuser que tenga la capacidad de usar nano como root sin necesidad de su credencial:

# User privilege specification
root    ALL=(ALL:ALL) ALL

sudoersuser ALL=(root) NOPASSWD: /usr/bin/nano

Si nos convertimos en el usuario y hacemos un sudo -l podremos ver el permiso que le hemos especificado en el /etc/sudoers:

sudoersuser@fc58d5436a68:~$ sudo -l
Matching Defaults entries for sudoersuser on fc58d5436a68:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin, use_pty

User sudoersuser may run the following commands on fc58d5436a68:
    (root) NOPASSWD: /usr/bin/nano

📌Permisos en Apache2#

Cuando creamos una web en apache2 puede ocurrir de que los usuarios puedan acceder a la carpeta images,js,db,etc.... Para impedir esto tendremos que crear un archivo .conf en el directorio de /etc/apache2/sites-enabled, en el veremos un archivo llamado 000-default.conf, lo editaremos y pondremos lo siguiente:

<Directory /var/www/html/images/>
    Options FollowSymLinks
    AllowOverride None
    Require all granted
</Directory>
<VirtualHost *:80>
        # The ServerName directive sets the request scheme, hostname and port that
        # the server uses to identify itself. This is used when creating
        # redirection URLs. In the context of virtual hosts, the ServerName
        # specifies what hostname must appear in the request's Host: header to
        # match this virtual host. For the default virtual host (this file) this
        # value is not decisive as it is used as a last resort host regardless.
        # However, you must set it for any further virtual host explicitly.
        #ServerName www.example.com

        ServerAdmin webmaster@localhost
        DocumentRoot /var/www/html

<Directory /var/www/html/images/>
    Options FollowSymLinks
    AllowOverride None
    Require all granted
</Directory>

        # Available loglevels: trace8, ..., trace1, debug, info, notice, warn,
        # error, crit, alert, emerg.
        # It is also possible to configure the loglevel for particular
        # modules, e.g.
        #LogLevel info ssl:warn

        ErrorLog ${APACHE_LOG_DIR}/error.log
        CustomLog ${APACHE_LOG_DIR}/access.log combined

        # For most configuration files from conf-available/, which are
        # enabled or disabled at a global level, it is possible to
        # include a line for only one particular virtual host. For example the
        # following line enables the CGI configuration for this host only
        # after it has been globally disabled with "a2disconf".
        #Include conf-available/serve-cgi-bin.conf
</VirtualHost>

Ahora guardaremos esos cambios y reiniciaremos el servicio apache2:

root@e5b9bcc4bcf6:/var/www/html/images# service apache2 restart
 * Restarting Apache httpd web server apache2
AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using 172.17.0.2. Set the 'ServerName' directive globally to suppress this message

Y todo esto lo podemos configurar para diferentes carpetas:

<Directory /var/www/html/images/>
    Options FollowSymLinks
    AllowOverride None
    Require all granted
</Directory>

<Directory /var/www/html/css/>
    Options FollowSymLinks
    AllowOverride None
    Require all granted
</Directory>

<Directory /var/www/html/js/>
    Options FollowSymLinks
    AllowOverride None
    Require all granted
</Directory>

⚙️ Configuración de Dockerfile#

Ya estamos casi por el final de nuestro CTF, ahora nos toca crear un Dockerfile apuntando a una snapshot o directamente al contenedor docker de la siguiente manera:

FROM snapshot1:latest

Ahora pondremos CMD y le indicaremos ejecutar todos los servicios que tienen que estar activos cuando se despliege nuestro contenedor docker:

FROM snapshot1:latest

CMD service apache2 start && tail -f /dev/null

Veréis que al final de los servicios he puesto tail -f /dev/null esto lo que hace es un bucle infinito apuntando al /dev/null, esto nos ayudara a cuando la persona inicie el contenedor docker los servicios no se apaguen y estén iniciados.

📌 Exportar contenedor a .tar#

Esta es una de las partes más importantes si queremos publicar el CTF en la plataforma de Dockerlabs que es que tendremos que construir una nueva imagen con el Dockerfile que hemos creado con los parámetros indicados de iniciar los servicios necesarios, así que vamos a construir una imagen nueva desde el Dockerfile llamada miprimerctf claramente no es necesario poner ese nombre, vosotros poner el que más os guste para el CTF:

Emplearemos docker build y el parametro --tag para establecerle un nombre a la imagen:

 docker build --tag miprimerctf .
Sending build context to Docker daemon  282.1kB
Step 1/2 : FROM snapshot1:latest
 ---> 8d230ff6c31a
Step 2/2 : CMD service apache2 start && tail -f /dev/null
 ---> Running in a2c8c3cd1aba
Removing intermediate container a2c8c3cd1aba
 ---> d0ba19aba1c4
Successfully built d0ba19aba1c4
Successfully tagged miprimerctf:latest

El . es que le estoy indicando que el archivo Dockerfile se encuentra en el directorio actual donde estoy. Si hacemos un docker images podremos ver la imagen que hemos construido con su respectivo nombre:

 docker images
REPOSITORY    TAG       IMAGE ID       CREATED              SIZE
miprimerctf   latest    d0ba19aba1c4   About a minute ago   226MB
snapshot1     latest    8d230ff6c31a   10 minutes ago       226MB
ubuntu        latest    35a88802559d   7 weeks ago          78.1MB

Ahora es cuando emplearemos docker save para guardar la imagen en un .tar:

 docker save -o miprimerctf.tar miprimerctf:latest
 ls
 Dockerfile  miprimerctf.tar

Para comprobar de que todo funciona correcto vamos a descargarnos una maquina al azar de Dockerlabs y utilizaremos su auto_deploy.sh a nuestro archivo .tar:

 bash auto_deploy.sh miprimerctf.tar

                            ##        .
                      ## ## ##       ==
                   ## ## ## ##      ===
               /""""""""""""""""\___/ ===
          ~~~ {~~ ~~~~ ~~~ ~~~~ ~~ ~ /  ===- ~~~
               \______ o          __/
                 \    \        __/
                  \____\______/

  ___  ____ ____ _  _ ____ ____ _    ____ ___  ____
  |  \ |  | |    |_/  |___ |__/ |    |__| |__] [__
  |__/ |__| |___ | \_ |___ |  \ |___ |  | |__] ___]


Se han detectado máquinas de DockerLabs previas, debemos limpiarlas para evitar problemas, espere un momento...
Se han detectado máquinas de DockerLabs previas, debemos limpiarlas para evitar problemas, espere un momento...

Estamos desplegando la máquina vulnerable, espere un momento.

Máquina desplegada, su dirección IP es --> 172.17.0.3

Presiona Ctrl+C cuando termines con la máquina para eliminarla

Ahora le haremos un escaneo nmap para ver si esta el puerto 80 abierto que es el que hemos establecido iniciando el servidor apache2 en el Dockerfile:

 nmap -p- --open -n -Pn -vvv 172.17.0.3
Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-07-28 10:15 EDT
Initiating ARP Ping Scan at 10:15
Scanning 172.17.0.3 [1 port]
Completed ARP Ping Scan at 10:15, 0.06s elapsed (1 total hosts)
Initiating SYN Stealth Scan at 10:15
Scanning 172.17.0.3 [65535 ports]
Discovered open port 80/tcp on 172.17.0.3
Completed SYN Stealth Scan at 10:15, 1.11s elapsed (65535 total ports)
Nmap scan report for 172.17.0.3
Host is up, received arp-response (0.0000070s latency).
Scanned at 2024-07-28 10:15:50 EDT for 1s
Not shown: 65534 closed tcp ports (reset)
PORT   STATE SERVICE REASON
80/tcp open  http    syn-ack ttl 64
MAC Address: 02:42:AC:11:00:03 (Unknown)

Read data files from: /usr/bin/../share/nmap
Nmap done: 1 IP address (1 host up) scanned in 1.31 seconds
           Raw packets sent: 65536 (2.884MB) | Rcvd: 65536 (2.621MB)

📝 Creación del Writeup#

Ya habiendo comprobado que la máquina funciona bien y está todo correcto, nos toca lo último que es el writeup. Os comparto una estructura en PDF de un writeup para Dockerlabs que es el que yo empleo:

El template esta basado en el oficial de HackTheBox.


👋 Despedida#

Espero que hayáis aprendido en como crear un CTF en Docker, tener en cuenta que ciertas cosas como los servicios se puede hacer en un Ubuntu Server y intentar crear una máquina para otra plataforma. Este es un blog que me ha parecido muy bueno crearlo para ayudar a la gente que quiere crear su CTF sin tener idea y obtenga cierta base con este blog. Adelanto que subiré un blog nuevo donde ensenaré como configuro una máquina desde el minuto 0 hasta que la tengo creada.

Att: Pylon