nepsign
题面:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 from gmssl import sm3from random import SystemRandomfrom ast import literal_evalimport osflag = os.environ["FLAG" ] def SM3 (data ): d = [i for i in data] h = sm3.sm3_hash(d) return h def SM3_n (data, n=1 , bits=256 ): for _ in range (n): data = bytes .fromhex(SM3(data)) return data.hex ()[:bits // 4 ] class Nepsign (): def __init__ (self ): self .n = 256 self .hex_symbols = '0123456789abcdef' self .keygen() def keygen (self ): rng = SystemRandom() self .sk = [rng.randbytes(32 ) for _ in range (48 )] self .pk = [SM3_n(self .sk[_], 255 , self .n) for _ in range (48 )] return self .sk, self .pk def sign (self, msg, sk=None ): sk = sk if sk else self .sk m = SM3(msg) m_bin = bin (int (m, 16 ))[2 :].zfill(256 ) a = [int (m_bin[8 * i: 8 * i + 8 ], 2 ) for i in range (self .n // 8 )] step = [0 ] * 48 ; qq = [0 ] * 48 for i in range (32 ): step[i] = a[i] qq[i] = SM3_n(sk[i], step[i]) sum = [0 ] * 16 for i in range (16 ): sum [i] = 0 for j in range (1 , 65 ): if m[j - 1 ] == self .hex_symbols[i]: sum [i] += j step[i + 32 ] = sum [i] % 255 qq[i + 32 ] = SM3_n(sk[i + 32 ], step[i + 32 ]) return [i for i in qq] def verify (self, msg, qq, pk=None ): qq = [bytes .fromhex(i) for i in qq] pk = pk if pk else self .pk m = SM3(msg) m_bin = bin (int (m, 16 ))[2 :].zfill(256 ) a = [int (m_bin[8 * i: 8 * i + 8 ], 2 ) for i in range (self .n // 8 )] step = [0 ] * 48 ; pk_ = [0 ] * 48 for i in range (32 ): step[i] = a[i] pk_[i] = SM3_n(qq[i], 255 - step[i]) sum = [0 ] * 16 for i in range (16 ): sum [i] = 0 for j in range (1 , 65 ): if m[j - 1 ] == self .hex_symbols[i]: sum [i] += j step[i + 32 ] = sum [i] % 255 pk_[i + 32 ] = SM3_n(qq[i + 32 ], 255 - step[i + 32 ]) return True if pk_ == pk else False print ('initializing...' )Sign = Nepsign() while 1 : match int (input ('> ' )): case 1 : msg = bytes .fromhex(input ('msg: ' )) if msg != b'happy for NepCTF 2025' : print (Sign.sign(msg)) else : print ("You can't do that" ) case 2 : qq = literal_eval(input ('give me a qq: ' )) if Sign.verify(b'happy for NepCTF 2025' , qq): print (flag)
分析:
迭代哈希函数的代数性质
S M 3 _ n ( x , a + b ) = S M 3 _ n ( S M 3 _ n ( x , a ) , b ) SM3\_n(x,a+b)=SM3\_n(SM3\_n(x,a),b)
S M 3 _ n ( x , a + b ) = S M 3 _ n ( S M 3 _ n ( x , a ) , b )
类似于一个加法群 的结构:
设 H ( x , k ) = S M 3 _ n ( x , k ) H(x, k) = SM3\_n(x, k) H ( x , k ) = S M 3 _ n ( x , k )
则有 H ( x , a + b ) = H ( H ( x , a ) , b ) H(x, a + b) = H(H(x, a), b) H ( x , a + b ) = H ( H ( x , a ) , b )
签名生成过程:
前32个签名元素:
将 m
转换为 256 位二进制 m_bin
分成 32 个 8 位组:a_i = m_bin[8i:8i+8]
(0 ≤ i < 32)
签名元素:q q i = S M 3 _ n ( s k i , a i ) qq_i = SM3\_n(sk_i, a_i) q q i = S M 3 _ n ( s k i , a i )
后16个签名元素:
对于十六进制字符 c
(0 ≤ i < 16):
计算位置和:s u m i = ∑ { j ∣ m [ j ] = c } sum_i = \sum \{j\ |\ m[j] = c\} s u m i = ∑ { j ∣ m [ j ] = c }
取模:s t e p i = s u m i m o d 255 step_i = sum_i \ mod\ 255 s t e p i = s u m i m o d 2 5 5
签名元素:q q 32 + i = S M 3 _ n ( s k 32 + i , s t e p i ) qq_{32+i} = SM3\_n(sk_{32+i}, step_i) q q 3 2 + i = S M 3 _ n ( s k 3 2 + i , s t e p i )
验证过程:
验证时使用逆操作:
p k i = SM3 _ n ( q q i , 255 − s t e p i ) pk_i = \text{SM3}\_n(qq_i, 255 - step_i)
p k i = SM3 _ n ( q q i , 2 5 5 − s t e p i )
这基于:
SM3 _ n ( SM3 _ n ( s k i , s t e p i ) , 255 − s t e p i ) = SM3 _ n ( s k i , 255 ) = p k i \text{SM3}\_n(\text{SM3}\_n(sk_i, step_i), 255 - step_i) = \text{SM3}\_n(sk_i, 255) = pk_i
SM3 _ n ( SM3 _ n ( s k i , s t e p i ) , 2 5 5 − s t e p i ) = SM3 _ n ( s k i , 2 5 5 ) = p k i
攻击原理:
根据迭代哈希的可组合性:
H ( x , a + b ) = H ( H ( x , a ) , b ) H(x, a + b) = H(H(x, a), b)
H ( x , a + b ) = H ( H ( x , a ) , b )
如果已知:
消息 M 的签名元素:q q M = H ( s k , s M ) qq_M = H(sk, s_M) q q M = H ( s k , s M )
目标消息 T 的步数:s T s_T s T
所以前32个步数值可以直接由位操作提取,后门16个步数值基于字符位置统计得到目标信息的step值
且满足 s M ≤ s T s_M \leq s_T s M ≤ s T ,则可以计算目标签名:
q q T = H ( q q M , Δ ) = H ( H ( s k , s M ) , Δ ) = H ( s k , s M + Δ ) = H ( s k , s T ) qq_T = H(qq_M, \Delta) = H(H(sk, s_M), \Delta) = H(sk, s_M + \Delta) = H(sk, s_T)
q q T = H ( q q M , Δ ) = H ( H ( s k , s M ) , Δ ) = H ( s k , s M + Δ ) = H ( s k , s T )
其中 Δ = s T − s M \Delta = s_T - s_M Δ = s T − s M
然后一直循环知道找到全部签名元素即可
题解:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 from gmssl import sm3from pwn import *import osimport astfrom tqdm import tqdmimport timeSERVER_IP = 'nepctf32-efjp-shsj-n0cr-uqygeog3d763.nepctf.com' SERVER_PORT = 443 TARGET_MSG = b'happy for NepCTF 2025' MAX_ATTEMPTS = 3000 RETRY_DELAY = 1 def SM3 (data ): if isinstance (data, str ): data = data.encode() return sm3.sm3_hash([i for i in data]) def SM3_n (data, n=1 , bits=256 ): if isinstance (data, str ): data = bytes .fromhex(data) for _ in range (n): data = bytes .fromhex(SM3(data)) return data.hex ()[:bits // 4 ] def calc_step (msg ): if isinstance (msg, str ): msg = msg.encode() m = SM3(msg) m_int = int (m, 16 ) step = [] for i in range (32 ): step.append((m_int >> (248 - 8 *i)) & 0xFF ) hex_symbols = '0123456789abcdef' char_positions = {c: [] for c in hex_symbols} for j in range (64 ): char_positions[m[j]].append(j+1 ) for c in hex_symbols: s = sum (char_positions.get(c, [])) step.append(s % 255 ) return step def main (): target_steps = calc_step(TARGET_MSG) print (f"目标消息的step值: {target_steps} " ) target_qq = [None ] * 48 collected = [False ] * 48 print (f"连接到服务器 {SERVER_IP} :{SERVER_PORT} ..." ) conn = remote(SERVER_IP, SERVER_PORT,ssl=True ) conn.recvuntil(b'> ' ) progress = tqdm(total=48 , desc="收集签名元素" , unit="element" ) attempt_count = 0 while not all (collected) and attempt_count < MAX_ATTEMPTS: attempt_count += 1 msg_rand = os.urandom(32 ).hex () try : conn.sendline(b'1' ) conn.recvuntil(b'msg: ' ) conn.sendline(msg_rand.encode()) response = conn.recvline().decode().strip() conn.recvuntil(b'> ' ) except EOFError: print ("\n连接中断,尝试重新连接..." ) time.sleep(RETRY_DELAY) conn = remote(SERVER_IP, SERVER_PORT) conn.recvuntil(b'> ' ) continue try : qq_list = ast.literal_eval(response) except : print (f"解析失败: {response} " ) continue current_steps = calc_step(bytes .fromhex(msg_rand)) new_collections = 0 for i in range (48 ): if not collected[i] and current_steps[i] <= target_steps[i]: delta = target_steps[i] - current_steps[i] target_qq[i] = SM3_n(qq_list[i], delta, 256 ) collected[i] = True new_collections += 1 if new_collections > 0 : progress.update(new_collections) progress.close() if not all (collected): print (f"未能在 {MAX_ATTEMPTS} 次尝试内收集所有签名元素" ) print (f"缺失位置: {[i for i, c in enumerate (collected) if not c]} " ) conn.close() return print ("成功构造目标签名!" ) try : conn.sendline(b'2' ) conn.recvuntil(b'give me a qq: ' ) conn.sendline(str (target_qq).encode()) flag_response = conn.recvline().decode() if b"NepCTF" in flag_response.lower(): print (f"获取flag: {flag_response} " ) else : print (f"验证失败: {flag_response} " ) except Exception as e: print (f"提交签名时出错: {str (e)} " ) conn.close() if __name__ == '__main__' : main()
LatticeBros
题面:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 from secret import a0,alphaimport gmpy2from Crypto.Util.number import long_to_bytesimport randomfrom math import sqrt,log2d=981020902672546902438782010902608140583199504862558032616415 p = d - a0 k=sqrt(log2(p))+log2(log2(p)) B = 2 **30 assert B < p/2 **km = 30 assert m > 2 *sqrt(log2(p))samples = [] betas = [] f = open ("samples.txt" ,'w' ) for _ in range (m): t = random.randint(1 , p-1 ) beta = random.randint(-B + 1 , B - 1 ) a = (t * alpha - beta) % p samples.append((t, a)) betas.append(beta) f.write(str (samples)) for i in range (0 ,30 ): assert (betas[i]-samples[i][0 ]*alpha+samples[i][1 ])%p == 0
分析:
经典HNP问题,但要先得到p,要得到p就要得到α \alpha α 的极小多项式的常数项a 0 a_0 a 0
极小多项式满足:
α 3 + a 2 α 2 + a 1 α + a 0 = 0 \alpha^3+a_2\alpha^2+a_1\alpha+a_0=0
α 3 + a 2 α 2 + a 1 α + a 0 = 0
构造格:
( α 3 1 α 2 1 α 1 1 ) \begin{pmatrix}\alpha^3&1&&\\\alpha^2&&1&\\\alpha&&&1\\1&&&\end{pmatrix}
⎝ ⎜ ⎜ ⎜ ⎛ α 3 α 2 α 1 1 1 1 ⎠ ⎟ ⎟ ⎟ ⎞
对第一列配平,然后规约即可得到a 1 , a 2 a_1,a_2 a 1 , a 2 ,则a 0 a_0 a 0 就得到了,至此得到了p
然后对于HNP问题,构造:
( t 1 t 2 … t 30 a 1 a 2 … a 30 p p ⋱ p ) \begin{pmatrix}t_1&t_2&\dots&t_{30}\\a_1&a_2&\dots&a_{30}\\p\\&p\\&&\ddots\\&&&p\end{pmatrix}
⎝ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎛ t 1 a 1 p t 2 a 2 p … … ⋱ t 3 0 a 3 0 p ⎠ ⎟ ⎟ ⎟ ⎟ ⎟ ⎟ ⎟ ⎞
即可规约出β i \beta_i β i ,由
α = ( a + β ) t − 1 m o d p \alpha = (a+\beta)t^{-1}\ \ mod\ \ p
α = ( a + β ) t − 1 m o d p
题解:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 from Crypto.Util.number import *B = 2 **30 m = 30 d=981020902672546902438782010902608140583199504862558032616415 a = QQ(54236.606188881754809671280151541781895183337725393 ) print (a)print (a.denominator().bit_length())M = Matrix(QQ,4 ) for i in range (4 ): M[i,(i+1 )%4 ] = 1.0 if i != 3 : M[i,0 ] = a^(3 -i) M[:,0 ] *= getPrime(76 ) a1 , a2 = M.LLL()[0 ][-1 ] , M.LLL()[0 ][-2 ] a0 = -a^3 - a2*a^2 - a1*a p = d - a0 print (int (p).bit_length())from math import sqrt,log2k=sqrt(log2(p))+log2(log2(p)) data = [(541847931463604073209188621415697353813245102261880389530448 , 293760933113243563398917466885108625646262447370201484418246 ), (235213326900086489464935804156966465366154623411555613791270 , 660823982268225103178763707015491421784294988488272636270997 ), (826464884761457937459245903152143755707241416981488127320435 , 428521663319038461250005113612781686761766888058391496085911 ), (589542000504317435156560078533519448295689695687499354390208 , 155284353896000150766154807679279597476176668344402166959399 ), (968823371588600973965757332601758200815345862153455338808286 , 870008943690791009196027169525956126827736285614393106689402 ), (621636099728440147413990266662022925118216803638588918660041 , 265635912066749696542909843111997941904342442664219734956888 ), (426696569424050102229606043215592727790577655338668728275370 , 279313121876980354011480010042682666651614765507190502627689 ), (89450479064580125731654556963306718472532905610952012502649 , 465933125964565419295325650759566635253450915499965633327941 ), (480355476500393865742379469913983270769356894135485925662119 , 894041172171871806404285309781862268351135623868845025443422 ), (842436524669577199024236805258573090764419350786291073287889 , 345478552143958037534551648319293899442551000874041707820740 ), (650054674429185550652935714084022116516082323269321462104664 , 441999979283903658157822753439653947343822546158589507765994 ), (46289431385578693366971976442426853079852982529357847290686 , 625618376463384339878849844467050454204685252824782609369180 ), (71444185449163133531919043374545893927347050624346741281881 , 955925578289311966288639224625142299309823207245807788495453 ), (192579726169321656812883068526498248523814846320328766176253 , 626481822474054336470183912297952839011392733501646931370367 ), (736527635648804640774976580747540045854351230084566721853611 , 276626211757586963928788091386096607703513204646314683038338 ), (177922521867185878959621840269164617147915792720210315529733 , 541058782621716573816245900423919799500476442285991532228641 ), (40610451174818168154306630612571678739921107216052349044576 , 727642592899858828601137105077611015328512898368636299587376 ), (385012983728389322601149562441674995471397288632464238356283 , 353921151307105661267278594470212933060655245893209524497156 ), (750447975601038834764379841158092390933760641866111445401426 , 391626416964965737035878375834907580903143512300198923948189 ), (115058604943298010958881205548782439407592353731185670266593 , 491630592857258949793489206081490523001249620510479961058022 ), (327389234395954477946639629629085910688793716425320663599360 , 24975272330009592102362429346350824580378490147041708568130 ), (115595274689129534885608766476695918464309130165432995990883 , 757961876891952019297626599379744405302595090402128271144165 ), (950804723308776351161744501221236453742418549093165078282534 , 20307246759635231945223392614290397512873344480184942904518 ), (724537610412063699714461780160573528810830178440136810747811 , 149681928388378582933943374524511804362928290938917573644613 ), (340891278018589324130004945217960336392205386747747011263373 , 683307718413135477104477081812052183267507312278283317237187 ), (104379682905784169840335131193505192063050242530811180817410 , 715010230598797717533306270232399781090458356371977748416491 ), (644160326926600986730919713173510327120201404569141824224075 , 127877985489410167008195578625004740882394608402141169695352 ), (549253388716005399852261816416312267100135940382820676807345 , 210560134643237517255193955173709174155305784935427470113433 ), (968265711632086435506163736279856957220961064226797549228006 , 273174723915971720522674140326199419265943707917542063022561 ), (704367622558261900937184683100177434487519780290678439135652 , 959106497548134540301589019840013331842784496835379005298630 )] Mat = Matrix(ZZ,32 ,30 ) for i in range (30 ): Mat[0 ,i] = data[i][0 ] Mat[1 ,i] = data[i][1 ] Mat[i+2 ,i] = int (p) for betas in Mat.LLL(): for i in range (len (betas)): flag = long_to_bytes((inverse(data[i][0 ],int (p))*(data[i][1 ]+betas[i]))%int (p)) if b'NepCTF' in flag: print (flag) break
ezRSA2
题面:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 from Crypto.Util.number import getStrongPrime, getRandomNBitInteger, GCD, inverse, long_to_bytes, bytes_to_long, sieve_basefrom flag import flagdef gen_parameters (gamma=0.33 , beta=0.33 ): p = getStrongPrime(1024 ) q = getStrongPrime(1024 ) N = p*q phi = (p-1 )*(q-1 ) while True : d = getRandomNBitInteger(int (2048 *beta)) if GCD(d, phi) == 1 : break e = inverse(d, phi) hints = [] M = 1 for i in range (1 , len (sieve_base)): li = sieve_base[i] hints.append(d%li) M *= li if M.bit_length() >= 1024 *gamma: break return e, N, hints def main (): e,N,hints = gen_parameters() print (f'e={hex (e)} ' ) print (f'N={hex (N)} \n' ) print (f'hints={hints} \n' ) flag_prefix = b'NepCTF{' assert flag.startswith(flag_prefix) assert flag.endswith(b'}' ) pt = bytes_to_long(flag[len (flag_prefix):-1 ]) ct = pow (pt, e, N) print (f'ct={hex (ct)} ' ) main() """ e=0x73915608ed64c9cf1a2279684cab4f4a78fba229d45d4f860971a241481363470a19cb0dc0d00f816b5befdaca017cf71483e96ef17b36179012f5194a0e6bf481bb06c2644f74c6812efb65d05c00631f282d6aa55c0bc140a1830b95a1cf4b6024cb0db53f2c2189897c41f22e2eec773723f531ec4bfa537fae6de5fe480cf46fe17850f7eb47df08194d95db3d26ac923b26e110ee645239ab586bbc546ddc5906f280a106edbb727ccb05536b5a3f5c0ebcf865c95ce58be54f7f3547aa53baa218b0dfa98e42d925fa341e45f94a3b16b0c83802660c7f34de3336cb21f219073cf8e9f5e39d47f0a9a9ee7c255f09a6add9a2f7a47960f4a853183d29 N=0xba8956e81394f3f1265ca5d9c4ad1ab0078bb43c4b80a231ab2cc62246ae45f66a562252622aed2cbbfc08647ef2fec0f97a632bf2242845f4b3af0c427cec3d90f42e90278a5a0feeed0922a8cd2278074ac54e9cfc0e96ff68f8d8f266dd87dc1cc59c2895ec884de2022311767f6a9a7e0bd288c79620e28b83bb3c8d8ad1047c839d6ccf5544eaf434a5f00b951769ab3121298d04b63a162757beb3d49917cd0c9e02ee1ac29398c8130961d5a2f2833aba1e538edb7bb97071f40fae543d1622f0c9206c6d4d8abb2ac1b93ebfb603c2f3a909ede357ade4043550fe540d13a4e87db8d731fe130f15a43a1a00364f5da2d87f7b660c3a04e734218a11 hints=[1, 3, 0, 3, 9, 16, 10, 14, 5, 11, 21, 18, 30, 30, 38, 2, 20, 62, 66, 1, 22, 56, 41, 13, 78, 59, 51, 6, 57, 117, 73, 75, 96, 112, 50, 93, 158, 97, 146, 8, 65, 96, 186, 161, 90, 131, 46, 32, 140, 133, 50, 43, 151, 234] ct=0x101b284ad196b5bbd3d3df00a7d3577caeb29c681bdd122582b705afc671febf45d4f3786640e55aadd6a31ecc49175f97b772720f1735f8555f768b137a4643cd6958f80a3dfca4d0270ad463d6dde93429940bd2abb5ad8408b0906fa8d776544a1c50cc0d95939bef4c3fb64d0b52dca81ff0f244fc265bfc0bc147435d05f8f1a146e963a1403b3c123b4d6e73d1fd897109995009be1673212607f0ea7ae33d23f3158448b05c28ea6636382eee9436c4a6c09023ead7182ecd55ac73a68d458d726e1abc208810468591e63f4b4c2c1f3ce27c4800b52f7421ccab432c03e88b3b255740d719e40e0226eabb7633d97ed210e32071e2ac36ed17ef442e """
分析:
hints给了d模若干小素数的结果,而且这些小素数我们都已知,所以可以crt得到d的低位,然后对于
e ( d h ∗ M + d l ) = 1 + k ( n − ( p + q ) + 1 ) e(d_h*M+d_l)=1+k(n-(p+q)+1)
e ( d h ∗ M + d l ) = 1 + k ( n − ( p + q ) + 1 )
其中M是前面若干小素数的乘积,令p+q=s,即求一个k,s的二元copper
题解:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 from Crypto.Util.number import *from sympy import nextprimeimport itertoolsfrom tqdm import *e=0x73915608ed64c9cf1a2279684cab4f4a78fba229d45d4f860971a241481363470a19cb0dc0d00f816b5befdaca017cf71483e96ef17b36179012f5194a0e6bf481bb06c2644f74c6812efb65d05c00631f282d6aa55c0bc140a1830b95a1cf4b6024cb0db53f2c2189897c41f22e2eec773723f531ec4bfa537fae6de5fe480cf46fe17850f7eb47df08194d95db3d26ac923b26e110ee645239ab586bbc546ddc5906f280a106edbb727ccb05536b5a3f5c0ebcf865c95ce58be54f7f3547aa53baa218b0dfa98e42d925fa341e45f94a3b16b0c83802660c7f34de3336cb21f219073cf8e9f5e39d47f0a9a9ee7c255f09a6add9a2f7a47960f4a853183d29 n=0xba8956e81394f3f1265ca5d9c4ad1ab0078bb43c4b80a231ab2cc62246ae45f66a562252622aed2cbbfc08647ef2fec0f97a632bf2242845f4b3af0c427cec3d90f42e90278a5a0feeed0922a8cd2278074ac54e9cfc0e96ff68f8d8f266dd87dc1cc59c2895ec884de2022311767f6a9a7e0bd288c79620e28b83bb3c8d8ad1047c839d6ccf5544eaf434a5f00b951769ab3121298d04b63a162757beb3d49917cd0c9e02ee1ac29398c8130961d5a2f2833aba1e538edb7bb97071f40fae543d1622f0c9206c6d4d8abb2ac1b93ebfb603c2f3a909ede357ade4043550fe540d13a4e87db8d731fe130f15a43a1a00364f5da2d87f7b660c3a04e734218a11 hints=[1 , 3 , 0 , 3 , 9 , 16 , 10 , 14 , 5 , 11 , 21 , 18 , 30 , 30 , 38 , 2 , 20 , 62 , 66 , 1 , 22 , 56 , 41 , 13 , 78 , 59 , 51 , 6 , 57 , 117 , 73 , 75 , 96 , 112 , 50 , 93 , 158 , 97 , 146 , 8 , 65 , 96 , 186 , 161 , 90 , 131 , 46 , 32 , 140 , 133 , 50 , 43 , 151 , 234 ] ct=0x101b284ad196b5bbd3d3df00a7d3577caeb29c681bdd122582b705afc671febf45d4f3786640e55aadd6a31ecc49175f97b772720f1735f8555f768b137a4643cd6958f80a3dfca4d0270ad463d6dde93429940bd2abb5ad8408b0906fa8d776544a1c50cc0d95939bef4c3fb64d0b52dca81ff0f244fc265bfc0bc147435d05f8f1a146e963a1403b3c123b4d6e73d1fd897109995009be1673212607f0ea7ae33d23f3158448b05c28ea6636382eee9436c4a6c09023ead7182ecd55ac73a68d458d726e1abc208810468591e63f4b4c2c1f3ce27c4800b52f7421ccab432c03e88b3b255740d719e40e0226eabb7633d97ed210e32071e2ac36ed17ef442e a = 3 primes = [a] for i in range (53 ): a = nextprime(a) primes.append(a) print (primes)d0 = crt(hints,primes) def small_roots (f, bounds, m=1 , d=None ): if not d: d = f.degree() R = f.base_ring() N = R.cardinality() f /= f.coefficients().pop(0 ) f = f.change_ring(ZZ) G = Sequence ([], f.parent()) for i in range (m + 1 ): base = N ^ (m - i) * f ^ i for shifts in itertools.product(range (d), repeat=f.nvariables()): g = base * prod(map (power, f.variables(), shifts)) G.append(g) B, monomials = G.coefficients_monomials() monomials = vector(monomials) factors = [monomial(*bounds) for monomial in monomials] for i, factor in enumerate (factors): B.rescale_col(i, factor) B = B.dense_matrix().LLL() B = B.change_ring(QQ) for i, factor in enumerate (factors): B.rescale_col(i, 1 / factor) H = Sequence ([], f.parent().change_ring(QQ)) for h in filter (None , B * monomials): H.append(h) I = H.ideal() if I.dimension() == -1 : H.pop() elif I.dimension() == 0 : roots = [] for root in I.variety(ring=ZZ): root = tuple (R(root[var]) for var in f.variables()) roots.append(root) return roots return [] M = prod(primes) PR.<k,s> = Zmod(e*M)[] f = 1 + k*n - k*s +k - e*d0 k,s = small_roots(f,(2 **680 ,2 **1024 ))[0 ] x, y = var('x y' ) eq1 = x * y - n eq2 = x + y - int (s) p = solve((eq1, eq2), (x, y))[0 ][0 ].rhs() d = inverse(e,p-1 ) m = pow (ct,d,p) print (b'NepCTF{' +long_to_bytes(m)+b'}' )
Transition
题面:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 from Crypto.Util.number import *from Crypto.Cipher import AESfrom hashlib import sha256import socketserverimport signalimport osimport stringimport randomfrom Crypto.PublicKey import RSAfrom Crypto.Util.number import getPrime,getStrongPrimefrom Crypto.Random import get_random_bytesfrom sample import sample_clock,sample_serialdataimport numpy as npfrom scipy.io import savemat,loadmatfrom random import randintFLAG=os.getenv("FLAG" ,"FAKE{" +6 *"0123456789" ).encode() class Task (socketserver.BaseRequestHandler): def _recvall (self ): BUFF_SIZE = 1024 data = b'' while True : part = self .request.recv(BUFF_SIZE) data += part if len (part) < BUFF_SIZE: break return data.strip() def send (self, msg, newline=True ): try : if newline: msg += b'\n' self .request.sendall(msg) except : pass def recv (self, prompt=b'> ' ): self .send(prompt, newline=False ) return self ._recvall() def handle (self ): ''' case of clock generate_clock_signal_with_slack( sampling_rate=500e6, signal_frequency=4e6, num_cycles=100, output_file="clock_signal.mat" ) ''' count=70 l=64 data=[random.randint(0 ,1 ) for i in range (l)] print (data) sample_serialdata( sampling_rate=500e6 , signal_frequency=4e6 , data=data, output_file="serial_data.mat" ) samples = np.array(loadmat("serial_data.mat" )["data" ][0 ]) while count>=0 : self .send(b"function\n1.query\n2.get flag" ) s=self .recv() try : s=int (s) except : exit() if s==1 : self .send(b"ur sample position?" ) s=self .recv() try : s=int (s) except : exit() if 0 <=s<len (samples): self .send(str (samples[s]).encode()) else : self .send(b"wrong position\n" ) elif s==2 : for i in range (l): self .send(b"ur guess data[%d]?" %i) s=self .recv() try : s=int (s) except : exit() if s!=data[i]: self .send(b"wrong" ) return self .send(b"congratulations! your part flag is:" ) self .send(FLAG[:count]) count=count-1 class ThreadedServer (socketserver.ThreadingMixIn, socketserver.TCPServer): pass class ForkedServer (socketserver.ForkingMixIn, socketserver.TCPServer): pass if __name__ == "__main__" : HOST, PORT = '0.0.0.0' , 9999 print ("HOST:POST " + HOST+":" + str (PORT)) server = ForkedServer((HOST, PORT), Task) server.allow_reuse_address = True server.serve_forever()
分析:
拿到.mat文件里可以看到
仔细看看就可以得到大概的上界和下界的抖动范围,想到了数电知识,大概分为四种情况:
这四种情况都可以同时确定2bit,这样就可以得到完整flag了。
通过输出连续的采样点大概可以知道这四种情况的边界以及采样点的起始点的选择,间隔是500/4=125
题解:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 from pwn import *from tqdm import *HOST = 'nepctf32-laom-rzt9-vzzd-hp7726mff271.nepctf.com' PORT = 443 io = remote(HOST, PORT,ssl=True ) l = 64 bits = [] for count in range (l//2 ): position = 127 + 250 *count io.recvuntil(b'function' ) io.sendafter(b">" , b"1\n" ) io.recvuntil(b'ur sample position?' ) io.sendafter(b">" , (str (position) + "\n" ).encode()) value = eval (io.recvline().decode().strip()) print (value) if value < 0.5 : bits.extend([0 ,0 ]) elif 0.5 < value < 2 : bits.extend([0 ,1 ]) elif 2 < value < 3 : bits.extend([1 ,0 ]) elif 3 < value: bits.extend([1 ,1 ]) print (bits)io.recvuntil(b'function' ) io.sendafter(b">" , b"2\n" ) for i in trange(64 ): io.recvuntil(b">" ) if bits[i] == 0 : io.sendline(b"0\n" ) else : io.sendline(b"1\n" ) io.recvuntil(b"congratulations! your part flag is:\n" ) print (io.recvline())