Beginner picoMini 2022 writeup

Beginner picoMini 2022

Beginner picoMini 2022 に参加しました。

picoCTFは、カーネギーメロン大学が中学生・高校生向けに運営するCTFサイトです。中高生向けですが、中高生に限らず誰でも利用できます。

Beginner picoMini 2022は、picoCTFで2022年1月11日(日本時間)から3週間半ほど開催されていた、picoCTFの中でも初心者向けの大会です。

CTFはwriteupを書くまでがCTFと聞いたので、前にちょっと触って以来全く使っていなかったブログを掘り出して書いてみることにしました。

出題された順や解いた順に書けるとよかったのですが、すでに大会期間は終わっており、大会中にどの順番で出題されたかがわからなくなってしまったので、picoGymの表示順に並べています。

Codebook

Run the Python script code.py in the same directory as codebook.txt.

code.pycodebook.txt と同じディレクトリで実行せよ、とのこと。

wn@wsl:~/workspace/ctf/picoCTF/picoMini2022$ ls | grep code
code.py
codebook.txt
wn@wsl:~/workspace/ctf/picoCTF/picoMini2022$ python code.py
picoCTF{c0d3b00k_455157_687087ee}

コードを読む必要もなく、問題文に書いてあったことをやるだけでした。

convertme.py

Run the Python script and convert the given number from decimal to binary to get the flag.

スクリプトを実行して、10進数を2進数に変換せよ、とのこと。

wn@wsl:~/workspace/ctf/picoCTF/picoMini2022$ python convertme.py
If 45 is in decimal base, what is it in binary base?
Answer: 101101
That is correct! Here's your flag: picoCTF{4ll_y0ur_b4535_722f6b39}

与えられる整数は実行のたびにランダムのようです。

なお、10進数から2進数への変換は自分で筆算なり暗算なりをしても問題ありませんが、せっかくなのでPythonで変換しました。

wn@wsl:~/workspace/ctf/picoCTF/picoMini2022$ python
Python 3.10.1 (main, Dec 10 2021, 15:33:48) [GCC 9.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> bin(45)
'0b101101'

これも、コードを読む必要はありませんでした。

fixme1.py

Fix the syntax error in this Python script to print the flag.

スクリプトシンタックスエラーを修正せよ、とのこと。 とりあえず、深く考えずに実行してみます。

wn@wsl:~/workspace/ctf/picoCTF/picoMini2022$ python fixme1.py
  File "/home/wn/workspace/ctf/picoCTF/picoMini2022/fixme1.py", line 20
    print('That is correct! Here\'s your flag: ' + flag)
IndentationError: unexpected indent

20行目でエラーが起こっており、インデントがおかしいようです。 20行目付近を確認してみます。

    18
    19  flag = str_xor(flag_enc, 'enkidu')
    20    print('That is correct! Here\'s your flag: ' + flag)
    21

たしかに不要なインデントが含まれています。 先頭のスペース2つを削除します。

20c20
<   print('That is correct! Here\'s your flag: ' + flag)
---
> print('That is correct! Here\'s your flag: ' + flag)

実行してみます。

wn@wsl:~/workspace/ctf/picoCTF/picoMini2022$ python fixme1-corrected.py
That is correct! Here's your flag: picoCTF{1nd3nt1ty_cr1515_79fb5597}

fixme2.py

Fix the syntax error in the Python script to print the flag.

こちらも、シンタックスエラーを修正せよとのこと。 やはり、深く考えずに実行してみます。

wn@wsl:~/workspace/ctf/picoCTF/picoMini2022$ python fixme2.py
  File "/home/wn/workspace/ctf/picoCTF/picoMini2022/fixme2.py", line 22
    if flag = "":
       ^^^^^^^^^
SyntaxError: invalid syntax. Maybe you meant '==' or ':=' instead of '='?

22行目でシンタックスエラーが起こっているようです。 丁寧に修正案まで提案してくれています。 提案の通りに修正します。

wn@wsl:~/workspace/ctf/picoCTF/picoMini2022$ diff fixme2.py fixme2-corrected.py
22c22
< if flag = "":
---
> if flag == "":

実行します。

wn@wsl:~/workspace/ctf/picoCTF/picoMini2022$ python fixme2-corrected.py
That is correct! Here's your flag: picoCTF{3qu4l1ty_n0t_4551gnm3nt_f6a5aefc}

Glitch Cat

Our flag printing service has started glitching!

フラグを出力してくれるサービスがあるようです。 やはり深く考えずに、与えられたコマンドで接続してみます。

wn@wsl:~/workspace/ctf/picoCTF/picoMini2022$ nc saturn.picoctf.net 53639
'picoCTF{gl17ch_m3_n07_' + chr(0x38) + chr(0x31) + chr(0x31) + chr(0x66) + chr(0x66) + chr(0x66) + chr(0x65) + chr(0x65) + '}'

フラグそのものではありませんが、Pythonの式を出力してくれます。 Pythonの式なので、Pythonに評価させてみます。

wn@wsl:~/workspace/ctf/picoCTF/picoMini2022$ python
Python 3.10.1 (main, Dec 10 2021, 15:33:48) [GCC 9.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> 'picoCTF{gl17ch_m3_n07_' + chr(0x38) + chr(0x31) + chr(0x31) + chr(0x66) + chr(0x66) + chr(0x66) + chr(0x65) + chr(0x65) + '}'
'picoCTF{gl17ch_m3_n07_811fffee}'

HashingJobApp

If you want to hash with the best, beat this test!

与えられたサーバに接続してみます。

wn@wsl:~/workspace/ctf/picoCTF/picoMini2022$ nc saturn.picoctf.net 51108
Please md5 hash the text between quotes, excluding the quotes: 'Michelangelo'
Answer:

クォーテーション間の、クォーテーションを除くテキストをMD5ハッシュ化せよ、とのこと。 Ubuntuではデフォルトで md5sum というコマンドが使えます。

wn@wsl:~/workspace/ctf/picoCTF/picoMini2022$ echo -n 'Michelangelo' | md5sum
0d46a3ff4160862bb8329524b99da972  -

echo コマンドでは -n オプションを付けないと改行まで含めた文字列のハッシュ化になってしまうことに注意。 あとはもとのターミナルでハッシュ値を答えるだけですが、この問答を3回繰り返す必要があります。 答えるのが遅いと時間切れになって再チャレンジが必要になりますが、特に急いで操作する必要はない程度の時間は待ってくれます。

wn@wsl:~/workspace/ctf/picoCTF/picoMini2022$ nc saturn.picoctf.net 51108
Please md5 hash the text between quotes, excluding the quotes: 'Michelangelo'
Answer:
0d46a3ff4160862bb8329524b99da972
0d46a3ff4160862bb8329524b99da972
Correct.
Please md5 hash the text between quotes, excluding the quotes: 'having an operation'
Answer:
e8911c168f3f28c385ac7f3e4d0682cc
e8911c168f3f28c385ac7f3e4d0682cc
Correct.
Please md5 hash the text between quotes, excluding the quotes: 'alien abductions'
Answer:
089a281a0b59fe14b5e9d2472ffc5049
089a281a0b59fe14b5e9d2472ffc5049
Correct.
picoCTF{4ppl1c4710n_r3c31v3d_bf2ceb02}

PW Crack 1

Can you crack the password to get the flag? Download the password checker here and you'll need the encrypted flag in the same directory too.

パスワードを解読せよ、とのこと。 パスワードチェッカー(Pythonスクリプト)と、暗号化されたフラグがヒントです。

パスワードチェッカーと暗号化されたフラグを同じディレクトリに配置し、パスワードチェッカーを実行してみます。

wn@wsl:~/workspace/ctf/picoCTF/picoMini2022$ ls | grep level1
level1.flag.txt.enc
level1.py
wn@wsl:~/workspace/ctf/picoCTF/picoMini2022$ python level1.py
Please enter correct password for flag: hoge
That password is incorrect

パスワードを求められます。 ここで正しいパスワードを入力する必要があるようです。

パスワードチェッカーのコードを見てみると、パスワードの正否をどのように判定しているかがわかります。

    17  def level_1_pw_check():
    18      user_pw = input("Please enter correct password for flag: ")
    19      if( user_pw == "60ab"):
    20          print("Welcome back... your flag, user:")
    21          decryption = str_xor(flag_enc.decode(), user_pw)
    22          print(decryption)
    23          return
    24      print("That password is incorrect")
    25

パスワードは 60ab のようです。 再度パスワードチェッカーを実行し、これを入力してみます。

wn@wsl:~/workspace/ctf/picoCTF/picoMini2022$ python level1.py
Please enter correct password for flag: 60ab
Welcome back... your flag, user:
picoCTF{545h_r1ng1ng_c26330ca}

PW Crack 2

Can you crack the password to get the flag? Download the password checker here and you'll need the encrypted flag in the same directory too.

やりたいことは PW Crack 1 と同様です。 パスワードチェッカーのコードを見てみると、

    16  def level_2_pw_check():
    17      user_pw = input("Please enter correct password for flag: ")
    18      if( user_pw == chr(0x33) + chr(0x39) + chr(0x63) + chr(0x65) ):
    19          print("Welcome back... your flag, user:")
    20          decryption = str_xor(flag_enc.decode(), user_pw)
    21          print(decryption)
    22          return
    23      print("That password is incorrect")
    24

ユーザの入力と chr(0x33) + chr(0x39) + chr(0x63) + chr(0x65) が一致する場合にフラグが出力されるようです。 せっかくなので、それがどういう文字列になるかもPythonに考えてもらいます。

wn@wsl:~/workspace/ctf/picoCTF/picoMini2022$ ls | grep level2
level2.flag.txt.enc
level2.py
wn@wsl:~/workspace/ctf/picoCTF/picoMini2022$ python -c 'print(chr(0x33) + chr(0x39) + chr(0x63) + chr(0x65))' | python level2.py
Please enter correct password for flag: Welcome back... your flag, user:
picoCTF{tr45h_51ng1ng_502ec42e}

PW Crack 3

Can you crack the password to get the flag? Download the password checker here and you'll need the encrypted flag and the hash in the same directory too. There are 7 potential passwords with 1 being correct. You can find these by examining the password checker script.

やりたいことは PW Crack 1, 2 と同様ですが、パスワードチェッカースクリプトにパスワードの候補が書いてあります。

    42  # The strings below are 7 possibilities for the correct password.
    43  #   (Only 1 is correct)
    44  pos_pw_list = ["80f4", "da1d", "eeda", "5561", "5449", "64ac", "668b"]
    45

7個くらいなら手で順番に試すほうが速そうです。

wn@wsl:~/workspace/ctf/picoCTF/picoMini2022$ python level3.py
Please enter correct password for flag: 80f4
Welcome back... your flag, user:
picoCTF{m45h_fl1ng1ng_024c521a}

最初の1個がいきなり当たりでした。

PW Crack 4

Can you crack the password to get the flag? Download the password checker here and you'll need the encrypted flag and the hash in the same directory too. There are 100 potential passwords with only 1 being correct. You can find these by examining the password checker script.

ほぼ PW Crack 3 と同様ですが、パスワードの候補が100個になっています。

さすがに手で順番に試すのは効率が悪いので、ブルートフォースで解読を行うコードを追加します。

39行目の level_4_pw_check() を呼び出す行を削除します。

    39  level_4_pw_check()

level4.py の末尾に、以下のように関数とその呼び出しを追加します。

def level_4_pw_brute_force(pos_pw_list):
    for user_pw in pos_pw_list:
        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


level_4_pw_brute_force(pos_pw_list)

この level_4_pw_brute_force() 関数は細部まで自分で考えて書いたわけではなく、もともとの level_4_pw_check() 関数のユーザ入力を受け付ける部分を、選択肢を正解が見つかるまで順番に試すように変更しただけです。

必要なファイルをディレクトリに揃えて実行するとフラグが表示されます。

wn@wsl:~/workspace/ctf/picoCTF/picoMini2022$ ls | grep level4
level4.flag.txt.enc
level4.hash.bin
level4.py
wn@wsl:~/workspace/ctf/picoCTF/picoMini2022$ python level4.py
Welcome back... your flag, user:
picoCTF{fl45h_5pr1ng1ng_e7668ddf}

PW Crack 5

Can you crack the password to get the flag? Download the password checker here and you'll need the encrypted flag and the hash in the same directory too. Here's a dictionary with all possible passwords based on the password conventions we've seen so far.

ほぼ PW Crack 4 と同様ですが、パスワードの候補がもっと増えて65536個になり、 dictionary.txt という別ファイルに書かれています。

PW Crack 4 と同じく、ブルートフォースを行います。

39行目の level_5_pw_check() を呼び出す行を削除します。

    39  level_5_pw_check()

level5.py の末尾に、以下のように関数とその呼び出しを追加します。

def level_5_pw_brute_force():
    for user_pw in [l.strip() for l in open('dictionary.txt').readlines()]:
        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


level_5_pw_brute_force()

必要なファイルをディレクトリに揃えて実行するとフラグが表示されます。

wn@wsl:~/workspace/ctf/picoCTF/picoMini2022$ ls | grep -e level5 -e dict
dictionary.txt
level5.flag.txt.enc
level5.hash.bin
level5.py
wn@wsl:~/workspace/ctf/picoCTF/picoMini2022$ python level5.py
Welcome back... your flag, user:
picoCTF{h45h_sl1ng1ng_40f26f81}

runme.py

Run the runme.py script to get the flag. Download the script with your browser or with wget in the webshell.

runme.py を実行せよ、とのこと。

実行してみます。

wn@wsl:~/workspace/ctf/picoCTF/picoMini2022$ python runme.py
picoCTF{run_s4n1ty_run}

終わりです。

Serpentine

Find the flag in the Python script!

スクリプトからフラグを見つけ出す必要があるようです。

実行してみます。

wn@wsl:~/workspace/ctf/picoCTF/picoMini2022$ python serpentine.py

    Y
  .-^-.
 /     \      .- ~ ~ -.
()     ()    /   _ _   `.                     _ _ _
 \_   _/    /  /     \   \                . ~  _ _  ~ .
   | |     /  /       \   \             .' .~       ~-. `.
   | |    /  /         )   )           /  /             `.`.
   \ \_ _/  /         /   /           /  /                `'
    \_ _ _.'         /   /           (  (
                    /   /             \  \
                   /   /               \  \
                  /   /                 )  )
                 (   (                 /  /
                  `.  `.             .'  /
                    `.   ~ - - - - ~   .'
                       ~ . _ _ _ _ . ~

Welcome to the serpentine encourager!


a) Print encouragement
b) Print flag
c) Quit

What would you like to do? (a/b/c) a

-----------------------------------------------------
Keep it up!
-----------------------------------------------------


a) Print encouragement
b) Print flag
c) Quit

What would you like to do? (a/b/c) b

Oops! I must have misplaced the print_flag function! Check my source code!


a) Print encouragement
b) Print flag
c) Quit

What would you like to do? (a/b/c) c

a, b, cの選択肢が与えられます。 "b"を入力するとフラグを出力してくれるかと思いきや、 print_flag 関数を書き忘れちゃった、というメッセージが出力されます。

メッセージに従ってソースコードを確認すると、

    20  def print_flag():
    21    flag = str_xor(flag_enc, 'enkidu')
    22    print(flag)

print_flag 関数が定義されてはいて、これを呼び出しさえすればよさそうなことと、

    68      elif choice == 'b':
    69        print('\nOops! I must have misplaced the print_flag function! Check my source code!\n\n')

"b"を入力した場合の挙動は69行目に記述されていることがわかります。 せっかくなので、これを正しい挙動に置き換えます。

69c69
<       print('\nOops! I must have misplaced the print_flag function! Check my source code!\n\n')
---
>       print_flag()

再度スクリプトを実行して、"b"を入力します。

wn@wsl:~/workspace/ctf/picoCTF/picoMini2022$ python serpentine.py

    Y
  .-^-.
 /     \      .- ~ ~ -.
()     ()    /   _ _   `.                     _ _ _
 \_   _/    /  /     \   \                . ~  _ _  ~ .
   | |     /  /       \   \             .' .~       ~-. `.
   | |    /  /         )   )           /  /             `.`.
   \ \_ _/  /         /   /           /  /                `'
    \_ _ _.'         /   /           (  (
                    /   /             \  \
                   /   /               \  \
                  /   /                 )  )
                 (   (                 /  /
                  `.  `.             .'  /
                    `.   ~ - - - - ~   .'
                       ~ . _ _ _ _ . ~

Welcome to the serpentine encourager!


a) Print encouragement
b) Print flag
c) Quit

What would you like to do? (a/b/c) b
picoCTF{7h3_r04d_l355_7r4v3l3d_569ab7a6}
a) Print encouragement
b) Print flag
c) Quit

What would you like to do? (a/b/c)

フラグが出力されました。

感想

Beginnerというだけあって本当に初心者向けでした。 CTF初心者というよりも、コマンドライン初心者向けくらいのノリだったと思います。

というか、このブログの前回の更新2016年なんですね。 すげえや。

最近CTFや競技プログラミングに興味が出てきたので(どっちも始めたてですが)、writeupやうまくいかなかった点のメモ、感想など書く予定です。 数か月で飽きてやめるような気もします。