
Eliminación predecible de delete_code mediante rand() en PictShare < 3.7.1 (CWE-338). PoC + informe del aviso.
delete_code predecible mediante rand() en PictShareInvestigación de seguridad autorizada. PoC para uso defensivo y educativo únicamente.
PictShare genera el token de autorización
delete_code con getRandomString(), que extrae cada carácter del rand() no
criptográfico de PHP. Dado que rand() no es un CSPRNG — y el mismo generador
produce el hash público del archivo que se muestra en cada URL compartida — el delete_code
es predecible en lugar de secreto, lo que permite la eliminación no autorizada de archivos
alojados sin necesidad de leer el código desde el endpoint de información.
| CVE | CVE-2026-104356 |
| Producto | PictShare (host de imágenes/media autoalojado) |
| Afectados | >= 2.0.0, < 3.7.1 |
| Corregido en | v3.7.1 |
| Vulnerabilidad | Uso de PRNG criptográficamente débil (CWE-338) → token de autenticación predecible |
| Privilegios | Ninguno (no autenticado) |
| CVSS 3.1 | 5.9 MEDIUM — AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:H |
| CVSS 4.0 | 8.2 HIGH — AV:N/AC:H/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H |
| Descubierto por | Alisher Qarshibayev |
| Aviso | VulnCheck |
// src/inc/core.php (< 3.7.1)
function getRandomString($length=32, $keyspace='0123456789abcdefghijklmnopqrstuvwxyz')
{
$str = '';
$max = (function_exists('mb_strlen') ? mb_strlen($keyspace,'8bit') : strlen($keyspace)) - 1;
for ($i = 0; $i < $length; ++$i) {
$str .= $keyspace[rand(0, $max)]; // <-- rand() is NOT cryptographically secure
}
return $str;
}
Esta única función produce ambos:
delete_code que autoriza la eliminación.Dado que ambos tokens provienen del mismo flujo no CSPRNG, un atacante que observa el hash
público obtiene información sobre el estado del generador — el delete_code ya no tiene la
entropía que su longitud de 32 caracteres sugiere. rand() no ofrece ninguna garantía de
impredecibilidad criptográfica, por lo que usarlo para un token de seguridad es el defecto
(CWE-338), independientemente de cualquier técnica específica de recuperación de estado.
random_int().rand() (con alias a la ruta Mersenne-Twister) por proceso, por lo que un predictor remoto
llave en mano depende del despliegue (reutilización de procesos, número de salidas
observables, compilación de PHP). Por lo tanto, esta PoC demuestra el principio de
predecibilidad de forma determinista en una semilla controlada, en lugar de afirmar una
ruptura remota universal de un solo intento. Ese es el alcance honesto del hallazgo — y es
exactamente por lo que el proveedor aún lo trató como una vulnerabilidad y lo corrigió.demo.php reproduce la función exacta getRandomString() del proveedor y muestra que, una
vez que el estado del PRNG es conocido/recuperado, la primera salida (el hash público)
determina completamente la segunda salida (el delete_code) — es decir, observar el hash
predice el código:
php demo.php
Salida esperada:
[*] Reproducing PictShare getRandomString() with rand()
[*] Seed used by victim process : 1337 (unknown to attacker a priori)
public file hash (output #1): k3f9... (visible in the shared URL)
secret delete_code(output #2): 8a1c...
[*] Attacker recovers seed by matching the OBSERVED public hash:
[+] Seed recovered: 1337
[+] Predicted delete_code: 8a1c...
[+] MATCH — delete_code predicted from the public hash alone (no info API needed)
Ver demo.php. Es autocontenido (sin servidor, sin red) y demuestra el
determinismo hash→código que hace que rand() sea inadecuado aquí.
Actualizar a PictShare 3.7.1 (commit de corrección ce5fc47),
que reemplaza rand() con random_int() (CSPRNG). Guía general: generar cada token de
seguridad (códigos de eliminación, tokens de restablecimiento, claves de API, sales) con un
RNG criptográficamente seguro — random_int() / random_bytes() en PHP — nunca rand(),
mt_rand() o uniqid().
Nota: los códigos de eliminación emitidos antes de la corrección no se rotan, por lo que las cargas previas a la corrección siguen siendo predecibles hasta que se vuelvan a subir.
| Fecha | Evento |
|---|---|
| 2026-10-01 | Divulgación pública, CVE reservado y publicado (VulnCheck), corregido en v3.7.1 |
Divulgado de forma responsable al proveedor y coordinado a través de VulnCheck. Corregido antes de la publicación de esta PoC. Publicado con fines defensivos y educativos.