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:
❯dockerps-aCONTAINERIDIMAGECOMMANDCREATEDSTATUSPORTSNAMESfc58d5436a68ubuntu"/bin/bash"3hoursagoUp3hoursfocused_mayer27fc4260aae8debian"/bin/bash"2hoursagoExited (137) 3 seconds ago agitated_jennings
Si queremos borrar contenedores usaremos docker rm:
Para emplear bien este comando seguiremos esta estructura:
dockerrmIDCONTENEDOR
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:
❯dockerps-aCONTAINERIDIMAGECOMMANDCREATEDSTATUSPORTSNAMES7050ab52f603debian"bash"2minutesagoExited (137) About a minute ago agitated_jenningsfc58d5436a68ubuntu"/bin/bash"3hoursagoUp3hoursfocused_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:
❯dockerps-aqfc58d5436a6827fc4260aae8157570dd64f0
Sabiendo esto podemos dentro del docker rm la ejecución del comando docker ps -aq:
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:
❯ nmap -p---open -n -Pn -vvv 172.17.0.2Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-07-28 06:47 EDTInitiatingARPPingScan at 06:47Scanning 172.17.0.2 [1 port]CompletedARPPingScan at 06:47, 0.07s elapsed (1 total hosts)InitiatingSYNStealthScan at 06:47Scanning 172.17.0.2 [65535 ports]Discovered open port 21/tcp on 172.17.0.2CompletedSYNStealthScan at 06:47, 0.98s elapsed (65535 total ports)Nmap scan report for172.17.0.2Host is up, received arp-response (0.0000070s latency).Scanned at 2024-07-2806:47:51EDTfor 1sNot shown:65534 closed tcp ports (reset)PORTSTATESERVICEREASON21/tcp open ftp syn-ack ttl 64MAC Address:02:42:AC:11:00:02 (Unknown)Read data files from:/usr/bin/../share/nmapNmap done:1IPaddress (1 host up) scanned in 1.19 secondsRaw 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:
aptinstallnano
Editamos el archivo vsftpd.conf y buscaremos la linea que ponga:
# Allow anonymous FTP? (Disabled by default).
anonymous_enable=NO
Ahora lanzaremos un nmap indicandole el script ftp-anon:
❯ nmap -p21 --script=ftp-anon 172.17.0.2Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-07-28 06:54 EDTNmap scan report for172.17.0.2Host is up (0.000042s latency).PORTSTATESERVICE21/tcp open ftp|_ftp-anon:AnonymousFTP login allowed (FTP code 230)MAC Address:02:42:AC:11:00:02 (Unknown)Nmap done:1IPaddress (1 host up) scanned in 0.39 seconds
📌Iniciar servicio Apache2
Para instalar Apache2 pondremos lo siguiente:
root@fc58d5436a68:~#aptinstallapache2
Una vez finalizada la instalación iniciaremos el servicio:
root@fc58d5436a68:~#serviceapache2start*StartingApachehttpdwebserverapache2AH00558:apache2:Couldnotreliablydeterminetheserver'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:~#aptinstallnginx
Una vez finalizada la instalación vamos a iniciar el servicio:
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….
Vamos a crear un servicio personalizado que corra un servidor de python3 en el 8080, para ello primero instalaremos python3:
root@fc58d5436a68:~#aptinstallpython3
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:
❯ nmap -p---open -n -Pn -vvv 172.17.0.2Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-07-28 07:32 EDTInitiatingARPPingScan at 07:32Scanning 172.17.0.2 [1 port]CompletedARPPingScan at 07:32, 0.05s elapsed (1 total hosts)InitiatingSYNStealthScan at 07:32Scanning 172.17.0.2 [65535 ports]Discovered open port 8080/tcp on 172.17.0.2CompletedSYNStealthScan at 07:32, 1.00s elapsed (65535 total ports)Nmap scan report for172.17.0.2Host is up, received arp-response (0.0000070s latency).Scanned at 2024-07-2807:32:16EDTfor 1sNot shown:65534 closed tcp ports (reset)PORTSTATESERVICEREASON8080/tcp open http-proxy syn-ack ttl 64MAC Address:02:42:AC:11:00:02 (Unknown)Read data files from:/usr/bin/../share/nmapNmap done:1IPaddress (1 host up) scanned in 1.20 secondsRaw 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:~#adduserusuarionuevoinfo:Addinguser`usuarionuevo' ...info: Selecting UID/GID from range 1000 to 59999 ...info: Adding new group `usuarionuevo' (1001) ...info:Addingnewuser `usuarionuevo' (1001) with group `usuarionuevo (1001)'...info:Creatinghomedirectory`/home/usuarionuevo' ...info: Copying files from `/etc/skel'...Newpassword:Retypenewpassword:passwd:passwordupdatedsuccessfullyChangingtheuserinformationforusuarionuevoEnterthenewvalue,orpressENTERforthedefaultFullName []:RoomNumber []:WorkPhone []:HomePhone []:Other []:Istheinformationcorrect? [Y/n]info:Addingnewuser `usuarionuevo' to supplemental / extra groups `users'...info:Addinguser`usuarionuevo' to group `users'...
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#serviceapache2restart*RestartingApachehttpdwebserverapache2AH00558:apache2:Couldnotreliablydeterminetheserver'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:latestCMD 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:
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:
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:
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.3Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-07-28 10:15 EDTInitiatingARPPingScan at 10:15Scanning 172.17.0.3 [1 port]CompletedARPPingScan at 10:15, 0.06s elapsed (1 total hosts)InitiatingSYNStealthScan at 10:15Scanning 172.17.0.3 [65535 ports]Discovered open port 80/tcp on 172.17.0.3CompletedSYNStealthScan at 10:15, 1.11s elapsed (65535 total ports)Nmap scan report for172.17.0.3Host is up, received arp-response (0.0000070s latency).Scanned at 2024-07-2810:15:50EDTfor 1sNot shown:65534 closed tcp ports (reset)PORTSTATESERVICEREASON80/tcp open http syn-ack ttl 64MAC Address:02:42:AC:11:00:03 (Unknown)Read data files from:/usr/bin/../share/nmapNmap done:1IPaddress (1 host up) scanned in 1.31 secondsRaw 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.