SECCON Beginners CTF 2022に参加しました。CryptoはHardの一問以外解けたのでWriteupを雑に書こうかなと思ったので書いてる。
Coughing Fox
problem.py
が与えられて解読する。正直やるだけ。
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)
テキトーに一文字ごとに全探索すればいい。
import gmpy2 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: x=gmpy2.iroot(c-i, 2) if x[0]*x[0]==c-i: flag += chr(x[0]-i) break print(flag)
できたー!
PrimeParty
server.py
が与えられて通信してflagあてる感じ。
デカい素数出力してやればいい。
from Crypto.Util.number import * from secret import flag from functools import reduce from operator import mul bits = 256 flag = bytes_to_long(flag.encode()) assert flag.bit_length() == 455 GUESTS = [] def invite(p): global GUESTS if isPrime(p): print("[*] We have been waiting for you!!! This way, please.") GUESTS.append(p) else: print("[*] I'm sorry... If you are not a Prime Number, you will not be allowed to join the party.") print("-*-*-*-*-*-*-*-*-*-*-*-*-") invite(getPrime(bits)) invite(getPrime(bits)) invite(getPrime(bits)) invite(getPrime(bits)) for i in range(3): print("[*] Do you want to invite more guests?") num = int(input(" > ")) invite(num) n = reduce(mul, GUESTS) e = 65537 cipher = pow(flag, e, n) print("n =", n) print("e =", e) print("cipher =", cipher)
十分に大きい素数をゲットする。
from Crypto.Util.number import getPrime print(getPrime(512))
あとは通信してe,cipherをゲットして普通に解読すればいい
from Crypto.Util.number import * p= e=65537 d=inverse(e, p-1) cipher= print(long_to_bytes(pow(cipher,d,p)))
おっけ~~~
Command
``chall.py''が与えられる。見るとAES暗号っぽいけどivを得られるから関係ないっぽい
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でもいいけどうまくxorとればいい。 コードなくした。パディングに気を付ける。
Unpredictable Pad
chall.py
が与えられる。乱数わかればいいのがわかる。
import random import os FLAG = os.getenv('FLAG', 'notflag{this_is_sample_flag}') def main(): r = random.Random() for i in range(3): try: inp = int(input('Input to oracle: ')) if inp > 2**64: print('input is too big') return oracle = r.getrandbits(inp.bit_length()) ^ inp print(f'The oracle is: {oracle}') except ValueError: continue intflag = int(FLAG.encode().hex(), 16) encrypted_flag = intflag ^ r.getrandbits(intflag.bit_length()) print(f'Encrypted flag: {encrypted_flag}') if __name__ == '__main__': main()
624個の32bit整数分の乱数吐かせると乱数を特定で切るっぽいので、1個目は62232bit、2,3個目は32bitの乱数を出力させたい。 62232bitは-(2622*32-1)を出力すればif文のところも突破できる!!あとはやるだけ~ (参考:Mersenne Twisterの出力を推測してみる - ももいろテクノロジー)
import random from Crypto.Util.number import * def untemper(x): x = unBitshiftRightXor(x, 18) x = unBitshiftLeftXor(x, 15, 0xefc60000) x = unBitshiftLeftXor(x, 7, 0x9d2c5680) x = unBitshiftRightXor(x, 11) return x def unBitshiftRightXor(x, shift): i = 1 y = x while i * shift < 32: z = y >> shift y = x ^ z i += 1 return y def unBitshiftLeftXor(x, shift, mask): i = 1 y = x while i * shift < 32: z = y << shift y = x ^ (z & mask) i += 1 return y payload1 = -(2**(32*622-1)) res1 =?? res1^=payload1 payload2 = 2**31 res2 =?? res2^=payload2 res3 = ?? res3^=payload2 value1=[] for i in range(622): value1.append(res1&(2**32-1)) res1>>=32 value1.append(res2) value1.append(res3) mt_state = tuple([untemper(x) for x in value1] + [624]) random.setstate((3, mt_state, None)) predicted =random.getrandbits(239) print(predicted) encrypted_flag = ?? flag=encrypted_flag^predicted print(flag.bit_length()) print(flag) print(long_to_bytes(flag))
これでできる~~
最後の問題242から探索範囲絞れるのか??になって終了 次のSECCON Beginners CTFのCrypto全完してえ~~~