*CTF 2021 | MyEnc

#*CTF_2021

from Crypto.Util.number import getPrime,bytes_to_long
import time,urandom
from flag import flag
iv=bytes_to_long(urandom(256))
assert len(flag)==15
keystream=bin(int(flag.encode('hex'),16))[2:].rjust(8*len(flag),'0')
p=getPrime(1024)
q=getPrime(1024)
n=p*q
print "n:",n
cnt=0
while True:
    try:
        print 'give me a number:'
        m=int(raw_input())
    except:
        break
    ct=iv
    for i in range(1,8):
        if keystream[cnt]=='1':
            ct+=pow(m^q,i**i**i,n)
            ct%=n
        cnt=(cnt+1)%len(keystream)
    print "done:",ct

15バイトのフラグからkey streamを作った上で、key streamの値によって  (m\oplus q)^{i^{i^i}} \mod n を計算してくれる。

 m = 0のとき、 ct = q^x + q^y + \dotsとなるので、 gcd(ct, n) = qかと思いきや、 ctには ivが含まれているのでこれはうまく行かない。しかし ivとかいうなまえのくせに教えてもらえない割に固定なので、2回やって減算すれば ivの影響を打ち消せて、RSAパートがわかってしまえば7bit全探索するだけ

from ptrlib import Socket
from Crypto.Util.number import isPrime
import math
from itertools import product
import proofofwork



# sock = Socket("localhost", 9999)
sock = Socket("52.163.228.53", 8081)

a, b = sock.recvregex(r"sha256\(xxxx\+(.+?)\) == (.+)")
s = proofofwork.sha256(b.decode(),text=b"????"+a)
sock.sendlineafter("xxxx:", s[:4])
print("[+] pass the PoW")


n = int(sock.recvlineafter("n: "))
sock.sendlineafter("number:\n", "0")
c1 = int(sock.recvlineafter("done: "))

sock.sendlineafter("number:\n", "0")
c2 = int(sock.recvlineafter("done: "))

q = math.gcd(n, (c1 - c2) % n)
assert isPrime(q)
p = n // q
phi = (p-1)*(q-1)

print("[+] p:{}".format(p))
print("[+] q:{}".format(q))

sock.sendlineafter("number:\n", str(q))
c3 = int(sock.recvlineafter("done: "))
iv = c3


pos = 21
plaintext = [None for _ in range(120)]
while True:
    # 7bit bruteforce
    table = {}
    for bits in product([0,1], repeat=7):
        ct = iv
        for j, b in enumerate(bits):
            if not b:
                continue
            i = j + 1
            ex = pow(i, i**i, phi)
            ct += pow(2, ex, n)
            ct = ct % n
        table[ct] = bits

    sock.sendlineafter("number:\n", str(q^2))
    ct = int(sock.recvlineafter("done: "))

    for bit in table[ct]:
        plaintext[pos] = bit
        pos = (pos + 1) % 120

    cnt = len([1 for b in plaintext if b is not None])
    print("[+] progress: {}/{}".format(cnt, 120))
    if cnt >= 120:
        break
print(plaintext)
print(bytes(plaintext))