# RSA ATTACK ## Introduction Mật mã RSA dựa trên số học modulo đơn giản. RSA có 2 key, Public Key là (n, e) và Private Key là (n, d). Trong đó: - n = p * q với p và q là các số nguyên tố - Để chọn được e thích hợp thì trước hết ta phải tính φ(n) = (p-1) * (q-1) - e là một số sao cho 1 < e < φ(n) và gcd(e, φ(n)) = 1, hay e và φ(n) sẽ không có ước số chung nào ngoài 1. - d = inverse(e, φ(n)), hay d * e = k * φ(n) + 1 Giờ ta sẽ 1 plaintext m, để mã hóa m, ta sẽ tính toán c = pow(m, e, n), ta sẽ có được ciphertext. Và để giải mã ciphertext c, ta sẽ phải có Private Key (n, d), m = pow(c, d, n), ta sẽ thu được plaintext cần tìm. Thế nếu ta không có Private Key thì tìm plaintext làm sao nhỉ ??? Bây giờ ta sẽ tìm hiểu RSA Attack để tìm hiểu thêm về một số trường hợp có thể tấn công được loại mã hóa này. ## Factorizing the public key Để có được Private Key để giải mã, ta cần phải có d, và để có d thì ta phải dựa vào cách tính d khi có φ(n) (d * e = k * φ(n) + 1), và để có φ(n) thì ta cần phải biết được p và q. Nếu số n quá nhỏ (ít hơn 256 bits), ta sẽ factor được số n này. Thế nên nếu muốn tránh loại tấn công này, khuyến cáo nên dùng số n có 2048 bits trở nên. ## Common Modulus ### As an External Attacker Bây giờ, Alice có 1 plaintext muốn gửi cho Bob và Cock, Bob và Cock sử dụng 2 Public Key khác nhau là (N, eb) và (N, ec). Eva là người tấn công, sẽ lấy được 2 ciphertext là Cb và Cc từ Eva khi gửi cho Bob và Cock. Thế giờ làm sao Eva có thể tìm lại được plaintext m của Alice. Ta sẽ tính toán gcd(eb, ec), thường thì sẽ là 1. Sử dụng Extended Euclidean Algorithm, ta sẽ tìm được u và v thỏa mãn ``eb*u + ec*v = 1``. ``` Cb = m^eb mod n và Cc = m^ec mod n Cb^u = m^(u * eb) mod n và Cc^v = m^(v * ec) mod n ---> Cb^u * Cc^v = m^(u * eb) * m^(v * ec) mod n = m^(u * eb + v * ec) mod n = m^1 mod n = m ``` Ví dụ: Ta có 1 message ``m = 600167100838453472459358035297984386711465500705 `` Ta có 2 khóa Public Key như sau ``` n = 19085995833312192524007220630153244389942263922006889142154298425751808612835625879164268530070480609 eb = 31 ec = 71 ``` Từ đó ta sẽ thu được 2 ciphertext đó là ``` Cb = 7248000011746393608920764478555324889797098623177063575022710990560749986650254745111540462514063633 Cc = 11182172287733531215626356979925885819483390539286301041874693701292216053376480402307816910743778627 ``` Ta sử dụng Extended Euclidean Algorithm sẽ tìm được 2 số u và v thỏa mãn ``eb * u + ec * v = 1``, ta tìm được ``u = -16`` và ``v = 7`` Ta thấy rằng u = -16 < 0 thế nên để có thể tính được Cb^u thì ta cần phải inverse(Cb,n) vì ``Cb^u mod n = Cb^[(-1)*(-u)] mod n = inverse(Cb,n)^(-u) mod n`` Giờ ta sẽ tính được ``m = (pow(inverse(Cb,n),-u,n)*pow(Cc,v,n)) mod n`` Giờ ta sẽ sử dụng python để giải quyết ``` from Crypto.Util.number import* m = 600167100838453472459358035297984386711465500705 n = 19085995833312192524007220630153244389942263922006889142154298425751808612835625879164268530070480609 eb = 31 ec = 71 cb = 7248000011746393608920764478555324889797098623177063575022710990560749986650254745111540462514063633 cc = 11182172287733531215626356979925885819483390539286301041874693701292216053376480402307816910743778627 u = -16 v = 7 print(long_to_bytes((pow(inverse(cb,n),-u,n)*pow(cc,v,n))%n)) ``` Message m = b'i love you so much !' ### Internal Attacker Ta biết được khóa d phải dựa vào khóa e và φ(n). Trong trường hợp, bạn là nhân viên và được cấp bộ khóa (n,e,d) và có 1 ông sếp có bộ khóa (n, e_boss, d_boss). Chắc chắn là bạn sẽ biết được Public Key của boss rồi, giờ ta cần phải tìm được khóa d_boss của ổng. d_boss của ổng cũng sẽ dùng tới φ(n), thế nhưng ta không thể factor(n) được. Giờ ta sẽ tìm cách tấn công để tìm được khóa d_boss của ông ta. Ta biết rằng ``k * φ(n) + 1 = d * e`` và ``k_boss * φ(n) + 1 = d_boss * e_boss``. Giờ ta sẽ cần tìm được k để tìm lại được φ(n) là bao nhiêu. Ta thấy rằng ``k = (e*d - 1) / φ(n) > (e*d - 1) / n`` thế nên ta sẽ tính ``k = (e*d - 1) / n`` rồi cộng dần k lên rồi tính `` φ(n) = (e*d - 1)/k``. Nếu ``k*φ(n) == (e*d - 1)`` thì lúc đó ta sẽ tìm được φ(n). Ví dụ như sau: ``` n = 13730129614804772880752289462783304761515939015020093221561636131237284843615057420238782770609522972779897464559870880621656031384890245461063527336575063520212170053984927001375709685496305697549444323762540974379841032836802437567934985405376229707618616476496280656706347867221251077231798652699138960795337971736737385768613807271829586487846817128045489170879950650371704605916087898243798748194979988358792610116890529133995964289953754892229284804853347124940125757859317160542053724461336937965356864205941364383726201201464704273080060575612938942759191475849545841070408774846327055098443851402443926907223 e = 65537 d = 12023526690621491987720747738813138281705925752033479867535039126109510878738907188380975357249357354331310487031074808272540111650063873495214869422972969017326039513072751965087711591319689340190429210752033035699428669538381810190600069386757819234172147309193250273418588132622714200732565672475949221035317015770404010091933464449805215046383253499225741965995314055140196259216185504885880375969486528172708580544458731168400724624074591153725513402527961730332556204152427575808471576393264405293657884074056992607573024433121976361451906626453906345264882561858456878706598474749674277815211319553881331985297 e_boss = 65537 ``` Ta sẽ sử dụng code như sau: ``` from Crypto.Util.number import* n = 1249110767794010895540410194153 e = 65537 d = 205119704640110252892051812353 e_boss = 3 k = (e*d - 1)//n while True: phi = (e*d - 1)//k if phi*k == e*d - 1: print("Found k:",k) print("Found φ(n):", phi) break else: k +=1 ``` Ta có challenge thực chiến như sau ``` from Crypto.Util.number import* p, q = getPrime(128), getPrime(128) phi = (p-1)*(q-1) print("p =", phi) n = p*q m = 600167100838453472459358035297984386711465500705 e = 65537 c = pow(m,e,n) print("c =", c) print("n =", n) while True: print("1. Nhap e cua ban") print("2. Nhap khoa de gia ma") inp = int(input()) if inp == 1: ur_e = int(input("Nhap e: ")) print("Your private key: ") print(inverse(ur_e,phi)) if inp == 2: ur_d = int(input("Nhap d: ")) print("ur_p =", pow(c,ur_d,n)) ``` Ta sẽ chọn e = 7, ta sẽ thu được khóa d với e = 7, từ đó ta có thể tìm được khóa d với e = 65537 vì m được mã hóa dựa vào e = 65537. ![image](https://hackmd.io/_uploads/HJbXw89dp.png) Từ đó ta tìm được ``φ(n) == 85639416467718659540681409319173517244166085303068104368992831187016124610080``, từ đó ta tìm được khóa d với e = 65537. Nhập khóa d vào mục 2, ta sẽ thu được m cần tìm. ![image](https://hackmd.io/_uploads/SJ9vD8cdT.png) ## Decipher oracle Cách tấn công này hay còn gọi là Blinding attack. Ví dụ như sau: Bạn có 1 tin nhắn m là "Send me $2 dollars" và nếu muốn đưa cho kế toán thì phải có chữ ký của boss, thế nhưng boss không ngu mà ký vào, thế nên ta cần phải lấy m' để đánh lừa boss ngu này, bằng cách như sau Ta có ``c = pow(m,e,n)``, ta sẽ gửi ``c' = (pow(r, e, n)*c) % n ``, khi đó boss sẽ ký vào bằng khóa d của mình ``S = pow(c', d, n) = r*pow(c,d,n)``, khi đó ta chỉ cần lấy S//r là sẽ thu được m. Đọc hơi khó hiểu đúng không, thử ví dụ này nha ``` from Crypto.Util.number import getStrongPrime, bytes_to_long, long_to_bytes f = open("flag.txt").read() m = bytes_to_long(f.encode()) p = getStrongPrime(512) q = getStrongPrime(512) n = p*q e = 65537 c = pow(m,e,n) print("n =",n) print("e =",e) print("c =",c) d = pow(e, -1, (p-1)*(q-1)) c = int(input("Text to decrypt: ")) if c == m or b"KCSC{" in long_to_bytes(pow(c, d, n)): print("No flag for you!") exit(1) print("m =", pow(c, d, n)) ``` Ta sẽ được mã hóa của flag, public key. Giờ ta sẽ nhập 1 số để decrypt mà khi giải mã không được có ký tự của flag trong đó. Nghe có vẻ no hope, nhưng giờ ta chỉ cần gửi ``pow(r,e,n)*c`` thì sẽ thu được m' = m*r, sau đó sẽ thu được flag thui. Mình sẽ lấy r bằng 2, sau đó lấy các khóa rùi làm như trên thui. ![image](https://hackmd.io/_uploads/BJQcuvcO6.png) Giờ nhập số đó vào để decrypt rồi lấy ra chia cho 2. ![image](https://hackmd.io/_uploads/BJEauPqO6.png) Ta thu được flag rùi :heartpulse: ## Small public exponent Nếu bạn có 1 quả n rất mạnh và lớn, thế nhưng chỉ lấy e = 3 thì cũng coi như là không mã hóa =))) Ta có 1 plaintext m, so với n thì m rất nhỏ và m^3 cũng nhỏ hơn n, thế nên ``pow(m,3,n)`` cũng chính bằng m^3. Thế nên ta chỉ cần căn bậc 3 là sẽ thu được m thui. ## Hastad’s Broadcast Attack Loại tấn công này cũng khá giống Small public exponent, thế nhưng message đã lớn hơn và gửi cho nhiều người với cùng 1 e. Giả sử với e = 5, M = m^5, ta có được rằng ``` M ≡ C1[N1] M ≡ C2[N2] M ≡ C3[N3] ``` Giờ ta sẽ dùng [Thặng dư Trung Hoa](https://github.com/trananhnhatviet/CryptoHack/blob/main/Mathematics/04_Chinese_Remainder_Theorem.md) để tấn công nhaaa. ``` from gmpy2 import iroot from Crypto.Util.number import* e = 5 n1 = 95118357989037539883272168746004652872958890562445814301889866663072352421703264985997800660075311645555799745426868343365321502734736006248007902409628540578635925559742217480797487130202747020211452620743021097565113059392504472785227154824117231077844444672393221838192941390309312484066647007469668558141 n2 = 98364165919251246243846667323542318022804234833677924161175733253689581393607346667895298253718184273532268982060905629399628154981918712070241451494491161470827737146176316011843738943427121602324208773653180782732999422869439588198318422451697920640563880777385577064913983202033744281727004289781821019463 n3 = 68827940939353189613090392226898155021742772897822438483545021944215812146809318686510375724064888705296373853398955093076663323001380047857809774866390083434272781362447147441422207967577323769812896038816586757242130224524828935043187315579523412439309138816335569845470021720847405857361000537204746060031 c1 = 44643885876928229894339687791147483889081873683080948952473220411937240291600604967009102215608200081775074249286001453402825883292279665567678123214807611531941351394608697836776234093223777794346332632216833837995399934797886939033689918289531745909283564566484245521666625007898978941078860669318735325434 c2 = 44143117410059647762095913003262912479704737449712953561967268794930036228463178516548026385008346137693280641753063385660830541131352911139821249120800415441910930574199714264780097158205338261528246884217457601137849376649972995621085773416683024133527965327866632045455956019650455063878227433961529064296 c3 = 43758298724220182639918572113571637071082391732944709390663779379112331840911652803134604996621805027217904388017247167682937227278450493841176029741223145586984980660890778932371017310827918656251333087908084340461067839975909450067417000566279146533723691038158029229130115336369769056411536254398779305744 N = n1*n2*n3 N1 = n2*n3 N2 = n1*n3 N3 = n1*n2 u1 = inverse(N1,n1) u2 = inverse(N2,n2) u3 = inverse(N3,n3) x = (N1*c1*u1 + N2*c2*u2+ N3*c3*u3) % N print(long_to_bytes(iroot(x,5)[0])) ``` ## Fermat’s attack Nếu p và q đều có độ dài bit như nhau, và nếu chọn quá gần nhau, thì sẽ bị tấn công Fermat. Thực chất, nếu p - q < n^(1/4) thì có thể sử dụng thuật toán Fermat Factoring để tìm lại được p và q. <math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> <mi>n</mi> <mo>=</mo> <mi>p</mi> <mi>q</mi> <mo>=</mo> <mo stretchy="false">(</mo> <mo stretchy="false">(</mo> <mi>p</mi> <mo>&#x2212;</mo> <mi>q</mi> <mo stretchy="false">)</mo> <mrow data-mjx-texclass="ORD"> <mo>/</mo> </mrow> <mn>2</mn> <msup> <mo stretchy="false">)</mo> <mn>2</mn> </msup> <mo>&#x2212;</mo> <mo stretchy="false">(</mo> <mo stretchy="false">(</mo> <mi>p</mi> <mo>+</mo> <mi>q</mi> <mo stretchy="false">)</mo> <mrow data-mjx-texclass="ORD"> <mo>/</mo> </mrow> <mn>2</mn> <msup> <mo stretchy="false">)</mo> <mn>2</mn> </msup> <mo>=</mo> <msup> <mi>x</mi> <mn>2</mn> </msup> <mo>&#x2212;</mo> <msup> <mi>y</mi> <mn>2</mn> </msup> <mo>=</mo> <mo stretchy="false">(</mo> <mi>x</mi> <mo>&#x2212;</mo> <mi>y</mi> <mo stretchy="false">)</mo> <mo stretchy="false">(</mo> <mi>x</mi> <mo>+</mo> <mi>y</mi> <mo stretchy="false">)</mo> </math> Giờ để tìm lại được p và q thì ta có thể sử dụng thuật toán [Fermat Factoring](https://en.wikipedia.org/wiki/Fermat's_factorization_method) này. Ngoài ra khi ta gặp những challenge RSA dạng tấn công này, ta có thể sử dụng tools factordb.com (có lúc hong được nha) hoặc có thể sử dụng các tools RSA trên github. ## Wiener’s attack Bạn tham khảo trước tại [đây](https://bitsdeep.com/posts/attacking-rsa-for-fun-and-ctf-points-part-2/) Điều kiện để có thể sử dụng cách tấn công này là: ![image](https://hackmd.io/_uploads/SJRrXjzYT.png) Chứng minh: ![image](https://hackmd.io/_uploads/Hysv7iMta.png) - Từ phương trình trên, ta có thể thấy được rằng ta không có φ(n), thế nên ta có thể thay φ(n) thành n để tính xấp xỉ của nó. ---> ``|e/n - k/d|`` - Ta có ``φ(n) = n - p - q + 1`` ---> ``|n - φ(n)| < p + q`` ``C4`` ![image](https://hackmd.io/_uploads/B19amoMYT.png) - Từ ``C4``, ta có ``|n - φ(n)| < 3√n`` ``C5`` ![image](https://hackmd.io/_uploads/SJNgVsMtp.png) ![image](https://hackmd.io/_uploads/B1GZfic_p.png) ![image](https://hackmd.io/_uploads/HJGQEofKa.png) - Từ đó ta được ![image](https://hackmd.io/_uploads/ByBLNjqd6.png) - Và ta sẽ được ![image](https://hackmd.io/_uploads/Byp1ri9da.png) - Bây giờ ta sử dụng [Phân số liên tục](https://en.wikipedia.org/wiki/Continued_fraction), khai triển e/n để có các cặp k/d. - Giờ có các k và d rồi, ta có thể tính được φ(n). Thử lại và cho tới khi nào đúng thì thui nhaaaa. ## Brute force attack on small secret CRT-Exponents Ta có được rằng: ``dp = inverse(e, p-1)`` và ``dq = inverse(e, q-1)``. Nếu ta chọn khóa e không chuẩn, thì dp và dq có thể nhỏ đến mức có thể tấn công bruteforce. Ta dùng dp đi haa, ta có: ``dp * e = 1 + k*(p-1)``, ta sẽ lấy 1 số r random sao cho ``gcd(r, p) = 1``. Ta thấy được rằng ``pow(r, e * dp, p) = pow(r, 1 + k * (p-1), p)`` tương đương với ``(r*pow(r, (p-1)^k, p))%p`` và theo Fermat’s little theorem thì ta sẽ thu được ``(r*pow(1, k, p))%p`` và bằng ``r % p``. Thế nên, ta sẽ có được ``r - r^(e * dp) = k*p``, tương đương với ``(r%p - r%p)%p = 0%p``. Và tất nhiên rồi, ``gcd(n, r - r^(e * dp)) == p`` nếu output là số nguyên tố. Ta có ví dụ như sau ``` import gmpy2 n = 81456722852928757238261542127151102587173197229707943752219811543156144375350699863083294551705799070878004194003070546338822440089905539549140825586980363561718569628995744214852601849629950615868613439865290355111413520845496729201667637139574520862615047519950667612227571310144108603794433291057865431359 e = 2446203909239906238862878052950988270795071430744877628420149012221572250593484910179302246981591737101667586509709578361904594855170615249056999067468389 # lấy đại r nha r = 7516789928765 for dp in range(1000000): f = gmpy2.gcd(r - pow(r, e*dp, n), n) if f > 1: print(dp,f) break ``` Mình chạy bằng sage cho nhanh và có kết quả như sau ![image](https://hackmd.io/_uploads/ryvFVzsup.png) Check lại thui nàoo ![image](https://hackmd.io/_uploads/SypnEGoO6.png) ## Fault attack on signatures Đây sẽ là cách tấn công RSA CRT signing. Ta thấy rằng, plaintext m, thay vì sign với n lun, thì đây sẽ sign với từng số nguyên tố, sau đó dùng CRT để đưa ra signature s cuối cùng với n. Các khóa lần lượt là: ``dp = inverse(e,p)`` và ``dq = inverse(e,q)``. Tiếp theo đó ta sẽ sign từng khóa với message ``sq = pow(m, dq, q)`` và ``sp = pow(m, dp, p)``. Sau đó dùng CRT sẽ thu được signature cuối cùng. ![image](https://hackmd.io/_uploads/H1778E3d6.png) Quá trình xác minh nếu như không có bất kỳ lỗi gì trong quá trình tính toán chữ ký qua sp và sq thì sẽ như sau: ![image](https://hackmd.io/_uploads/rJOoZBn_p.png) Điều này có nghĩa là ``s^e - m`` sẽ là bội của cả p và q, tương đương ``gcd(pow(s, e, n) - m, n) == n`` Thế nhưng, khi chỉ cần 1 phần sign với p hoặc q bị lỗi 1 bit hoặc là nhiều bits, ta sẽ thấy ngay 1 điều nguy hiểm ![image](https://hackmd.io/_uploads/S1U3-Hhda.png) Nếu ta bị lỗi với sp thì``s^e - m``sẽ chỉ chia hết cho q và ngược lại, thế nên chỉ cần``gcd(pow(s, e, n) - m, n)``ta sẽ thu được 1 trong 2 số nguyên tố. Ta có ví dụ như sau ``` from Crypto.Util.number import* from functools import reduce import gmpy2 with open("flag.txt", "rb") as file: flag = file.read() print(flag) p, q = getPrime(1024), getPrime(1024) e = 65537 n = p*q d = inverse(e,(p-1)*(q-1)) c = pow(bytes_to_long(flag), e, n) print("n =",n) print("e =",e) print("c =",c) def chinese_remainder(rests, modulus): somme = 0 prod = reduce(lambda a, b: a * b, modulus) for n_i, a_i in zip(modulus, rests): p = prod // n_i somme += a_i * gmpy2.invert(p, n_i) * p return int(somme % prod) def sign(m, fault = False): dp = inverse(e,p-1) dq = inverse(e,q-1) sp = pow(m, dp, p) sq = pow(m, dq, q) if fault: sq += 1 s = chinese_remainder([sp, sq], [p, q]) return s while True: m = int(input("Nhap m = ")) print(sign(m,True)) ``` Ta chạy code thì thu được n và e (không hề biết p và q), ta sẽ lấy 1 message ngẫu nhiên và để mode fault là True, ta sẽ thu được 1 sign, giờ ta sẽ lấy sign đó mũ e và trừ đi cho m, lấy ước chung lớn nhất, ta sẽ thu được số nguyên tố. ![image](https://hackmd.io/_uploads/rJZd8KBnda.png) ## Parity Oracle Giờ ta có 1 challenge như thế này: ``` from Crypto.Util.number import * from os import urandom flag = (395666745139103690621251672998591647401211535741) p,q = getPrime(512),getPrime(512) n = p*q e = 0x10001 d = inverse(e,(p-1)*(q-1)) assert flag < n ct = pow(flag, e, n) print(f"{n = }") print(f"{e = }") print(f"{ct = }") while True: ct = int(input("> ")) pt = pow(ct, d, n) out = "" if pt == flag: exit() else: if pt % 2 == 0: print("Even") else: print("Odd") ``` Như Blinding Attack, ta sẽ gửi 1 ciphertext vào, thế nhưng đầu ra không phải là ký với Private Key mà chỉ có là bit 1 và 0 thì ta làm như thế nào ??? Giờ ta tìm hiểu các tấn công LSB. Ta biết rằng, N luôn luôn là số lẻ vì được nhân bởi 2 số nguyên tố. Và 1 số a chẵn mod cho số lẻ b mà ra số lẻ thì a < b. Tương đương, nếu mod mà ra chẵn thì ``a > b``. Tương đương như thế, ta sẽ gửi ct sao cho khi giải mã bằng khóa d sẽ bằng 2p. Khi đó, ta có 2 trường hợp: - 2p < n thì modulo sẽ không hoạt động và sẽ trả về Even - 2p > n thì modulo sẽ trả về là Odd Ta biết rằng, khi p sẽ ∈ [0,n]. Thế nên, nếu ``2p > n`` --> ``p > n/2`` --> ``p ∈ [n/2, n]``. Còn nếu ``2p < n`` --> ``p ∈ [0, n/2]``. Thế ta sẽ gửi dần 2, 4, 8, 16, .... nha Ta sẽ bo dần độ rộng của p dựa vào đường [link](https://crypto.stackexchange.com/questions/11053/rsa-least-significant-bit-oracle-attack) này. Dựa vào đó, ta có source sau đây ``` from pwn import* from binascii import* from Crypto.Util.number import* import base64 from Crypto.Util.number import* def get_process(): return remote("localhost", 1337) def oracle(x): io.sendlineafter(b'> ', str(pow(x, e, n) * ct % n).encode()) return io.recvline() io = get_process() e = 65537 io.recvuntil(b'n = ') n = int(io.recvuntil(b"\n").decode()) io.recvuntil(b'ct = ') ct = int(io.recvuntil(b"\n").decode()) lb = 0 ub = n k = 1 while True: k *= 2 if oracle(k) == b"Even\n": ub = ((ub + lb )//2) else : lb = ((ub+lb)//2) if (ub - lb) < 65537: print(int(ub),int(lb)) break ``` ![image](https://hackmd.io/_uploads/BJGnqn1Kp.png) Ta thu được 2 số ``395666745139103690621251672998591647401211504165`` và ``395666745139103690621251672998591647401211539815`` khá gần với flag là ``395666745139103690621251672998591647401211535741``. Thế nên ta chỉ cần bruteforce trong khoảng đó là sẽ thu được flag nha. ***Tham khảo*** https://bitsdeep.com/posts/attacking-rsa-for-fun-and-ctf-points-part-1/ https://bitsdeep.com/posts/attacking-rsa-for-fun-and-ctf-points-part-2/ https://bitsdeep.com/posts/attacking-rsa-for-fun-and-ctf-points-part-3/ https://bitsdeep.com/posts/attacking-rsa-for-fun-and-ctf-points-part-4/ # RSA CHALLENGE CRYPTOHACK ## STARTER ### RSA Starter 1 Chall này chỉ cần sử dụng sage, hoặc là dùng code python để lấy được đáp án thôi. ![image](https://hackmd.io/_uploads/B100SW6_6.png) > 19906 ### RSA Starter 2 ![image](https://hackmd.io/_uploads/B14NL-pdp.png) Chall này cho ta hiểu được cách mã hóa của rsa thui. ![image](https://hackmd.io/_uploads/BymHUbTO6.png) > 301 ### RSA Starter 3 ![image](https://hackmd.io/_uploads/SJGIU-6u6.png) Chall này nói về cách tính Φ(n) trong mã hóa rsa. ``Φ(n) = (p-1)*(q-1)`` ![image](https://hackmd.io/_uploads/r1DeDZpu6.png) > 882564595536224140639625987657529300394956519977044270821168 ### RSA Starter 4 ![image](https://hackmd.io/_uploads/BJrUvWad6.png) Chall hướng dẫn ta cách tính khóa d khi đã có phi và e. ![image](https://hackmd.io/_uploads/rkCcvWTuT.png) > 121832886702415731577073962957377780195510499965398469843281 ### RSA Starter 5 ![image](https://hackmd.io/_uploads/rk_g_bpdT.png) Chall này sẽ chỉ cho ta cách giải mã khi đã có Private Key và Public Key. Giờ ta chỉ cần ``pow(c,d,p*q)`` là sẽ giải mã được ciphertext. ![image](https://hackmd.io/_uploads/BkvDubpup.png) > 13371337 ### RSA Starter 6 Chall này sẽ chỉ cho ta cách sign message với private key thui. Ngoài ra sẽ phải sha256 flag trước khi signing. ``` from hashlib import sha256 from Crypto.Util.number import* N = 15216583654836731327639981224133918855895948374072384050848479908982286890731769486609085918857664046075375253168955058743185664390273058074450390236774324903305663479046566232967297765731625328029814055635316002591227570271271445226094919864475407884459980489638001092788574811554149774028950310695112688723853763743238753349782508121985338746755237819373178699343135091783992299561827389745132880022259873387524273298850340648779897909381979714026837172003953221052431217940632552930880000919436507245150726543040714721553361063311954285289857582079880295199632757829525723874753306371990452491305564061051059885803 d = 11175901210643014262548222473449533091378848269490518850474399681690547281665059317155831692300453197335735728459259392366823302405685389586883670043744683993709123180805154631088513521456979317628012721881537154107239389466063136007337120599915456659758559300673444689263854921332185562706707573660658164991098457874495054854491474065039621922972671588299315846306069845169959451250821044417886630346229021305410340100401530146135418806544340908355106582089082980533651095594192031411679866134256418292249592135441145384466261279428795408721990564658703903787956958168449841491667690491585550160457893350536334242689 flag = b'crypto{Immut4ble_m3ssag1ng}' flag = bytes_to_long(sha256(flag).digest()) c = pow(flag,d,N) print(c) ``` > 13480738404590090803339831649238454376183189744970683129909766078877706583282422686710545217275797376709672358894231550335007974983458408620258478729775647818876610072903021235573923300070103666940534047644900475773318682585772698155617451477448441198150710420818995347235921111812068656782998168064960965451719491072569057636701190429760047193261886092862024118487826452766513533860734724124228305158914225250488399673645732882077575252662461860972889771112594906884441454355959482925283992539925713424132009768721389828848907099772040836383856524605008942907083490383109757406940540866978237471686296661685839083475 ## PRIMES PART 1 ### Factoring ![image](https://hackmd.io/_uploads/rkWhqZaO6.png) Chall này phải phân tích 1 số thành các nhân tử nguyên tố. Đề cho ta 1 con số rất lớn là ``510143758735509025530880200653196460532653147`` Để giải được bài này, ta lên website http://factordb.com/, đây là 1 trang web giúp ta phân tích số thành các nhân tử nguyên tố Nhập số vào và ta được 2 số là ``19704762736204164635843`` và ``25889363174021185185929`` Và số nhỏ hơn là ``19704762736204164635843`` > 19704762736204164635843 ### Inferius Prime ![image](https://hackmd.io/_uploads/HyBzoWT_p.png) Chall này khá giống với RSA Starter 5, chỉ decode plaintext thành bytes Chall cho ta 1 file output.txt gồm: ``` n = 742449129124467073921545687640895127535705902454369756401331 e = 3 ct = 39207274348578481322317340648475596807303160111338236677373 ``` Từ n, ta factor được ``` p = 752708788837165590355094155871 q = 986369682585281993933185289261 ``` Tiếp theo ta tính phi ``phi = (p-1)*(q-1)`` Tìm private key ``d = inverse(e,phi)`` ``Plaintext = pow (ct,d,n)`` Khi ta đã có được plaintext là 1 số nguyên lớn, ta decode nó sang byte ``decrypted = long_to_bytes(plaintext)`` Decrypted chính là flag cần tìm ``` from Crypto.Util.number import * n = 742449129124467073921545687640895127535705902454369756401331 e = 3 ct = 39207274348578481322317340648475596807303160111338236677373 p = 752708788837165590355094155871 q = 986369682585281993933185289261 phi = (p-1)*(q-1) d = inverse (e,phi) pt = pow(ct, d, n) pt = long_to_bytes(pt).decode() print(pt) ``` > crypto{N33d_b1g_pR1m35} ### Monoprime ![image](https://hackmd.io/_uploads/S1nKoZ6uT.png) Chall này cho ta hiểu vì sao phải dùng 2 số nguyên tố p và q trong mật mã RSA Ta có ``c = pow(p, e, n)``. Mà n lại chính là 1 số nguyên tố --> ``Φ(n) = (n-1)`` Đã có phi rồi thì ta chỉ cần tìm lại khóa d rồi lụm flag thui ``` from Crypto.Util.number import * n = 171731371218065444125482536302245915415603318380280392385291836472299752747934607246477508507827284075763910264995326010251268493630501989810855418416643352631102434317900028697993224868629935657273062472544675693365930943308086634291936846505861203914449338007760990051788980485462592823446469606824421932591 e = 65537 ct = 161367550346730604451454756189028938964941280347662098798775466019463375610700074840105776873791605070092554650190486030367121011578171525759600774739890458414593857709994072516290998135846956596662071379067305011746842247628316996977338024343628757374524136260758515864509435302781735938531030576289086798942 phi = n-1 d = inverse (e,phi) pt = pow(ct, d, n) pt = long_to_bytes(pt).decode() print(pt) ``` > crypto{0n3_pr1m3_41n7_pr1m3_l0l} ### Square Eyes ![image](https://hackmd.io/_uploads/BJGA3WTOa.png) Chall này cho ta hiểu về cách tấn công rsa khi ``n = p*q`` và ``p = q``. ![image](https://hackmd.io/_uploads/BJCan-Tu6.png) Từ công thức này, ta có ``φ(n) = φ(p^2) = p*(p-1)`` Giờ có φ(n) rồi thì lụm flag thui nào ``` from Crypto.Util.number import * from math import isqrt N = 535860808044009550029177135708168016201451343147313565371014459027743491739422885443084705720731409713775527993719682583669164873806842043288439828071789970694759080842162253955259590552283047728782812946845160334801782088068154453021936721710269050985805054692096738777321796153384024897615594493453068138341203673749514094546000253631902991617197847584519694152122765406982133526594928685232381934742152195861380221224370858128736975959176861651044370378539093990198336298572944512738570839396588590096813217791191895941380464803377602779240663133834952329316862399581950590588006371221334128215409197603236942597674756728212232134056562716399155080108881105952768189193728827484667349378091100068224404684701674782399200373192433062767622841264055426035349769018117299620554803902490432339600566432246795818167460916180647394169157647245603555692735630862148715428791242764799469896924753470539857080767170052783918273180304835318388177089674231640910337743789750979216202573226794240332797892868276309400253925932223895530714169648116569013581643192341931800785254715083294526325980247219218364118877864892068185905587410977152737936310734712276956663192182487672474651103240004173381041237906849437490609652395748868434296753449 e = 65537 c = 222502885974182429500948389840563415291534726891354573907329512556439632810921927905220486727807436668035929302442754225952786602492250448020341217733646472982286222338860566076161977786095675944552232391481278782019346283900959677167026636830252067048759720251671811058647569724495547940966885025629807079171218371644528053562232396674283745310132242492367274184667845174514466834132589971388067076980563188513333661165819462428837210575342101036356974189393390097403614434491507672459254969638032776897417674577487775755539964915035731988499983726435005007850876000232292458554577437739427313453671492956668188219600633325930981748162455965093222648173134777571527681591366164711307355510889316052064146089646772869610726671696699221157985834325663661400034831442431209123478778078255846830522226390964119818784903330200488705212765569163495571851459355520398928214206285080883954881888668509262455490889283862560453598662919522224935145694435885396500780651530829377030371611921181207362217397805303962112100190783763061909945889717878397740711340114311597934724670601992737526668932871436226135393872881664511222789565256059138002651403875484920711316522536260604255269532161594824301047729082877262812899724246757871448545439896 p = isqrt(N) phi = (p)*(p-1) d = inverse(e, phi) plaintext = pow(c, d, N) decrypted = long_to_bytes(plaintext).decode() print(decrypted) ``` > crypto{squar3_r00t_i5_f4st3r_th4n_f4ct0r1ng!} ### Manyprime ![image](https://hackmd.io/_uploads/BypRp-6da.png) Chall này ta factor n thì thu được rất nhiều số nguyên tố. Theo công thức: ``φ(n) = (p1-1)*(p2-1)*...*(pn-1)`` Giờ ta tính φ(n) rồi lụm flag thui ``` from Crypto.Util.number import inverse, long_to_bytes N = 580642391898843192929563856870897799650883152718761762932292482252152591279871421569162037190419036435041797739880389529593674485555792234900969402019055601781662044515999210032698275981631376651117318677368742867687180140048715627160641771118040372573575479330830092989800730105573700557717146251860588802509310534792310748898504394966263819959963273509119791037525504422606634640173277598774814099540555569257179715908642917355365791447508751401889724095964924513196281345665480688029639999472649549163147599540142367575413885729653166517595719991872223011969856259344396899748662101941230745601719730556631637 e = 65537 ciphertext = 320721490534624434149993723527322977960556510750628354856260732098109692581338409999983376131354918370047625150454728718467998870322344980985635149656977787964380651868131740312053755501594999166365821315043312308622388016666802478485476059625888033017198083472976011719998333985531756978678758897472845358167730221506573817798467100023754709109274265835201757369829744113233607359526441007577850111228850004361838028842815813724076511058179239339760639518034583306154826603816927757236549096339501503316601078891287408682099750164720032975016814187899399273719181407940397071512493967454225665490162619270814464 s='9282105380008121879<20> · 9303850685953812323<20> · 9389357739583927789<20> · 10336650220878499841<20> · 10638241655447339831<20> · 11282698189561966721<20> · 11328768673634243077<20> · 11403460639036243901<20> · 11473665579512371723<20> · 11492065299277279799<20> · 11530534813954192171<20> · 11665347949879312361<20> · 12132158321859677597<20> · 12834461276877415051<20> · 12955403765595949597<20> · 12973972336777979701<20> · 13099895578757581201<20> · 13572286589428162097<20> · 14100640260554622013<20> · 14178869592193599187<20> · 14278240802299816541<20> · 14523070016044624039<20> · 14963354250199553339<20> · 15364597561881860737<20> · 15669758663523555763<20> · 15824122791679574573<20> · 15998365463074268941<20> · 16656402470578844539<20> · 16898740504023346457<20> · 17138336856793050757<20> · 17174065872156629921<20> · 17281246625998849649' lst=s.split('<20> · ') factor_list=[] for i in lst: factor_list.append(int(i)) phi = 1 for prime in factor_list: phi = phi * (int(prime) - 1) d = inverse(e, phi) plaintext = pow(ciphertext, d, N) print(long_to_bytes(plaintext).decode()) ``` > crypto{700_m4ny_5m4ll_f4c70r5} ## PUBLIC EXPONENT ### Salty Source code của chall như sau: ``` #!/usr/bin/env python3 from Crypto.Util.number import getPrime, inverse, bytes_to_long, long_to_bytes e = 1 d = -1 while d == -1: p = getPrime(512) q = getPrime(512) phi = (p - 1) * (q - 1) d = inverse(e, phi) n = p * q flag = b"XXXXXXXXXXXXXXXXXXXXXXX" pt = bytes_to_long(flag) ct = pow(pt, e, n) print(f"n = {n}") print(f"e = {e}") print(f"ct = {ct}") pt = pow(ct, d, n) decrypted = long_to_bytes(pt) assert decrypted == flag ``` Ta thấy ``e = 1`` --> Nếu flag < n thì sau khi mã hóa, ct chính là flag do không bị ảnh hưởng. Giờ ta chỉ cần decode ct ra byte là thu được flag thôi ![image](https://hackmd.io/_uploads/HyvNJM6u6.png) > crypto{saltstack_fell_for_this!} ### Modulus Inutilis Source code của chall như sau: ``` #!/usr/bin/env python3 from Crypto.Util.number import getPrime, inverse, bytes_to_long, long_to_bytes e = 3 d = -1 while d == -1: p = getPrime(1024) q = getPrime(1024) phi = (p - 1) * (q - 1) d = inverse(e, phi) n = p * q flag = b"XXXXXXXXXXXXXXXXXXXXXXX" pt = bytes_to_long(flag) ct = pow(pt, e, n) print(f"n = {n}") print(f"e = {e}") print(f"ct = {ct}") pt = pow(ct, d, n) decrypted = long_to_bytes(pt) assert decrypted == flag ``` Chall này thấy được e = 3, và số n rất chi là to lun, thế nên ``flag^3 < n`` thì mã hóa sẽ không ảnh hưởng tới flag. Giờ chỉ cần căn bậc 3 của ct là ta sẽ thu được flag thuiii ![image](https://hackmd.io/_uploads/r1hExG6uT.png) ![image](https://hackmd.io/_uploads/BknSez6uT.png) > crypto{N33d_m04R_p4dd1ng} ### Everything is Big Chall này cho ta quả output rất chi là to lunn ``` N = 0xb8af3d3afb893a602de4afe2a29d7615075d1e570f8bad8ebbe9b5b9076594cf06b6e7b30905b6420e950043380ea746f0a14dae34469aa723e946e484a58bcd92d1039105871ffd63ffe64534b7d7f8d84b4a569723f7a833e6daf5e182d658655f739a4e37bd9f4a44aff6ca0255cda5313c3048f56eed5b21dc8d88bf5a8f8379eac83d8523e484fa6ae8dbcb239e65d3777829a6903d779cd2498b255fcf275e5f49471f35992435ee7cade98c8e82a8beb5ce1749349caa16759afc4e799edb12d299374d748a9e3c82e1cc983cdf9daec0a2739dadcc0982c1e7e492139cbff18c5d44529407edfd8e75743d2f51ce2b58573fea6fbd4fe25154b9964d e = 0x9ab58dbc8049b574c361573955f08ea69f97ecf37400f9626d8f5ac55ca087165ce5e1f459ef6fa5f158cc8e75cb400a7473e89dd38922ead221b33bc33d6d716fb0e4e127b0fc18a197daf856a7062b49fba7a86e3a138956af04f481b7a7d481994aeebc2672e500f3f6d8c581268c2cfad4845158f79c2ef28f242f4fa8f6e573b8723a752d96169c9d885ada59cdeb6dbe932de86a019a7e8fc8aeb07748cfb272bd36d94fe83351252187c2e0bc58bb7a0a0af154b63397e6c68af4314601e29b07caed301b6831cf34caa579eb42a8c8bf69898d04b495174b5d7de0f20cf2b8fc55ed35c6ad157d3e7009f16d6b61786ee40583850e67af13e9d25be3 c = 0x3f984ff5244f1836ed69361f29905ca1ae6b3dcf249133c398d7762f5e277919174694293989144c9d25e940d2f66058b2289c75d1b8d0729f9a7c4564404a5fd4313675f85f31b47156068878e236c5635156b0fa21e24346c2041ae42423078577a1413f41375a4d49296ab17910ae214b45155c4570f95ca874ccae9fa80433a1ab453cbb28d780c2f1f4dc7071c93aff3924d76c5b4068a0371dff82531313f281a8acadaa2bd5078d3ddcefcb981f37ff9b8b14c7d9bf1accffe7857160982a2c7d9ee01d3e82265eec9c7401ecc7f02581fd0d912684f42d1b71df87a1ca51515aab4e58fab4da96e154ea6cdfb573a71d81b2ea4a080a1066e1bc3474 ``` Sau khi thấy output to như này, mình nghĩ ngay tới owiener attack. Đoạn code sẽ như sau ``` import owiener from Crypto.Util.number import long_to_bytes N = int('0xb8af3d3afb893a602de4afe2a29d7615075d1e570f8bad8ebbe9b5b9076594cf06b6e7b30905b6420e950043380ea746f0a14dae34469aa723e946e484a58bcd92d1039105871ffd63ffe64534b7d7f8d84b4a569723f7a833e6daf5e182d658655f739a4e37bd9f4a44aff6ca0255cda5313c3048f56eed5b21dc8d88bf5a8f8379eac83d8523e484fa6ae8dbcb239e65d3777829a6903d779cd2498b255fcf275e5f49471f35992435ee7cade98c8e82a8beb5ce1749349caa16759afc4e799edb12d299374d748a9e3c82e1cc983cdf9daec0a2739dadcc0982c1e7e492139cbff18c5d44529407edfd8e75743d2f51ce2b58573fea6fbd4fe25154b9964d',16) e = int('0x9ab58dbc8049b574c361573955f08ea69f97ecf37400f9626d8f5ac55ca087165ce5e1f459ef6fa5f158cc8e75cb400a7473e89dd38922ead221b33bc33d6d716fb0e4e127b0fc18a197daf856a7062b49fba7a86e3a138956af04f481b7a7d481994aeebc2672e500f3f6d8c581268c2cfad4845158f79c2ef28f242f4fa8f6e573b8723a752d96169c9d885ada59cdeb6dbe932de86a019a7e8fc8aeb07748cfb272bd36d94fe83351252187c2e0bc58bb7a0a0af154b63397e6c68af4314601e29b07caed301b6831cf34caa579eb42a8c8bf69898d04b495174b5d7de0f20cf2b8fc55ed35c6ad157d3e7009f16d6b61786ee40583850e67af13e9d25be3',16) c = int('0x3f984ff5244f1836ed69361f29905ca1ae6b3dcf249133c398d7762f5e277919174694293989144c9d25e940d2f66058b2289c75d1b8d0729f9a7c4564404a5fd4313675f85f31b47156068878e236c5635156b0fa21e24346c2041ae42423078577a1413f41375a4d49296ab17910ae214b45155c4570f95ca874ccae9fa80433a1ab453cbb28d780c2f1f4dc7071c93aff3924d76c5b4068a0371dff82531313f281a8acadaa2bd5078d3ddcefcb981f37ff9b8b14c7d9bf1accffe7857160982a2c7d9ee01d3e82265eec9c7401ecc7f02581fd0d912684f42d1b71df87a1ca51515aab4e58fab4da96e154ea6cdfb573a71d81b2ea4a080a1066e1bc3474',16) d = owiener.attack(e,N) m = pow(c,d,N) print(long_to_bytes(m).decode()) ``` > crypto{s0m3th1ng5_c4n_b3_t00_b1g} ### Crossed Wires ``` from Crypto.Util.number import getPrime, long_to_bytes, bytes_to_long, inverse import math from gmpy2 import next_prime FLAG = b"crypto{????????????????????????????????????????????????}" p = getPrime(1024) q = getPrime(1024) N = p*q phi = (p-1)*(q-1) e = 0x10001 d = inverse(e, phi) my_key = (N, d) friends = 5 friend_keys = [(N, getPrime(17)) for _ in range(friends)] cipher = bytes_to_long(FLAG) for key in friend_keys: cipher = pow(cipher, key[1], key[0]) print(f"My private key: {my_key}") print(f"My Friend's public keys: {friend_keys}") print(f"Encrypted flag: {cipher}") ``` Chall này sẽ mã hóa 6 lần, với 6 lần e khác nhau, thế nên ta cần phải bruteforce φ(n) để có thể tìm lại được khóa d tương ứng. ``` n,d = (21711308225346315542706844618441565741046498277716979943478360598053144971379956916575370343448988601905854572029635846626259487297950305231661109855854947494209135205589258643517961521594924368498672064293208230802441077390193682958095111922082677813175804775628884377724377647428385841831277059274172982280545237765559969228707506857561215268491024097063920337721783673060530181637161577401589126558556182546896783307370517275046522704047385786111489447064794210010802761708615907245523492585896286374996088089317826162798278528296206977900274431829829206103227171839270887476436899494428371323874689055690729986771, 2734411677251148030723138005716109733838866545375527602018255159319631026653190783670493107936401603981429171880504360560494771017246468702902647370954220312452541342858747590576273775107870450853533717116684326976263006435733382045807971890762018747729574021057430331778033982359184838159747331236538501849965329264774927607570410347019418407451937875684373454982306923178403161216817237890962651214718831954215200637651103907209347900857824722653217179548148145687181377220544864521808230122730967452981435355334932104265488075777638608041325256776275200067541533022527964743478554948792578057708522350812154888097) e = 65537 k = (e*d - 1)//n while True: phi = (e*d - 1)//k if phi*k == e*d - 1: print("Found φ(n):", phi) break else: k +=1 ``` ``` φ(n) = 21711308225346315542706844618441565741046498277716979943478360598053144971379956916575370343448988601905854572029635846626259487297950305231661109855854947494209135205589258643517961521594924368498672064293208230802441077390193682958095111922082677813175804775628884377724377647428385841831277059274172982280249307490374900729021320924716697863966277266625488626020771604596923670543560857724741855180400786259195735908618899535592322919617065525626834782656528352786625383923291590279348919015437292702452668873586799771898145386750557481851748649545280807708561842706253359025121644739401403945728547286791397492272 ``` Giờ khóa d sẽ lần lượt là inverse(e,phi), làm ngược lại là được thoiooiii :v: ``` from Crypto.Util.number import* n,d = (21711308225346315542706844618441565741046498277716979943478360598053144971379956916575370343448988601905854572029635846626259487297950305231661109855854947494209135205589258643517961521594924368498672064293208230802441077390193682958095111922082677813175804775628884377724377647428385841831277059274172982280545237765559969228707506857561215268491024097063920337721783673060530181637161577401589126558556182546896783307370517275046522704047385786111489447064794210010802761708615907245523492585896286374996088089317826162798278528296206977900274431829829206103227171839270887476436899494428371323874689055690729986771, 2734411677251148030723138005716109733838866545375527602018255159319631026653190783670493107936401603981429171880504360560494771017246468702902647370954220312452541342858747590576273775107870450853533717116684326976263006435733382045807971890762018747729574021057430331778033982359184838159747331236538501849965329264774927607570410347019418407451937875684373454982306923178403161216817237890962651214718831954215200637651103907209347900857824722653217179548148145687181377220544864521808230122730967452981435355334932104265488075777638608041325256776275200067541533022527964743478554948792578057708522350812154888097) e = 65537 c = 20304610279578186738172766224224793119885071262464464448863461184092225736054747976985179673905441502689126216282897704508745403799054734121583968853999791604281615154100736259131453424385364324630229671185343778172807262640709301838274824603101692485662726226902121105591137437331463201881264245562214012160875177167442010952439360623396658974413900469093836794752270399520074596329058725874834082188697377597949405779039139194196065364426213208345461407030771089787529200057105746584493554722790592530472869581310117300343461207750821737840042745530876391793484035024644475535353227851321505537398888106855012746117 k = (e*d - 1)//n while True: phi = (e*d - 1)//k if phi*k == e*d - 1: print("Found φ(n):", phi) break else: k +=1 friend_keys = [(21711308225346315542706844618441565741046498277716979943478360598053144971379956916575370343448988601905854572029635846626259487297950305231661109855854947494209135205589258643517961521594924368498672064293208230802441077390193682958095111922082677813175804775628884377724377647428385841831277059274172982280545237765559969228707506857561215268491024097063920337721783673060530181637161577401589126558556182546896783307370517275046522704047385786111489447064794210010802761708615907245523492585896286374996088089317826162798278528296206977900274431829829206103227171839270887476436899494428371323874689055690729986771, 106979), (21711308225346315542706844618441565741046498277716979943478360598053144971379956916575370343448988601905854572029635846626259487297950305231661109855854947494209135205589258643517961521594924368498672064293208230802441077390193682958095111922082677813175804775628884377724377647428385841831277059274172982280545237765559969228707506857561215268491024097063920337721783673060530181637161577401589126558556182546896783307370517275046522704047385786111489447064794210010802761708615907245523492585896286374996088089317826162798278528296206977900274431829829206103227171839270887476436899494428371323874689055690729986771, 108533), (21711308225346315542706844618441565741046498277716979943478360598053144971379956916575370343448988601905854572029635846626259487297950305231661109855854947494209135205589258643517961521594924368498672064293208230802441077390193682958095111922082677813175804775628884377724377647428385841831277059274172982280545237765559969228707506857561215268491024097063920337721783673060530181637161577401589126558556182546896783307370517275046522704047385786111489447064794210010802761708615907245523492585896286374996088089317826162798278528296206977900274431829829206103227171839270887476436899494428371323874689055690729986771, 69557), (21711308225346315542706844618441565741046498277716979943478360598053144971379956916575370343448988601905854572029635846626259487297950305231661109855854947494209135205589258643517961521594924368498672064293208230802441077390193682958095111922082677813175804775628884377724377647428385841831277059274172982280545237765559969228707506857561215268491024097063920337721783673060530181637161577401589126558556182546896783307370517275046522704047385786111489447064794210010802761708615907245523492585896286374996088089317826162798278528296206977900274431829829206103227171839270887476436899494428371323874689055690729986771, 97117), (21711308225346315542706844618441565741046498277716979943478360598053144971379956916575370343448988601905854572029635846626259487297950305231661109855854947494209135205589258643517961521594924368498672064293208230802441077390193682958095111922082677813175804775628884377724377647428385841831277059274172982280545237765559969228707506857561215268491024097063920337721783673060530181637161577401589126558556182546896783307370517275046522704047385786111489447064794210010802761708615907245523492585896286374996088089317826162798278528296206977900274431829829206103227171839270887476436899494428371323874689055690729986771, 103231)] for key in friend_keys: d_friend = inverse(key[1], phi) c = pow(c,d_friend,key[0]) print(long_to_bytes(c).decode()) ``` > crypto{3ncrypt_y0ur_s3cr3t_w1th_y0ur_fr1end5_publ1c_k3y} ### Everything is Still Big Source code của chall như sau ``` #!/usr/bin/env python3 from Crypto.Util.number import getPrime, bytes_to_long, inverse from random import getrandbits from math import gcd FLAG = b"crypto{?????????????????????????????????????}" m = bytes_to_long(FLAG) def get_huge_RSA(): p = getPrime(1024) q = getPrime(1024) N = p*q phi = (p-1)*(q-1) while True: d = getrandbits(512) if (3*d)**4 > N and gcd(d,phi) == 1: e = inverse(d, phi) break return N,e N, e = get_huge_RSA() c = pow(m, e, N) print(f'N = {hex(N)}') print(f'e = {hex(e)}') print(f'c = {hex(c)}') ``` Ban đầu mình tưởng là dạng Wiener attack, mình thử thì vẫn được, nhưng sau khi tìm hiểu thì nó là dạng ``Boneh-Durfee Attack`` với ``d < N^0.292``. Bạn cũng có thể tham khảo tại [đây](https://cryptohack.gitbook.io/cryptobook/untitled/low-private-component-attacks/boneh-durfee-attack). Chall này bạn vẫn có thể factor N trên factordb được, hoặc cũng có thể tấn công Wiener được, nhưng mình sợ nếu gặp lại chall này nhưng mà thay số khác thì sẽ không có được, nên bạn nên tham khảo source code sau đây hoặc tại [link](https://raw.githubusercontent.com/mimoo/RSA-and-LLL-attacks/master/boneh_durfee.sage) này: ``` from __future__ import print_function import time ############################################ # Config ########################################## """ Setting debug to true will display more informations about the lattice, the bounds, the vectors... """ debug = True """ Setting strict to true will stop the algorithm (and return (-1, -1)) if we don't have a correct upperbound on the determinant. Note that this doesn't necesseraly mean that no solutions will be found since the theoretical upperbound is usualy far away from actual results. That is why you should probably use `strict = False` """ strict = False """ This is experimental, but has provided remarkable results so far. It tries to reduce the lattice as much as it can while keeping its efficiency. I see no reason not to use this option, but if things don't work, you should try disabling it """ helpful_only = True dimension_min = 7 # stop removing if lattice reaches that dimension ############################################ # Functions ########################################## # display stats on helpful vectors def helpful_vectors(BB, modulus): nothelpful = 0 for ii in range(BB.dimensions()[0]): if BB[ii,ii] >= modulus: nothelpful += 1 print(nothelpful, "/", BB.dimensions()[0], " vectors are not helpful") # display matrix picture with 0 and X def matrix_overview(BB, bound): for ii in range(BB.dimensions()[0]): a = ('%02d ' % ii) for jj in range(BB.dimensions()[1]): a += '0' if BB[ii,jj] == 0 else 'X' if BB.dimensions()[0] < 60: a += ' ' if BB[ii, ii] >= bound: a += '~' print(a) # tries to remove unhelpful vectors # we start at current = n-1 (last vector) def remove_unhelpful(BB, monomials, bound, current): # end of our recursive function if current == -1 or BB.dimensions()[0] <= dimension_min: return BB # we start by checking from the end for ii in range(current, -1, -1): # if it is unhelpful: if BB[ii, ii] >= bound: affected_vectors = 0 affected_vector_index = 0 # let's check if it affects other vectors for jj in range(ii + 1, BB.dimensions()[0]): # if another vector is affected: # we increase the count if BB[jj, ii] != 0: affected_vectors += 1 affected_vector_index = jj # level:0 # if no other vectors end up affected # we remove it if affected_vectors == 0: print("* removing unhelpful vector", ii) BB = BB.delete_columns([ii]) BB = BB.delete_rows([ii]) monomials.pop(ii) BB = remove_unhelpful(BB, monomials, bound, ii-1) return BB # level:1 # if just one was affected we check # if it is affecting someone else elif affected_vectors == 1: affected_deeper = True for kk in range(affected_vector_index + 1, BB.dimensions()[0]): # if it is affecting even one vector # we give up on this one if BB[kk, affected_vector_index] != 0: affected_deeper = False # remove both it if no other vector was affected and # this helpful vector is not helpful enough # compared to our unhelpful one if affected_deeper and abs(bound - BB[affected_vector_index, affected_vector_index]) < abs(bound - BB[ii, ii]): print("* removing unhelpful vectors", ii, "and", affected_vector_index) BB = BB.delete_columns([affected_vector_index, ii]) BB = BB.delete_rows([affected_vector_index, ii]) monomials.pop(affected_vector_index) monomials.pop(ii) BB = remove_unhelpful(BB, monomials, bound, ii-1) return BB # nothing happened return BB """ Returns: * 0,0 if it fails * -1,-1 if `strict=true`, and determinant doesn't bound * x0,y0 the solutions of `pol` """ def boneh_durfee(pol, modulus, mm, tt, XX, YY): """ Boneh and Durfee revisited by Herrmann and May finds a solution if: * d < N^delta * |x| < e^delta * |y| < e^0.5 whenever delta < 1 - sqrt(2)/2 ~ 0.292 """ # substitution (Herrman and May) PR.<u, x, y> = PolynomialRing(ZZ) Q = PR.quotient(x*y + 1 - u) # u = xy + 1 polZ = Q(pol).lift() UU = XX*YY + 1 # x-shifts gg = [] for kk in range(mm + 1): for ii in range(mm - kk + 1): xshift = x^ii * modulus^(mm - kk) * polZ(u, x, y)^kk gg.append(xshift) gg.sort() # x-shifts list of monomials monomials = [] for polynomial in gg: for monomial in polynomial.monomials(): if monomial not in monomials: monomials.append(monomial) monomials.sort() # y-shifts (selected by Herrman and May) for jj in range(1, tt + 1): for kk in range(floor(mm/tt) * jj, mm + 1): yshift = y^jj * polZ(u, x, y)^kk * modulus^(mm - kk) yshift = Q(yshift).lift() gg.append(yshift) # substitution # y-shifts list of monomials for jj in range(1, tt + 1): for kk in range(floor(mm/tt) * jj, mm + 1): monomials.append(u^kk * y^jj) # construct lattice B nn = len(monomials) BB = Matrix(ZZ, nn) for ii in range(nn): BB[ii, 0] = gg[ii](0, 0, 0) for jj in range(1, ii + 1): if monomials[jj] in gg[ii].monomials(): BB[ii, jj] = gg[ii].monomial_coefficient(monomials[jj]) * monomials[jj](UU,XX,YY) # Prototype to reduce the lattice if helpful_only: # automatically remove BB = remove_unhelpful(BB, monomials, modulus^mm, nn-1) # reset dimension nn = BB.dimensions()[0] if nn == 0: print("failure") return 0,0 # check if vectors are helpful if debug: helpful_vectors(BB, modulus^mm) # check if determinant is correctly bounded det = BB.det() bound = modulus^(mm*nn) if det >= bound: print("We do not have det < bound. Solutions might not be found.") print("Try with highers m and t.") if debug: diff = (log(det) - log(bound)) / log(2) print("size det(L) - size e^(m*n) = ", floor(diff)) if strict: return -1, -1 else: print("det(L) < e^(m*n) (good! If a solution exists < N^delta, it will be found)") # display the lattice basis if debug: matrix_overview(BB, modulus^mm) # LLL if debug: print("optimizing basis of the lattice via LLL, this can take a long time") BB = BB.LLL() if debug: print("LLL is done!") # transform vector i & j -> polynomials 1 & 2 if debug: print("looking for independent vectors in the lattice") found_polynomials = False for pol1_idx in range(nn - 1): for pol2_idx in range(pol1_idx + 1, nn): # for i and j, create the two polynomials PR.<w,z> = PolynomialRing(ZZ) pol1 = pol2 = 0 for jj in range(nn): pol1 += monomials[jj](w*z+1,w,z) * BB[pol1_idx, jj] / monomials[jj](UU,XX,YY) pol2 += monomials[jj](w*z+1,w,z) * BB[pol2_idx, jj] / monomials[jj](UU,XX,YY) # resultant PR.<q> = PolynomialRing(ZZ) rr = pol1.resultant(pol2) # are these good polynomials? if rr.is_zero() or rr.monomials() == [1]: continue else: print("found them, using vectors", pol1_idx, "and", pol2_idx) found_polynomials = True break if found_polynomials: break if not found_polynomials: print("no independant vectors could be found. This should very rarely happen...") return 0, 0 rr = rr(q, q) # solutions soly = rr.roots() if len(soly) == 0: print("Your prediction (delta) is too small") return 0, 0 soly = soly[0][0] ss = pol1(q, soly) solx = ss.roots()[0][0] # return solx, soly def example(): ############################################ # How To Use This Script ########################################## # # The problem to solve (edit the following values) # # the modulus N = 0xb12746657c720a434861e9a4828b3c89a6b8d4a1bd921054e48d47124dbcc9cfcdcc39261c5e93817c167db818081613f57729e0039875c72a5ae1f0bc5ef7c933880c2ad528adbc9b1430003a491e460917b34c4590977df47772fab1ee0ab251f94065ab3004893fe1b2958008848b0124f22c4e75f60ed3889fb62e5ef4dcc247a3d6e23072641e62566cd96ee8114b227b8f498f9a578fc6f687d07acdbb523b6029c5bbeecd5efaf4c4d35304e5e6b5b95db0e89299529eb953f52ca3247d4cd03a15939e7d638b168fd00a1cb5b0cc5c2cc98175c1ad0b959c2ab2f17f917c0ccee8c3fe589b4cb441e817f75e575fc96a4fe7bfea897f57692b050d2b e = 0x9d0637faa46281b533e83cc37e1cf5626bd33f712cc1948622f10ec26f766fb37b9cd6c7a6e4b2c03bce0dd70d5a3a28b6b0c941d8792bc6a870568790ebcd30f40277af59e0fd3141e272c48f8e33592965997c7d93006c27bf3a2b8fb71831dfa939c0ba2c7569dd1b660efc6c8966e674fbe6e051811d92a802c789d895f356ceec9722d5a7b617d21b8aa42dd6a45de721953939a5a81b8dffc9490acd4f60b0c0475883ff7e2ab50b39b2deeedaefefffc52ae2e03f72756d9b4f7b6bd85b1a6764b31312bc375a2298b78b0263d492205d2a5aa7a227abaf41ab4ea8ce0e75728a5177fe90ace36fdc5dba53317bbf90e60a6f2311bb333bf55ba3245f # the hypothesis on the private exponent (the theoretical maximum is 0.292) delta = .18 # this means that d < N^delta # # Lattice (tweak those values) # # you should tweak this (after a first run), (e.g. increment it until a solution is found) m = 4 # size of the lattice (bigger the better/slower) # you need to be a lattice master to tweak these t = int((1-2*delta) * m) # optimization from Herrmann and May X = 2*floor(N^delta) # this _might_ be too much Y = floor(N^(1/2)) # correct if p, q are ~ same size # # Don't touch anything below # # Problem put in equation P.<x,y> = PolynomialRing(ZZ) A = int((N+1)/2) pol = 1 + x * (A + y) # # Find the solutions! # # Checking bounds if debug: print("=== checking values ===") print("* delta:", delta) print("* delta < 0.292", delta < 0.292) print("* size of e:", int(log(e)/log(2))) print("* size of N:", int(log(N)/log(2))) print("* m:", m, ", t:", t) # boneh_durfee if debug: print("=== running algorithm ===") start_time = time.time() solx, soly = boneh_durfee(pol, e, m, t, X, Y) # found a solution? if solx > 0: print("=== solution found ===") if False: print("x:", solx) print("y:", soly) d = int(pol(solx, soly) / e) print("private key found:", d) else: print("=== no solution was found ===") if debug: print(("=== %s seconds ===" % (time.time() - start_time))) if __name__ == "__main__": example() ``` > crypto{bon3h5_4tt4ck_i5_sr0ng3r_th4n_w13n3r5} ### Endless Emails Source code của chall đây ``` #!/usr/bin/env python3 from Crypto.Util.number import bytes_to_long, getPrime from secret import messages def RSA_encrypt(message): m = bytes_to_long(message) p = getPrime(1024) q = getPrime(1024) N = p * q e = 3 c = pow(m, e, N) return N, e, c for m in messages: N, e, c = RSA_encrypt(m) print(f"n = {N}") print(f"e = {e}") print(f"c = {c}") ``` Mình thấy có rất nhiều output gồm n, e, c. Mình thử dùng CRT với 3 bộ đầu tiên thì thấy không thu được gì, thế nên mình đã thử hết tất cả các trường hợp 3 bộ. Sau đó sẽ dùng CRT để xem cái nào căn bậc 3 sẽ trả về TRUE. ``` data = """ n = 14528915758150659907677315938876872514853653132820394367681510019000469589767908107293777996420037715293478868775354645306536953789897501630398061779084810058931494642860729799059325051840331449914529594113593835549493208246333437945551639983056810855435396444978249093419290651847764073607607794045076386643023306458718171574989185213684263628336385268818202054811378810216623440644076846464902798568705083282619513191855087399010760232112434412274701034094429954231366422968991322244343038458681255035356984900384509158858007713047428143658924970374944616430311056440919114824023838380098825914755712289724493770021 e = 3 c = 6965891612987861726975066977377253961837139691220763821370036576350605576485706330714192837336331493653283305241193883593410988132245791554283874785871849223291134571366093850082919285063130119121338290718389659761443563666214229749009468327825320914097376664888912663806925746474243439550004354390822079954583102082178617110721589392875875474288168921403550415531707419931040583019529612270482482718035497554779733578411057633524971870399893851589345476307695799567919550426417015815455141863703835142223300228230547255523815097431420381177861163863791690147876158039619438793849367921927840731088518955045807722225 n = 20463913454649855046677206889944639231694511458416906994298079596685813354570085475890888433776403011296145408951323816323011550738170573801417972453504044678801608709931200059967157605416809387753258251914788761202456830940944486915292626560515250805017229876565916349963923702612584484875113691057716315466239062005206014542088484387389725058070917118549621598629964819596412564094627030747720659155558690124005400257685883230881015636066183743516494701900125788836869358634031031172536767950943858472257519195392986989232477630794600444813136409000056443035171453870906346401936687214432176829528484662373633624123 e = 3 c = 5109363605089618816120178319361171115590171352048506021650539639521356666986308721062843132905170261025772850941702085683855336653472949146012700116070022531926476625467538166881085235022484711752960666438445574269179358850309578627747024264968893862296953506803423930414569834210215223172069261612934281834174103316403670168299182121939323001232617718327977313659290755318972603958579000300780685344728301503641583806648227416781898538367971983562236770576174308965929275267929379934367736694110684569576575266348020800723535121638175505282145714117112442582416208209171027273743686645470434557028336357172288865172 n = 19402640770593345339726386104915705450969517850985511418263141255686982818547710008822417349818201858549321868878490314025136645036980129976820137486252202687238348587398336652955435182090722844668488842986318211649569593089444781595159045372322540131250208258093613844753021272389255069398553523848975530563989367082896404719544411946864594527708058887475595056033713361893808330341623804367785721774271084389159493974946320359512776328984487126583015777989991635428744050868653379191842998345721260216953918203248167079072442948732000084754225272238189439501737066178901505257566388862947536332343196537495085729147 e = 3 c = 5603386396458228314230975500760833991383866638504216400766044200173576179323437058101562931430558738148852367292802918725271632845889728711316688681080762762324367273332764959495900563756768440309595248691744845766607436966468714038018108912467618638117493367675937079141350328486149333053000366933205635396038539236203203489974033629281145427277222568989469994178084357460160310598260365030056631222346691527861696116334946201074529417984624304973747653407317290664224507485684421999527164122395674469650155851869651072847303136621932989550786722041915603539800197077294166881952724017065404825258494318993054344153 n = 12005639978012754274325188681720834222130605634919280945697102906256738419912110187245315232437501890545637047506165123606573171374281507075652554737014979927883759915891863646221205835211640845714836927373844277878562666545230876640830141637371729405545509920889968046268135809999117856968692236742804637929866632908329522087977077849045608566911654234541526643235586433065170392920102840518192803854740398478305598092197183671292154743153130012885747243219372709669879863098708318993844005566984491622761795349455404952285937152423145150066181043576492305166964448141091092142224906843816547235826717179687198833961 e = 3 c = 1522280741383024774933280198410525846833410931417064479278161088248621390305797210285777845359812715909342595804742710152832168365433905718629465545306028275498667935929180318276445229415104842407145880223983428713335709038026249381363564625791656631137936935477777236936508600353416079028339774876425198789629900265348122040413865209592074731028757972968635601695468594123523892918747882221891834598896483393711851510479989203644477972694520237262271530260496342247355761992646827057846109181410462131875377404309983072358313960427035348425800940661373272947647516867525052504539561289941374722179778872627956360577 n = 17795451956221451086587651307408104001363221003775928432650752466563818944480119932209305765249625841644339021308118433529490162294175590972336954199870002456682453215153111182451526643055812311071588382409549045943806869173323058059908678022558101041630272658592291327387549001621625757585079662873501990182250368909302040015518454068699267914137675644695523752851229148887052774845777699287718342916530122031495267122700912518207571821367123013164125109174399486158717604851125244356586369921144640969262427220828940652994276084225196272504355264547588369516271460361233556643313911651916709471353368924621122725823 e = 3 c = 8752507806125480063647081749506966428026005464325535765874589376572431101816084498482064083887400646438977437273700004934257274516197148448425455243811009944321764771392044345410680448204581679548854193081394891841223548418812679441816502910830861271884276608891963388657558218620911858230760629700918375750796354647493524576614017731938584618983084762612414591830024113057983483156974095503392359946722756364412399187910604029583464521617256125933111786441852765229820406911991809039519015434793656710199153380699319611499255869045311421603167606551250174746275803467549814529124250122560661739949229005127507540805 n = 25252721057733555082592677470459355315816761410478159901637469821096129654501579313856822193168570733800370301193041607236223065376987811309968760580864569059669890823406084313841678888031103461972888346942160731039637326224716901940943571445217827960353637825523862324133203094843228068077462983941899571736153227764822122334838436875488289162659100652956252427378476004164698656662333892963348126931771536472674447932268282205545229907715893139346941832367885319597198474180888087658441880346681594927881517150425610145518942545293750127300041942766820911120196262215703079164895767115681864075574707999253396530263 e = 3 c = 23399624135645767243362438536844425089018405258626828336566973656156553220156563508607371562416462491581383453279478716239823054532476006642583363934314982675152824147243749715830794488268846671670287617324522740126594148159945137948643597981681529145611463534109482209520448640622103718682323158039797577387254265854218727476928164074249568031493984825273382959147078839665114417896463735635546290504843957780546550577300001452747760982468547756427137284830133305010038339400230477403836856663883956463830571934657200851598986174177386323915542033293658596818231793744261192870485152396793393026198817787033127061749 n = 19833203629283018227011925157509157967003736370320129764863076831617271290326613531892600790037451229326924414757856123643351635022817441101879725227161178559229328259469472961665857650693413215087493448372860837806619850188734619829580286541292997729705909899738951228555834773273676515143550091710004139734080727392121405772911510746025807070635102249154615454505080376920778703360178295901552323611120184737429513669167641846902598281621408629883487079110172218735807477275590367110861255756289520114719860000347219161944020067099398239199863252349401303744451903546571864062825485984573414652422054433066179558897 e = 3 c = 15239683995712538665992887055453717247160229941400011601942125542239446512492703769284448009141905335544729440961349343533346436084176947090230267995060908954209742736573986319254695570265339469489948102562072983996668361864286444602534666284339466797477805372109723178841788198177337648499899079471221924276590042183382182326518312979109378616306364363630519677884849945606288881683625944365927809405420540525867173639222696027472336981838588256771671910217553150588878434061862840893045763456457939944572192848992333115479951110622066173007227047527992906364658618631373790704267650950755276227747600169403361509144 """ data = data.split("\n") lst_n = [] lst_c = [] for i in data: if "n" in i: lst_n.append(int(i.replace("n = ",""))) if "c" in i: lst_c.append(int(i.replace("c = ",""))) print(lst_c) print(lst_n) ``` ``` from sage.all import* from itertools import combinations lst_c = [6965891612987861726975066977377253961837139691220763821370036576350605576485706330714192837336331493653283305241193883593410988132245791554283874785871849223291134571366093850082919285063130119121338290718389659761443563666214229749009468327825320914097376664888912663806925746474243439550004354390822079954583102082178617110721589392875875474288168921403550415531707419931040583019529612270482482718035497554779733578411057633524971870399893851589345476307695799567919550426417015815455141863703835142223300228230547255523815097431420381177861163863791690147876158039619438793849367921927840731088518955045807722225, 5109363605089618816120178319361171115590171352048506021650539639521356666986308721062843132905170261025772850941702085683855336653472949146012700116070022531926476625467538166881085235022484711752960666438445574269179358850309578627747024264968893862296953506803423930414569834210215223172069261612934281834174103316403670168299182121939323001232617718327977313659290755318972603958579000300780685344728301503641583806648227416781898538367971983562236770576174308965929275267929379934367736694110684569576575266348020800723535121638175505282145714117112442582416208209171027273743686645470434557028336357172288865172, 5603386396458228314230975500760833991383866638504216400766044200173576179323437058101562931430558738148852367292802918725271632845889728711316688681080762762324367273332764959495900563756768440309595248691744845766607436966468714038018108912467618638117493367675937079141350328486149333053000366933205635396038539236203203489974033629281145427277222568989469994178084357460160310598260365030056631222346691527861696116334946201074529417984624304973747653407317290664224507485684421999527164122395674469650155851869651072847303136621932989550786722041915603539800197077294166881952724017065404825258494318993054344153, 1522280741383024774933280198410525846833410931417064479278161088248621390305797210285777845359812715909342595804742710152832168365433905718629465545306028275498667935929180318276445229415104842407145880223983428713335709038026249381363564625791656631137936935477777236936508600353416079028339774876425198789629900265348122040413865209592074731028757972968635601695468594123523892918747882221891834598896483393711851510479989203644477972694520237262271530260496342247355761992646827057846109181410462131875377404309983072358313960427035348425800940661373272947647516867525052504539561289941374722179778872627956360577, 8752507806125480063647081749506966428026005464325535765874589376572431101816084498482064083887400646438977437273700004934257274516197148448425455243811009944321764771392044345410680448204581679548854193081394891841223548418812679441816502910830861271884276608891963388657558218620911858230760629700918375750796354647493524576614017731938584618983084762612414591830024113057983483156974095503392359946722756364412399187910604029583464521617256125933111786441852765229820406911991809039519015434793656710199153380699319611499255869045311421603167606551250174746275803467549814529124250122560661739949229005127507540805, 23399624135645767243362438536844425089018405258626828336566973656156553220156563508607371562416462491581383453279478716239823054532476006642583363934314982675152824147243749715830794488268846671670287617324522740126594148159945137948643597981681529145611463534109482209520448640622103718682323158039797577387254265854218727476928164074249568031493984825273382959147078839665114417896463735635546290504843957780546550577300001452747760982468547756427137284830133305010038339400230477403836856663883956463830571934657200851598986174177386323915542033293658596818231793744261192870485152396793393026198817787033127061749, 15239683995712538665992887055453717247160229941400011601942125542239446512492703769284448009141905335544729440961349343533346436084176947090230267995060908954209742736573986319254695570265339469489948102562072983996668361864286444602534666284339466797477805372109723178841788198177337648499899079471221924276590042183382182326518312979109378616306364363630519677884849945606288881683625944365927809405420540525867173639222696027472336981838588256771671910217553150588878434061862840893045763456457939944572192848992333115479951110622066173007227047527992906364658618631373790704267650950755276227747600169403361509144] lst_n = [14528915758150659907677315938876872514853653132820394367681510019000469589767908107293777996420037715293478868775354645306536953789897501630398061779084810058931494642860729799059325051840331449914529594113593835549493208246333437945551639983056810855435396444978249093419290651847764073607607794045076386643023306458718171574989185213684263628336385268818202054811378810216623440644076846464902798568705083282619513191855087399010760232112434412274701034094429954231366422968991322244343038458681255035356984900384509158858007713047428143658924970374944616430311056440919114824023838380098825914755712289724493770021, 20463913454649855046677206889944639231694511458416906994298079596685813354570085475890888433776403011296145408951323816323011550738170573801417972453504044678801608709931200059967157605416809387753258251914788761202456830940944486915292626560515250805017229876565916349963923702612584484875113691057716315466239062005206014542088484387389725058070917118549621598629964819596412564094627030747720659155558690124005400257685883230881015636066183743516494701900125788836869358634031031172536767950943858472257519195392986989232477630794600444813136409000056443035171453870906346401936687214432176829528484662373633624123, 19402640770593345339726386104915705450969517850985511418263141255686982818547710008822417349818201858549321868878490314025136645036980129976820137486252202687238348587398336652955435182090722844668488842986318211649569593089444781595159045372322540131250208258093613844753021272389255069398553523848975530563989367082896404719544411946864594527708058887475595056033713361893808330341623804367785721774271084389159493974946320359512776328984487126583015777989991635428744050868653379191842998345721260216953918203248167079072442948732000084754225272238189439501737066178901505257566388862947536332343196537495085729147, 12005639978012754274325188681720834222130605634919280945697102906256738419912110187245315232437501890545637047506165123606573171374281507075652554737014979927883759915891863646221205835211640845714836927373844277878562666545230876640830141637371729405545509920889968046268135809999117856968692236742804637929866632908329522087977077849045608566911654234541526643235586433065170392920102840518192803854740398478305598092197183671292154743153130012885747243219372709669879863098708318993844005566984491622761795349455404952285937152423145150066181043576492305166964448141091092142224906843816547235826717179687198833961, 17795451956221451086587651307408104001363221003775928432650752466563818944480119932209305765249625841644339021308118433529490162294175590972336954199870002456682453215153111182451526643055812311071588382409549045943806869173323058059908678022558101041630272658592291327387549001621625757585079662873501990182250368909302040015518454068699267914137675644695523752851229148887052774845777699287718342916530122031495267122700912518207571821367123013164125109174399486158717604851125244356586369921144640969262427220828940652994276084225196272504355264547588369516271460361233556643313911651916709471353368924621122725823, 25252721057733555082592677470459355315816761410478159901637469821096129654501579313856822193168570733800370301193041607236223065376987811309968760580864569059669890823406084313841678888031103461972888346942160731039637326224716901940943571445217827960353637825523862324133203094843228068077462983941899571736153227764822122334838436875488289162659100652956252427378476004164698656662333892963348126931771536472674447932268282205545229907715893139346941832367885319597198474180888087658441880346681594927881517150425610145518942545293750127300041942766820911120196262215703079164895767115681864075574707999253396530263, 19833203629283018227011925157509157967003736370320129764863076831617271290326613531892600790037451229326924414757856123643351635022817441101879725227161178559229328259469472961665857650693413215087493448372860837806619850188734619829580286541292997729705909899738951228555834773273676515143550091710004139734080727392121405772911510746025807070635102249154615454505080376920778703360178295901552323611120184737429513669167641846902598281621408629883487079110172218735807477275590367110861255756289520114719860000347219161944020067099398239199863252349401303744451903546571864062825485984573414652422054433066179558897] choices = [(x, y) for x, y in zip(lst_c, lst_n)] triples = list(combinations(choices, 3)) print(triples) ans = [] for i in triples: c = [x[0] for x in i] n = [x[1] for x in i] l = crt(c, n) l = pow(l, 1/3) if(l.is_integer()): print(bytes.fromhex(hex(l)[2:])) ``` > crypto{1f_y0u_d0nt_p4d_y0u_4r3_Vuln3rabl3} ## PRIMES PART 2 ### Infinite Descent Source code của chall sẽ như sau ``` import random from Crypto.Util.number import bytes_to_long, isPrime FLAG = b"crypto{???????????????????}" def getPrimes(bitsize): r = random.getrandbits(bitsize) p, q = r, r while not isPrime(p): p += random.getrandbits(bitsize//4) while not isPrime(q): q += random.getrandbits(bitsize//8) return p, q m = bytes_to_long(FLAG) p, q = getPrimes(2048) n = p * q e = 0x10001 c = pow(m, e, n) print(f"n = {n}") print(f"e = {e}") print(f"c = {c}") ``` Ta thấy rằng, ban đầu p và q đều bằng r, thế nhưng nếu chưa là số nguyên tố thì p và q sẽ cộng thêm 1 số nhỏ hơn. Thế nên, p và q sẽ rất gần nhau. Mình thử lấy thử 2 số p q thì sẽ thấy được ``` from gmpy2 import iroot p = 2472821142620574019830186301893550269217348973615161682839123365686833286218185919375748583753835314573675041708248293197071862365303277003452257786419780575704976455189074575818952641950066572636488531007468343917336049549777576371227790588540886976946928067638611533089826599659226222811809718780151634661236042917475169461434815000805260920821783771376771298168412750315093762852176130992606156043143166330732747127246383040897710070406271294750339982844642505220750453075739089254063543852221083290521949188996908542257340501676666356123431729225686209081590038743715108230514746279388032476992037937034839823131 q = 2472821142620574019830186301893550269217348973615161682839123365686833286218185919375748583753835314573675041708248293197071862365303277003452257786419780575704976455189074575818952641950066572636488531007468343917336049549777576371227790588540886976946928067638611533089826599659226222811809718780151634661236042917475169461434815000805260920821783771376771298168412750315093762852176130992606156043143166330732747127246383040897710070406271294750339982844634159308964759610469302551028504388758969047272579699134196924099063326032649063096442708019230510714578264458605661791405520843295913192449859941582014158713 n = p*q print(iroot(n,4)[0] > abs(p-q)) ``` Ta thấy được ``p - q < n^(1/4)`` --> Ta sẽ tấn công Fermat nha. Đọc hiểu thuật toán [Fermat](https://bitsdeep.com/posts/attacking-rsa-for-fun-and-ctf-points-part-2/) Sử dụng source code này để factor N nha ``` import sympy from sympy.ntheory.primetest import is_square def fermat(n): a = int(sympy.sqrt(n)) # Fermat's Algorithm b = (a*a) - n while not is_square(b): a += 1 b = (a*a) - n else: p = int(a - (sympy.sqrt(b))) q = n//p if p * q == n: return p,q else: return "No Luck" ``` Giờ ta có p và q rồi thì sẽ làm như bình thường thui nha :v: ``` import sympy from Crypto.Util.number import* from sympy.ntheory.primetest import is_square def fermat(n): a = int(sympy.sqrt(n)) # Fermat's Algorithm b = (a*a) - n while not is_square(b): a += 1 b = (a*a) - n else: p = int(a - (sympy.sqrt(b))) q = n//p if p * q == n: return p,q else: return "No Luck" n = 383347712330877040452238619329524841763392526146840572232926924642094891453979246383798913394114305368360426867021623649667024217266529000859703542590316063318592391925062014229671423777796679798747131250552455356061834719512365575593221216339005132464338847195248627639623487124025890693416305788160905762011825079336880567461033322240015771102929696350161937950387427696385850443727777996483584464610046380722736790790188061964311222153985614287276995741553706506834906746892708903948496564047090014307484054609862129530262108669567834726352078060081889712109412073731026030466300060341737504223822014714056413752165841749368159510588178604096191956750941078391415634472219765129561622344109769892244712668402761549412177892054051266761597330660545704317210567759828757156904778495608968785747998059857467440128156068391746919684258227682866083662345263659558066864109212457286114506228470930775092735385388316268663664139056183180238043386636254075940621543717531670995823417070666005930452836389812129462051771646048498397195157405386923446893886593048680984896989809135802276892911038588008701926729269812453226891776546037663583893625479252643042517196958990266376741676514631089466493864064316127648074609662749196545969926051 e = 65537 c = 98280456757136766244944891987028935843441533415613592591358482906016439563076150526116369842213103333480506705993633901994107281890187248495507270868621384652207697607019899166492132408348789252555196428608661320671877412710489782358282011364127799563335562917707783563681920786994453004763755404510541574502176243896756839917991848428091594919111448023948527766368304503100650379914153058191140072528095898576018893829830104362124927140555107994114143042266758709328068902664037870075742542194318059191313468675939426810988239079424823495317464035252325521917592045198152643533223015952702649249494753395100973534541766285551891859649320371178562200252228779395393974169736998523394598517174182142007480526603025578004665936854657294541338697513521007818552254811797566860763442604365744596444735991732790926343720102293453429936734206246109968817158815749927063561835274636195149702317415680401987150336994583752062565237605953153790371155918439941193401473271753038180560129784192800351649724465553733201451581525173536731674524145027931923204961274369826379325051601238308635192540223484055096203293400419816024111797903442864181965959247745006822690967920957905188441550106930799896292835287867403979631824085790047851383294389 p, q = (fermat(n)) phi = (p-1)*(q-1) d = inverse(e,phi) print(long_to_bytes(pow(c,d,n)).decode()) ``` Ngoài ra giờ tools nào cũng chơi được dạng này nên vất vào tool cho nhanh. > crypto{f3rm47_w45_4_g3n1u5} ### Marin's Secrets Source code của chall như sau: ``` import random from Crypto.Util.number import bytes_to_long, inverse from secret import secrets, flag def get_prime(secret): prime = 1 for _ in range(secret): prime = prime << 1 return prime - 1 random.shuffle(secrets) m = bytes_to_long(flag) p = get_prime(secrets[0]) q = get_prime(secrets[1]) n = p * q e = 0x10001 c = pow(m, e, n) print(f"n = {n}") print(f"e = {e}") print(f"c = {c}") ``` Ta thấy rằng, có 1 hàm get_prime lạ so với bình thường, mình phân tích hàm này nha. ``` p = 1 for i in range(10): p = p << 1 print(p) ``` Output là 1024 = 2^10. Hóa ra là hàm này sẽ là trả về 1 số nguyên tố có dạng là ``2^n - 1``. Sau thời gian tra google thì mình tìm được quả [link](https://en.wikipedia.org/wiki/Mersenne_prime) này Chall này ta sẽ bruteforce số lần chạy của vòng for, nếu thu được 2 số nhân với nhau bằng n thì sẽ trả về p và q. ``` from Crypto.Util.number import* def find_prime(n): p = 1 for i in range(3000): p = p << 1 q = 1 for j in range(3000): q = q << 1 if (p-1)*(q-1) == n: return (p-1,q-1) n = 658416274830184544125027519921443515789888264156074733099244040126213682497714032798116399288176502462829255784525977722903018714434309698108208388664768262754316426220651576623731617882923164117579624827261244506084274371250277849351631679441171018418018498039996472549893150577189302871520311715179730714312181456245097848491669795997289830612988058523968384808822828370900198489249243399165125219244753790779764466236965135793576516193213175061401667388622228362042717054014679032953441034021506856017081062617572351195418505899388715709795992029559042119783423597324707100694064675909238717573058764118893225111602703838080618565401139902143069901117174204252871948846864436771808616432457102844534843857198735242005309073939051433790946726672234643259349535186268571629077937597838801337973092285608744209951533199868228040004432132597073390363357892379997655878857696334892216345070227646749851381208554044940444182864026513709449823489593439017366358869648168238735087593808344484365136284219725233811605331815007424582890821887260682886632543613109252862114326372077785369292570900594814481097443781269562647303671428895764224084402259605109600363098950091998891375812839523613295667253813978434879172781217285652895469194181218343078754501694746598738215243769747956572555989594598180639098344891175879455994652382137038240166358066403475457 c = 400280463088930432319280359115194977582517363610532464295210669530407870753439127455401384569705425621445943992963380983084917385428631223046908837804126399345875252917090184158440305503817193246288672986488987883177380307377025079266030262650932575205141853413302558460364242355531272967481409414783634558791175827816540767545944534238189079030192843288596934979693517964655661507346729751987928147021620165009965051933278913952899114253301044747587310830419190623282578931589587504555005361571572561916866063458812965314474160499067525067495140150092119620928363007467390920130717521169105167963364154636472055084012592138570354390246779276003156184676298710746583104700516466091034510765027167956117869051938116457370384737440965109619578227422049806566060571831017610877072484262724789571076529586427405780121096546942812322324807145137017942266863534989082115189065560011841150908380937354301243153206428896320576609904361937035263985348984794208198892615898907005955403529470847124269512316191753950203794578656029324506688293446571598506042198219080325747328636232040936761788558421528960279832802127562115852304946867628316502959562274485483867481731149338209009753229463924855930103271197831370982488703456463385914801246828662212622006947380115549529820197355738525329885232170215757585685484402344437894981555179129287164971002033759724456 e = 65537 p, q = find_prime(n) d = inverse(e,(p-1)*(q-1)) print(long_to_bytes(pow(c,d,n)).decode()) ``` > crypto{Th3se_Pr1m3s_4r3_t00_r4r3} ### Fast Primes ``` import math import random from Crypto.Cipher import PKCS1_OAEP from Crypto.PublicKey import RSA from Crypto.Util.number import bytes_to_long, inverse from gmpy2 import is_prime FLAG = b"crypto{????????????}" primes = [] def sieve(maximum=10000): # In general Sieve of Sundaram, produces primes smaller # than (2*x + 2) for a number given number x. Since # we want primes smaller than maximum, we reduce maximum to half # This array is used to separate numbers of the form # i+j+2ij from others where 1 <= i <= j marked = [False]*(int(maximum/2)+1) # Main logic of Sundaram. Mark all numbers which # do not generate prime number by doing 2*i+1 for i in range(1, int((math.sqrt(maximum)-1)/2)+1): for j in range(((i*(i+1)) << 1), (int(maximum/2)+1), (2*i+1)): marked[j] = True # Since 2 is a prime number primes.append(2) # Print other primes. Remaining primes are of the # form 2*i + 1 such that marked[i] is false. for i in range(1, int(maximum/2)): if (marked[i] == False): primes.append(2*i + 1) def get_primorial(n): result = 1 for i in range(n): result = result * primes[i] return result def get_fast_prime(): M = get_primorial(40) while True: k = random.randint(2**28, 2**29-1) a = random.randint(2**20, 2**62-1) p = k * M + pow(e, a, M) if is_prime(p): return p sieve() e = 0x10001 m = bytes_to_long(FLAG) p = get_fast_prime() q = get_fast_prime() n = p * q phi = (p - 1) * (q - 1) d = inverse(e, phi) key = RSA.construct((n, e, d)) cipher = PKCS1_OAEP.new(key) ciphertext = cipher.encrypt(FLAG) assert cipher.decrypt(ciphertext) == FLAG exported = key.publickey().export_key() with open("key.pem", 'wb') as f: f.write(exported) with open('ciphertext.txt', 'w') as f: f.write(ciphertext.hex()) ``` Đây chính là dạng tấn công [ROCA](http://bitsdeep.com/posts/analysis-of-the-roca-vulnerability/) (Return of CopperSmith Attack). Bài này ta có thể dùng tool sẽ factor được N. Thế nhưng mình khuyên nghị nên dùng [yafu](https://sourceforge.net/projects/yafu/) vì nhiều bài factordb sẽ không sử dụng được. Mình factor ra rồi dùng code sau để thu được flag. ``` from Crypto.PublicKey import RSA from Crypto.Cipher import PKCS1_OAEP from Crypto.Util.number import long_to_bytes from factordb.factordb import FactorDB key = RSA.import_key(open("key.pem", "rb").read()) f = FactorDB(key.n) f.connect() factors = f.get_factor_list() if factors: p, q = factors phi = (p - 1) * (q - 1) d = pow(key.e, -1, phi) key_decrypt = RSA.construct((key.n, key.e, d)) cipher = PKCS1_OAEP.new(key_decrypt) hexc = 0x249d72cd1d287b1a15a3881f2bff5788bc4bf62c789f2df44d88aae805b54c9a94b8944c0ba798f70062b66160fee312b98879f1dd5d17b33095feb3c5830d28 c = long_to_bytes(hexc) m = cipher.decrypt(c) print(m.decode('utf-8')) ``` > crypto{p00R_3570n14} ### Ron was Wrong, Whit is Right Source code sẽ như sau ``` from Crypto.Cipher import PKCS1_OAEP from Crypto.PublicKey import RSA msg = "???" with open('21.pem') as f: key = RSA.importKey(f.read()) cipher = PKCS1_OAEP.new(key) ciphertext = cipher.encrypt(msg) with open('21.ciphertext', 'w') as f: f.write(ciphertext.hex()) ``` Unzip output ra thì ta thấy rằng có rất nhiều public key, ý tưởng là mình sẽ thử tìm ước chung lớn nhất của 2 n xem có ra giá trị p nào không. ``` from Crypto.PublicKey import RSA from Crypto.Cipher import PKCS1_OAEP from Crypto.Util.number import long_to_bytes from factordb.factordb import FactorDB from math import gcd from Crypto.Util.number import* all_pem = [] all_e = [] for i in range(1, 51): path = "./keys_and_messages/" + str(i) + ".pem" with open(path, "rb") as f: pem_data = f.read() all_pem.append(RSA.import_key(pem_data).n) all_e.append(RSA.import_key(pem_data).e) all_cipher = [] for i in range(1, 51): path = "./keys_and_messages/" + str(i) + ".ciphertext" with open(path, "r") as f: ciphertext_data = (bytes.fromhex(f.read())) all_cipher.append((ciphertext_data)) for i in range(len(all_cipher)): for j in range(len(all_cipher)): n_i, n_j = all_pem[i], all_pem[j] if gcd(n_i,n_j) != 1 and n_i != n_j: p = (gcd(n_i,n_j)) q = n_j // p assert p*q == n_j d = inverse(all_e[j], (p-1)*(q-1)) key = RSA.construct((n_j, all_e[j], d)) cipher = PKCS1_OAEP.new(key) print(cipher.decrypt(all_cipher[j])) ``` > crypto{3ucl1d_w0uld_b3_pr0ud} ### RSA Backdoor Viability Source code của chall như sau: ``` **import random from Crypto.Util.number import bytes_to_long, getPrime, isPrime FLAG = b"crypto{????????????????????????????????}" def get_complex_prime(): D = 427 while True: s = random.randint(2 ** 1020, 2 ** 1021 - 1) tmp = D * s ** 2 + 1 if tmp % 4 == 0 and isPrime((tmp // 4)): return tmp // 4 m = bytes_to_long(FLAG) p = get_complex_prime() q = getPrime(2048) n = p * q e = 0x10001 c = pow(m, e, n) print(f"n = {n}") print(f"e = {e}") print(f"c = {c}") ``` Chall này mình chưa học tại vì dính tới đường cong eliptics, nhưng factordb thì ra được p và q =))) Có p và q rùi thì tìm flag thui ``` from Crypto.Util.number import* p = 20365029276121374486239093637518056591173153560816088704974934225137631026021006278728172263067093375127799517021642683026453941892085549596415559632837140072587743305574479218628388191587060262263170430315761890303990233871576860551166162110565575088243122411840875491614571931769789173216896527668318434571140231043841883246745997474500176671926153616168779152400306313362477888262997093036136582318881633235376026276416829652885223234411339116362732590314731391770942433625992710475394021675572575027445852371400736509772725581130537614203735350104770971283827769016324589620678432160581245381480093375303381611323 q = 34857423162121791604235470898471761566115159084585269586007822559458774716277164882510358869476293939176287610274899509786736824461740603618598549945273029479825290459062370424657446151623905653632181678065975472968242822859926902463043730644958467921837687772906975274812905594211460094944271575698004920372905721798856429806040099698831471709774099003441111568843449452407542799327467944685630258748028875103444760152587493543799185646692684032460858150960790495575921455423185709811342689185127936111993248778962219413451258545863084403721135633428491046474540472029592613134125767864006495572504245538373207974181 n = p*q e = 65537 c = 608484617316138126443275660524263025508135383745665175433229598517433030003704261658172582370543758277685547533834085899541036156595489206369279739210904154716464595657421948607569920498815631503197235702333017824993576326860166652845334617579798536442066184953550975487031721085105757667800838172225947001224495126390587950346822978519677673568121595427827980195332464747031577431925937314209391433407684845797171187006586455012364702160988147108989822392986966689057906884691499234298351003666019957528738094330389775054485731448274595330322976886875528525229337512909952391041280006426003300720547721072725168500104651961970292771382390647751450445892361311332074663895375544959193148114635476827855327421812307562742481487812965210406231507524830889375419045542057858679609265389869332331811218601440373121797461318931976890674336807528107115423915152709265237590358348348716543683900084640921475797266390455366908727400038393697480363793285799860812451995497444221674390372255599514578194487523882038234487872223540513004734039135243849551315065297737535112525440094171393039622992561519170849962891645196111307537341194621689797282496281302297026025131743423205544193536699103338587843100187637572006174858230467771942700918388 print(long_to_bytes(pow(c,inverse(e,(p-1)*(q-1)),n))) ``` > crypto{I_want_to_Break_Square-free_4p-1}