PictShare < 3.7.1 における rand() を介した予測可能な delete_code (CWE-338)。PoC とアドバイザリの解説。
rand()による予測可能なdelete_code許可されたセキュリティ研究。防御および教育目的でのみ使用するPoC。
PictShareは、delete_code認証トークンをgetRandomString()で生成しており、この関数はすべての文字をPHPの非暗号学的なrand()から取得している。rand()はCSPRNGではなく、さらに同じジェネレータが共有URLに表示される公開ファイルハッシュを生成しているため、delete_codeは秘密ではなく予測可能であり、infoエンドポイントからコードを読むことなく、ホストされたファイルを不正に削除できてしまう。
| CVE | CVE-2026-104356 |
| 製品 | PictShare(セルフホスト型の画像/メディアホスト) |
| 影響を受けるバージョン | >= 2.0.0, < 3.7.1 |
| 修正バージョン | v3.7.1 |
| 脆弱性 | 暗号学的に脆弱なPRNGの使用(CWE-338)→ 予測可能な認証トークン |
| 権限 | なし(未認証) |
| 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 |
| 発見者 | Alisher Qarshibayev |
| アドバイザリ | 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;
}
この1つの関数が両方を生成している:
delete_code**。両方のトークンが同じ非CSPRNGストリームから生成されるため、公開ハッシュを観察した攻撃者はジェネレータの状態に関する情報を得る — delete_codeはもはや、その32文字という長さが示唆するエントロピーを持たない。rand()は暗号学的な予測不可能性を保証しないため、これをセキュリティトークンに使用することが欠陥(CWE-338)であり、特定の状態復元手法とは独立している。
random_int()に切り替えている。rand()(Mersenne-Twisterの経路にエイリアスされる)を自動シードするため、すぐに使えるリモート予測器はデプロイメント(プロセスの再利用、観測可能な出力の数、PHPビルド)に依存する。したがってこのPoCは、万能のワンショット・リモート突破を主張するのではなく、制御されたシードにおいて予測可能性の原理を決定論的に実証する。これがこの発見の正直な範囲であり、まさにベンダーが依然としてこれを脆弱性として扱い修正した理由である。demo.phpはベンダーのgetRandomString()を正確に再現し、PRNGの状態が判明/復元されれば、最初の出力(公開ハッシュ)が2番目の出力(delete_code)を完全に決定すること — すなわちハッシュを観察することでコードが予測できることを示す:
php demo.php
期待される出力:
[*] 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)
demo.phpを参照。これは自己完結型(サーバー不要、ネットワーク不要)であり、rand()をここで不適切にするハッシュ→コードの決定論を証明している。
PictShare 3.7.1(修正コミットce5fc47)にアップグレードする。これはrand()をrandom_int()(CSPRNG)に置き換えている。一般的なガイダンス:すべてのセキュリティトークン(削除コード、リセットトークン、APIキー、ソルト)を暗号学的に安全なRNG — PHPではrandom_int() / random_bytes() — で生成し、rand()、mt_rand()、uniqid()は決して使用しないこと。
注:修正前に発行された削除コードはローテーションされないため、修正前のアップロードは再アップロードされるまで予測可能なままである。
| 日付 | 出来事 |
|---|---|
| 2026-10-01 | 公開開示、CVEの予約と公開(VulnCheck)、v3.7.1で修正 |
ベンダーに責任を持って開示し、VulnCheckを通じて調整した。このPoCの公開前に修正済み。防御および教育目的で公開。