Upgrade to Pro — share decks privately, control downloads, hide ads and more …

PHPerチャレンジ 解説LT / PHPerKaigi2023challenge

Cybozu
March 25, 2023

PHPerチャレンジ 解説LT / PHPerKaigi2023challenge

PHPerKaigi 2023 の PHPer チャレンジ用にサイボウズから問題を出題しました。
そちらの解説 LT で仕様したスライドです。

PHPerKaigi 2023: https://phperkaigi.jp/2023/
問題: https://blog.cybozu.io/entry/phperkaigi2023-sponser
LT: https://fortee.jp/phperkaigi-2023/proposal/07411094-2fc1-4abd-904f-9470454531e6

Cybozu

March 25, 2023
Tweet

More Decks by Cybozu

Other Decks in Technology

Transcript

  1. getPHPerToken.php <?php /** * This program can display a PHPer

    token. * But to get the correct PHPer token, * there are a few things you need to do before running this program. * * 1. You should get key1, key2 and key3 from getKey.php, * and fill <key1>, <key2> and <key3> of this program. * 2. The decrypt function does not work as expected for some reason. * This should be fixed. **/ $key1=<key1>; $key2=<key2>; $key3=<key3>; function decrypt(array $ciphertext, int $public_key, int $secret_key) { $hex = ""; foreach($ciphertext as $value) { $hex .= dechex($value ** $secret_key % $public_key); } $cleartext=hex2bin($hex); return $cleartext; } $ciphertext = [3181896, 6283063, 4748177, 3723679, 5707941]; $public_key = 8555851; $secret_key = ($key1 + $key2 * 2 + $key3 * 3) ** 2 * 2 ** 3 + $key2 * $key3 + $key2 + $key3 + 3; $cleartext=decrypt($ciphertext, $public_key, $secret_key); if(substr(md5($cleartext),0,30) === "97097d30ceb203d46ab08edf0308ba") { echo "PHPerToken is #" . $cleartext; } else { echo "Failed to get PHPerToken..."; }
  2. getPHPerToken.php(冒頭) <?php /** * This program can display a PHPer

    token. * But to get the correct PHPer token, * there are a few things you need to do before running this program. * * 1. You should get key1, key2 and key3 from getKey.php, * and fill <key1>, <key2> and <key3> of this program. * 2. The decrypt function does not work as expected for some reason. * This should be fixed. **/ $key1=<key1>; $key2=<key2>; $key3=<key3>; PHPerToken を表⽰できるプログラムだ けど、事前に以下2つをやってね。 1.getKey.php からキーを3つ取得 2.decrypt 関数の修正
  3. getPHPerToken.php(冒頭) <?php /** * This program can display a PHPer

    token. * But to get the correct PHPer token, * there are a few things you need to do before running this program. * * 1. You should get key1, key2 and key3 from getKey.php, * and fill <key1>, <key2> and <key3> of this program. * 2. The decrypt function does not work as expected for some reason. * This should be fixed. **/ $key1=<key1>; $key2=<key2>; $key3=<key3>; PHPerToken を表⽰できるプログラムだ けど、事前に以下2つをやってね。 1.getKey.phpからキーを3つ取得 2.decrypt 関数の修正 じゃあ、getKey.php を⾒てみよう
  4. getKey.php <?php /** * This program can display key1, key2

    and key3. **/ class A { public static function counter() { static $counter = 0; $counter += (2023 != "2023PHP"); return $counter; } } class B extends A {} A::counter(); $key = B::counter() . 3 << 2; $version = substr(phpversion(), 0, 3); if ($version === "7.4"){ echo "key1: " . $key; } elseif ($version === "8.0") { echo "key2: " . $key; } elseif ($version === "8.1") { echo "key3: " . $key; } else { echo "Get no key!"; }
  5. getKey.php <?php /** * This program can display key1, key2

    and key3. **/ class A { public static function counter() { static $counter = 0; $counter += (2023 != "2023PHP"); return $counter; } } class B extends A {} A::counter(); $key = B::counter() . 3 << 2; $version = substr(phpversion(), 0, 3); if ($version === "7.4"){ echo "key1: " . $key; } elseif ($version === "8.0") { echo "key2: " . $key; } elseif ($version === "8.1") { echo "key3: " . $key; } else { echo "Get no key!"; } PHP のバージョンごとに 異なるキーを出⼒しそう
  6. getKey.php <?php /** * This program can display key1, key2

    and key3. **/ class A { public static function counter() { static $counter = 0; $counter += (2023 != "2023PHP"); return $counter; } } class B extends A {} A::counter(); $key = B::counter() . 3 << 2; $version = substr(phpversion(), 0, 3); if ($version === "7.4"){ echo "key1: " . $key; } elseif ($version === "8.0") { echo "key2: " . $key; } elseif ($version === "8.1") { echo "key3: " . $key; } else { echo "Get no key!"; } PHP 7.4, 8.0, 8.1 で挙動が変化 するコードなので、$key が全部異なる
  7. getKey.php の実⾏ <?php /** * This program can display key1,

    key2 and key3. **/ class A { public static function counter() { static $counter = 0; $counter += (2023 != "2023PHP"); return $counter; } } class B extends A {} A::counter(); $key = B::counter() . 3 << 2; $version = substr(phpversion(), 0, 3); if ($version === "7.4"){ echo "key1: " . $key; } elseif ($version === "8.0") { echo "key2: " . $key; } elseif ($version === "8.1") { echo "key3: " . $key; } else { echo "Get no key!"; } ▌各 PHP バージョンでの実⾏⽅法は、お好みでOK n ⾃前でそれぞれ実⾏環境⽤意するもよし n Web 上で済ますもよし n https://3v4l.org/ など n 暗算できたらとてもつよし ▌key1=12, key2=112, key3=212
  8. getPHPerToken.php(冒頭) <?php /** * This program can display a PHPer

    token. * But to get the correct PHPer token, * there are a few things you need to do before running this program. * * 1. You should get key1, key2 and key3 from getKey.php, * and fill <key1>, <key2> and <key3> of this program. * 2. The decrypt function does not work as expected for some reason. * This should be fixed. **/ $key1=12; $key2=112; $key3=212; 取得した key1, key2, key3 を⼊れる
  9. getPHPerToken.php(冒頭) PHPerToken を表⽰できるプログラムだ けど、事前に以下2つをやってね。 1.getKey.php からキーを3つ取得 2.decrypt 関数の修正 <?php /**

    * This program can display a PHPer token. * But to get the correct PHPer token, * there are a few things you need to do before running this program. * * 1. You should get key1, key2 and key3 from getKey.php, * and fill <key1>, <key2> and <key3> of this program. * 2. The decrypt function does not work as expected for some reason. * This should be fixed. **/ $key1=12; $key2=112; $key3=212; ࡁ
  10. getPHPerToken.php(decrypt 関数) function decrypt(array $ciphertext, int $public_key, int $secret_key) {

    $hex = ""; foreach($ciphertext as $value) { $hex .= dechex($value ** $secret_key % $public_key); } $cleartext=hex2bin($hex); return $cleartext; } $ciphertext = [3181896, 6283063, 4748177, 3723679, 5707941]; $public_key = 8555851; $secret_key = ($key1 + $key2 * 2 + $key3 * 3) ** 2 * 2 ** 3 + $key2 * $key3 + $key2 + $key3 + 3; $cleartext=decrypt($ciphertext, $public_key, $secret_key); どこを直す必要が あるかな︖
  11. getPHPerToken.php(decrypt 関数) function decrypt(array $ciphertext, int $public_key, int $secret_key) {

    $hex = ""; foreach($ciphertext as $value) { $hex .= dechex($value ** $secret_key % $public_key); } $cleartext=hex2bin($hex); return $cleartext; } $ciphertext = [3181896, 6283063, 4748177, 3723679, 5707941]; $public_key = 8555851; $secret_key = ($key1 + $key2 * 2 + $key3 * 3) ** 2 * 2 ** 3 + $key2 * $key3 + $key2 + $key3 + 3; $cleartext=decrypt($ciphertext, $public_key, $secret_key); 実際の処理を イメージしてみよう
  12. getPHPerToken.php(decrypt 関数) function decrypt(array $ciphertext, int $public_key, int $secret_key) {

    $hex = ""; foreach($ciphertext as $value) { $hex .= dechex($value ** $secret_key % $public_key); } $cleartext=hex2bin($hex); return $cleartext; } $ciphertext = [3181896, 6283063, 4748177, 3723679, 5707941]; $public_key = 8555851; $secret_key = ($key1 + $key2 * 2 + $key3 * 3) ** 2 * 2 ** 3 + $key2 * $key3 + $key2 + $key3 + 3; $cleartext=decrypt($ciphertext, $public_key, $secret_key); $secret_key は なんだかデカい数が ⼊りそう
  13. getPHPerToken.php(decrypt 関数) function decrypt(array $ciphertext, int $public_key, int $secret_key) {

    $hex = ""; foreach($ciphertext as $value) { $hex .= dechex($value ** $secret_key % $public_key); } $cleartext=hex2bin($hex); return $cleartext; } $ciphertext = [3181896, 6283063, 4748177, 3723679, 5707941]; $public_key = 8555851; $secret_key = ($key1 + $key2 * 2 + $key3 * 3) ** 2 * 2 ** 3 + $key2 * $key3 + $key2 + $key3 + 3; $cleartext=decrypt($ciphertext, $public_key, $secret_key); なんだかデカい数を 使った累乗計算 ※7桁の7桁乗
  14. getPHPerToken.php(decrypt 関数) function decrypt(array $ciphertext, int $public_key, int $secret_key) {

    $hex = ""; foreach($ciphertext as $value) { $hex .= dechex($value ** $secret_key % $public_key); } $cleartext=hex2bin($hex); return $cleartext; } $ciphertext = [3181896, 6283063, 4748177, 3723679, 5707941]; $public_key = 8555851; $secret_key = ($key1 + $key2 * 2 + $key3 * 3) ** 2 * 2 ** 3 + $key2 * $key3 + $key2 + $key3 + 3; $cleartext=decrypt($ciphertext, $public_key, $secret_key); 64bit の範囲では、 正確な計算が 無理そう… ※7桁の7桁乗
  15. decrypt 関数の修正⽅針 ▌𝑎! ≡ 𝑥 (mod 𝑛) を次のように計算できる n 𝑎"

    ≡ 𝑚" n 𝑎# ≡ 𝑎𝑚" ≡ 𝑚# n 𝑎$ ≡ 𝑎𝑚# ≡ 𝑚$ n ・・・ n 𝑎! ≡ 𝑎𝑚!%& ≡ 𝑚! ≡ 𝑥
  16. decrypt 関数の修正⽅針 ▌𝑎! ≡ 𝑥 (mod 𝑛) を次のように計算できる n 𝑎"

    ≡ 𝑚" n 𝑎# ≡ 𝑎𝑚" ≡ 𝑚# n 𝑎$ ≡ 𝑎𝑚# ≡ 𝑚$ n ・・・ n 𝑎! ≡ 𝑎𝑚!%& ≡ 𝑚! ≡ 𝑥 𝑎を1回かけるごとに逐⼀𝑛で割った余りを求めて いけば、⼤きすぎる数(𝑎!)の計算を回避できる︕
  17. decrypt 関数の修正⼀案 foreach($ciphertext as $value) { $hex .= dechex($value **

    $secret_key % $public_key); } for($i = 0; $i < count($ciphertext); $i++){ $value = 1; for ($count = 0; $count < $secret_key; $count++){ $value = $value * $ciphertext[$i] % $public_key; } $hex .= dechex($value); }
  18. decrypt 関数の修正⼀案 foreach($ciphertext as $value) { $hex .= dechex($value **

    $secret_key % $public_key); } for($i = 0; $i < count($ciphertext); $i++){ $value = 1; for ($count = 0; $count < $secret_key; $count++){ $value = $value * $ciphertext[$i] % $public_key; } $hex .= dechex($value); } 1回かけるたびに余りを 求める、を繰り返す
  19. getPHPerToken.php(さいご) if(substr(md5($cleartext),0,30) === "97097d30ceb203d46ab08edf0308ba") { echo "PHPerToken is #" .

    $cleartext; } else { echo "Failed to get PHPerToken..."; } あとは実⾏するだけなので、 ぜひ⾃分の⼿で答えを確認してみてください︕