
Suppression prévisible de delete_code via rand() dans PictShare < 3.7.1 (CWE-338). PoC + rapport d'avis.
delete_code prévisible via rand() dans PictShareRecherche de sécurité autorisée. PoC à usage défensif et éducatif uniquement.
PictShare génère le jeton d'autorisation
delete_code avec getRandomString(), qui tire chaque caractère du rand() non
cryptographique de PHP. Comme rand() n'est pas un CSPRNG — et que le même générateur
produit le hash public du fichier affiché dans chaque URL partagée — le delete_code est
prévisible plutôt que secret, ce qui permet la suppression non autorisée de fichiers hébergés
sans jamais lire le code depuis le point de terminaison info.
| CVE | CVE-2026-104356 |
| Produit | PictShare (hébergeur d'images/médias auto-hébergé) |
| Affecté | >= 2.0.0, < 3.7.1 |
| Corrigé dans | v3.7.1 |
| Vulnérabilité | Utilisation d'un PRNG cryptographiquement faible (CWE-338) → jeton d'authentification prévisible |
| Privilèges | Aucun (non authentifié) |
| 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 |
| Découvert par | Alisher Qarshibayev |
| Avis | 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;
}
Cette unique fonction produit à la fois :
delete_code qui autorise la suppression.Comme les deux jetons proviennent du même flux non-CSPRNG, un attaquant qui observe le hash
public obtient des informations sur l'état du générateur — le delete_code n'a plus l'entropie
que suggère sa longueur de 32 caractères. rand() n'offre aucune garantie d'imprévisibilité
cryptographique, donc son utilisation pour un jeton de sécurité constitue le défaut (CWE-338),
indépendamment de toute technique spécifique de récupération d'état.
random_int().rand() (aliasé vers le chemin Mersenne-Twister) par processus, donc un
prédicteur distant clé en main dépend du déploiement (réutilisation de processus, nombre de
sorties observables, build PHP). Ce PoC démontre donc le principe de prévisibilité de
manière déterministe dans un contexte de graine contrôlée, plutôt que de revendiquer une
cassure distante universelle en un seul coup. C'est la portée honnête de la découverte — et
c'est exactement pourquoi l'éditeur l'a tout de même traitée comme une vulnérabilité et l'a
corrigée.demo.php reproduit exactement le getRandomString() de l'éditeur et montre que, une fois
l'état du PRNG connu/récupéré, la première sortie (le hash public) détermine entièrement la
seconde sortie (le delete_code) — c'est-à-dire qu'observer le hash prédit le code :
php demo.php
Sortie attendue :
[*] 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)
Voir demo.php. Il est autonome (pas de serveur, pas de réseau) et prouve le
déterminisme hash→code qui rend rand() inadapté ici.
Mettre à niveau vers PictShare 3.7.1 (commit de correction ce5fc47),
qui remplace rand() par random_int() (CSPRNG). Recommandation générale : générer chaque
jeton de sécurité (codes de suppression, jetons de réinitialisation, clés d'API, sels) avec un
RNG cryptographiquement sûr — random_int() / random_bytes() en PHP — jamais rand(),
mt_rand() ou uniqid().
Remarque : les codes de suppression émis avant la correction ne sont pas renouvelés, donc les téléversements antérieurs au correctif restent prévisibles jusqu'à un nouveau téléversement.
| Date | Événement |
|---|---|
| 2026-10-01 | Divulgation publique, CVE réservée et publiée (VulnCheck), corrigée dans la v3.7.1 |
Divulguée de manière responsable à l'éditeur et coordonnée via VulnCheck. Corrigée avant la publication de ce PoC. Publiée à des fins défensives et éducatives.