🛡️

setodaNote CTF Exhibition Writeup (Programming)

2024/10/20に公開

Programming - ZZZIPPP

あなたはあるファイルの解析作業を依頼されました。何重にも入れ子になった箱のようなファイルのようで、その中に組織にとって重要な機密情報が入っているようです。

添付されたファイルを解析してフラグを入手してください。

問題にはflag1000.zipが添付されており、その中にはflag999.zipが、さらにその中にはflag888.zipが…という状態です。
PythonでZIPファイルを解凍していくコードを書きます。

unzip_old.py
import zipfile
import os

current_zip = "flag1000.zip"

while True:
    # ZIPファイルの解凍先フォルダ
    folder = os.path.splitext(current_zip)[0]
    
    # ZIPファイルを解凍
    with zipfile.ZipFile(current_zip, "r") as file:
        file.extractall(folder)
    
    # 解凍後のフォルダに格納されているファイル名を取得
    file_names = os.listdir(folder)
    
    # ZIPファイルが格納されていた場合はcurrent_zipにZIPファイルのパスを格納して処理を繰り返す
    if file_names[0].endswith(".zip"):
        current_zip = os.path.join(folder, file_names[0])
    # ZIPファイル以外が格納されていた場合はフォルダのパスを出力して終了
    else:
        print(f"Path: {folder}")
        break
$ python unzip_old.py
Traceback (most recent call last):
  File "F:\CTF\setodaNote CTF Exhibition\Programming\ZZZIPPP\unzip_old.py", line 12, in <module>
    file.extractall(folder)
  File "D:\Users\judenfly\AppData\Local\Programs\Python\Python312\Lib\zipfile\__init__.py", line 1744, in extractall
    self._extract_member(zipinfo, path, pwd)
  File "D:\Users\judenfly\AppData\Local\Programs\Python\Python312\Lib\zipfile\__init__.py", line 1793, in _extract_member
    os.makedirs(upperdirs)
  File "<frozen os>", line 225, in makedirs
FileNotFoundError: [WinError 206] ファイル名または拡張子が長すぎます。: 'flag1000\\flag999\\flag998\\flag997\\flag996\\flag995\\flag994\\flag993\\flag992\\flag991\\flag990\\flag989\\flag988\\flag987\\flag986\\flag985\\flag984\\flag983\\flag982\\flag981\\flag980\\flag979\\flag978\\flag977\\flag976'
$

Windowsのパスは既定で260文字まで[1]という制限をすっかり忘れていました…。
各ZIPファイルを常に同一のフォルダ内で解凍するようコードを修正します。

unzip.py
import zipfile
import os

current_zip = "F:\\CTF\\setodaNote CTF Exhibition\\Programming\\ZZZIPPP\\flag1000.zip"
folder = "F:\\CTF\\setodaNote CTF Exhibition\\Programming\\ZZZIPPP\\result\\"

while True:
    # ZIPファイルを解凍
    with zipfile.ZipFile(current_zip, "r") as file:
        file.extractall(folder)

    # ZIPファイルを解凍したら削除
    if os.path.exists(current_zip):
        os.remove(current_zip)
    
    # 解凍後のフォルダに格納されているファイル名を取得
    file_names = os.listdir(folder)
    
    # ZIPファイルが格納されていた場合はcurrent_zipにZIPファイルのパスを格納して処理を繰り返す
    if file_names[0].endswith(".zip"):
        current_zip = os.path.join(folder, file_names[0])
    # ZIPファイル以外が格納されていた場合は終了
    else:
        break

$ python unzip.py
$

実行が完了するとfolderで指定したフォルダにflag.txtが格納されており、そのテキストファイルにflagが記載されています。
正答:flag{loop-zip-1989-zip-loop}

Programming - echo_me

山登りが趣味だという同僚が疲れた様子で話しかけてきます。山でヤッホーと声を出せば、いつでもヤッホーと返ってくる。そんなあたりまえを支えるやまびこさんの気持ちって、どんな感じなんでしょうね。その眼には若干の狂気が宿っているようにも思えました。あなたは同僚を狂気から救うため、解析作業を手伝うことにしました。

以下の例のようにサーバにアクセスしてフラグを得てください。

nc nc.ctf.setodanote.net 26512

この設問では用意されたサーバにアクセスして答えます。接続先は以下の通りです。

  • Server: nc.ctf.setodanote.net
  • Port: 26512

まず、サーバに接続して動作を確認します。

$ nc nc.ctf.setodanote.net 26512
==========
echo me: 374551
==========
374551
Correct!

==========
echo me: 562204
==========
562204
Correct!

==========
echo me: 488611
==========
488611
Correct!

==========
echo me: 666918
==========
^C
$ 

「echo me」の後に表示される数字を入力すればよいのですが、どこまで続ければよいのか分からないので、Pythonでコードを書いて自動化します。

echo.py
import socket

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
    # サーバに接続
    s.connect(("nc.ctf.setodanote.net", 26512))
    
    while True:
        # サーバから受信したデータ
        data = s.recv(1024).decode("utf-8")

        # サーバが接続を切った場合は終了
        if not data:
            break

        # サーバから受信したデータを出力
        print(data)
        
        # 「echo me」の後に表示される数字をサーバに送信
        lines = data.split("\n")
        for line in lines:
            if "echo me:" in line:
                number = line.split("echo me: ")[1].strip()
                s.sendall(f"{number}\n".encode("utf-8"))
$ python echo.py
==========
echo me: 842071
==========

Correct!


==========
echo me: 839218
==========

Correct!


==========
echo me: 748336
==========

Correct!

~省略~

==========
echo me: 37300568
==========

Correct!

flag{Hellow_yamabiko_Yoo-hoo!}
$

正答:flag{Hellow_yamabiko_Yoo-hoo!}

Programming - EZZZIPPP

あなたは再びあるファイルの解析作業を依頼されました。何重にも入れ子になった箱のようなファイルですが、今度は鍵までかかっているようです。ファイルを解析し中に封じ込められている機密情報を取得してください。

ファイルを解析してフラグを入手してください。

問題にはflag1000.zippass.txtが添付されており、その中にはflag999.zippass.txtが、さらにその中にはflag888.zippass.txtが…という状態です。
どうやらpass.txtには同フォルダ内のZIPファイルのパスワードが書いてあるようなので、Pythonでパスワード付きZIPファイルを解凍していくコードを書きます。

unzip_pass.py
import zipfile
import os

current_zip = "F:\\CTF\\setodaNote CTF Exhibition\\Programming\\EZZZIPPP\\flag1000.zip"
folder = "F:\\CTF\\setodaNote CTF Exhibition\\Programming\\EZZZIPPP\\result\\"

while True:
    # パスワードを取得
    password_file = os.path.join(os.path.dirname(current_zip), "pass.txt")
    with open(password_file, "r") as f:
        password = f.read().strip().encode("utf-8")

    # ZIPファイルを解凍
    with zipfile.ZipFile(current_zip, "r") as file:
        file.extractall(folder, pwd=password)

    # 解凍したらZIPファイルを削除
    if os.path.exists(current_zip):
        os.remove(current_zip)

    # 解凍後のフォルダに格納されているファイル名を取得
    file_names = os.listdir(folder)
    
    # ZIPファイルが格納されていた場合はcurrent_zipにZIPファイルのパスを格納して処理を繰り返す
    zip_files = [f for f in file_names if f.endswith(".zip")]
    if zip_files:
        current_zip = os.path.join(folder, zip_files[0])
    else:
        break
$ python unzip_pass.py
$

実行が完了するとfolderで指定したフォルダにflag.txtが格納されており、そのテキストファイルにflagが記載されています。
正答:flag{bdf574f15645df736df13daef06128b8}

Programming - deep_thought

普段は寡黙に働き続けているサーバが不意に話しかけてきました。計算勝負をしましょう。珍しいこともあるものだと思いつつも、あなたはそのサーバからの挑戦を受けることにしました。

以下の例のようにサーバにアクセスしてフラグを得てください。

nc nc.ctf.setodanote.net 26511

この設問では用意されたサーバにアクセスして答えます。接続先は以下の通りです。

  • Server: nc.ctf.setodanote.net
  • Port: 26511

まず、サーバに接続して動作を確認します。

$ nc nc.ctf.setodanote.net 26511
[ Q1 ]
1 + 2
3
Correct!

[ Q2 ]
3 + 7
10
Correct!

[ Q3 ]
4 - 7
-3
Correct!

[ Q4 ]
18 - 10
8
Correct!

[ Q5 ]
16 + 18
34
Correct!

[ Q6 ]
36 + 14
50
Correct!

[ Q7 ]
34 + 49
83
Correct!

[ Q8 ]
22 + 28
50
Correct!

[ Q9 ]
60 - 30
30
Correct!

[ Q10 ]
72 + 65
^C
$

表示される計算式の答えを入力すればよいのですが、どこまで続ければよいのか分からないので、Pythonでコードを書いて自動化します。

deep_thought.py
import socket
import re

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
    # サーバに接続
    s.connect(("nc.ctf.setodanote.net", 26511))
    
    while True:
        # サーバから受信したデータ
        data = s.recv(1024).decode("utf-8")

        # サーバが接続を切った場合は終了
        if not data:
            break

        # サーバから受信したデータを出力
        print(data)
        
        # 数式を計算してサーバに送信
        lines = data.split("\n")
        for line in lines:
            match = re.match(r"^\d+.*", line)
            if match:
                expression = match.group(0).strip()
                number = eval(expression)
                s.sendall(f"{number}\n".encode("utf-8"))
$ python deep_thought.py
[ Q1 ]
1 + 1

Correct!


[ Q2 ]
4 + 6

Correct!


[ Q3 ]
9 + 4

Correct!

~省略~

[ Q50 ]
1541 + 1638

Correct!

flag{__42__}
$

正答:flag{__42__}

脚注
  1. https://learn.microsoft.com/ja-jp/windows/win32/fileio/maximum-file-path-limitation?tabs=registry ↩︎

Discussion