1.3 – [Herramientas] – Qué es curl

curl es un cliente de línea de comandos para hacer peticiones a URLs (HTTP/HTTPS y más). En ciberseguridad sirve para:

  • Reproducir una request exacta (sin navegador, sin JS, sin “magia”).
  • Cambiar headers, cookies, método (GET/POST/PUT/DELETE…), body, redirects, TLS, etc.
  • Guardar evidencias: headers, cuerpo, tiempos, códigos, trazas.
  • Automatizar pruebas: loops, wordlists, comparación de respuestas.

En OWASP A01, curl es ideal para comprobar:

“¿Puedo acceder a algo que no debería si cambio un ID, un rol, un método, una ruta o un header?”


1) Base rápida: anatomía de una request con curl

1.1 GET simple

curl https://ejemplo.com/

1.2 Ver solo headers (respuesta)

curl -I https://ejemplo.com/

1.3 Ver request + response (debug)

  • -v muestra el intercambio HTTP (ideal para auditoría).
curl -v https://ejemplo.com/

1.4 Seguir redirecciones

Muchas apps redirigen a /login.

curl -L https://ejemplo.com/zona-privada

2) Opciones clave (las que usarás todo el rato)

Salida y verbosidad

  • -i incluye headers de respuesta en la salida
  • -I hace HEAD (solo headers)
  • -v verbose (request/response)
  • -s silent (menos ruido)
  • -S muestra errores aunque uses -s
  • -D <file> guarda headers de respuesta en un fichero
  • -o <file> guarda el cuerpo en fichero
  • -w "<fmt>" imprime métricas (código, tiempos, etc.)

Ejemplo “limpio pero con datos útiles”:

curl -sS -D headers.txt -o body.html -w "STATUS=%{http_code}\nTIME=%{time_total}\n" https://ejemplo.com/

Método HTTP

  • -X GET|POST|PUT|DELETE|PATCH|OPTIONS|HEAD

Ojo nerd: curl elige método según uses -d (si usas -d, por defecto es POST). A veces conviene ser explícito con -X.

Headers y autenticación

  • -H "Header: valor" añade header(s)
  • -u user:pass Basic Auth
  • -b "cookie=valor" envía cookies
  • -c cookies.txt guarda cookies en “cookie jar”
  • -b cookies.txt reutiliza cookies guardadas

Datos (body)

  • -d "a=1&b=2" envía body tipo form-url-encoded
  • --data-urlencode codifica bien caracteres especiales
  • -H "Content-Type: application/json" -d '{"x":1}' JSON
  • -F "f=@archivo" multipart/form-data (subidas)

TLS y certificados (útil en auditoría)

  • --tlsv1.2 fuerza versión TLS
  • --ciphers ... fuerza cifrados (avanzado)
  • --cert, --key (mTLS)
  • -k ignora validación del certificado (solo pruebas controladas, jamás en producción como “solución”)

3) Plantilla de trabajo “A01-ready” (para evidencias)

Esto te deja una salida reproducible, guardando pruebas:

curl -sS -i -L \
  -w "\n\n---\nSTATUS=%{http_code}\nSIZE=%{size_download}\nTIME=%{time_total}\nIP=%{remote_ip}\nURL=%{url_effective}\n" \
  https://objetivo.tld/ruta

Para separar headers y body en ficheros:

curl -sS -D evidencias_headers.txt -o evidencias_body.txt \
  -w "STATUS=%{http_code}\nTIME=%{time_total}\n" \
  https://objetivo.tld/ruta

4) OWASP A01: qué probar con curl (y cómo)

4.1 IDOR (Insecure Direct Object Reference) — cambiar el ID

Caso típico: /api/users/123 te devuelve tu usuario. ¿Qué pasa con /api/users/124?

curl -sS -i https://victima.tld/api/users/123
curl -sS -i https://victima.tld/api/users/124

Qué buscar:

  • Si cambia el contenido y sigue devolviendo 200 OK, mala pinta.
  • Si debería ser 403 Forbidden (autorización) o 404 (no revelar existencia), depende del diseño, pero 200 para todo suele ser alarma.

4.2 Bypass por método HTTP (GET vs PUT/DELETE)

Muchas apps protegen el “ver” pero se olvidan del “modificar”.

# ver
curl -sS -i https://victima.tld/api/orders/55

# intentar modificar
curl -sS -i -X PUT -H "Content-Type: application/json" \
  -d '{"status":"CANCELLED"}' \
  https://victima.tld/api/orders/55

# intentar borrar
curl -sS -i -X DELETE https://victima.tld/api/orders/55

Señal A01: endpoints sensibles aceptan métodos peligrosos sin chequear rol/propiedad.

4.3 Acceso a rutas “admin” sin ser admin

curl -sS -i https://victima.tld/admin
curl -sS -i https://victima.tld/api/admin/users

4.4 Cambiar rol en el cliente (cabeceras “de pega”)

Si una app confía en algo como X-Role: admin (error grave), lo detectas así:

curl -sS -i -H "X-Role: admin" https://victima.tld/api/me

Nota: Esto no debería funcionar jamás si el backend está bien (el rol debe venir de sesión/token firmado).

Primero sin login:

curl -sS -i https://victima.tld/api/profile
~~`

Luego con cookie de sesión (simulando usuario normal):

1) Logueas una vez y guardas cookies:
~~~bash
curl -sS -c cookies.txt -d "user=alumno&pass=1234" https://victima.tld/login
  1. Accedes con cookie:
curl -sS -b cookies.txt -i https://victima.tld/api/profile
  1. Intentas un endpoint que debería ser solo admin:
curl -sS -b cookies.txt -i https://victima.tld/api/admin/dashboard

A01 clásico: usuario normal consigue 200 donde debería ser 403.

4.6 Tokens Bearer (JWT / API tokens)

curl -sS -i \
  -H "Authorization: Bearer TU_TOKEN" \
  https://victima.tld/api/orders

Pruebas A01 típicas:

  • Usar token de usuario A para pedir recursos de usuario B (IDOR).
  • Ver si el backend solo valida “token válido” pero no valida “dueño del recurso”.

4.7 Enumeración y filtrado: parámetros peligrosos

Algunos endpoints aceptan filtros como ?userId=...:

curl -sS -i "https://victima.tld/api/invoices?userId=10"
curl -sS -i "https://victima.tld/api/invoices?userId=11"

4.8 CORS mal configurado (pista de exposición)

CORS no es “A01 puro”, pero a veces se mezcla con acceso indebido desde otros orígenes.

curl -sS -i \
  -H "Origin: https://malicioso.tld" \
  https://victima.tld/api/profile
~~`

Busca en respuesta:
- `Access-Control-Allow-Origin: *` o refleja el Origin sin control
- `Access-Control-Allow-Credentials: true` combinado con cosas raras

### 4.9 OPTIONS y descubrimiento de métodos permitidos
~~~bash
curl -sS -i -X OPTIONS https://victima.tld/api/orders/55

Mira Allow: o los headers CORS.


5) Autenticación práctica con curl (cookies, sesiones, CSRF)

5.1 Mantener sesión con cookies

# login
curl -sS -c cookies.txt -d "username=alumno&password=1234" https://victima.tld/login

# usar sesión
curl -sS -b cookies.txt -i https://victima.tld/mi-cuenta

5.2 CSRF token (patrón general)

Muchas apps: primero GET a formulario para obtener token, luego POST con token + cookie.
Ejemplo conceptual (depende del sitio):

# 1) cargar formulario y guardar cookies
curl -sS -c cookies.txt https://victima.tld/form > form.html

# 2) extraer token (si está en HTML; esto es un ejemplo típico)
grep -oP 'name="csrf"\s+value="\K[^"]+' form.html > token.txt

# 3) enviar POST con token + cookie
curl -sS -b cookies.txt -d "csrf=$(cat token.txt)&campo=valor" https://victima.tld/submit

En A01, CSRF no es el centro, pero aparece en flujos reales al automatizar.


6) Subidas de archivo y pruebas de acceso (muy común en A01)

6.1 Subir con multipart

curl -sS -i \
  -b cookies.txt \
  -F "file=@prueba.txt" \
  https://victima.tld/upload

6.2 Acceder al archivo de otro usuario

Si el sistema guarda en /uploads/ID/archivo, prueba el ID (solo en entorno controlado):

curl -sS -i https://victima.tld/uploads/10/prueba.txt
curl -sS -i https://victima.tld/uploads/11/prueba.txt

7) Reporting: sacar “pruebas bonitas” para un informe

7.1 Guardar todo + métricas

curl -sS -i -L \
  -o respuesta.txt \
  -w "\nSTATUS=%{http_code}\nTIME=%{time_total}\nSIZE=%{size_download}\n" \
  https://victima.tld/api/orders/55

7.2 Solo el código HTTP (útil para scripts)

curl -s -o /dev/null -w "%{http_code}\n" https://victima.tld/admin

7.3 Comparar respuestas rápidamente (cambia ID y mira tamaño)

curl -s -o /dev/null -w "ID=55 CODE=%{http_code} SIZE=%{size_download}\n" https://victima.tld/api/orders/55
curl -s -o /dev/null -w "ID=56 CODE=%{http_code} SIZE=%{size_download}\n" https://victima.tld/api/orders/56

Si los tamaños cambian de forma consistente… probablemente estás viendo datos distintos.


8) Performance y timing (cuando el comportamiento cambia)

  • --max-time 10 corta si tarda más de 10s
  • --connect-timeout 3 timeout de conexión
  • --retry 3 --retry-all-errors reintentos (ojo en auditorías para no “machacar”)
curl -sS --connect-timeout 3 --max-time 10 https://victima.tld/

9) “Modo alumno”: 10 comandos que deberían saberse sí o sí

curl -v https://host/ruta
curl -I https://host/
curl -L https://host/ruta
curl -i https://host/api/recurso
curl -H "Authorization: Bearer TOKEN" https://host/api
curl -c cookies.txt -d "u=a&p=b" https://host/login
curl -b cookies.txt https://host/privado
curl -X PUT -H "Content-Type: application/json" -d '{"x":1}' https://host/api/x/1
curl -X DELETE https://host/api/x/1
curl -s -o /dev/null -w "%{http_code}\n" https://host/admin

10) Errores típicos (y cómo no volverte loco)

  • 403 vs 401
    • 401 Unauthorized: no autenticado (falta login/token).
    • 403 Forbidden: autenticado pero sin permiso.
    • A01 suele ser “debería ser 403 y me dan 200”.
  • curl “no hace lo mismo que el navegador”
    • El navegador añade cookies, headers, y sigue redirects.
    • Usa -L, y si necesitas replicar, usa -v y añade -H y -b.
  • El endpoint requiere JSON
    • Añade -H "Content-Type: application/json".
  • El servidor redirige a login
    • Mira Location: con -i o sigue con -L.

11) Tratamiento de sesiones.

Si una web usa sesión, curl solo verá la respuesta correcta si:

  • primero creas la sesión (login)
  • luego reenvías la cookie de sesión en las peticiones siguientes

El navegador lo hace solo. curl, no.


Qué es realmente una sesión (visión backend)

Cuando haces login:

  1. El servidor valida usuario/contraseña
  2. Crea una sesión en servidor
  3. Envía una cookie al cliente, por ejemplo:
Set-Cookie: PHPSESSID=abc123; Path=/; HttpOnly

A partir de ahí:

  • la cookie es tu identidad
  • sin cookie → eres un desconocido

Paso 1️⃣ – Login y guardar la sesión

Usamos -c para guardar cookies.

curl -i \
  -c cookies.txt \
  -d "username=usuario1&password=1234" \
  http://servidor/login.php

Comprueba el fichero:

cat cookies.txt

Verás algo tipo:

PHPSESSID    abc123

Eso es tu sesión.


Paso 2️⃣ – Reutilizar la sesión para ver la respuesta real

Usamos -b para enviar la cookie.

curl -i \
  -b cookies.txt \
  http://servidor/api/profile.php

Ahora verás:

  • la respuesta real
  • no el redirect al login
  • no el “no autorizado”

📌 Sin -b cookies.txt, curl siempre será anónimo.


Flujo completo (plantilla mental)

Siempre que una web use sesión:

  1. curl -c cookies.txtcrear sesión
  2. curl -b cookies.txtusar sesión
  3. Repetir paso 2 para cada endpoint protegido

Ver exactamente qué devuelve el servidor

Para auditoría, conviene ver headers + body:

curl -i -b cookies.txt http://servidor/api/orders.php

Si quieres guardar evidencias:

curl -b cookies.txt \
  -D headers.txt \
  -o body.txt \
  -w "STATUS=%{http_code}\n" \
  http://servidor/api/orders.php

Caso real típico: “me redirige al login”

Si haces esto:

curl http://servidor/api/profile.php

y ves:

HTTP/1.1 302 Found
Location: /login.php

No es que curl “no funcione”:
es que no llevas la sesión.

Solución:

curl -L -b cookies.txt http://servidor/api/profile.php

¿Y si el login es JSON (API REST)?

Muy común en APIs modernas.

Login:

curl -c cookies.txt \
  -H "Content-Type: application/json" \
  -d '{"user":"usuario1","pass":"1234"}' \
  http://servidor/api/login

Acceso protegido:

curl -b cookies.txt \
  http://servidor/api/orders

Alternativa: tokens (Bearer)

Si en vez de sesión usa token:

curl -H "Authorization: Bearer TOKEN_AQUI" \
  http://servidor/api/orders

Aquí no hay cookie, el token es la identidad.


curl no tiene memoria
si no le pasas la sesión, no eres nadie


CASO PRACTICO

Vamos un caso clásico:

login en PHP con session_start() + usuario y contraseña.
Sin suposiciones raras, sin frameworks, sin magia.

Voy a explicarlo como piensa el servidor PHP, que es la clave para que curl tenga sentido.


Qué hace PHP cuando haces login (lo importante)

En un login típico en PHP ocurre esto:

  1. session_start();
  2. PHP crea (o reanuda) una sesión
  3. Si el usuario es correcto: $_SESSION['user_id'] = 3; $_SESSION['role'] = 'user';
  4. PHP envía al navegador: Set-Cookie: PHPSESSID=abc123

📌 Ese PHPSESSID es la identidad real del usuario
No el usuario, no la contraseña: la cookie.


Cómo reproducir eso con curl (paso a paso real)


Hacer login con curl y guardar la sesión

Supongamos:

  • Login: /login.php
  • Campos POST: username y password

Comando correcto

curl -i \
  -c cookies.txt \
  -d "username=usuario1&password=1234" \
  http://servidor/login.php

Qué hace cada cosa

  • -d → envía el POST (login)
  • -c cookies.txtguarda la cookie PHPSESSID
  • -i → ves headers (para comprobar que hay Set-Cookie)

Si todo va bien, verás algo como:

Set-Cookie: PHPSESSID=abc123; path=/; HttpOnly

📌 Eso significa: sesión creada correctamente


cat cookies.txt

Contenido típico:

# Netscape HTTP Cookie File
servidor   FALSE   /   FALSE   0   PHPSESSID   abc123

Esto es la sesión PHP.


Acceder a una página protegida usando la sesión

Ahora reenvías la cookie:

curl -i \
  -b cookies.txt \
  http://servidor/privado.php

Si el login fue correcto:

  • verás el contenido real
  • no redirige a /login.php
  • no dice “no autorizado”

Cada vez que vean PHP + sesión:

  1. curl -c cookies.txtcrear sesión
  2. curl -b cookies.txtusar sesión
  3. Repetir paso 2 tantas veces como haga falta

CASO REAL: “me redirige al login”

Si haces:

curl http://servidor/privado.php

y ves:

HTTP/1.1 302 Found
Location: login.php

👉 No es un error
👉 Es que no llevas la sesión

Solución:

curl -L -b cookies.txt http://servidor/privado.php

SIMULAR DOS USUARIOS DISTINTOS (muy A01)

Usuario normal

curl -c cookies_user.txt \
  -d "username=user&password=1234" \
  http://servidor/login.php

Admin

curl -c cookies_admin.txt \
  -d "username=admin&password=admin123" \
  http://servidor/login.php

Comparar accesos

curl -b cookies_user.txt  http://servidor/admin.php
curl -b cookies_admin.txt http://servidor/admin.php

📌 Si ambos ven lo mismo → Broken Access Control


PROBAR IDOR CON SESIÓN REAL

curl -b cookies_user.txt \
  "http://servidor/order.php?id=1"

curl -b cookies_user.txt \
  "http://servidor/order.php?id=2"

Si cambia el contenido → IDOR confirmado


GUARDAR EVIDENCIA (modo auditor)

curl -b cookies_user.txt \
  -D headers.txt \
  -o body.txt \
  -w "STATUS=%{http_code}\n" \
  http://servidor/order.php?id=2

Eso es evidencia técnica válida.