<summary><strong>Aprende hacking en AWS de cero a héroe con</strong><ahref="https://training.hacktricks.xyz/courses/arte"><strong>htARTE (HackTricks AWS Red Team Expert)</strong></a><strong>!</strong></summary>
* Si quieres ver a tu **empresa anunciada en HackTricks** o **descargar HackTricks en PDF**, consulta los [**PLANES DE SUSCRIPCIÓN**](https://github.com/sponsors/carlospolop)!
* Consigue el [**merchandising oficial de PEASS & HackTricks**](https://peass.creator-spring.com)
* Descubre [**La Familia PEASS**](https://opensea.io/collection/the-peass-family), nuestra colección de [**NFTs**](https://opensea.io/collection/the-peass-family) exclusivos
* **Únete al** 💬 [**grupo de Discord**](https://discord.gg/hRep4RUj7f) o al [**grupo de telegram**](https://t.me/peass) o **sigue** a **Twitter** 🐦 [**@carlospolopm**](https://twitter.com/carlospolopm)**.**
* **Comparte tus trucos de hacking enviando PRs a los repositorios de github de** [**HackTricks**](https://github.com/carlospolop/hacktricks) y [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud).
Desde su creación en 2008, el uso del framework [Symfony](https://symfony.com) ha ido creciendo cada vez más en aplicaciones basadas en PHP. Ahora es un componente central de muchos CMS conocidos, como [Drupal](https://www.drupal.org), [Joomla!](https://www.joomla.org), [eZPlatform](https://ezplatform.com) (anteriormente eZPublish), o [Bolt](https://bolt.cm), y se utiliza a menudo para construir sitios web personalizados.
Una de las características integradas de Symfony, diseñada para manejar [ESI (Edge-Side Includes)](https://en.wikipedia.org/wiki/Edge\_Side\_Includes), es la clase [`FragmentListener`](https://github.com/symfony/symfony/blob/5.1/src/Symfony/Component/HttpKernel/EventListener/FragmentListener.php). Esencialmente, cuando alguien emite una solicitud a `/_fragment`, este listener establece atributos de solicitud a partir de los parámetros GET proporcionados. Dado que esto permite **ejecutar código PHP arbitrario** (_más sobre esto más adelante_), la solicitud debe estar firmada usando un valor HMAC. La clave criptográfica secreta de este HMAC se almacena bajo un valor de configuración de Symfony llamado `secret`.
Este valor de configuración, `secret`, también se utiliza, por ejemplo, para construir tokens CSRF y tokens de función "recuérdame". Dada su importancia, este valor debe ser obviamente muy aleatorio.
Desafortunadamente, hemos descubierto que a menudo, el `secret` tiene un **valor predeterminado**, o existen **formas de obtener el valor, forzarlo offline, o simplemente eludir la verificación de seguridad con la que está involucrado**. Afecta notablemente a Bolt, eZPlatform y eZPublish.
Aunque esto puede parecer un problema de configuración benigno, hemos encontrado que los valores predeterminados, forzables o adivinables están **muy, muy a menudo presentes** en los CMS mencionados, así como en aplicaciones personalizadas. Esto se debe principalmente a no enfatizar lo suficiente su importancia en la documentación o guías de instalación.
Además, un atacante puede escalar vulnerabilidades de menor impacto para leer el `secret` (a través de una divulgación de archivos), eludir el proceso de firma de `/_fragment` (usando un SSRF), ¡e incluso filtrarlo a través de `phpinfo()`!
Siendo un framework moderno, Symfony ha tenido que lidiar con la generación de subpartes de una solicitud desde su creación hasta nuestros días. Antes de `/_fragment`, estaba `/_internal` y `/_proxy`, que hacían esencialmente lo mismo. Esto produjo muchas vulnerabilidades a lo largo de los años: [CVE-2012-6432](https://symfony.com/blog/security-release-symfony-2-0-20-and-2-1-5-released#cve-2012-6432-code-execution-vulnerability-via-the-internal-routes), [CVE-2014-5245](https://symfony.com/blog/cve-2014-5245-direct-access-of-esi-urls-behind-a-trusted-proxy), y [CVE-2015-4050](https://symfony.com/blog/cve-2015-4050-esi-unauthorized-access), por ejemplo.
Desde Symfony 4, el `secret` se genera durante la instalación, y la página `/_fragment` está desactivada por defecto. Uno podría pensar, por lo tanto, que la conjunción de ambos, tener un `secret` débil y `/_fragment` habilitado, sería rara. No es así: muchos frameworks dependen de versiones antiguas de Symfony (incluso la versión 2.x sigue estando muy presente), e implementan un valor `secret` estático o lo generan de manera deficiente. Además, muchos dependen de ESI y, como tal, habilitan la página `/_fragment`. Además, como veremos, otras vulnerabilidades de menor impacto pueden permitir volcar el `secret`, incluso si se ha generado de forma segura.
Primero demostraremos cómo un atacante, teniendo conocimiento del valor de configuración `secret`, puede obtener ejecución de código. Esto se hace para la última versión de `symfony/http-kernel`, pero es similar para otras versiones.
`FragmentListener:onKernelRequest` se ejecutará en cada solicitud: si la ruta de la solicitud es `/_fragment` \[1], el método primero verificará que la solicitud sea válida (_es decir_, debidamente firmada), y lanzará una excepción en caso contrario \[2]. Si las comprobaciones de seguridad tienen éxito, analizará el parámetro `_path` codificado en la URL y establecerá los atributos de `$request` en consecuencia.
Los atributos de la solicitud no deben confundirse con los parámetros de la solicitud HTTP: son valores internos, mantenidos por Symfony, que generalmente no pueden ser especificados por un usuario. Uno de estos atributos de la solicitud es `_controller`, que especifica qué controlador de Symfony (una tupla _(clase, método)_ o simplemente una _función_) debe ser llamado. Los atributos cuyo nombre no comienza con `_` son argumentos que se van a proporcionar al controlador. Por ejemplo, si quisiéramos llamar a este método:
Esencialmente, esto permite llamar a cualquier función o método de cualquier clase, con cualquier parámetro. Dada la gran cantidad de clases que tiene Symfony, **conseguir la ejecución de código es trivial**. Podemos, por ejemplo, llamar a `system()`:
Para verificar la firma de una URL, se calcula un HMAC contra la _URL completa_. El hash obtenido se compara entonces con el especificado por el usuario.
En resumen, Symfony extrae el parámetro GET `_hash`, luego reconstruye la URL completa, por ejemplo `https://symfony-site.com/_fragment?_path=controller%3d...%26argument1=test%26...`, calcula un HMAC de esta URL utilizando el `secret` como clave \[1], y lo compara con el valor de hash dado \[2]. Si no coinciden, se lanza una excepción `AccessDeniedHttpException` \[3], resultando en un error `403`.
Al verificar `http://localhost:8000/_fragment?_hash=lNweS5nNP8QCtMqyqrW8HIl4j9JXIfscGeRm%2FcmFOh8%3D`, ahora tenemos un código de estado `404`. La firma era correcta, pero no especificamos ningún atributo de solicitud, por lo que Symfony no encuentra nuestro controlador.
Dado que podemos llamar a cualquier método, con cualquier argumento, podemos, por ejemplo, elegir `system($command, $return_value)`, y proporcionar un payload de la siguiente manera:
Podemos ahora visitar la URL del exploit: `http://localhost:8000/_fragment?_path=_controller%3Dsystem%26command%3Did%26return_value%3Dnull&_hash=GFhQ4Hr1LIA8mO1M%2FqSfwQaSM8xQj35vPhyrF3hvQyI%3D`.
De nuevo: nada de esto importaría si los secretos no fueran accesibles. A menudo, lo son. Describiremos varias formas de obtener ejecución de código sin conocimiento previo.
### A través de vulnerabilidades <a href="#through-vulnerabilities" id="through-vulnerabilities"></a>
Comencemos con lo obvio: usar vulnerabilidades de menor impacto para obtener el secreto.
#### Lectura de archivos <a href="#file-read" id="file-read"></a>
Evidentemente, una vulnerabilidad de lectura de archivos podría usarse para leer los siguientes archivos y obtener `secret`:
*`app/config/parameters.yml`
*`.env`
_Como ejemplo, algunas barras de herramientas de depuración de Symfony permiten leer archivos._
#### PHPinfo <a href="#phpinfo" id="phpinfo"></a>
En versiones recientes de Symfony (3.x), `secret` se almacena en `.env` como `APP_SECRET`. Dado que luego se importa como una variable de entorno, se pueden ver a través de una página `phpinfo()`.
El código detrás de `FragmentListener` ha evolucionado a lo largo de los años: hasta la versión _2.5.3_, cuando la solicitud provenía de un proxy de confianza (léase: `localhost`), se consideraría segura y, como tal, no se verificaría el hash. Un SSRF, por ejemplo, puede permitir ejecutar código inmediatamente, independientemente de tener `secret` o no. Esto afecta notablemente a eZPublish hasta 2014.7.
Al configurar un sitio web Symfony, el primer paso es instalar el esqueleto [symfony-standard](https://github.com/symfony/symfony-standard). Una vez instalado, se solicita ingresar algunos valores de configuración. Por defecto, la clave es `ThisTokenIsNotSoSecretChangeIt`.
[ezPlatform](https://ezplatform.com), el sucesor de [ezPublish](https://en.wikipedia.org/wiki/EZ\_Publish), todavía utiliza Symfony. El 10 de junio de 2019, un [commit](https://github.com/ezsystems/ezplatform/commit/974f2a70d9d0507ba7ca17226693b1a4967f23cf#diff-f579cccc964135c7d644c7b2d3b0d3ecR59) estableció la clave predeterminada en `ff6dc61a329dc96652bb092ec58981f7`. Las versiones vulnerables van desde 3.0-alpha1 hasta 3.1.1 (actual).
Aunque la [documentación](https://doc.ezplatform.com/en/latest/getting\_started/install\_ez\_platform/#change-installation-parameters) indica que se debe cambiar el secreto, no se hace cumplir.
Al igual que el esqueleto de Symfony, se le pedirá que ingrese un secreto durante la instalación. El valor predeterminado es `ThisEzPlatformTokenIsNotSoSecret_PleaseChangeIt`.
[Bolt CMS](https://bolt.cm) utiliza [Silex](https://github.com/silexphp/Silex), un micro-framework obsoleto basado en Symfony. Configura la clave secreta utilizando este cálculo:
Dado que el secreto a menudo se establece manualmente (en lugar de generarse aleatoriamente), las personas a menudo usarán una frase de paso en lugar de un valor aleatorio seguro, lo que lo hace susceptible a fuerza bruta si tenemos un hash contra el cual realizar la fuerza bruta. Obviamente, una URL válida de `/_fragment`, como una generada por Symfony, nos proporcionaría una tupla de mensaje-hash válida para forzar bruta el secreto.
Al principio de este blogpost, dijimos que el secreto de Symfony tenía varios usos. Uno de esos usos es que también se utiliza para generar tokens CSRF. Otro uso de `secret` es firmar cookies de recordar usuario. En algunos casos, un atacante puede usar su propio token CSRF o cookie de recordar usuario para forzar bruta el valor de `secret`.
Como ejemplo de cómo los secretos pueden ser forzados bruta para lograr la ejecución de código, veremos cómo podemos descubrir el secreto de eZPublish 2014.07.
Para construir este token, eZP utiliza dos valores que conocemos, y el secreto: `getIntention()` es la acción que el usuario está intentando realizar (`authenticate` por ejemplo), `session_id()` es el ID de sesión de PHP, y `getSecret()`, bueno, es el `secret` de Symfony.
Desafortunadamente, ezPublish incorporó un paquete de sensiolabs, [sensio/distribution-bundle](https://packagist.org/packages/sensio/distribution-bundle). Este paquete asegura que la clave secreta sea aleatoria. La genera de esta manera, durante la instalación:
Esto parece muy difícil de forzar bruscamente: `mt_rand()` puede producir 231 valores diferentes, y `uniqid()` se construye a partir de la marca de tiempo actual (con microsegundos).
Afortunadamente, sabemos que este secreto se genera en el último paso de la instalación, justo después de que se configura el sitio web. Esto significa que probablemente podemos filtrar el timestamp utilizado para generar este hash.
Una forma de hacerlo es utilizando los registros (_por ejemplo_, `/var/log/storage.log`); se puede filtrar la primera vez que se creó una entrada de caché. La entrada de caché se crea justo después de que se llama a `generateRandomSecret()`.
Si los registros no están disponibles, se puede utilizar el motor de búsqueda muy potente de eZPublish para encontrar el momento de creación del primer elemento del sitio web. De hecho, cuando se crea el sitio, se introducen muchos timestamps en la base de datos. Esto significa que el timestamp de los datos iniciales del sitio web eZPublish es el mismo que se utilizó para calcular `uniqid()`. Podemos buscar el _ContentObject_`landing_page` y descubrir su timestamp.
Esto nos deja con un total de 231 \* 106 posibilidades. Parece factible con [hashcat](https://hashcat.net) y un buen conjunto de GPUs, pero hashcat no proporciona un kernel `sha1(sha1($pass).$salt)`. Afortunadamente, ¡lo implementamos! Puedes encontrar [la solicitud de extracción aquí](https://github.com/hashcat/hashcat/pull/2536).
Symfony es ahora un componente central de muchas aplicaciones PHP. Como tal, cualquier riesgo de seguridad que afecte al framework afecta a muchos sitios web. Como se demostró en este artículo, ya sea una clave secreta débil o una vulnerabilidad de menor impacto permite a los atacantes obtener **ejecución de código remoto**.
Como miembro de un equipo azul, deberías revisar todos tus sitios web que dependen de Symfony. El software actualizado no puede descartarse por vulnerabilidades, ya que la clave secreta se genera en la primera instalación del producto. Por lo tanto, si creaste un sitio web basado en Symfony-3.x hace unos años y lo mantuviste actualizado en el camino, es probable que la clave secreta siga siendo la predeterminada.
* El HMAC se calcula usando la **URL completa**. Si el sitio web está detrás de un proxy inverso, necesitamos usar la URL interna del servicio en lugar de la que estamos enviando nuestro payload. Por ejemplo, la URL interna podría ser sobre HTTP en lugar de HTTPS.
* El algoritmo de HMAC ha cambiado a lo largo de los años: era **SHA-1** antes y ahora es **SHA-256**.
* Dado que Symfony elimina el parámetro `_hash` de la solicitud y luego genera la URL de nuevo, necesitamos calcular el hash en la misma URL que lo hace.
* Hay muchas claves secretas que se pueden usar, por lo que necesitamos verificarlas todas.
* En algunas versiones de PHP, no podemos llamar a funciones que tienen parámetros "por referencia", como `system($command, &$return_value)`.
* En algunas versiones de Symfony, `_controller` no puede ser una función, tiene que ser un método. Necesitamos encontrar un método de Symfony que nos permita ejecutar código.
* URL interna: podría ser `https://target.com/_fragment`, o quizás `http://target.com/_fragment`, o algo completamente diferente (_por ejemplo_, `http://target.website.internal`), lo cual no podemos adivinar
* Clave secreta: tenemos una lista de claves secretas habituales, como `ThisTokenIsNotSoSecretChangeIt`, `ThisEzPlatformTokenIsNotSoSecret_PleaseChangeIt`, etc.
No necesitamos preocuparnos por el payload efectivo (el contenido de `_path`) todavía, porque una URL firmada correctamente no resultará en una excepción `AccessDeniedHttpException`, y como tal no resultará en un `403`. Por lo tanto, el exploit intentará cada combinación de `(algoritmo, URL, secreto)`, generará una URL y verificará si no produce un código de estado `403`.
Luego, necesitamos averiguar si podemos llamar a una función directamente, o si necesitamos usar un método de clase. Podemos intentar primero la forma más directa y sencilla, usando una función como `phpinfo ([ int $what = INFO_ALL ] )` ([documentación](https://www.php.net/manual/en/function.phpinfo.php)). El parámetro GET `_path` se vería así:
De lo contrario, esto significa que necesitaremos usar un método de clase en su lugar. Un buen candidato para esto es `Symfony\Component\Yaml\Inline::parse`, que es una clase incorporada de Symfony y, como tal, está presente en sitios web de Symfony.
Obviamente, este método analiza una cadena de entrada YAML. El analizador [YAML](https://yaml.org) de Symfony admite la etiqueta `php/object`, que convertirá una cadena de entrada serializada en un objeto utilizando `unserialize()`. ¡Esto nos permite usar nuestra herramienta PHP favorita, [PHPGGC](https://github.com/ambionics/phpggc)!
En lugar de construir `_path` para cada uno de estos, podemos aprovechar el hecho de que si damos un argumento cuyo nombre no coincide con el prototipo del método, será ignorado. Por lo tanto, podemos agregar todos los argumentos posibles al método, sin preocuparnos por el prototipo real.
Por lo tanto, podemos construir `_path` de la siguiente manera:
El exploit, por lo tanto, ejecutará todas las posibles combinaciones de variables y luego probará los dos métodos de explotación. El código está disponible en [nuestro GitHub](https://github.com/ambionics/symfony-exploits).
Como ves en la captura de pantalla anterior, hay un logo de `sf` en la parte inferior derecha de la página. Este logo se muestra cuando Symfony está en modo de depuración. Hay casos en los que este logo no aparece, así que intenta acceder a `/_profiler` y verás la página como se muestra a continuación
Esta característica se llama Symfony Profiler, y no hay mucha información sobre esta característica en internet. La intención de esta característica es muy clara; te ayuda a depurar cuando hay un error o un bug. Por supuesto, esta característica solo se puede usar cuando el modo de depuración está habilitado.
El framework Symfony en sí es muy seguro, pero habilitar el modo de depuración hará que este framework sea extremadamente vulnerable. Por ejemplo, Profiler tiene una característica llamada Búsqueda de Perfiles, como la siguiente captura de pantalla.
Como ves en la captura de pantalla anterior, puedes acceder a todas las solicitudes enviadas al servidor. Al hacer clic en los hashes en el token, verás que todos los parámetros POST se pueden leer, como se ve en la siguiente captura de pantalla. Con esta característica, podemos secuestrar las credenciales de las cuentas de administradores y usuarios.
<summary><strong>Aprende hacking en AWS de cero a héroe con</strong><ahref="https://training.hacktricks.xyz/courses/arte"><strong>htARTE (HackTricks AWS Red Team Expert)</strong></a><strong>!</strong></summary>
* Si quieres ver a tu **empresa anunciada en HackTricks** o **descargar HackTricks en PDF** consulta los [**PLANES DE SUSCRIPCIÓN**](https://github.com/sponsors/carlospolop)!
* Consigue el [**merchandising oficial de PEASS & HackTricks**](https://peass.creator-spring.com)
* Descubre [**La Familia PEASS**](https://opensea.io/collection/the-peass-family), nuestra colección de [**NFTs**](https://opensea.io/collection/the-peass-family) exclusivos
* **Únete al** 💬 [**grupo de Discord**](https://discord.gg/hRep4RUj7f) o al [**grupo de telegram**](https://t.me/peass) o **sigue** a **Twitter** 🐦 [**@carlospolopm**](https://twitter.com/carlospolopm)**.**
* **Comparte tus trucos de hacking enviando PRs a los repositorios de GitHub** [**HackTricks**](https://github.com/carlospolop/hacktricks) y [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud).