SECCON Beginners CTF 2021 writeup(crypto:Imaginary)

※本サイトはアフィリエイト広告を利用しています。
広告

本記事について

2021年5月22日と2021年5月23日に開催された「SECCON Beginners CTF 2021」の「crypto」の「Imaginary」問題について、解きましたので、手順を詳細に記載します。

crypto(Imaginary)

以下の問題文に記載がありますが、虚数に関する問題のようです。
SECCON Beginners CTF 2021_crypto_Imaginary(1)
提供されたプログラム(aap.py)は以下の通りです。
プログラムを確認すると、「変数self.numbersが1337iにして、5のself._secret()」を実行したらいい。

import json
import os
from socketserver import ThreadingTCPServer, BaseRequestHandler
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
from secret import flag, key


class ImaginaryService(BaseRequestHandler):
    def handle(self):
        try:
            self.request.sendall(b'Welcome to Secret IMAGINARY NUMBER Store!\n')
            self.numbers = {}

            while True:
                num = self._menu()
                if num == 1:
                    self._save()
                elif num == 2:
                    self._show()
                elif num == 3:
                    self._import()
                elif num == 4:
                    self._export()
                elif num == 5:
                    self._secret()
                else:
                    break

        except Exception as e:
            try:
                self.request.sendall(f'ERR: {e}\n'.encode())
            except Exception:
                pass

    def _menu(self):
        self.request.sendall(b'1. Save a number\n')
        self.request.sendall(b'2. Show numbers\n')
        self.request.sendall(b'3. Import numbers\n')
        self.request.sendall(b'4. Export numbers\n')
        self.request.sendall(b'0. Exit\n')
        self.request.sendall(b'> ')
        try:
            return int(self.request.recv(128).strip())
        except ValueError:
            return 0

    def _save(self):
        try:
            self.request.sendall(b'Real part> ')
            re = int(self.request.recv(128).strip())

            self.request.sendall(b'Imaginary part> ')
            im = int(self.request.recv(128).strip())

            name = f'{re} + {im}i'
            self.numbers[name] = [re, im]
        except ValueError:
            pass

    def _show(self):
        self.request.sendall(b'-' * 50 + b'\n')
        for name in self.numbers:
            re, im = self.numbers[name]
            self.request.sendall(f'{name}: ({re}, {im})\n'.encode())
        self.request.sendall(b'-' * 50 + b'\n')

    def _import(self):
        self.request.sendall(b'Exported String> ')
        data = self.request.recv(1024).strip().decode()
        enc = bytes.fromhex(data)
        cipher = AES.new(key, AES.MODE_ECB)
        plaintext = unpad(cipher.decrypt(enc), AES.block_size)

        self.numbers = json.loads(plaintext.decode())
        self.request.sendall(b'Imported.\n')
        self._show()

    def _export(self):
        cipher = AES.new(key, AES.MODE_ECB)
        dump = pad(json.dumps(self.numbers).encode(), AES.block_size)
        self.request.sendall(dump + b'\n')
        enc = cipher.encrypt(dump)
        self.request.sendall(b'Exported:\n')
        self.request.sendall(enc.hex().encode() + b'\n')

    def _secret(self):
        if '1337i' in self.numbers:
            self.request.sendall(b'Congratulations!\n')
            self.request.sendall(f'The flag is {flag}\n'.encode())


if __name__ == '__main__':
    host = os.getenv('CTF4B_HOST')
    port = os.getenv('CTF4B_PORT')

    if not host:
        host = 'localhost'

    if not port:
        port = '1337'

    ThreadingTCPServer.allow_reuse_address = True
    server = ThreadingTCPServer((host, int(port)), ImaginaryService)

    print(f'Start server at {host}:{port}')
    server.serve_forever()

そのため、問題のサーバに接続して、以下のコマンドを実行しましたが、「1337i」ではなく「0 + 1337i」になってしまいます。

$ nc imaginary.quals.beginners.seccon.jp 1337
Welcome to Secret IMAGINARY NUMBER Store!
1. Save a number
2. Show numbers
3. Import numbers
4. Export numbers
0. Exit
> 1
Real part> 0
Imaginary part> 1337

1. Save a number
2. Show numbers
3. Import numbers
4. Export numbers
0. Exit
> 2
--------------------------------------------------
0 + 1337i: (0, 1337)
--------------------------------------------------

1. Save a number
2. Show numbers
3. Import numbers
4. Export numbers
0. Exit
> 5   ←フラグが表示されるか確認します。

1. Save a number
2. Show numbers
3. Import numbers
4. Export numbers
0. Exit
> 4   ←どのようにデータが保存されているか表示します。
{"0 + 1337i": [0, 1337]}
Exported:
817351cf3a18d9fd90837146b4f0d3341a4a895666e342950bb77e9683918d4c

1. Save a number
2. Show numbers
3. Import numbers
4. Export numbers
0. Exit
> 1
Real part> 123
Imaginary part> 56789

1. Save a number
2. Show numbers
3. Import numbers
4. Export numbers
0. Exit
> 4
{"0 + 1337i": [0, 1337], "123 + 56789i": [123, 56789]}










Exported:
817351cf3a18d9fd90837146b4f0d3343c6233d81b8c476fa82e2be85f3a5dee92451f4aeda1d03ded8edfd624c9358f852c6bade1c4bf8aba7b7c0c884570e6

「def _import(self)」と「def _import(self)」でAESのECBモードで暗号化と復号化しています。
AESのECBモードとは以下のイメージ図のように平文をブロックに分けて、それぞれ暗号化する方式です。
SECCON Beginners CTF 2021_crypto_Imaginary(2)-1
今回のプログラムは自身で作成したAESのECBモードの暗号化の文字列をインポートできるため、データを作成してインポートする方法を検討します。

以下のようなデータを入力して、出力します。

# nc imaginary.quals.beginners.seccon.jp 1337
Welcome to Secret IMAGINARY NUMBER Store!
1. Save a number
2. Show numbers
3. Import numbers
4. Export numbers
0. Exit
> 1
Real part> 12345678
Imaginary part> 1
1. Save a number
2. Show numbers
3. Import numbers
4. Export numbers
0. Exit
> 1
Real part> 1234
Imaginary part> 1234
1. Save a number
2. Show numbers
3. Import numbers
4. Export numbers
0. Exit
> 1
Real part> 12
Imaginary part> 12
1. Save a number
2. Show numbers
3. Import numbers
4. Export numbers
0. Exit
> 1
Real part> 1234567
Imaginary part> 1337
1. Save a number
2. Show numbers
3. Import numbers
4. Export numbers
0. Exit
> 4
{"12345678 + 1i": [12345678, 1], "1234 + 1234i": [1234, 1234], "12 + 12i": [12, 12], "1234567 + 1337i": [1234567, 1337]}
Exported:
0fbc6fb787d5b0b56f14be9b4775cba59a1344ebac6b5aff7919452c00ac2f5b5117599006744dbe5ddf4d60d51ab2ea94abcd3a925a967168930c9f8d8a3697929c268f98a3ab1310ac85dc483318fc9b8bed0b9438686bf2b1d50e82a20822712cf584affea482cdba77dc6c642e3c1a4a895666e342950bb77e9683918d4c
1. Save a number
2. Show numbers
3. Import numbers
4. Export numbers
0. Exit

上記の出力結果(AESのECBモードの平文)を32文字区切りにして、特定の行を削除すると、「”1337i”」を作成することができます。

【AESのECBモードの平文(1行32文字区切り)】
{"12345678 + 1i": [12345678, 1],
 "1234 + 1234i": [1234, 1234], "
12 + 12i": [12, 12], "1234567 +       ←この行を削除予定
1337i": [1234567, 1337]}

【AESのECBモードの平文(削除後の値)】
{"12345678 + 1i": [12345678, 1], "1234 + 1234i": [1234, 1234], "1337i": [1234567, 1337]}

暗号文も16文字で区切ると平文と同じブロックに同じデータがあるため、同様の場所を削除した文字列を作成します。

【AESのECBモードの暗号文(1行32文字区切り)】
0fbc6fb787d5b0b56f14be9b4775cba5
9a1344ebac6b5aff7919452c00ac2f5b
5117599006744dbe5ddf4d60d51ab2ea
94abcd3a925a967168930c9f8d8a3697
929c268f98a3ab1310ac85dc483318fc       ←この行を削除予定
9b8bed0b9438686bf2b1d50e82a20822       ←この行を削除予定
712cf584affea482cdba77dc6c642e3c
1a4a895666e342950bb77e9683918d4c

【AESのECBモードの暗号文(削除後の値)】
0fbc6fb787d5b0b56f14be9b4775cba59a1344ebac6b5aff7919452c00ac2f5b5117599006744dbe5ddf4d60d51ab2ea94abcd3a925a967168930c9f8d8a3697712cf584affea482cdba77dc6c642e3c1a4a895666e342950bb77e9683918d4c

上記の作成した暗号文を使って、以下のコマンドを実行すると、フラグ(ctf4b{yeah_you_are_a_member_of_imaginary_number_club})が表示されました。

# nc imaginary.quals.beginners.seccon.jp 1337
Welcome to Secret IMAGINARY NUMBER Store!
1. Save a number
2. Show numbers
3. Import numbers
4. Export numbers
0. Exit
> 3
Exported String> 0fbc6fb787d5b0b56f14be9b4775cba59a1344ebac6b5aff7919452c00ac2f5b5117599006744dbe5ddf4d60d51ab2ea94abcd3a925a967168930c9f8d8a3697712cf584affea482cdba77dc6c642e3c1a4a895666e342950bb77e9683918d4c
Imported.
--------------------------------------------------
12345678 + 1i: (12345678, 1)
1234 + 1234i: (1234, 1234)
1337i: (1234567, 1337)
--------------------------------------------------
1. Save a number
2. Show numbers
3. Import numbers
4. Export numbers
0. Exit
> 5
Congratulations!
The flag is ctf4b{yeah_you_are_a_member_of_imaginary_number_club}


SECCON Beginners CTF 2021に関する記事

SECCON Beginners CTF 2021についての関連記事は以下の通りです。
必要に応じて、ご確認ください。

Noタイトル記事の概要
1SECCON Beginners CTF 2021 writeup以下のカテゴリの問題のwriteupを記載します。
【カテゴリ:welcome】
  ・welcome
【カテゴリ:crypto】
  ・imple_RSA
【カテゴリ:reversing】
  ・only_read
  ・children
【カテゴリ:pwnable】
  ・rewriter
【カテゴリ:web】
  ・osoba
【カテゴリ:misc】
  ・git-leak
  ・Mail_Address_Validator
2SECCON Beginners CTF 2021 writeup
(crypto:simple_RSA)
cryptカテゴリのsimple_RSAの問題の
writeupを記載します。
3SECCON Beginners CTF 2021 writeup
(crypto:Logical_SEESAW)
cryptカテゴリのLogical_SEESAWの問題の
writeupを記載します。
4SECCON Beginners CTF 2021 writeup
(crypto:GFM)
cryptカテゴリのGFMの問題の
writeupを記載します。
5SECCON Beginners CTF 2021 writeup
(crypto:Imaginary)
cryptカテゴリのImaginaryの問題の
writeupを記載します。
6SECCON Beginners CTF 2021 writeup
(web:Werewolf)
webカテゴリのWerewolfの問題の
writeupを記載します。
7SECCON Beginners CTF 2021 writeup
(web:check_url)
webカテゴリのcheck_urlの問題の
writeupを記載します。
8SECCON Beginners CTF 2021 writeup
(web:json)
webカテゴリのjsonの問題の
writeupを記載します。
9SECCON Beginners CTF 2021 writeup
(web:cant_use_db)
webカテゴリのcant_use_dbの問題の
writeupを記載します。