type
status
date
slug
summary
tags
category
icon
password
Crypto
beginners_aes
key和iv分别爆一个字节就可以了:
from Crypto.Cipher import AES from Crypto.Util.Padding import unpad from tqdm import tqdm key = b'the_enc_key_is_' iv = b'my_great_iv_is_' enc = b'\x16\x97,\xa7\xfb_\xf3\x15.\x87jKRaF&"\xb6\xc4x\xf4.K\xd77j\xe5MLI_y\xd96\xf1$\xc5\xa3\x03\x990Q^\xc0\x17M2\x18' for i in tqdm(range(256)): for j in range(256): key_ = key + bytes([i]) iv_ = iv + bytes([j]) cipher = AES.new(key_, AES.MODE_CBC, iv_) res = cipher.decrypt(enc) try: res = unpad(res,16) print(res.decode()) break except: continue
beginners_rsa
from Crypto.Util.number import * import requests from functools import reduce def queryFactors(n): s=[] url="http://factordb.com/api?query="+str(n) r = requests.get(url) factors=r.json()['factors'] for f in factors: for i in range(f[1]): s.append(int(f[0])) return s n = 317903423385943473062528814030345176720578295695512495346444822768171649361480819163749494400347 e = 65537 enc = 127075137729897107295787718796341877071536678034322988535029776806418266591167534816788125330265 ps = queryFactors(n) ps_ = [i-1 for i in ps] phi = reduce(lambda x, y: x * y, ps_) d = inverse(e,phi) print(long_to_bytes(pow(enc,d,n))) # FLAG{S0_3a5y_1254!!}
Easy calc
给了个迭代函数f,该函数决定了A、s、p的关系:

整理一下有:
典型的高中数学,对A两边乘s:
相减:
因为是在模p下,
因此得到,
解密脚本:
from binascii import unhexlify from Crypto.Util.number import * from Crypto.Cipher import AES from hashlib import md5 p = 108159532265181242371960862176089900437183046655107822712736597793129430067645352619047923366465213553080964155205008757015024406041606723580700542617009651237415277095236385696694741342539811786180063943404300498027896890240121098409649537982185247548732754713793214557909539077228488668731016501718242238229 A = 60804426023059829529243916100868813693528686280274100232668009387292986893221484159514697867975996653561494260686110180269479231384753818873838897508257692444056934156009244570713404772622837916262561177765724587140931364577707149626116683828625211736898598854127868638686640564102372517526588283709560663960 ciphertext = '9fb749ef7467a5aff04ec5c751e7dceca4f3386987f252a2fc14a8970ff097a81fcb1a8fbe173465eecb74fb1a843383' enc = unhexlify(ciphertext) print(len(enc)) s = A * pow(1 + A, -1, p) % p key = long_to_bytes(s) key = md5(key).digest() iv,c = enc[:16],enc[16:] cipher = AES.new(key, AES.MODE_CBC, iv=iv) print(cipher.decrypt(c)) # FLAG{Do_the_math396691ba7d7270a}
replacement
from string import printable from hashlib import md5 enc = for h in enc: for i in printable: x = ord(i) x = md5(str(x).encode()).hexdigest() y = int(x,16) if y == h: print(i,end='') # Wednesday, 11/8, clear skies. This morning, I had breakfast at my favorite cafe. Drinking the freshly brewed coffee and savoring the warm buttery toast is the best. Changing the subject, I received an email today with something rather peculiar in it. It contained a mysterious message that said "This is a secret code, so please don't tell anyone. FLAG{13epl4cem3nt}". How strange!
dance
整个系统是一个身份认证系统,该系统调用了
mycipher.py
作为token加密算法,该加密算法是chacha20。漏洞出在Register这里:def Register(): global d username = input('Enter username: ') if username in d: print('Username already exists') return dt_now = datetime.datetime.now() minutes = dt_now.minute sec = dt_now.second data1 = f'user: {username}, {minutes}:{sec}' data2 = f'{username}'+str(random.randint(0, 10)) d[username] = make_token(data1, data2) print('Registered successfully!') print('Your token is:', d[username]) return
这里就是用当前时间的分秒值和username以及一个小的随机数来生成token,然后token生成chacha20的key和nonce进行加密。题目直接给了user,因此直接爆时间可随机数得到token就行。总的说来难度主要还是在审代码逻辑。
import hashlib from mycipher import MyCipher from tqdm import tqdm username = 'gureisya' ciphertext = '3da5f9fa6998a991cb244a12fa72d311f3e6e9fbcac9984c0c' def make_token(data1: str, data2: str): sha256 = hashlib.sha256() sha256.update(data1.encode()) right = sha256.hexdigest()[:20] sha256.update(data2.encode()) left = sha256.hexdigest()[:12] token = left + right return token for sec in tqdm(range(60)): for minutes in range(60): data1 = f'user: {username}, {minutes}:{sec}' for i in range(10): data2 = f'{username}{i}' token = make_token(data1, data2) sha256 = hashlib.sha256() sha256.update(token[:32].encode()) key = sha256.hexdigest()[:32] nonce = token[:12] cipher = MyCipher(key.encode(), nonce.encode()) decrypted = cipher.encrypt(bytes.fromhex(ciphertext)) if b'FLAG' in decrypted: print(decrypted) exit()
speedy
MyCipher其实是Xoroshiro128+这一PRNG的实现,注意get_key_stream全是异或和循环移位,是可逆的。

然后注意
secret = b'FLAG{'+b'*'*19+b'}'
pt = pad(secret, 8)
根据flag长度,这里的pad一定是0x07。
然后encrypt这里是直接泄露了X:
def encrypt(self, pt: bytes): ct = b'' for i in range(0, len(pt), self.BLOCK_SIZE): ct += long_to_bytes(self.X) key = self.get_key_stream() block = pt[i:i+self.BLOCK_SIZE] ct += bytes([block[j] ^ key[j] for j in range(len(block))]) return ct
那么只要搞到Y就可以恢复s0和s1了,由于上述pad所以ct[-8:]只有第一个字节是未知的,那么爆破一下就能得到最后一轮的key,然后利用sum=X+Y计算Y,然后逆get_key_stream得到s0和s1,再解密就可以了。
from Crypto.Util.number import * from Crypto.Util.Padding import * import os def rotl(x, y): x &= 0xFFFFFFFFFFFFFFFF return ((x << y) | (x >> (64 - y))) & 0xFFFFFFFFFFFFFFFF class MyCipher: def __init__(self, s0, s1): self.X = s0 self.Y = s1 self.mod = 0xFFFFFFFFFFFFFFFF self.BLOCK_SIZE = 8 def get_key_stream(self): s0 = self.X s1 = self.Y sum = (s0 + s1) & self.mod s1 ^= s0 key = [] for _ in range(8): key.append(sum & 0xFF) sum >>= 8 self.X = (rotl(s0, 24) ^ s1 ^ (s1 << 16)) & self.mod self.Y = rotl(s1, 37) & self.mod return key def set_seed(self, s0, s1): self.X = s0 self.Y = s1 def encrypt(self, pt: bytes): ct = b'' for i in range(0, len(pt), self.BLOCK_SIZE): ct += long_to_bytes(self.X) key = self.get_key_stream() block = pt[i:i+self.BLOCK_SIZE] ct += bytes([block[j] ^ key[j] for j in range(len(block))]) return ct def decrypt(self, ct: bytes, s0=None, s1=None): pt = b'' if s0 is not None and s1 is not None: self.set_seed(s0, s1) for i in range(0, len(ct), 2*self.BLOCK_SIZE): key = self.get_key_stream() block = ct[i+self.BLOCK_SIZE:i+2*self.BLOCK_SIZE] pt += bytes([block[j] ^ key[j] for j in range(len(block))]) return unpad(pt, self.BLOCK_SIZE) def rotl(x, y): x &= 0xFFFFFFFFFFFFFFFF return ((x << y) | (x >> (64 - y))) & 0xFFFFFFFFFFFFFFFF def back(x, y, times): s0 = x s1 = y for _ in range(times): s0 = (rotl(x, 40) ^ rotl(y, 3) ^ rotl((rotl(y, 27)<<16), 40)) & 0xFFFFFFFFFFFFFFFF s1 = (rotl(x, 40) ^ rotl(y, 3) ^ rotl((rotl(y, 27)<<16), 40) ^ rotl(y, 27)) & 0xFFFFFFFFFFFFFFFF x = s0 y = s1 return s0, s1 ct = b'"G:F\xfe\x8f\xb0<O\xc0\x91\xc8\xa6\x96\xc5\xf7N\xc7n\xaf8\x1c,\xcb\xebY<z\xd7\xd8\xc0-\x08\x8d\xe9\x9e\xd8\xa51\xa8\xfbp\x8f\xd4\x13\xf5m\x8f\x02\xa3\xa9\x9e\xb7\xbb\xaf\xbd\xb9\xdf&Y3\xf3\x80\xb8' flag = b'' for b in range(0x00, 0x100): sum = 0 pred = long_to_bytes(b)+b'\x07'*7 for i in range(8): sum += (ct[-8+i] ^ pred[i]) << 8*i x = bytes_to_long(ct[-16:-8]) y = (sum-x) & 0xFFFFFFFFFFFFFFFF seed0, seed1 = back(x, y, 3) cipher = MyCipher(0xdeadbeef, 0x12345678) pt = cipher.decrypt(ct, seed0, seed1) if pt.startswith(b'FLAG{'): flag = pt break print(flag)
Many Xor Shift
可以看作mod2下的矩阵方程:
N = 7 M = 17005450388330379 WORD_SIZE = 32 state = [1927245640, 871031439, 789877080, 4042398809, 3950816575, 2366948739, 935819524] xor_shift = matrix(GF(2), N*32, N*32) for i in range(N-1): for j in range(32): xor_shift[i*32+j, (i+1)*32+j] += 1 for j in range(32): xor_shift[(N-1)*32+j, (N-1)*32+j] += 1 # state[-1] if j+19 < 32: xor_shift[(N-1)*32+j, (N-1)*32+j+19] += 1 # state[-1] >> 19 xor_shift[(N-1)*32+j, j] += 1 # state[0] if j-11 >= 0: xor_shift[(N-1)*32+j, j-11] += 1 # state[0] << 11 if j+8 < 32: xor_shift[(N-1)*32+j, j+8] += 1 # state[0] >> 8 if j+8 < 32 and j+8-11 >= 0: xor_shift[(N-1)*32+j, j+8-11] += 1 # (state[0] << 11) >> 8 encrypt = xor_shift^M decrypt = encrypt^(-1) cipher = vector(GF(2), N*32) for i in range(N): for j in range(32): cipher[i*32+j] = state[i] >> j & 1 plain = decrypt*cipher msg = b"" for i in range(N): x = sum(int(plain[i*32+j]) << j for j in range(32)) msg += x.to_bytes(4) print(msg)
uf
整个问题可以转为AGCD,具体看encrypt过程:
def encrypt(m: int, n: int = 512) -> int: x = 0 for i in range(n): x <<= 1 x += m * randbits(1) if i >= n // 2: x ^= randbits(1) return x
整个过程转化为:
其中为512比特,为256比特。造格子:

from Crypto.Util.number import long_to_bytes X = [6643852762092641655051592752286380661448697120839285262713138738793179330857521051418707355387198243788554658967735136760757552410466512939791351078152197994352930016306075464400264019640466277732596022216246131141036813931972036259910390741311141390889450882074162723823607552591155184799627590418587536982033939537563823, 4495106960532238798978878322218382764459613684889887356979907395021294655849239390809608204284927849117763119933285899077777162943233437728643056322845118660545730870443735090094400144586494098834221418487123653668703665085461676013454922344247818407399456870636622800919629442727075235809213114639237367651539678560390951, 7622226387024225267485603541284038981214490586915816777231024576546652676746968149372915915975325662783469952634025859954515971134032563991925283958708572235632178937041656690377178266198211581176947491463237398083133658483056792368618417698027992083481412961301906342594056438180675328433412539805240307255787971167535638, 1149407465454162408488208063367931363888120160126632926627929705372269921465081968665764846439238807939361247987642326885758277171318666479752274577607727935160689442316433824450832192798328252739495913920016290902086534688608562545166349970831960156036289570935410160077618096614135121287858428753273136461851339553609896] n = len(X) A = matrix(ZZ, n, n) K = 2^256 A[0, 0] = K for i in range(1, n): A[0, i] = X[i] A[i, i] = -X[0] A = A.LLL() a = A[0] r0 = a[0] // K s0 = X[0] % r0 m = (X[0]-s0) // r0 m = abs(m) print(long_to_bytes(m))
📎 参考文章
- Author:ZimaBlue
- URL:https://www.zimablue.life/article/wanictf2024
- Copyright:All articles in this blog, except for special statements, adopt BY-NC-SA agreement. Please indicate the source!