Docker Quiz とは
二時間目コンテナの講義ではDocker Quizが出題。君は、すべて解くことができるか?
— hatenatech@はてなの技術情報アカウント (@hatenatech) September 7, 2020
$ docker run --rm -i hatena/intern-2020-docker-quiz #hatenaintern2020
まだ挑戦したことがない方は是非この記事を読む前に挑戦してみてください。Docker の知識があまりなくても、調べれば回答できる問題が多いです。
後に講義の動画もアップされるようです。
解いてみる
Q1 [sanity check] 次の文字列を入力してください: FLAG_3u8z4xst
sanity check です。FLAG_3u8z4xst
と入力します。
Q2 -q2 オプション引数を渡して起動せよ
docker run --rm hatena/intern-2020-docker-quiz -q2
のようにオプション引数付きで起動すると答えが表示されます。
ちなみに -hint
オプションをつけて起動すると、各問題のヒントを見ることができます。
Q3 このイメージのentrypointとして指定されているコマンドのフルパスは何か
docker inspect hatena/intern-2020-docker-quiz
でこのイメージの情報を見ることができます。その中に entrypoint が記載されているので、入力します。
Q4 /app/flag.txt をホストにコピーしてファイルの内容を取得せよ
docker cp <コンテナ名 or コンテナ ID>:/app/flag.txt ./flag.txt
のようにしてホストにコピーできます。
コンテナ名は docker ps
などから確認できます。
Q5 シェルを起動して /app/get_flag2.exe を実行せよ
docker run --rm --entrypoint /app/get_flag2.exe hatena/intern-2020-docker-quiz
のようにして entrypoint を指定して実行します。
Q6 このイメージに含まれるpythonについて、Trivyによって検出されるseverityがHIGHの脆弱性のCVE番号を答えよ
Trivy というコンテナの脆弱性スキャナーを使います。
下記のコマンドで簡単にスキャンできます(macOS)。
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock -v $HOME/Library/Caches:/root/.cache/ aquasec/trivy image hatena/intern-2020-docker-quiz
スキャンが完了すると脆弱性のあるパッケージの一覧が表示されるので、python パッケージの中から severity(重症度)が HIGH の CVE 番号(共通脆弱性識別子)を探して入力する...
...はずですが、python で脆弱性が HIGH の CVE 番号は見つかりませんでした。原因は分かりませんでしたが、表示された python の CVE 番号を片っ端から入力していったら正解することができました。何かご存知の方がいましたら教えていただけるとありがたいです。
(補足)オプションに --severity HIGH
を指定すると severity が HIGH のものだけを表示できます。
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock -v $HOME/Library/Caches:/root/.cache/ aquasec/trivy image --severity HIGH hatena/intern-2020-docker-quiz
Q7 このイメージのentrypointとして指定されているコマンドのソースコードを復元して、この問題の答えを取得せよ
docker history hatena/intern-2020-docker-quiz
でこのイメージの履歴を表示することができます。
truncate されている & 見づらいので、docker history --no-trunc --format "{{.CreatedAt}}: {{.CreatedBy}}" hatena/intern-2020-docker-quiz
のようにして整形して出力します。
履歴を見てみると、下記の 2 ファイルがコンテナにコピーされていることが分かります。
-
docker_quiz.go.enc
(暗号化されたソースコードっぽい) -
password__delete_me_after_decrypting
(復号に使うパスワードが書かれたファイルっぽい)
残念ながら、 password__delete_me_after_decrypting
は復号に使われたあと削除されています。
docker save hatena/intern-2020-docker-quiz > docker-quiz.tar
で、イメージを tar ファイルとして出力することができます。tar の中身はレイヤの集合になっています。その中から前述の 2 ファイルがコピーされたときのレイヤを探し出せば復元することができそうです。
tar ファイルを展開し、2 つのファイルがコピーされたときっぽいレイヤの layer.tar を見つけ出し、展開します。tar tvf path/to/layer.tar
で tar ファイルの中身を表示したり、ファイルサイズから推測したりすると比較的容易に見つけられます。
2 つのファイルを回収することができたら、前述の docker history
で見つかった履歴と同じコマンドでソースコードを復号します。
openssl enc -d -aes-256-cbc -pbkdf2 -in docker_quiz.go.enc -out docker_quiz.go -pass file:password__delete_me_after_decrypting
ソースコードを見てみると、Q7 は入力した答えのバイト列のそれぞれの値に 0xff
との排他的論理和をとった値が、[0xb9, 0xb3, 0xbe, 0xb8, 0xa0, 0xce, 0xc9, 0x8a, 0xca, 0x95, 0x88, 0x9d, 0x8d]
と一致するかどうかを判定しているのが分かります。この答えは逆算することができます。下記は JavaScript で逆算する例です。
[0xb9, 0xb3, 0xbe, 0xb8, 0xa0, 0xce, 0xc9, 0x8a, 0xca, 0x95, 0x88, 0x9d, 0x8d].map((b) => String.fromCharCode(b ^ 0xff)).join('');
手に入った文字列が答えになります。
失敗した方法
/app/docker_quiz
をホストにコピーし、 objdump -d ./docker_quiz
で逆アセンブルをしてみたけど何も分からなかった。
感想
Docker は普段から使っていましたが知らないことも多く、勉強になりました。楽しかったです。