好奇心の足跡

飽きっぽくすぐ他のことをしてしまうので、忘れないため・形にして頭に残すための備忘録。

Beginner picoMini 2022 writeup

中高生向けのpicoCTF の更に初心者向けの mini CTF Beginner picoMini 2022 が2022年1月10日 8:00pm GMT ~ 2月4日 8:00pm GMT で開催されました。名前の通りちゃんととっても初心者向けで、全部解けた人も多いかと思いますがwriteupを残しておきます。
python問題が結構多めで、セキュリティ要素はかなり弱めだった印象です。

こちら私のスコアグラフ。起きてすぐ取り組んだけど351位でした。

f:id:kusuwada:20220203231119p:plain

期間が終わっても下記のpicoGymからプレイできるので、もし興味を持たれた方がいらっしゃいましたら是非トライしてみてください!

play.picoctf.org

[General Skills] runme.py

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

python scriptが配布されます。中にflagが書いてあったけど、多分pythonスクリプトを実行させる問題。

#!/usr/bin/python3
################################################################################
# Python script which just prints the flag
################################################################################

flag ='picoCTF{run_s4n1ty_run}'
print(flag)

なるほどかなり初心者向けだ。

[General Skills] ncme

Connect to a remote computer using nc and get the flag. $ nc saturn.picoctf.net 57688

言われたとおり接続してみます。

$ nc saturn.picoctf.net 57688
picoCTF{s4n1ty_c4t}

[General Skills] convertme.py

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

またpython scriptが配布されます。

import random



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 = chr(0x15) + chr(0x07) + chr(0x08) + chr(0x06) + chr(0x27) + chr(0x21) + chr(0x23) + chr(0x15) + chr(0x5f) + chr(0x05) + chr(0x08) + chr(0x2a) + chr(0x1c) + chr(0x5e) + chr(0x1e) + chr(0x1b) + chr(0x3b) + chr(0x17) + chr(0x51) + chr(0x5b) + chr(0x58) + chr(0x5c) + chr(0x3b) + chr(0x10) + chr(0x57) + chr(0x0f) + chr(0x5e) + chr(0x51) + chr(0x5c) + chr(0x46) + chr(0x53) + chr(0x13)


num = random.choice(range(10,101))

print('If ' + str(num) + ' is in decimal base, what is it in binary base?')

ans = input('Answer: ')

try:
  ans_num = int(ans, base=2)
  
  if ans_num == num:
    flag = str_xor(flag_enc, 'enkidu')
    print('That is correct! Here\'s your flag: ' + flag)
  else:
    print(str(ans_num) + ' and ' + str(num) + ' are not equal.')
  
except ValueError:
  print('That isn\'t a binary number. Binary numbers contain only 1\'s and 0\'s')

今回はflagをそのまま書いてはないので、実行してみます。

$ python convertme.py 
If 26 is in decimal base, what is it in binary base?
Answer: 11010
That is correct! Here's your flag: picoCTF{4ll_y0ur_b4535_e2a58836}

2進数に直した値を答えさせる問題でした。今回はお手軽に、CyberChefの To Base フィルタを使って解きました。

[General Skills] Codebook

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

問題文にある2つのファイルが配布されるので、同じディレクトリで実行してみます。

$ python code.py
picoCTF{c0d3b00k_455157_8100c7c1}

[General Skills] fixme1.py

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

python scriptが配布されます。エラーが出るから直してくれとのこと。まずは実行してみます。

$ python fixme1.py 
  File "fixme1.py", line 20
    print('That is correct! Here\'s your flag: ' + flag)
    ^
IndentationError: unexpected indent

インデントエラー。よく出るよね。indentいらないところなので、削除して再実行。

$ python fixme1.py 
That is correct! Here's your flag: picoCTF{1nd3nt1ty_cr1515_09ee727a}

[General Skills] fixme2.py

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

こっちもまずは実行してみます。

$ python fixme2.py 
  File "fixme2.py", line 22
    if flag = "":
            ^
SyntaxError: invalid syntax

イコールの書き方は = ではなくて == なので修正して再実行。

$ python fixme2.py 
That is correct! Here's your flag: picoCTF{3qu4l1ty_n0t_4551gnm3nt_4863e11b}

[General Skills] 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.

level1.flag.txt.enclevel1.py が配布されます。

### 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('level1.flag.txt.enc', 'rb').read()



def level_1_pw_check():
    user_pw = input("Please enter correct password for flag: ")
    if( user_pw == "691d"):
        print("Welcome back... your flag, user:")
        decryption = str_xor(flag_enc.decode(), user_pw)
        print(decryption)
        return
    print("That password is incorrect")



level_1_pw_check()

まずは実行してみると、

$ python level1.py
Please enter correct password for flag: 
That password is incorrect

passwordがいるらしい。コードを読んでみると、パスワードは 691d なのでこれを入れる。

$ python level1.py
Please enter correct password for flag: 691d
Welcome back... your flag, user:
picoCTF{545h_r1ng1ng_56891419}

[General Skills] Glitch Cat

Our flag printing service has started glitching! $ nc saturn.picoctf.net 52026

接続してみます。

$ nc saturn.picoctf.net 52026
'picoCTF{gl17ch_m3_n07_' + chr(0x62) + chr(0x65) + chr(0x63) + chr(0x66) + chr(0x33) + chr(0x38) + chr(0x36) + chr(0x31) + '}'

おお、hexからasciiへ変換が必要。今回もCyberChefFrom Hex フィルタを使って変換。

[General Skills] 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.

今度は level2.flag.txt.enclevel2.py が配布されます。
pythonの方にまたpasswordが書いてあるので、これをasciiに直します。

    user_pw = input("Please enter correct password for flag: ")
    if( user_pw == chr(0x34) + chr(0x65) + chr(0x63) + chr(0x39) ):

-> 4ec9 がpassword。あとは実行してpasswordを入れるだけ。

$ python level2.py
Please enter correct password for flag: 4ec9
Welcome back... your flag, user:
picoCTF{tr45h_51ng1ng_9701e681}

[General Skills] HashingJobApp

If you want to hash with the best, beat this test! nc saturn.picoctf.net 65352

接続してみると、指定された単語のmd5を計算して答える問題が。またCyberChefの MD5 フィルタを使った。

$ nc saturn.picoctf.net 65352
Please md5 hash the text between quotes, excluding the quotes: 'Joan of Arc'
Answer: 
19ba425a542946fcf13228d9ddd53139
19ba425a542946fcf13228d9ddd53139
Correct.
Please md5 hash the text between quotes, excluding the quotes: 'Clint Eastwood'
Answer: 
b84954cb41831fa842dd69f6e1836b6e
b84954cb41831fa842dd69f6e1836b6e
Correct.
Please md5 hash the text between quotes, excluding the quotes: 'grave robbers'
Answer: 
bf48d2ac4e5d0532912c8e8e0998645f
bf48d2ac4e5d0532912c8e8e0998645f
Correct.
picoCTF{4ppl1c4710n_r3c31v3d_674c1de2}

ちなみに、macでコマンドラインでmd5を計算するときは

$ echo -n "Joan of Arc" | md5
19ba425a542946fcf13228d9ddd53139

こんな感じ。-n option で改行をしないようにするのを忘れないように。

[General Skills] Serpentine

Find the flag in the Python script!

pythonスクリプトが配布されます。長かったので読む前に実行してみた。

$ 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) 

flagを表示してほしかったので b を選択すると、

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

とのこと。ソースコードを見てみます。

  while True:
    print('a) Print encouragement')
    print('b) Print flag')
    print('c) Quit\n')
    choice = input('What would you like to do? (a/b/c) ')
    
    if choice == 'a':
      print_encouragement()
      
    elif choice == 'b':
      print('\nOops! I must have misplaced the print_flag function! Check my source code!\n\n')
      
    elif choice == 'c':
      sys.exit(0)
      
    else:
      print('\nI did not understand "' + choice + '", input only "a", "b" or "c"\n\n')

この choice == 'b' のところに、print_flag() を持ってきてあげれば良さそう。
書き換えて実行すると

What would you like to do? (a/b/c) b
picoCTF{7h3_r04d_l355_7r4v3l3d_8e47d128}

[General Skills] 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.

今回は level3.py, level3.flag.txt.enc, level3.hash.bin が配布されます。
pythonのスクリプトを見てみると、level3.hash.binにpasswordのhashがあり、これを戻したものがpassword何だけどもhashは一応不可逆なのでpasswordに戻せない。
問題文にもある通り、スクリプトの最後の方にある7つのpassword候補を全部試してあっているものを探すのが早い。

$ python level3.py
Please enter correct password for flag: 1ea2
Welcome back... your flag, user:
picoCTF{m45h_fl1ng1ng_6f98a49f}

3つ目が正解でした。

[General Skills] 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.

level4.py, level4.hash.bin, level4.flag.txt.enc が配布されます。
ほぼlevel3の問題と同じだけど、候補が7つではなく100個。これはscriptを書いたほうが良さそう。
元のスクリプトを少し書き換えて

import hashlib

# The strings below are 100 possibilities for the correct password. 
#   (Only 1 is correct)
pos_pw_list = ["6b3e", "989c", "4b17", "d06f", "f495", "6ea1", "44e4", "1d45", "3e1a", "b0b4", "8c65", "3276", "c496", "9d3d", "2476", "6ef4", "6b7f", "c184", "c2a8", "9708", "7bea", "9a2d", "4a22", "93ae", "826b", "9a50", "8b39", "5410", "a86c", "3760", "6426", "ec8e", "c294", "a909", "cbc6", "2e75", "f137", "9cb3", "79e7", "469f", "a9f9", "3e37", "b33e", "3f31", "4b27", "2f06", "cc2f", "d9e4", "2de7", "7328", "b4d4", "8e74", "a677", "b139", "9c74", "8ea4", "36f6", "613b", "7a7a", "5710", "838c", "44d5", "7190", "99d9", "c0a6", "b218", "3223", "477e", "38e5", "19b4", "3267", "2287", "b947", "a8d0", "fd9c", "e99c", "d8b7", "4c82", "b289", "332b", "bba5", "716d", "653e", "eb5d", "ad77", "ad3a", "3922", "7565", "947d", "928c", "2937", "823f", "f362", "79cf", "4582", "c0d0", "ed20", "d89a", "129c", "4e81"]


### 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(pw):
    # 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(), pw)
    print(decryption)
    return
    print("That password is incorrect")


def choose_pw():
    for p in pos_pw_list:
        if hash_pw(p) == correct_pw_hash:
            return p

pw = choose_pw()
level_4_pw_check(pw)

実行結果

$ python level4.py 
Welcome back... your flag, user:
picoCTF{fl45h_5pr1ng1ng_89490f2d}

[General Skills] 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.

今度は100個ではなくdictionay(65536行)で配布されます。level4同じように全部試してあげればOK。

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()
dictionary = open('dictionary.txt', 'r').readlines()

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(pw):
    # 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(), pw)
    print(decryption)
    return
    print("That password is incorrect")


def find_pw():
    for d in dictionary:
        d = d.strip()
        if hash_pw(d) == correct_pw_hash:
            return d

pw = find_pw()
level_5_pw_check(pw)

実行結果

$ python level5.py
Welcome back... your flag, user:
picoCTF{h45h_sl1ng1ng_2f021ce9}