前回 は、サイバーセキュリティのトレーニングができるサイトを始めてみましたが、少し合わなかったかなって感じでした。
今回も、こりずに、picoCTF という CTF の学習や、コンテストを開催しているサイトがあるので、挑戦してみたいと思います。
それでは、やっていきます。
※2024/10/3:追記
今回は、picoCTF の picoGym で、「Beginner picoMini 2022」の 全13問をやってみました。もし、これから「Beginner picoMini 2022」をやってみようと思う方にお知らせです。問題のタイトルに番号が付いてるものがあります(例:PW Crack 2)。それらの問題については、番号の若い順に解くことをお勧めします。番号が進むにつれて難易度が上がる形になっているためです。
はじめに
「セキュリティ」の記事一覧です。良かったら参考にしてください。
・第2回:Ghidraで始めるリバースエンジニアリング(使い方編)
・第3回:VirtualBoxにParrotOS(OVA)をインストールする
・第4回:tcpdumpを理解して出力を正しく見れるようにする
・第5回:nginx(エンジンエックス)を理解する
・第6回:Python+Flask(WSGI+Werkzeug+Jinja2)を動かしてみる
・第7回:Python+FlaskのファイルをCython化してみる
・第8回:shadowファイルを理解してパスワードを解読してみる
・第9回:安全なWebアプリケーションの作り方(徳丸本)の環境構築
・第10回:Vue.jsの2.xと3.xをVue CLIを使って動かしてみる(ビルドも行う)
・第11回:Vue.jsのソースコードを確認する(ビルド後のソースも見てみる)
・第12回:徳丸本:OWASP ZAPの自動脆弱性スキャンをやってみる
・第13回:徳丸本:セッション管理を理解してセッションID漏洩で成りすましを試す
・第14回:OWASP ZAPの自動スキャン結果の分析と対策:パストラバーサル
・第15回:OWASP ZAPの自動スキャン結果の分析と対策:クロスサイトスクリプティング(XSS)
・第16回:OWASP ZAPの自動スキャン結果の分析と対策:SQLインジェクション
・第17回:OWASP ZAPの自動スキャン結果の分析と対策:オープンリダイレクト
・第18回:OWASP ZAPの自動スキャン結果の分析と対策:リスク中すべて
・第19回:CTF初心者向けのCpawCTFをやってみた
・第20回:hashcatの使い方とGPUで実行したときの時間を見積もってみる
・第21回:Scapyの環境構築とネットワークプログラミング
・第22回:CpawCTF2にチャレンジします(クリア状況は随時更新します)
・第23回:K&Rのmalloc関数とfree関数を理解する
・第24回:C言語、アセンブラでシェルを起動するプログラムを作る(ARM64)
・第25回:機械語でシェルを起動するプログラムを作る(ARM64)
・第26回:入門セキュリhttps://github.com/SECCON/SECCON2017_online_CTF.gitティコンテスト(CTFを解きながら学ぶ実践技術)を読んだ
・第27回:x86-64 ELF(Linux)のアセンブラをGDBでデバッグしながら理解する(GDBコマンド、関連ツールもまとめておく)
・第28回:入門セキュリティコンテスト(CTFを解きながら学ぶ実践技術)のPwnable問題をやってみる
・第29回:実行ファイルのセキュリティ機構を調べるツール「checksec」のまとめ
・第30回:setodaNote CTF Exhibitionにチャレンジします(クリア状況は随時更新します)
・第31回:常設CTFのksnctfにチャレンジします(クリア状況は随時更新します)
・第32回:セキュリティコンテストチャレンジブックの「Part2 pwn」を読んだ
・第33回:セキュリティコンテストチャレンジブックの「付録」を読んでx86とx64のシェルコードを作った
・第34回:TryHackMeを始めてみたけどハードルが高かった話
・第35回:picoCTFを始めてみた(Beginner picoMini 2022:全13問完了) ← 今回
picoCTF の公式サイトは以下です。英語のサイトですが、こちらも、やってる方が多いみたいです。
それでは、やっていきます。
picoCTFに登録する
公式サイトにアクセスすると、以下の項目が並んでいます。
| 項目 | 内容 | 詳細 |
|---|---|---|
| Learn | picoPrimerで知識を習得 | 役に立ちそうな大量のテキストがありました |
| Practice | picoGymとミニコンテストで練習 | 過去のコンテストの内容で練習できるようです |
| Compete | コンテストに参加 | 1年に1、2回開催されているようです、前回は2024年3月に実施されたようです |
| Visualize | サイバーセキュリティのキャリアの紹介 | カーネギーメロン大学のWebサイトに飛ぶ |
| Facilitate | 生徒、または、グループの進捗状況を監視? | よく分かりません |
| Play | 参加して楽しくプレイ | コンテストと同じ画面に見えます、観客用? |
まずは、Practice(picoGym)に行ってみようと思います。

ユーザ名や、E-Mailアドレスを入力して、いくつかの質問に答えて、Sign Up をクリックします。

確認メールが届くので、届いたら、リンクをクリックすると、完了です。
登録は以上です。
Practice(picoGym)を始めてみる
登録が完了すると、以下のようなトップページが表示されます。左側のフィルタをチェックすると、所望の問題が表示されるようです。

試しに、「Beginner picoMini 2022」というのがあったので、それでフィルタしてみます。全部で 13問あるようです。最初の方は、Easy の問題が続き、最後の方は、Medium があります。

良さそうな感じなので、「Beginner picoMini 2022」をやってみます。
runme.py
最初は、Pythonスクリプト(runme.py)がダウンロードできて、それを実行するとフラグが表示されるようです。

PW Crack 2
先に PW Crack 2 が並んでますが、次の問題は PW Crack です。
Pythonスクリプト(level2.py)と暗号化されたパスワードファイル(level2.flag.txt.enc)がダウンロードできます。

パスワードファイルをバイナリエディタで開くと、31byte でした。
Pythonスクリプトを実行してみると、パスワードを聞かれます。Pythonスクリプトを読むと、入力するパスワードが分かります。簡単でした。
$ python level2.py
Please enter correct password for flag:
PW Crack 1
上の「PW Crack 2」と同じ問題形式です。level1.py と level1.flag.txt.enc がダウンロードできます。

バイナリファイルは、今度は、30byte でした。Pythonスクリプトを見ると、すぐに答えが分かります。PW Crack 2 より簡単でした。
$ python level1.py
Please enter correct password for flag:
HashingJobApp
次は、インスタンスを起動します。すると、「nc saturn.picoctf.net 53399」と表示されて、15分のカウントダウンが始まります。15分以内に、答える必要があるようです。

netcat で接続してみます。3回正解すると、フラグが表示されました。
$ nc saturn.picoctf.net 53399 Please md5 hash the text between quotes, excluding the quotes: 'Al Pacino' Answer: 1c9909508d984c65fb3a2f3b28d27faf 1c9909508d984c65fb3a2f3b28d27faf Correct. Please md5 hash the text between quotes, excluding the quotes: 'choir boys' Answer: ce6b6964402e279f878ce9cf5e19d14b ce6b6964402e279f878ce9cf5e19d14b Correct. Please md5 hash the text between quotes, excluding the quotes: 'carnival workers' Answer: d3818fc79b44a9f961c502fb3a9bd42d d3818fc79b44a9f961c502fb3a9bd42d Correct.
Glitch Cat
次の問題もインスタンスを起動する形式です。

以下のように表示されました。
$ nc saturn.picoctf.net 61936 'picoCTF{gl17ch_m3_n07_' + chr(0x61) + chr(0x34) + chr(0x33) + chr(0x39) + chr(0x32) + chr(0x64) + chr(0x32) + chr(0x65) + '}'
別のターミナルで、以下を実行すればフラグが表示されました。
$ python -c "print('picoCTF{gl17ch_m3_n07_' + chr(0x61) + chr(0x34) + chr(0x33) + chr(0x3 9) + chr(0x32) + chr(0x64) + chr(0x32) + chr(0x65) + '}')"
fixme2.py
Pythonスクリプト(fixme2.py)をダウンロードできます。次の問題が、「fixme1.py」なので、順番を変えてやってみてもいいかもしれません。

Pythonスクリプトを見て、修正すればいいようです。修正すると、Pythonスクリプトを実行できるようになって、フラグが表示されました。
$ python fixme2.py That is correct! Here's your flag:
fixme1.py
「fixme2.py」と同じ問題形式です。Pythonスクリプト(fixme1.py)をダウンロードできます。

修正できると、Pythonスクリプトを実行できるようになります。修正すると、Pythonスクリプトを実行できるようになって、フラグが表示されました。
$ python fixme1.py That is correct! Here's your flag:
convertme.py
これもPythonスクリプト(convertme.py)をダウンロードする問題形式です。

以下のように質問されます。
$ python convertme.py If 23 is in decimal base, what is it in binary base? Answer:
以下で、2進数に変換できます。
$ python -c 'print(f"{bin(23)}")' 0b10111
回答するとフラグが表示されました。
Codebook
Pythonスクリプト(code.py)と、テキストファイル(codebook.txt)をダウンロードできます。

テキストファイルには、azbycxdwevfugthsirjqkplomn と書かれていました。
Pythonスクリプトを実行すると、フラグが表示されました。
Serpentine
ここから難易度が Medium になります。Pythonスクリプト(serpentine.py)をダウンロードできます。

Pythonスクリプトを見てみると、フラグを表示する関数(print_flag関数)は、どこからも呼ばれていません。main関数の先頭で、print_flag関数を呼び出すようにすると、フラグが表示れました。
Pythonスクリプトを変更したらダメだったかもしれません。それが許されたら、どの問題も簡単になってしまいそうです。もう一度、Pythonスクリプトを見直してみると、print_flag関数の置き場所を間違えたので、ソースをチェックしてください!というメッセージがありました。ソース変更で良かったようです。
PW Crack 5
次も Medium の問題です。4つのファイル(level5.py、level5.flag.txt.enc、level5.hash.bin、dictionary.txt)をダウンロードできます。

Pythonスクリプト(level5.py)は以下です。
import hashlib ### THIS FUNCTION WILL NOT HELP YOU FIND THE FLAG --LT ######################## def str_xor(secret, key): #extend key to secret length new_key = key i = 0 while len(new_key) < len(secret): new_key = new_key + key[i] i = (i + 1) % len(key) return "".join([chr(ord(secret_c) ^ ord(new_key_c)) for (secret_c,new_key_c) in zip(secret,new_key)]) ############################################################################### flag_enc = open('level5.flag.txt.enc', 'rb').read() correct_pw_hash = open('level5.hash.bin', 'rb').read() def hash_pw(pw_str): pw_bytes = bytearray() pw_bytes.extend(pw_str.encode()) m = hashlib.md5() m.update(pw_bytes) return m.digest() def level_5_pw_check(): user_pw = input("Please enter correct password for flag: ") user_pw_hash = hash_pw(user_pw) if( user_pw_hash == correct_pw_hash ): print("Welcome back... your flag, user:") decryption = str_xor(flag_enc.decode(), user_pw) print(decryption) return print("That password is incorrect") level_5_pw_check()
実行すると、パスワードを聞かれるので、正しいパスワードを入力するとフラグが表示されそうです。
パスワードの辞書が提供されているので、それを 1つずつ入力すれば、いつかは正解しますが、辞書には、65536個のパスワードが含まれていました。
Pythonスクリプトを繰り返し実行する Pythonスクリプトを実装すれば良さそうです。pwntools を使います。
import os, sys from pwn import * def prologue_python( prog, ss ): # level5.py起動 proc = process( ['python', prog] ) # "Please enter correct password for flag: " print( proc.recv(timeout=1) ) print( ss ) proc.sendline( ss ) # "That password is incorrect" ret = proc.recvline() print( ret ) return proc, ret fpath = "dictionary.txt" prog = "level5.py" with open(fpath) as ff: for line in ff: print( f"line={line}" ) proc, ret = prologue_python( prog, line.encode('utf-8') ) if "Welcome" in ret.decode('utf-8'): break else: proc.close() print( proc.recvline() ) print( proc.recvline() ) print( proc.recvline() ) print( proc.recvline() )
実装して実行しましたが、かなり時間がかかります(2時間ぐらい)。逆順にやった方が絶対早いだろうなと思いましたが、やっぱり、だいぶ後ろの方でヒットしました(笑)。
$ python tmp.py line=0000 [+] Starting local process '/home/user/20240819/bin/python': pid 209134 b'Please enter correct password for flag: ' b'0000\n' b'That password is incorrect\n' [*] Stopped process '/home/user/20240819/bin/python' (pid 209134) line=0001 [+] Starting local process '/home/user/20240819/bin/python': pid 209136 b'Please enter correct password for flag: ' b'0001\n' b'That password is incorrect\n' [*] Stopped process '/home/user/20240819/bin/python' (pid 209136) line=0002 (以下、省略)
PW Crack 4
次も Medium の問題です。3つのファイル(level4.py、level4.flag.txt.enc、level4.hash.bin)をダウンロードできます。

上の「PW Crack 5」と似ていて、ちょっと簡単です。前回と同じく、パスワードを入力して、正しければ、フラグが表示されます。前回は、辞書ファイルに 65536個のパスワードの候補が入っていましたが、こちらは、ダウンロードした「level4.py」の中に、100個のパスワード候補がリストに定義されている状態です。
Pythonスクリプト(level4.py)は以下です。
import hashlib ### THIS FUNCTION WILL NOT HELP YOU FIND THE FLAG --LT ######################## def str_xor(secret, key): #extend key to secret length new_key = key i = 0 while len(new_key) < len(secret): new_key = new_key + key[i] i = (i + 1) % len(key) return "".join([chr(ord(secret_c) ^ ord(new_key_c)) for (secret_c,new_key_c) in zip(secret,new_key)]) ############################################################################### flag_enc = open('level4.flag.txt.enc', 'rb').read() correct_pw_hash = open('level4.hash.bin', 'rb').read() def hash_pw(pw_str): pw_bytes = bytearray() pw_bytes.extend(pw_str.encode()) m = hashlib.md5() m.update(pw_bytes) return m.digest() def level_4_pw_check(): user_pw = input("Please enter correct password for flag: ") user_pw_hash = hash_pw(user_pw) if( user_pw_hash == correct_pw_hash ): print("Welcome back... your flag, user:") decryption = str_xor(flag_enc.decode(), user_pw) print(decryption) return print("That password is incorrect") level_4_pw_check() # The strings below are 100 possibilities for the correct password. # (Only 1 is correct) pos_pw_list = ["158f", "1655", "d21e", "4966", "ed69", "1010", "dded", "844c", "40ab", "a948", "156c", "ab7f", "4a5f", "e38c", "ba12", "f7fd", "d780", "4f4d", "5ba1", "96c5", "55b9", "8a67", "d32b", "aa7a", "514b", "e4e1", "1230", "cd19", "d6dd", "b01f", "fd2f", "7587", "86c2", "d7b8", "55a2", "b77c", "7ffe", "4420", "e0ee", "d8fb", "d748", "b0fe", "2a37", "a638", "52db", "51b7", "5526", "40ed", "5356", "6ad4", "2ddd", "177d", "84ae", "cf88", "97a3", "17ad", "7124", "eff2", "e373", "c974", "7689", "b8b2", "e899", "d042", "47d9", "cca9", "ab2a", "de77", "4654", "9ecb", "ab6e", "bb8e", "b76b", "d661", "63f8", "7095", "567e", "b837", "2b80", "ad4f", "c514", "ffa4", "fc37", "7254", "b48b", "d38b", "a02b", "ec6c", "eacc", "8b70", "b03e", "1b36", "81ff", "77e4", "dbe6", "59d9", "fd6a", "5653", "8b95", "d0e5"]
上の「PW Crack 5」で実装した Pythonスクリプトを少し変更します。
import os, sys from pwn import * def prologue_python( prog, ss ): # level4.py起動 proc = process( ['python', prog] ) # "Please enter correct password for flag: " print( proc.recv(timeout=1) ) print( ss ) proc.sendline( ss ) # "That password is incorrect" ret = proc.recvline() print( ret ) return proc, ret prog = "level4.py" pos_pw_list = ["158f", "1655", "d21e", "4966", "ed69", "1010", "dded", "844c", "40ab", "a948", "156c", "ab7f", "4a5f", "e38c", "ba12", "f7fd", "d780", "4f4d", "5ba1", "96c5", "55b9", "8a67", "d32b", "aa7a", "514b", "e4e1", "1230", "cd19", "d6dd", "b01f", "fd2f", "7587", "86c2", "d7b8", "55a2", "b77c", "7ffe", "4420", "e0ee", "d8fb", "d748", "b0fe", "2a37", "a638", "52db", "51b7", "5526", "40ed", "5356", "6ad4", "2ddd", "177d", "84ae", "cf88", "97a3", "17ad", "7124", "eff2", "e373", "c974", "7689", "b8b2", "e899", "d042", "47d9", "cca9", "ab2a", "de77", "4654", "9ecb", "ab6e", "bb8e", "b76b", "d661", "63f8", "7095", "567e", "b837", "2b80", "ad4f", "c514", "ffa4", "fc37", "7254", "b48b", "d38b", "a02b", "ec6c", "eacc", "8b70", "b03e", "1b36", "81ff", "77e4", "dbe6", "59d9", "fd6a", "5653", "8b95", "d0e5"] for line in pos_pw_list: print( f"line={line}" ) proc, ret = prologue_python( prog, line.encode('utf-8') ) if "Welcome" in ret.decode('utf-8'): break else: proc.close() print( proc.recvline() ) print( proc.recvline() ) print( proc.recvline() ) print( proc.recvline() )
実行します。今度は 1分ぐらいでヒットしました。
$ python tmp.py line=158f [+] Starting local process '/home/user/20240819/bin/python': pid 209456 b'Please enter correct password for flag: ' b'158f' b'That password is incorrect\n' [*] Stopped process '/home/user/20240819/bin/python' (pid 209456) line=1655 [+] Starting local process '/home/user/20240819/bin/python': pid 209458 b'Please enter correct password for flag: ' b'1655' b'That password is incorrect\n' [*] Stopped process '/home/user/20240819/bin/python' (pid 209458) line=d21e [+] Starting local process '/home/user/20240819/bin/python': pid 209460 b'Please enter correct password for flag: ' b'd21e' b'That password is incorrect\n' [*] Stopped process '/home/user/20240819/bin/python' (pid 209460) (以下、省略)
PW Crack 3
次も Medium の問題です。3つのファイルをダウンロードできます。

内容は、上の「PW Crack 4」と同じ問題で、パスワードの候補がリストに格納されているところも同じで、候補数が 100個から 7個に減っています。
先ほどの Pythonスクリプトを変更します。
import os, sys from pwn import * def prologue_python( prog, ss ): # level4.py起動 proc = process( ['python', prog] ) # "Please enter correct password for flag: " print( proc.recv(timeout=1) ) print( ss ) proc.sendline( ss ) # "That password is incorrect" ret = proc.recvline() print( ret ) return proc, ret prog = "level3.py" pos_pw_list = ["8799", "d3ab", "1ea2", "acaf", "2295", "a9de", "6f3d"] for line in pos_pw_list: print( f"line={line}" ) proc, ret = prologue_python( prog, line.encode('utf-8') ) if "Welcome" in ret.decode('utf-8'): break else: proc.close() print( proc.recvline() ) print( proc.recvline() ) print( proc.recvline() ) print( proc.recvline() )
実行します。今度はすぐにヒットしました。
$ python tmp.py line=8799 [+] Starting local process '/home/user/20240819/bin/python': pid 210010 b'Please enter correct password for flag: ' b'8799' b'That password is incorrect\n' [*] Stopped process '/home/user/20240819/bin/python' (pid 210010) line=d3ab [+] Starting local process '/home/user/20240819/bin/python': pid 210012 b'Please enter correct password for flag: ' b'd3ab' b'That password is incorrect\n' [*] Stopped process '/home/user/20240819/bin/python' (pid 210012) line=1ea2 [+] Starting local process '/home/user/20240819/bin/python': pid 210014 b'Please enter correct password for flag: ' b'1ea2' b'That password is incorrect\n' [*] Stopped process '/home/user/20240819/bin/python' (pid 210014) (以降、省略)
以上で、「Beginner picoMini 2022」の全13問を完了しました!
おわりに
今回は、picoCTF に登録して、その中の picoGym で、「Beginner picoMini 2022」を全問(13問)やりました。
picoCTF は、とても良かったです。シンプルで、分かりやすいシステムで、英語ですが、特に困ったりはしませんでした。
「Beginner picoMini 2022」は、CTF をやったことない方にお勧めでしょうか。逆に、CTF をやったことある方は、早く解くことを目標にやってみてはいかがでしょうか。
最後になりましたが、エンジニアグループのランキングに参加中です。
気楽にポチッとよろしくお願いいたします🙇
今回は以上です!
最後までお読みいただき、ありがとうございました。