[K!nd4SUS 2025] Crypto [复制链接]
发表于 2025-10-12 16:10:05 | 显示全部楼层 |阅读模式
末了一个把周末的补完。这个本日问了小鸡块神终于把一个补上,完成5/6,末了一个网站也上不去不弄了。
Matrices Matrices Matrices

这个是不是叫LWE呀,名词忘了,但意思照旧知道。
b = a*s +e 这里的e是高斯分成,用10000个数测试会出现2,3但猜也就是1,0
别的这里a*s是反的,须要转置一下。
  1. from sage.all import GF, Matrix
  2. import os, random
  3. assert("FLAG" in os.environ)
  4. FLAG = os.environ["FLAG"]
  5. assert(FLAG.startswith("KSUS{") and FLAG.endswith("}"))
  6. q = 271
  7. qf = GF(q)
  8. m = 70
  9. n = 30
  10. def key_gen():
  11.     a = Matrix(qf, [[qf.random_element() for _ in range(n)] for _ in range(m)])
  12.     s = Matrix(qf, [[ord(c)] for c in FLAG])
  13.     e = Matrix(qf, [[int(round(random.gauss(0, 2/3)))] for _ in range(m)])  #噪音[-3,3] 正常[-1,0,1]
  14.     b = a * s + e
  15.     return s, (a,b)
  16. sk, pk = key_gen()
  17. a, b = pk
  18. print(f"a={[list(a[i]) for i in range(m)]}")
  19. print(f"b={[list(b[i]) for i in range(m)]}")
复制代码
  1. q = 271
  2. a = ...
  3. b = ...
  4. A = matrix(ZZ,a).T
  5. B = matrix(ZZ,b).T
  6. A1 = A.stack(B)
  7. M = block_matrix(ZZ,[[1,A1],[0,q]])
  8. K = 256
  9. M[30,30] = K
  10. M[:,31:] *= K
  11. L = M.LLL()
  12. for i in L:
  13.     if i[30] in [256,-256] and all(k in [-768,-512,-256,0,256,512,768] for k in i[30:]):
  14.         print(i)
  15.         v = [abs(k) for k in i[:30]]
  16.         if all(0x20<=k<0x7f for k in v):
  17.             print(bytes(v))
  18. #(-75, -83, -85, -83, -123, -73, -95, -103, -117, -51, -115, -115, -95, -112, -52, -114, -52, -109, -115, -95, -109, -52, -55, -55, -51, -114, -95, -58, -47, -125, 256, -256, 0, 0, -256, -256, 256, 0, 0, 0, 256, 0, 0, 0, 256, 256, 0, 0, 0, 0, -256, 0, -256, 0, 0, 256, 256, 0, 0, -512, 256, 0, -256, 0, 0, 256, 256, -512, 0, -256, -256, 0, -256, 256, 512, 256, 0, -256, 0, 0, 256, 0, 0, -256, 0, 256, -256, 0, 0, 0, 256, -256, 0, 0, 256, 256, 0, 0, 0, 0, -256)
  19. #KSUS{I_gu3ss_p4r4ms_m4773r_:/}
复制代码
Lightning Fast Scrambling

名字指出是LFS,但有点标题,当一开始使用时返回的直接就是从后前前的key每次8位。
  1. from hashlib import sha256
  2. from base64 import b64encode, b64decode
  3. # utility wrapper for hashing
  4. def digest(message):
  5.         "Gives a bytes object representing the sha256 encoding of its argument (a sequence of bytes)"
  6.         return sha256(message).digest()
  7. # utility wrapper for encoding and decoding
  8. def base64_encode(x):
  9.         "Encodes a sequence of bytes in a string, using base64 encoding"
  10.         return b64encode(x).decode()
  11. def base64_decode(x):
  12.         return b64decode(x, validate=True)
  13. # crypto magic
  14. def create_key(passphrase):
  15.         h = passphrase.encode()
  16.         h = digest(h)
  17.         k = 0
  18.         for i in range(8):
  19.                 k <<= 8
  20.                 k |= h[i]
  21.         return k if k else 1
  22. def secret_byte_stream(key):
  23.         x = key
  24.         mask = 255
  25.         while True:
  26.                 y = x
  27.                 a = y & mask #返回尾部8位
  28.                 yield a
  29.                 y >>=  8
  30.                 x = y
  31.                 y >>=  1
  32.                 a ^= y & mask
  33.                 y >>= 14
  34.                 a ^= y & mask
  35.                 y >>= 17
  36.                 a ^= y & mask
  37.                 x |= a << 56
  38. def scramble(message, key):
  39.         stream = secret_byte_stream(key)
  40.         return bytes(x ^ y for x, y in zip(message, stream))
  41. # user-facing stuff
  42. def encrypt(text, passphrase):
  43.         message = text.encode()
  44.         hash = digest(message)
  45.         key = create_key(passphrase)
  46.         e = scramble(message, key)
  47.         return '#'.join(map(base64_encode, [e, hash]))
  48. def decrypt(text, passphrase):
  49.         e, hash = map(base64_decode, text.split('#'))
  50.         key = create_key(passphrase)
  51.         message = scramble(e, key)
  52.         if hash != digest(message):
  53.                 raise ValueError("Wrong key")
  54.         return message.decode()
  55. def create_flag(secret):
  56.         return "".join(["KSUS{", secret.encode().hex(), "}"])
  57. if __name__ == "__main__":
  58.         secret = input("secret > ")
  59.         passphrase = input("passphrase > ")
  60.         flag = create_flag(secret)
  61.         print("flag :", flag)
  62.         challenge = encrypt(flag, passphrase)
  63.         assert flag == decrypt(challenge, passphrase)
  64.         print("challenge :", challenge)
复制代码
由于flag头有5字节,以是只须要爆破3字节即可。通过hash值判定。
  1. enc = 'VERY/Rjwj1U4DQZ/zyyHxSsMY1iYuOZHs//qWPVYInUz/5cxidrFCrSqco4bbVLpWjHHI4Z+JZOwOfsT#SUS/PDQPS4DlVum2aO+5+SuczHag7/rnYMBUr+pEqEU='
  2. enc, h = map(b64decode, enc.split('#'))
  3. #由已知头得到key的后5字节,前3字节爆破
  4. key_tail = xor(b'KSUS{', enc[:5])[::-1]
  5. for i1 in trange(256):
  6.   for i2 in range(256):
  7.     for i3 in range(256):
  8.       key = bytes([i1,i2,i3])+key_tail
  9.       key = bytes_to_long(key)
  10.       m = scramble(enc,key)
  11.       if h == sha256(m).digest():
  12.           print(m)
  13.          
  14. #KSUS{6c6673725f6172655f6e6f745f7365637572653038363834363137}
复制代码
Feistel <3

这个代码有点长
  1. from Crypto.Util.number import bytes_to_long, getPrime, long_to_bytes
  2. from Crypto.Util.Padding import pad
  3. import os, signal
  4. assert("FLAG" in os.environ)
  5. FLAG = os.environ["FLAG"]
  6. assert(FLAG.startswith("KSUS{") and FLAG.endswith("}"))
  7. def xor_bytes(bytes_a, bytes_b):
  8.     return bytes(a ^ b for a, b in zip(bytes_a, bytes_b)).ljust(2, b'\x00')
  9. def f(sub_block, round_key, modulus):
  10.     return long_to_bytes((bytes_to_long(sub_block) + pow(65537, bytes_to_long(round_key), modulus)) % (1<<17-1)).ljust(2, b'\x00')
  11. def encrypt_block(block, key, modulus, rounds=8, shortcut=False):
  12.     sub_block_1 = block[:2].ljust(2, b'\x00')
  13.     sub_block_2 = block[2:4].ljust(2, b'\x00')
  14.     sub_block_3 = block[4:].ljust(2, b'\x00')
  15.     for i in range(0, rounds):
  16.         round_key = key[i*2:i*2+2]
  17.         new_sub_block_1 = xor_bytes(sub_block_1, sub_block_2)
  18.         new_sub_block_2 = f(sub_block_3, round_key, modulus)
  19.         new_sub_block_3 = xor_bytes(sub_block_2, round_key)
  20.         sub_block_1 = new_sub_block_1
  21.         sub_block_2 = new_sub_block_2
  22.         sub_block_3 = new_sub_block_3
  23.         print(sub_block_1 + sub_block_2 + sub_block_3)
  24.         if shortcut and sub_block_1 == b"\xff\xff":
  25.             break
  26.     return sub_block_1 + sub_block_2 + sub_block_3
  27. def encrypt(plaintext, key, modulus):
  28.     iv = os.urandom(6)
  29.     padded = pad(plaintext.encode(), 6)
  30.     blocks = [padded[i:i+6] for i in range(0, len(padded), 6)]
  31.     res = []
  32.     for i in range(len(blocks)):
  33.         if i == 0: block = xor_bytes(blocks[i], iv)
  34.         else: block = xor_bytes(blocks[i], bytes.fromhex(res[-1]))
  35.         res.append(encrypt_block(block, key, modulus).hex())
  36.     return iv.hex() + "".join(res)
  37. def handle():
  38.     key = os.urandom(16)
  39.     N = getPrime(1024)
  40.     print("flag =", encrypt(FLAG, key, N))
  41.     print("N =", N)
  42.     encrypted = []
  43.     while True:
  44.         print("[1] Encrypt")
  45.         print("[2] Exit")
  46.         opt = input("> ")
  47.         
  48.         if opt == "1":
  49.             plaintext = input("Enter your fantastic plaintext (in hex): ")
  50.             if len(plaintext) % 2 != 0 or len(plaintext) < 2 or len(plaintext) > 12:
  51.                 print("It doesn't look fine to me :/")
  52.             elif plaintext in encrypted:
  53.                 print("Nah, you've already encrypted it!")
  54.             else:
  55.                 encrypted.append(plaintext)
  56.                 ciphertext = encrypt_block(bytes.fromhex(plaintext).rjust(6, b"\x00"), key, N, shortcut=True)
  57.                 print("Here it is: " + ciphertext.hex())
  58.         elif opt == "2":
  59.             print("Bye (^-^)")
  60.             exit(0)
  61.         else:
  62.             print("Nope :/")
  63. if __name__ == "__main__":
  64.     signal.alarm(300)
  65.     handle()
复制代码
16字节密钥分成8段轮密钥,每段2字节。加密将密文分成6字节块块加密,块加密将明文分成3块:b1,b2,b3,然后得到b1^b2, b3+e^key_round, b2^key_round。
但在末了给了后门:当c1==FFFF里会退出并不都须要颠末8轮。
如许就有了爆破的方法,只要让运行指定轮里使c1==FFFF即可。第一轮直接FFFF00000000则可根据第3段密文得到第1个轮密钥。第2轮是上一轮的c2和b1,b2云云类推。
先从远程得到key和密文。
  1. #-------------远程获取密文,key,N
  2. '''
  3. i    b1^b2^c(i-1)   x    c(i-1)^ki
  4. i+1  b1^b2^c(i-1)^c(i)   c(i)^k(i+1)
  5. '''
  6. from pwn import *
  7. def getenc(tmp):
  8.     p.sendlineafter(b"> ", b'1')
  9.     p.sendlineafter(b"Enter your fantastic plaintext (in hex): ", tmp.hex().encode())
  10.     p.recvuntil(b"Here it is: ")
  11.     return bytes.fromhex(p.recvline().strip().decode())
  12. p = remote('chall.ctf.k1nd4sus.it', 31013)
  13. print(p.recvline())
  14. print(p.recvline())
  15. c2 = b'\x00\x00'
  16. lc2 = b'\x00\x00'
  17. tk = b''
  18. for i in range(8):
  19.     tmp = xor(b'\xff\xff', c2)+b'\x00'*4
  20.     #enc = encrypt_block(tmp, key, N, shortcut=True)
  21.     enc = getenc(tmp)
  22.     c2 = xor(c2,enc[2:4])
  23.     tk+=xor(enc[4:],lc2)
  24.     lc2 = enc[2:4]
  25.     print(tk.hex())
  26. p.close()   
复制代码
然后弄个解密函数解一下。这东西居然不是每次都乐成,会出乱字符,不清晰怎么来的。
  1. def decrypt_block(block, key):
  2.     c1,c2,c3 = block[:2],block[2:4],block[4:]
  3.     for i in range(7,-1,-1):
  4.         round_key = key[i*2:i*2+2]
  5.         b2 = xor(c3, round_key)
  6.         b1 = xor(c1, b2)
  7.         b3 = long_to_bytes((bytes_to_long(c2) - pow(65537,bytes_to_long(round_key), N))&0xffff)
  8.         c1,c2,c3 = b1,b2,b3
  9.         #print(c1,c2,c3)
  10.     return c1+c2+c3
  11. def decrypt(ciphertext, key, modulus):
  12.     iv = ciphertext[:6]
  13.     padded = ciphertext[6:]
  14.     blocks = [padded[i:i+6] for i in range(0, len(padded), 6)]
  15.     res = b''
  16.     for i in range(len(blocks)):
  17.         tmp = decrypt_block(blocks[i], key)
  18.         if i == 0:
  19.             r = xor(tmp, iv)
  20.         else:
  21.             r = xor(tmp,blocks[i-1])
  22.         res += r
  23.         #print(res)
  24.     return res
  25. key = bytes.fromhex('35e7c26a66bc651827cac73bc99c6667')
  26. N = 175914002278057050406831961452237183138299948079975109116384718227058692202299804814271876290451098159041914033459568540766514412008363701006284852804260357617529486527991021342873932212136053758342488462611451121664507695932811146083705960145782839959600560090211913120599024239421171256321562939861953258223
  27. flag = bytes.fromhex('9f3d4928ba479f1e53a7f287efe0dba974745b5fb4a472e24ecdcefb3a70824f9ec87aba16cf7ab4551324af56035c387cb9bf390888')
  28. decrypt(flag,key,N)
  29. #b'KSUS{N3veR_Ev3r_5hOr7cuT_F3ist3l_Ne7w0rks}\x06\x06\x06\x06\x06\x06'
复制代码
key in the haystack

后边这两个是同一标题,一个小一个大,放一起
  1. from Crypto.Util import number
  2. from base64 import b64encode
  3. prime = lambda: number.getPrime(512)
  4. def b64enc(x):
  5.         h = hex(x)[2:]
  6.         if len(h) % 2:
  7.                 h = '0' + h
  8.         return b64encode(bytes.fromhex(h)).decode()
  9. p = prime()
  10. q = prime()
  11. with open("flag.txt") as f:
  12.         flag = f.readline().strip()
  13. n = p * q
  14. m = int(flag.encode().hex(), 16)
  15. c = pow(m, 65537, n)
  16. print("ciphertext:", hex(c)[2:])
  17. bale = [p, q]
  18. bale.extend(prime() for _ in range(1<<6))
  19. def add_hay(stack, straw):
  20.         x = stack[0]
  21.         for i in range(1, len(stack)):
  22.                 y = stack[i]
  23.                 stack[i] = y + (straw * x)
  24.                 x = y
  25.         stack.append(straw * x)
  26. stack = [1]
  27. add_hay(stack, p) #[1,p]
  28. add_hay(stack, q) #[1,p+q,p*q]
  29. for straw in bale:
  30.         add_hay(stack, straw)
  31. print("size:", len(stack))
  32. for x in stack:
  33.         print(b64enc(x))
复制代码
天生包罗p,q的66个素数。然后颠末add_hay天生stack。
这里是我自己的想法,这个方法不实用增强后的第2题,但也是个思绪记载一下。
先设一个变量,从后向前导一下:Kn = S(n) + v*S(n-1) => Sn = Kn - v*S(n-1) 
末了得到即是f = ss[-1]*v - stack[-1]  这是一个66次1元方程,不算太大sage可以直接解。由于这里的66个素数都是对称的,直接可以解出66个根,这里边就包罗p,q
  1. from base64 import *
  2. from Crypto.Util.number import *
  3. outs = open('output.txt').readlines()
  4. st = [bytes_to_long(b64decode(outs[2+i])) for i in range(69)]
  5. #Kn = S(n) + v*S(n-1) => Sn = Kn - v*S(n-1)
  6. var('v')
  7. ss = [1]
  8. for i in range(1,68):
  9.     ss.append(st[i]-v*ss[-1])
  10. #K68 = v*S68
  11. f = ss[-1]*v - st[-1]
  12. ps = f.roots()
  13. #v有66个解,对应66个素数
  14. ps = [int(i[0]) for i in ps]
  15. c = 0x7434d263623892ca660f4139c54ab02a8a14d87cd5c658fca9105f88f7ed5c888a744e949b716094c1d73fd8084eeaf72b23e97325829a69ca57a34e5e0b5272ddaf039bcc0aed2055968c8dfa7cd0373cca072c31123e6259659af03ce87b224bb7fdf13fb89b4ceb580d2d11524025ccb4f86560f3b006d99d86a63ab3aa5a
  16. #猜flag小于512位
  17. for p in ps:
  18.     m = pow(c,invert(65537,p-1),p)
  19.     print(long_to_bytes(int(m)))
  20. #b'KSUS{6465726976617469766573206172652061206e69636520747269636b}'
复制代码
key in the big haystack

这个升级版想了一天也没效果,然后问小鸡块给秒了。(数据有44M,就不贴了,也贴不上)
  1. bale = [p, q]
  2. bale.extend(prime() for _ in range(1<<9))
  3. stack = [1]
  4. add_hay(stack, p)
  5. add_hay(stack, q)
  6. for straw in bale:
  7.     add_hay(stack, straw)
  8. for straw in bale[2:]:
  9.     add_hay(stack, straw + 2)
复制代码
这题与上边只有这些厘革,素数由64改为512,而且用了两次,以是效果是1029项。
这些给定的值现实上是一个1029项多项式的系数。可以先用3个变量试下。
(x+p)*(x+q)*(x+r) = x^3 + (p+q+r)*x^2 + (pq+qr+rp)*x + pqr
然后把这些数据代入含x的方程

而这个式子里除了p,q是2次外别的都是1次(包罗512和素数和512个素数+2,素数+2后跟原来不形成平方关系)以是对f求导后与原来的f作gcd就能得到对于p,q的多项式。然后直接求解可得p,q
  1. from base64 import *
  2. from Crypto.Util.number import *
  3. from gmpy2 import iroot
  4. outs = open('big_output.txt').readlines()
  5. enc = bytes.fromhex('5894f38180b9f41fb816c7428b64b63cf207e349832aeb256977526ec750239c5b75e846f2c7db19fc84d44e57c1a6181562487cd4a7e58bab9903feead90d884b574dcc9d35b0d6ae7d491d399dcdf6aacc74efff2135c673178e08b50ac1a09f5334cd0d4b48355b28219dbc31b45a2c7687114b69c4f8a0ae20740e9ce1fe')
  6. c = bytes_to_long(enc)
  7. stack = [bytes_to_long(b64decode(outs[2+i])) for i in range(1029)]
  8. PR.<x> = PolynomialRing(ZZ)
  9. f = sum([stack[i] * x^(1028-i) for i in range(len(stack))])
  10. g = gcd(f.derivative(), f)
  11. print(g)
  12. #x^2 + 20450065261452182016584260876047399525896704233984001074890163119931284325571387726435254377483741593577102784943815777723994032502151858215013739509078298*x + 101344562563413148702503209034490415272295393794389109823061195011331285068194700252292458323839836319978741203645201395179109429423054143994480684843212974225776158987058221755014377991550489320194566899710227109943050130147641582913932721996451645082208066936338974731735770555839605141969367848387625804201
  13. #系数分别是p+q,p*q
  14. v = g.roots()
  15. p = abs(v[0][0])
  16. #8434298218257235619456993018380042120260367010962473119979826477888279532268658434816651817828253000282579391409905630356600119281235242527375263115386349
  17. long_to_bytes(int(pow(c,inverse_mod(65537,p-1),p)))
  18. #KSUS{43525420697320612076657279206e69636520747269636b}
复制代码

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表