Skip to content
KitploitKITPLOIT
HerramientasExploitsBlog
Log in
Enviar
HerramientasExploitsBlog
Enviar

¡Herramientas de Hacking, PenTest y Ciberseguridad para tu Arsenal de Seguridad!

Kitploit es un directorio de herramientas de hacking, ciberseguridad y pentesting. Descubre las últimas actualizaciones de proyectos para encontrar vulnerabilidades, analizar sistemas, automatizar pruebas y fortalecer tu seguridad.

FeedsContactoPrivacidad© 2026 Kitploit

Directorio de Herramientas

Categorías

Ver todas las categorías
Loading categories
AfterLife — Laboratorio de detección de persistencia de revocación: cuando el restablecimiento de contraseña tiene éxito pero el atacante nunca se va. Reproduce el fallo de revocación condicional de Strapi CVE-2026-22706, su corrección, un paquete de detección de tres reglas y la regla ingenua que no lo detecta. | Kitploit
Herramientas/GitHubGitHub/het-p301204/afterlife
Herramientas DefensivasAnálisis de VulnerabilidadesSeguridad WebAutenticaciónAprendizaje y EducaciónRed TeamingRespuesta a IncidentesLabs y Práctica
GitHubhet-p301204/afterlife

AfterLife

Ver Repositorio
35hace 1 mesAún no revisado

Más Populares

Ver todos →

Descubre las herramientas más usadas por nuestra comunidad.

Explora todas las herramientas

Explora nuestra colección de herramientas

Ver todas las herramientas →

Acerca de

Laboratorio de detección de persistencia de revocación: cuando el restablecimiento de contraseña tiene éxito pero el atacante nunca se va. Reproduce el fallo de revocación condicional de Strapi CVE-2026-22706, su corrección, un paquete de detección de tres reglas y la regla ingenua que no lo detecta.

Compartir

AFTERLIFE

Laboratorio de Detección de Persistencia de Revocación

Cuando el restablecimiento de contraseña tiene éxito pero el atacante nunca se va.

Un laboratorio local red/blue para una clase de vulnerabilidad: la credencial que sobrevive al evento que se suponía debía eliminarla. Incluye el exploit, la causa raíz, la solución, un paquete de detección de tres reglas, una auditoría de estado para lo que las reglas estructuralmente no pueden ver, una consola forense — y la regla de detección que no funciona, conservada en el repositorio para ser demostrada fallando.

CI tests python rules OWASP CWE license

Inicio rápido · El hallazgo · Por qué la detección ingenua falla · El paquete de reglas · Consola · La solución · Matriz · Pruebas · Documentación


El mismo ataque contra ambas implementaciones. Una barra por credencial, desde la emisión hasta la muerte, agrupadas en linajes. La línea discontinua es el cambio de contraseña. En modo vulnerable cinco barras la cruzan y continúan; en modo corregido cada barra del linaje robado se detiene allí.

El mismo ataque. Las mismas peticiones. Una diferencia. Generado por scripts/figures.py a partir del mismo payload que dibuja la consola — regenerado y comparado en CI, para que una figura no pueda desviarse del código.


La acción de contención que no contiene```text

09:00 alice logs in ┐ refresh credential rt-001 │ the attacker steals rt-001 ┘

10:00 alice changes her password ← the one thing a victim can do alone HTTP 200 · password changed · fresh session issued

10:00:03 attacker: POST /refresh rt-002 → HTTP 200 new access credential at-004, issued 10:00:03

10:01:00 attacker: GET /me at-004 → HTTP 200 {"username": "alice", "authenticated": true}

Sin error. Sin anomalía. Sin autenticación fallida que contar. La credencial de acceso del atacante tiene *tres segundos de antigüedad* y fue emitida por el servidor, a petición, después del restablecimiento.

Aquí está la vulnerabilidad completa:```python
def _revoke_for_security_change(self, user, kind, device_id):
    if device_id:
        revoke_credentials(user, device_id=device_id)   # ← the finding

Léelo como lo haría un revisor. La lógica de revocación está justo ahí. Llama a la función correcta con el alcance correcto. El endpoint a su alrededor actualiza el hash de la contraseña, devuelve 200 y emite una sesión nueva — cada comportamiento observable de un cambio de contraseña correcto está presente.

Y si quien llama omite device_id, no se revoca nada, y el endpoint igualmente informa éxito.

Esto no es hipotético. Es CVE-2026-22706 en Strapi ≤ 5.33.2, donde el paso de invalidación del refresh-token era condicional a un deviceId proporcionado por quien llama. Puntuado 2.1, Low. Se discute abajo.


Por qué esto importa

La puntuación es baja porque el atacante ya tenía acceso — esa es la condición de entrada, y este bug no concede nada nuevo. Lo que concede es duración, y lo hace rompiendo el único control que la víctima puede operar por sí misma.

  • Todo runbook de toma de control de cuenta comienza con restablecer la contraseña. Todo producto le dice lo mismo al usuario.
  • Cuando falla silenciosamente, a la víctima se le dice que el problema está resuelto y deja de buscar — lo que desactiva la señal de detección que más importa en la toma de control de cuenta: que el usuario lo note.
  • La ventana de persistencia es la vida útil de la credencial de refresh: 30 días por defecto, renovable indefinidamente mediante rotación. test_persistence_lasts_as_long_as_the_refresh_credential recorre siete días de tiempo simulado para demostrarlo.
  • No hay una segunda acción que el usuario pueda tomar. Un segundo cambio de contraseña hace exactamente lo mismo que el primero.

Un bug con puntuación Low en una ruta de contención cuesta más que un bug con puntuación Low en una ruta de funcionalidad, porque el coste se paga durante un incidente, cuando nadie está leyendo avisos.


El detector ingenuo

La regla que escribes primero:```text IF credential.issued_at < credential_change.timestamp: ALERT

No es una regla estúpida. Es barata, necesita un solo campo, se lee como la definición
del problema, y es **correcta para el caso simple** — un atacante que usa un
token de *acceso* robado después del restablecimiento queda atrapado.
`test_the_naive_rule_catches_the_simple_case` afirma que funciona.

Ambas reglas hacen la misma pregunta sobre la misma credencial. Leen campos
distintos para responderla, y los dos campos no coinciden:

![Dos filas, una por campo. credential.issued_at marca 10:00:03, tres segundos después del cambio, y concluye que la credencial parece limpia. lineage.root_issued_at marca 09:00:00, una hora antes del cambio, y alerta.](https://raw.githubusercontent.com/het-p301204/afterlife/main/docs/figures/two-fields.svg)

**Tres segundos después, o una hora antes — la misma credencial, en el mismo
instante.** La regla ingenua pregunta por un campo que el atacante controla, y un
`POST /refresh` lo restablece.

Ejecútala sobre el registro de solicitudes, que es donde realmente se escribe esta consulta,
porque los registros de solicitudes son lo que tienes:```text
NAIVE DETECTOR (NAIVE-001), over the request log

  Result: NO ALERT

  Six requests were served to the attacker after the reset. Every one of them
  carried at-004, minted at 10:00:03 -- three seconds *after* the password
  change. By its own timestamp it is the newest credential on the account.

Sería fácil detenerse ahí, y deshonesto, así que el laboratorio también ejecuta la versión más justa de la regla ingenua — ampliada para vigilar también el endpoint de refresh:```text NAIVE DETECTOR, widened to include POST /refresh

1 alert at 2026-09-11T10:00:03.000Z: the credential presented to /refresh was rt-002, issued 2026-09-11T09:30:00.000Z. Then it goes blind. 6 events follow that hop and it flags none of them, because every credential from there on carries a post-reset timestamp. Its incident covers 1 accepted request; the lineage rule's covers all of them.

Descargar herramienta