新歓ブログリレー2020
PARIPIPPI CHALLENGE 2020 解説
公開日:
2020/04/16
PARIPIPPI CHALLENGE 2020 解説

はじめに

これは謎解きではなく, チャレンジです. トライ & エラー前提の問題の出し方でした.

解説記事では, ある程度の思考の過程は省かれるのでご了承ください.

元記事

{{< post 19 >}}

記事を読みながら解き直したい兄貴達はスクロールのし過ぎに注意です.

第 1 のカギ

Submit ボタンにカーソルを合わせると, "1st key is 3301." と出てきます.

第 2 のカギ Part.1

"Good luck." の付近にカーソルを合わせると, "exif is important." と出てきます.

exif とは

デジタルカメラで撮影した画像データに, 撮影条件に関する情報 (メタデータ) を追加して保存できる, 画像ファイル形式の規格のことである. 画像データそのものは主に JPEG 形式で扱われる.

とのことなので, 画像を取り敢えず調べればいいことが分かります. 敢えてサイトのレイアウトを色々非表示にしているので, サムネイル画像しかありませんね.

Windows PC なら画像を保存してから右クリック -> プロパティ -> 詳細 で見ることが出来ます. 下の画像のように Google Drive へのリンクが添えられています.

第 2 のカギ Part.2

大体の方がここまで来て「何でこんなのにマジになっちゃってんの ?」と辞めたと思います. 予想通りです.

リンク先には以下の 3 つが入っていました.

  • https://randomer.ch-random.net/assets/274433b2-8015-43d5-a25e-7a4c41e94bcd

  • hint.html

  • https://randomer.ch-random.net/assets/31298832-9b1e-471c-931c-38ac3daef321

以下, それぞれについて解説していきます.

https://randomer.ch-random.net/assets/eaf2d1c0-0019-4da4-bd49-72c5818c300d & hint.html

html ファイルを開くと "F(R, G, B)" と書かれたボタンのみが表示され, クリックするとアスキーメディアワークスのサイトに飛びます. F が何を意味しているか分かりませんが, RGB は恐らく画像の RGB チャンネルのことだと分かりますので RGB チャンネルに "何か" 処理をする Function であることに気付いてほしいです.

次に, https://randomer.ch-random.net/assets/4d948882-583b-46bf-b2a3-d753289f6837 という名前からも F がなす意味はこの画像にありそうです. 足の数から含まれているのは C, H, N, O だと推測できるので, それぞれの数を数えると https://randomer.ch-random.net/assets/4d948882-583b-46bf-b2a3-d753289f6837 は "C20H25N3O" を表していることが分かります. この分子式で表される物質で特に有名なのは LSD (麻薬) だと調べれば分かります. (ちゃんと形も合ってる... はずです)

つまり, LSD(R, G, B) なのか ? もう少し LSD に他の意味がないか, RGB に適応できそうなものはないか調べると "Least Significant Digit" (最下位けた数字) にたどり着けると思います. "Least Significant Bit" (最下位ビット) はよく競プロなどで聞くので知っとくといいかも. RGB 値はそれぞれ 0 から 255 までの値を取るのでとりあえずはできそう ! の精神でお願いします.

https://randomer.ch-random.net/assets/2bf197a6-2a42-46ce-8d0e-a929ab3c479b

どの画像に LSD(R, G, B) を適応させるか. まあ多分 https://randomer.ch-random.net/assets/28d50c26-2f10-42c6-87f8-bcf17b605990 でしょう... (謎の色合いで違和感を感じた人はランダムウェカリーちゃん通)

取り敢えず, RGB 値それぞれの LSD を取りましょう. はい, 実装してください. 色々な形で出力していくと, 1 ピクセルごとに R, G, B 値の LSD を順に並べて区切った時, 意味のありそうな結果が得られます.

序盤以外は全て "000" となっています. 察しが良ければ, これらは文字コードを表していることに気付くことでしょう. そして先ほどの html ファイルでのリンク先, アスキーメディアワークス ... アスキー ... ASCII ... つまり, "LSD(R, G, B) to ASCII" ということでした. 例として実装した Python のソースコードを載せておきます. openCV のインストール方法はコレとかを参考にしてください. どの言語を使ってもこんな感じです, 多分.

import cv2

## 画像を読み込む
img = cv2.imread("https://randomer.ch-random.net/assets/ad7b348a-bedf-4444-815a-60140f8ba28f")
## cv2 の仕様上 B, G, R の順がデフォなので入れ替える
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)

## 高さ と 幅
H = img.shape[0]
W = img.shape[1]

## デコード結果の初期化
res = ""

for y in range(H):
    for x in range(W):
        ## 1 ピクセルごとに RGB 値を取ってくる
        r, g, b = img_rgb[y, x]

        ## それぞれの LSD を R, G, B の順に並べて数値化
        ascii_num = 100 * (r % 10) + 10 * (g % 10) + (b % 10)

        ## ASCIIコードを文字に直してデコード結果に追加
        res += chr(ascii_num)

## デコード結果を出力
print(res)

実際に動かしてみると以下のような文章が出力されます. 詳しくは自分で動かしてみてください. txt ファイルに出力すると見やすいかも.

You are awesome! 2nd key is xxxx.
Let's move on to the next step.
https://youtu.be/xxxxxxxxxxx

ステガノグラフィー

今回用いたこのような暗号化手法をステガノグラフィーといいます. この企画で一番伝えたかったことです. 第 1 のカギから元ネタが分かった人はこの言葉にすぐ行き着くんじゃないかなと思います. 下図がステガノグラフィーの概念図です.

今回用いた KK は誰でも実装でき, CCSS の色合いに違いが見て分かるので弱い暗号化です. 一般に質の高いステガノグラフィーを解析し, 復号することは非常に困難です. 画像の中にコードを仕込んでマルウェアを送り込むといった悪用もされているようなので気を付けてください.

第 3 のカギ

新歓記事か ?? のような雰囲気しかありませんが, 得られた Youtube のリンクを踏むと "3rd key" というタイトルの動画にたどり着きます.

音声に注目すれば, 流れてくる正弦波の周波数ピークを解析すればいいことが分かります. 手法は以下の 2 つです.

  • FFT について学び, 実装する.
  • 音感から予測する. (オススメ)

折角なので, FFT (高速フーリエ変換) について説明します.

FFT とは

簡単に言えば時系列データである波形から, どの周波数成分をどのぐらい含んでいるかを検出する手法. 時間領域の信号から周波数領域の信号への変換と言える. 細かいことは大学の授業で学びましょう.

実装した Python のソースコードを載せておきます. ラの音が 440Hz だと知っていれば, そっちから攻める方が早いです. (どうせ素数だろという予想もできますしね)

import numpy as np
import wave
import matplotlib.pyplot as plt

## wav ファイル読み込み
wr = wave.open("3rd_key.wav", "r")
data = wr.readframes(wr.getnframes())
numch = wr.getnchannels()
samplewidth = wr.getsampwidth()
dt = wr.getframerate()
N = wr.getnframes()
print("チャンネル数 = ", numch)
print("サンプル幅 (バイト数) = ", samplewidth)
print("サンプリングレート (Hz) =", dt)
print("サンプル数 =", N)
print("録音時間 =", N / dt)
wr.close()

## 振幅
F_abs = abs(np.fft.fft(np.frombuffer(data, dtype="int16") / float((2 ^ 15))))
## 振幅をもとの信号に揃える
F_abs_amp = F_abs # 交流成分はデータ数で割って2倍
F_abs_amp[0] = F_abs_amp[0] / 2 # 直流成分は2倍不要
## 周波数軸のデータ作成
fq = np.linspace(0, dt, N) # 周波数軸

## グラフ表示
fig = plt.figure(figsize=(12, 6))
## FFT のグラフ(周波数軸)
plt.xlabel('freqency(Hz)', fontsize=14)
plt.ylabel('amplitude', fontsize=14)
plt.plot(fq[:int(N/2)+1], F_abs_amp[:int(N/2)+1]) # ナイキスト定数まで表示 (詳しくは大学)
plt.savefig('result.png')
plt.show()

結果はこんな感じで出ます. 拡大して出力すれば 3rd key が分かります.

いざ鎌倉

3 つの鍵を手に入れたわけですから, 掛け合わしたものをサイトに戻って入力しましょう. リンク先はとある Twitter アカウントです. おめでとうございます. ランダムの秘宝はあなたのものです.

ABC

とはいかず, 最後にカカシの役割を果たす関門があります. そのアカウントのツイートに貼られている QR コードからギガファイル便のリンクを踏むとパスワードとして

$ A^B \bmod C$

が求められます. 実は, 先ほどの Youtube の動画の概要欄に A, B, C と書かれた写真投稿サイト imgur へのリンクが 3 つあります. それぞれの写真はランダムの合宿で淡路島に行った時のものです. 楽しかったです. 合宿については後輩くんの記事を読んでね. 同様に画像を解析することで A, B, C の値が分かります.

べき乗の剰余を求めるときは繰り返し二乗法ですね. 競プロを始めてちょっと経ったときにオーバーフローが直らなくて悶々としました. 実装は活発な部員のペンギンが既にコチラで紹介してるので見てください. 速くなくてもいいという場合は Python で次のように 1 発で書けます.

A = int(input())
B = int(input())
C = int(input())
print(pow(A, B, C)) # A^B mod C を出力

このカカシのおかげで 記事内で動かしてた JavaScript を解析した悪いカラス達を撃退することが出来ました. (ちゃんと策を講じない私の技量に問題があるのだけれど)

ギガファイル便の仕様上, 記事が公開されてからほんの数日はまだリンクが生きているので, 是非真相を知りたい暇人の皆様はやってみてください. そして, 今のこのご時世を考えて色々察してください. よろしくおねがいします.

最後のあとがき

新歓ブログリレーとは ? って内容でしたが, これを正攻法で解く新入生がいたら滅茶苦茶パリピッピだな ? という趣旨のチャレンジでした.

元ネタは Cicada 3301 です. 頑張って入門の扉を叩き, かつモチベと暇な時間がある人が 2 週間で実装できるレベルで組むのが難しかったです. (できてない) 私なら絶対無理だ !

ランダムは名の通り (?) 何でもできる環境が整いつつあるので, この新歓ブログリレーを通じて興味を持ってくださった新入生たちは是非ともランダムでランダムな生活を送って欲しいなと思います. できる環境が無ければ自分で作っていきましょう. あと, 一緒に beatmania IIDX しませんか ?

それではまた !

loading...