lab-api-vuln — una API deliberadamente vulnerable ================================================= ESTADO: LABORATORIO LOCAL Target: http://localhost:8096 Autorizacion: sistema local creado por ti para entrenamiento. Esta API tiene fallos a proposito. Todo lo que aprendes aqui lo aprendes contra esta API tuya, dentro de tu maquina. Ninguna de estas tecnicas debe apuntarse a un servicio que no sea tuyo o para el que no tengas autorizacion escrita. Abusar de una API ajena es un delito; abusar de la tuya es como se estudia. La usan las lecciones f06-authz-api, f06-validacion y f06-abuso. Que levanta ----------- api API en Python (solo biblioteca estandar), 127.0.0.1:8096 cliente alpine con curl y jq, para atacar desde dentro de la red Dos redes: api_interna internal: true, SIN salida a internet; aqui vive el cliente api_borde bridge normal; solo la usa la api para que el puerto 8096 llegue desde el host El estado vive en memoria. Para volver al punto de partida: docker compose restart api Comandos -------- docker compose up -d --build levantar docker compose exec cliente sh entrar al puesto de trabajo docker compose logs -f api ver lo que registra la API docker compose restart api restablecer el estado docker compose down -v destruir todo Abre despues: http://localhost:8096/salud Rutas de la API --------------- GET /salud comprobacion de vida GET /usuarios lista TODOS los nombres de usuario GET /perfil/ devuelve el registro entero, clave incluida POST /registro alta de usuario (fusiona todo el cuerpo) POST /login autentica; no limita intentos POST /transferencia mueve saldo; sin idempotencia ni firma Usuarios de partida: ana clave sol-de-verano-19 rol cliente saldo 100 beto clave montana-azul-42 rol cliente saldo 250 tesoreria clave no-la-vas-a-adivinar-8x rol admin saldo 0 Los tres fallos, uno por leccion -------------------------------- 1. MASS ASSIGNMENT (f06-authz-api) /registro fusiona el cuerpo completo: quien envie "rol" o "saldo" se los queda. curl -s -X POST http://localhost:8096/registro \ -H 'Content-Type: application/json' \ -d '{"usuario":"mallory","clave":"1234","rol":"admin","saldo":999999}' | jq La respuesta trae rol: "admin". Acabas de darte de alta como administrador. 2. SIN VALIDACION Y ENUMERACION (f06-validacion) /registro acepta cualquier tipo y cualquier campo extra. Y la API distingue "no existe" de "clave incorrecta": curl -s -o /dev/null -w "%{http_code}\n" \ http://localhost:8096/perfil/ana # 200: existe curl -s -o /dev/null -w "%{http_code}\n" \ http://localhost:8096/perfil/zzz # 404: no existe /perfil ademas devuelve la clave en la respuesta. 3. SIN LIMITE, REPLAY E IDEMPOTENCIA (f06-abuso) /login no limita intentos: fuerza bruta a placer. Y una transferencia se puede reenviar tal cual, moviendo el dinero otra vez: curl -s -X POST http://localhost:8096/transferencia \ -H 'Content-Type: application/json' \ -d '{"origen":"beto","destino":"ana","monto":50}' | jq # repite la MISMA linea: el saldo se mueve de nuevo (replay) Como se corrige (lo haces tu en las lecciones) ---------------------------------------------- 1. Lista de campos permitidos en /registro: rol y saldo NO se leen del cuerpo, se fijan en el servidor. 2. Validar tipos y campos antes de tocar la logica; devolver solo los campos publicos (nunca la clave). 3. Contar intentos por usuario/IP, exigir clave de idempotencia en /transferencia y verificar una firma del cuerpo. Editas cursos/seguridad/labs/lab-api-vuln/api/servidor.py, y luego: docker compose up -d --build para reconstruir con tu correccion y volver a probar. Si algo falla ------------- "port is already allocated" otro proceso usa el 8096; cambia el mapeo a 127.0.0.1:8097:8000 la respuesta no es JSON te falto -H 'Content-Type: application/json' el estado quedo raro docker compose restart api lo restablece Cuando termines, siempre: docker compose down -v