linode / linode/linode-cli

[Bug]: Incomplete URL substring sanitization Unvalidated Redirects and Forwards

Abierto
#735 0 comentarios 0 reacciones 0 asignados Ver en GitHub

Nadie ha tomado este issue todavía.

bug
Lenguaje dominante
Python
Estrellas
442
Forks
159
Merge medio
7 d 21 h
PR fusionados (30 d)
8

Descripción

### CLI Version

v5.56.2

### Command

https://github.com/linode/linode-cli/blob/fafe73e1f48a48ab9cbdf9b01f679e041f6bf3fa/tests/integration/domains/test_slave_domains.py#L73-L73

Sanitizing untrusted URLs is a common technique for preventing attacks such as request forgeries and malicious redirections. Usually, this is done by checking that the host of a URL is in a set of allowed hosts. However, treating the URL as a string and checking if one of the allowed hosts is a substring of the URL is very prone to errors. Malicious URLs can bypass such security checks by embedding one of the allowed hosts in an unexpected location.

Even if the substring check is not used in a security-critical context, the incomplete check may still cause undesirable behaviors when the check succeeds accidentally.

### Output

_No response_

### Expected Behavior

## Recommendation
Parse a URL before performing a check on its host value, and ensure that the check handles arbitrary subdomain sequences correctly.

### Actual Behavior

CWE-20

### Steps to Reproduce

## POC
The following code checks that a URL redirection will reach the `example.com` domain.
```python
from flask import Flask, request, redirect
from urllib.parse import urlparse

app = Flask(__name__)

# Not safe, as "evil-example.net/example.com" would be accepted

@app.route('/some/path/bad1')
def unsafe1(request):
target = request.args.get('target', '')
if "example.com" in target:
return redirect(target)

# Not safe, as "benign-looking-prefix-example.com" would be accepted

@app.route('/some/path/bad2')
def unsafe2(request):
target = request.args.get('target', '')
if target.endswith("example.com"):
return redirect(target)

#Simplest and safest approach is to use an allowlist

@app.route('/some/path/good1')
def safe1(request):
allowlist = [
"example.com/home",
"example.com/login",
]
target = request.args.get('target', '')
if target in allowlist:
return redirect(target)

#More complex example allowing sub-domains.

@app.route('/some/path/good2')
def safe2(request):
target = request.args.get('target', '')
host = urlparse(target).hostname
#Note the '.' preceding example.com
if host and host.endswith(".example.com"):
return redirect(target)
```
The first two examples show unsafe checks that are easily bypassed. In `unsafe1` the attacker can simply add `example.com` anywhere in the url. For example, `http://evil-example.net/example.com`. In `unsafe2` the attacker must use a hostname ending in example.com, but that is easy to do. For example, `http://benign-looking-prefix-example.com`.

The second two examples show safe checks. In `safe1`, an allowlist is used. Although fairly inflexible, this is easy to get right and is most likely to be safe. In `safe2`, urlparse is used to parse the URL, then the hostname is checked to make sure it ends with `.example.com`.

## References
[SSRF](https://www.owasp.org/index.php/Server_Side_Request_Forgery)
[XSS Unvalidated Redirects and Forwards Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html).
[CWE-20](https://cwe.mitre.org/data/definitions/20.html).

Guía de contribución

Abrir la guía de contribución

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Línea de trabajo

Empieza en tests/integration/domains/test_slave_domains.py, en la línea 73, y sigue el manejo de URL que cubre. Comprueba si la validación del host depende de la coincidencia de subcadenas y, a continuación, ejecuta la prueba de integración correspondiente. La tarea estará terminada cuando los prefijos arbitrarios de nombres de host o el texto incrustado de un host permitido ya no superen la validación, mientras los hosts permitidos legítimos sigan funcionando.

Escrito por el modelo de indexación a partir del texto del issue.

Evaluación

Stack tecnológico
python
Área
cli, security
Tipo de issue
Error
Dificultad
3/5
Tiempo estimado
1-2 días
Estado de actividad
Estancado
Claridad
Bastante claro
Aptitud para principiantes
45/100

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.