OverTheWire やってみた(Bandit 編)

最近、CTF(Capture the flag)という言葉をよく聞くので入門者向けのやつをやってみた。YouTube 上にビギナー向けの CTF サービスを紹介している 動画 があり、それが OverTheWire の Wargames をオススメしていたのでやってみた。

overthewire.org

今回は初心者向けの bandit 編を順番に解いてみた。次の問題に行くにはパスワードが必要であり、そのパスワードを発見するのが各問題の課題という感じ。ちなみに自分は Linux は自宅用デスクトップ PC として普段使いしているので基本的なコマンドや OS の概念はだいたい分かっている。しかし、何事もあまりマニアックな使い方をしない性格なので、そういう知識を要するのがあったら詰みそう。

ではレッツゴー。

Level 0

ターミナルから ssh コマンドを叩いてリモートサーバにログインするだけ。ユーザは bandit0

Level1

cat コマンドを叩いてファイルの中身を見るとパスワードの文字列がある。これがユーザ bandit1 のパスワードになっているので再度 ssh でログインする。

Level2

パスワードは - という名前のファイル内にある。cat - だとコマンドオプションだと認識されるのが、ファイルのフルパスで指定すると意図通りのファイルが認識される。知らなかったがググればすぐ分かる話ではある。

Level3

パスワードを含むファイル名に空白文字(スペース)が含まれるパターン。ファイル名をエスケープして cat すればよいが、これは tab キーでファイル名を補完できるのでなんてことない。これは Linux を普段使いしているとたまにやる操作。

Level4

パスワードは隠しファイル内にあるだけ。簡単。

Level5

パスワードが複数のファイルのうちどれかの中にある。ほとんどのファイルが空っぽなので、find . -type f | xargs wc -l あたりでファイルごとの行数を見て発見した。

Level6

パスワードが複数のファイルのうちどれかの中にある。ファイルサイズがヒントとして与えられている。find . -type f | xargs -d '\n' wc | grep 1033 あたりでファイルサイズで調べて発見した。

Level7

パスワードが複数のファイルのうちどれかの中にある。user と group とファイルサイズがヒントとして与えられているので、find / -type f | xargs ls -l | grep bandit7 あたりでユーザ名で grep すると1件しか見つからなかったのでそれが答えだった。find コマンドのマニュアル読めばユーザの検索とかできそう。

Level8

パスワードが指定のファイル内にあることが分かっている。そのファイル内において millionth という文字列の横にパスワードがあるらしい。普通に grep したら発見。

Level9

パスワードが指定のファイル内にあることが分かっている。このファイルに含まれるユニークな文字列が正解らしい。リダイレクトしながら diff ってみた。diff <(sort -u data.txt) <(sort data.txt | uniq -D | uniq) で差分が1行だけになったのでこれが正解。なんか無理矢理感がある。ユニークな列だけ抽出するみたいなコマンドオプションがあればもっとスマートにできそう。

Level10

パスワードが指定のファイル内にあることが分かっている。このファイルに含まれる、人間が読めて いくつか = が続く文字列の直後がパスワードらしい。ってことで strings コマンドを用いて ASCII 文字列っぽいものを抽出してみた。strings data.txt | grep == で答えを発見。strings コマンド初めて使った。バイナリファイルの解析とかに使えそう。

Level11

パスワードが Base64エンコードされている。base64 -d data.txt みたいに base64 コマンドを使ってデコードするとパスワードを発見。

Level12

パスワードが ROT13 により暗号化されている。シーザー暗号の一種。ただし数字はそのまま。各文字を元に戻せばよいので tr コマンドで置換した。対応する文字を愚直に列挙した。パスワードの復号に成功。

cat data.txt | tr -s 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' 'nopqrstuvwxyzabcdefghijklmNOPQRSTUVWXYZABCDEFGHIJKLM'

Level13

パスワードを含むファイルが何らかの方式で繰り返し圧縮されている。圧縮ファイルの先頭を見れば圧縮方式が分かるようになっており xxd コマンドで見ながら bzip2 ,gzip, tar コマンドを繰り返し適用した。けっこうダルかった。結果的にパスワードを含むテキストファイルが出てきた。

圧縮ファイルの方式は以下を参考にした。よく見ると2つ目の回答の file コマンドをたたくやり方の方が楽そう。

stackoverflow.com

Level14

ssh コマンドを bandit14 でログインするが、このときプライベートキーを食わるだけ。

Level15

telnet コマンドでポート 30000 に現在のパスワードを送信する。具体的には telnet localhost 30000 で接続したあとパスワードを貼り付ければオッケー。

Level16

SSL 通信でポート 30001 に接続する。openssls_client を使うと SSL 通信ができる。openssl s_client -connect localhost:30001 を実行すればオッケー。このあたりは使い慣れてないのでググりながらやった。

www.openssl.org

Level17

localhost のポートの範囲 31000 - 32000 のどれかに SSL 通信を受け継いているものがあるらしい。nmap コマンドを使って nmap -p31000-32000 localhost のようにスキャン。いくつか有効なポートが見つかるのでひとつずつ opensll s_client -connect で接続できるか試した。ひとつだけ秘密鍵を返すものがあたのでそれを次の SSH に食わせた。

nmap コマンドはよく知らなかったので man コマンドでマニュアルを見た。

Level18

diff コマンド叩くだけ。

Level19

パスワードは readme ファイルにあるが、.bashrc の設定により、ssh コマンドでログインすると即座にログアウトされてしまうらしい。ってことで ssh コマンドを叩くときに cat readme することでパスワードを取得できた。ssh って Docker 動かすときみたいにコマンド叩けるんだ。知らなかった。

Level20

/etc/bandit_pass/bandit20 にパスワードが書かれているが、現在のユーザ bandit19 では読み取り権限がない。そこで、bandit20-do コマンドを使って一時的に bandit20 の権限を獲得したうえでパスワードが書かれたファイルへアクセスすればよい。具体的には ./bandit20-do cat /etc/bandit_pass/bandit20 というコマンドを実行した。

こういう権限を一時的に獲得するような仕組みは setuid と呼ばれている。実行ファイルに対して chmod コマンドで設定できるらしい。

Level21

tmux コマンドでターミナルを2画面開く。片方のターミナルで nc -l -p 1234 のようにサーバを起動して接続待ちをする。それに対して、もう片方のターミナルで ./suconnect 1234 のようにアクセスすることで接続が確立される。nc コマンドを実行している方で前回のパスワードを送信すると次のパスワードが返される。nc コマンドの使い方がよく分かっていなかったのでけっこう時間かかった。

Level22

cron の設定ファイルが /etc/cron.d/ の中にいくつかある。それらはスクリプトファイル(sh ファイル)を実行するように設定されているが、その中に現在のユーザ bandit21 でも読み取り権限があるスクリプトがある。それの中を見るとパスワードの書かれた /etc/bandit_pass/bandit22cat していることが分かるので、そのリダイレクト先のファイルを見ればパスワードが手に入る。

ちなみに cron の設定ファイルである crontab ファイルの5つのアスタリスク * * * * * は指定されたコマンドを毎分実行するようなスケジュールを意味する。

Level23

前の問題と同様に現在のユーザ bandit22 で読み取り権限のあるスクリプトが1つだけある。その中身を見ると、whoami コマンドの実行結果をもとに計算したハッシュ値をファイル名としてパスワードを書き込んでいることが分かる。cron によってこのスクリプトが実行されるときのユーザは bandit23 なので bandit23 という文字列を与えてこのハッシュ値を計算してみる。すると書き込まれたファイル名が分かるのでパスワードを入手できる。

Level24

bandit24 によって定期実行される /usr/bin/cronjob_bandit24.shスクリプトの中身を見ると /var/spool/bandit24 の中のスクリプトが実行されることが分かる。よってパスワードを出力する cat コマンドをこのディレクトリ内に仕込めばよい。具体的には、結果を /tmp 配下にリダイレクトする cat コマンドをもつスクリプトを作成し bandit24 が実行できるように chmod し、cp コマンドによってコピーした。1分ほど待てばパスワードがリダイレクト先のファイルに書き込まれる。

Level25

brute-force で 0000 から 999930002 番ポートに送り続ける必要がある。そこで for i in {0000..9999}; do echo (省略) $i; done | nc localhost 30002 | tail のように for 文を書いて入力候補をすべて作ったあとで nc コマンドでポートに送信。やがて答えが見つかった。

Level26

getent passwd | grep bandit26 するとユーザ bandit26 のログインシェルが bash ではなく/usr/bin/showtext になっていることが分かる。このなかでは more コマンドが実行されている。このため bandit26 にログインしてもすぐにログアウトされてしまう。

ここからどう打開するか分からずこの問題の正解をググった。どうやら、ターミナルの画面サイズを小さくしておくことで more の画面がコマンドを受け付ける状態で止まり、ここで v を押すと vi が立ち上がるらしい。なるほど。vi 内で :set shell=/bin/bash に続けて :shell を実行すればシェルが起動する。これで bandit26 としてログインできたことになる。

Level27

前の手順に続き、bandit27-do なる実行ファイルがあるため、これに cat を引数として与えるとパスワードを表示できる。

Level28

/tmp ディレクトリに移動して git clone するだけ。clone したディレクトリにパスワードが書かれている。

Level29

/tmp ディレクトリに移動して git clone する。なぜかパスワードがマスキングされているが、git log を見るとマスキング前のデータがありそうなことが分かる。そこで git diff (前のバージョンのハッシュ値) してパスワードゲット。

Level30

/tmp ディレクトリに移動して git clone する。パスワードは not in production らしい。リモートブランチを見てみると dev ブランチとかがあって怪しいのでこれを pull してきて中身をみるとパスワード発見。

Level31

git tag で secret という名前のタグを発見できる。これを git show するとパスワードの文字列を発見。これはなかなか時間かかった。タグという発想はなかった。

Level32

.gitignore に注意しながら push するだけ。ここに来て簡単。

Level33

打った文字が大文字になって実行されるシェルがいきなり起動する。大文字に変換されても問題なく実行できるコマンドが必要そう。しかし思いつかずこの問題の解法をググった。どうやら $0sh が呼ばれるらしい。これでコマンドが叩けるようになったのでパスワードを cat できるようになった。

2021年8月現在ではこれにてすべての課題クリア。めでたし。

所感

初めて CTF 系の問題をやってみたが、各問題が上手く作られた感じがあって飽きずに楽しめた。普段はネットワーク系など低めのレイヤに触れることがあまりないので今回はよい勉強になった。それにしても、手がかりが見つからないときは本当に何をしてよいのか分からない。CTF が強い人はたくさんの攻めパターンを知っていて、それを順番に調べてみるみたいなことをするんだろうと思う。これぞハッカーの道だ。また気が向いたらこういうのやろう。